summaryrefslogtreecommitdiff
path: root/src/mscorlib/src/System/Globalization/CompareInfo.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/mscorlib/src/System/Globalization/CompareInfo.cs')
-rw-r--r--src/mscorlib/src/System/Globalization/CompareInfo.cs897
1 files changed, 394 insertions, 503 deletions
diff --git a/src/mscorlib/src/System/Globalization/CompareInfo.cs b/src/mscorlib/src/System/Globalization/CompareInfo.cs
index 6c2230b66b..285a81d906 100644
--- a/src/mscorlib/src/System/Globalization/CompareInfo.cs
+++ b/src/mscorlib/src/System/Globalization/CompareInfo.cs
@@ -12,57 +12,28 @@
//
////////////////////////////////////////////////////////////////////////////
-namespace System.Globalization {
-
- //
- // We pass all of the sorting calls to the native side, preferrably to the OS to do
- // the actual work.
- //
-
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Reflection;
- using System.Runtime.Serialization;
- using System.Runtime.CompilerServices;
- using System.Runtime.ConstrainedExecution;
- using System.Runtime.InteropServices;
- using System.Runtime.Versioning;
- using System.Threading;
- using Microsoft.Win32;
- using System.Security;
- using System.Diagnostics;
- using System.Diagnostics.Contracts;
-
- //
- // Options can be used during string comparison.
- //
- // Native implementation (COMNlsInfo.cpp & SortingTable.cpp) relies on the values of these,
- // If you change the values below, be sure to change the values in native part as well.
- //
-
-
-[Serializable]
+using System.Reflection;
+using System.Diagnostics;
+using System.Diagnostics.Contracts;
+using System.Runtime.Serialization;
+
+namespace System.Globalization
+{
[Flags]
+ [Serializable]
public enum CompareOptions
{
- None = 0x00000000,
- IgnoreCase = 0x00000001,
- IgnoreNonSpace = 0x00000002,
- IgnoreSymbols = 0x00000004,
- IgnoreKanaType = 0x00000008, // ignore kanatype
- IgnoreWidth = 0x00000010, // ignore width
- OrdinalIgnoreCase = 0x10000000, // This flag can not be used with other flags.
- StringSort = 0x20000000, // use string sort method
- Ordinal = 0x40000000, // This flag can not be used with other flags.
-
- // StopOnNull = 0x10000000,
-
- // StopOnNull is defined in SortingTable.h, but we didn't enable this option here.
- // Do not use this value for other flags accidentally.
+ None = 0x00000000,
+ IgnoreCase = 0x00000001,
+ IgnoreNonSpace = 0x00000002,
+ IgnoreSymbols = 0x00000004,
+ IgnoreKanaType = 0x00000008, // ignore kanatype
+ IgnoreWidth = 0x00000010, // ignore width
+ OrdinalIgnoreCase = 0x10000000, // This flag can not be used with other flags.
+ StringSort = 0x20000000, // use string sort method
+ Ordinal = 0x40000000, // This flag can not be used with other flags.
}
-
[Serializable]
public partial class CompareInfo : IDeserializationCallback
{
@@ -81,6 +52,11 @@ namespace System.Globalization {
~(CompareOptions.IgnoreCase | CompareOptions.IgnoreSymbols | CompareOptions.IgnoreNonSpace |
CompareOptions.IgnoreWidth | CompareOptions.IgnoreKanaType);
+ // Mask used to check if we have the right flags.
+ private const CompareOptions ValidSortkeyCtorMaskOffFlags =
+ ~(CompareOptions.IgnoreCase | CompareOptions.IgnoreSymbols | CompareOptions.IgnoreNonSpace |
+ CompareOptions.IgnoreWidth | CompareOptions.IgnoreKanaType | CompareOptions.StringSort);
+
//
// CompareInfos have an interesting identity. They are attached to the locale that created them,
// ie: en-US would have an en-US sort. For haw-US (custom), then we serialize it as haw-US.
@@ -88,33 +64,21 @@ namespace System.Globalization {
// locale, which is what SCOMPAREINFO does.
[OptionalField(VersionAdded = 2)]
- private String m_name; // The name used to construct this CompareInfo
-
+ private string _name; // The name used to construct this CompareInfo
[NonSerialized]
- private String m_sortName; // The name that defines our behavior
+ private string _sortName; // The name that defines our behavior
- [NonSerialized]
- private IntPtr m_dataHandle;
+ [OptionalField(VersionAdded = 3)]
+ private SortVersion _sortVersion;
- [NonSerialized]
- private IntPtr m_handleOrigin;
+ // _invariantMode is defined for the perf reason as accessing the instance field is faster than access the static property GlobalizationMode.Invariant
+ [NonSerialized]
+ private readonly bool _invariantMode = GlobalizationMode.Invariant;
- ////////////////////////////////////////////////////////////////////////
- //
- // CompareInfo Constructor
- //
- //
- ////////////////////////////////////////////////////////////////////////
- // Constructs an instance that most closely corresponds to the NLS locale
- // identifier.
internal CompareInfo(CultureInfo culture)
{
- this.m_name = culture.m_name;
- this.m_sortName = culture.SortName;
-
- IntPtr handleOrigin;
- this.m_dataHandle = InternalInitSortHandle(m_sortName, out handleOrigin);
- this.m_handleOrigin = handleOrigin;
+ _name = culture._name;
+ InitSort(culture);
}
/*=================================GetCompareInfo==========================
@@ -122,49 +86,52 @@ namespace System.Globalization {
** Warning: The assembly versioning mechanism is dead!
**Returns: The CompareInfo for the specified culture.
**Arguments:
- ** culture the ID of the culture
+ ** culture the ID of the culture
** assembly the assembly which contains the sorting table.
**Exceptions:
** ArugmentNullException when the assembly is null
** ArgumentException if culture is invalid.
============================================================================*/
-#if FEATURE_USE_LCID
// Assembly constructor should be deprecated, we don't act on the assembly information any more
- public static CompareInfo GetCompareInfo(int culture, Assembly assembly){
+ public static CompareInfo GetCompareInfo(int culture, Assembly assembly)
+ {
// Parameter checking.
- if (assembly == null) {
+ if (assembly == null)
+ {
throw new ArgumentNullException(nameof(assembly));
}
- if (assembly!=typeof(Object).Module.Assembly) {
- throw new ArgumentException(Environment.GetResourceString("Argument_OnlyMscorlib"));
+ if (assembly != typeof(Object).Module.Assembly)
+ {
+ throw new ArgumentException(SR.Argument_OnlyMscorlib);
}
Contract.EndContractBlock();
return GetCompareInfo(culture);
}
-#endif
-
/*=================================GetCompareInfo==========================
**Action: Get the CompareInfo constructed from the data table in the specified assembly for the specified culture.
** The purpose of this method is to provide version for CompareInfo tables.
**Returns: The CompareInfo for the specified culture.
**Arguments:
- ** name the name of the culture
- ** assembly the assembly which contains the sorting table.
+ ** name the name of the culture
+ ** assembly the assembly which contains the sorting table.
**Exceptions:
** ArugmentNullException when the assembly is null
** ArgumentException if name is invalid.
============================================================================*/
// Assembly constructor should be deprecated, we don't act on the assembly information any more
- public static CompareInfo GetCompareInfo(String name, Assembly assembly){
- if (name == null || assembly == null) {
+ public static CompareInfo GetCompareInfo(string name, Assembly assembly)
+ {
+ if (name == null || assembly == null)
+ {
throw new ArgumentNullException(name == null ? nameof(name) : nameof(assembly));
}
Contract.EndContractBlock();
- if (assembly!=typeof(Object).Module.Assembly) {
- throw new ArgumentException(Environment.GetResourceString("Argument_OnlyMscorlib"));
+ if (assembly != typeof(Object).Module.Assembly)
+ {
+ throw new ArgumentException(SR.Argument_OnlyMscorlib);
}
return GetCompareInfo(name);
@@ -179,20 +146,17 @@ namespace System.Globalization {
**Exceptions:
** ArgumentException if culture is invalid.
============================================================================*/
-
-#if FEATURE_USE_LCID
// People really shouldn't be calling LCID versions, no custom support
public static CompareInfo GetCompareInfo(int culture)
{
if (CultureData.IsCustomCultureId(culture))
{
// Customized culture cannot be created by the LCID.
- throw new ArgumentException(Environment.GetResourceString("Argument_CustomCultureCannotBePassedByNumber", nameof(culture)));
+ throw new ArgumentException(SR.Argument_CustomCultureCannotBePassedByNumber, nameof(culture));
}
return CultureInfo.GetCultureInfo(culture).CompareInfo;
}
-#endif
/*=================================GetCompareInfo==========================
**Action: Get the CompareInfo for the specified culture.
@@ -203,7 +167,7 @@ namespace System.Globalization {
** ArgumentException if name is invalid.
============================================================================*/
- public static CompareInfo GetCompareInfo(String name)
+ public static CompareInfo GetCompareInfo(string name)
{
if (name == null)
{
@@ -214,64 +178,51 @@ namespace System.Globalization {
return CultureInfo.GetCultureInfo(name).CompareInfo;
}
- public static bool IsSortable(char ch) {
- return(IsSortable(ch.ToString()));
+ public static unsafe bool IsSortable(char ch)
+ {
+ if (GlobalizationMode.Invariant)
+ {
+ return true;
+ }
+ char *pChar = &ch;
+ return IsSortable(pChar, 1);
}
- public static bool IsSortable(String text) {
- if (text == null) {
+ public static unsafe bool IsSortable(string text)
+ {
+ if (text == null)
+ {
// A null param is invalid here.
throw new ArgumentNullException(nameof(text));
}
- if (0 == text.Length) {
+ if (text.Length == 0)
+ {
// A zero length string is not invalid, but it is also not sortable.
- return(false);
+ return (false);
}
- CompareInfo c = CultureInfo.InvariantCulture.CompareInfo;
-
- return (InternalIsSortable(c.m_dataHandle, c.m_handleOrigin, c.m_sortName, text, text.Length));
+ if (GlobalizationMode.Invariant)
+ {
+ return true;
+ }
+
+ fixed (char *pChar = text)
+ {
+ return IsSortable(pChar, text.Length);
+ }
}
-#region Serialization
- // the following fields are defined to keep the compatibility with Whidbey.
- // don't change/remove the names/types of these fields.
-#if FEATURE_USE_LCID
- [OptionalField(VersionAdded = 1)]
- private int win32LCID; // mapped sort culture id of this instance
- private int culture; // the culture ID used to create this instance.
-#endif
[OnDeserializing]
private void OnDeserializing(StreamingContext ctx)
{
- this.m_name = null;
+ _name = null;
}
- private void OnDeserialized()
+ void IDeserializationCallback.OnDeserialization(Object sender)
{
- CultureInfo ci;
- // If we didn't have a name, use the LCID
- if (this.m_name == null)
- {
-#if FEATURE_USE_LCID
- // From whidbey, didn't have a name
- ci = CultureInfo.GetCultureInfo(this.culture);
- this.m_name = ci.m_name;
- this.m_sortName = ci.SortName;
-#endif
- }
- else
- {
- ci = CultureInfo.GetCultureInfo(m_name);
- this.m_sortName = ci.SortName;
- }
-
- IntPtr handleOrigin;
- this.m_dataHandle = InternalInitSortHandle(m_sortName, out handleOrigin);
- this.m_handleOrigin = handleOrigin;
-
+ OnDeserialized();
}
[OnDeserialized]
@@ -280,23 +231,16 @@ namespace System.Globalization {
OnDeserialized();
}
- [OnSerializing]
- private void OnSerializing(StreamingContext ctx)
- {
-#if FEATURE_USE_LCID
- // This is merely for serialization compatibility with Whidbey/Orcas, it can go away when we don't want that compat any more.
- culture = CultureInfo.GetCultureInfo(this.Name).LCID; // This is the lcid of the constructing culture (still have to dereference to get target sort)
- Debug.Assert(m_name != null, "CompareInfo.OnSerializing - expected m_name to be set already");
-#endif
- }
-
- void IDeserializationCallback.OnDeserialization(Object sender)
+ private void OnDeserialized()
{
- OnDeserialized();
+ if (_name != null)
+ {
+ InitSort(CultureInfo.GetCultureInfo(_name));
+ }
}
-#endregion Serialization
-
+ [OnSerializing]
+ private void OnSerializing(StreamingContext ctx) { }
///////////////////////////----- Name -----/////////////////////////////////
//
@@ -311,65 +255,20 @@ namespace System.Globalization {
//
////////////////////////////////////////////////////////////////////////
- public virtual String Name
+ public virtual string Name
{
get
{
- Debug.Assert(m_name != null, "CompareInfo.Name Expected m_name to be set");
- return (m_sortName);
- }
- }
+ Debug.Assert(_name != null, "CompareInfo.Name Expected _name to be set");
+ if (_name == "zh-CHT" || _name == "zh-CHS")
+ {
+ return _name;
+ }
- // These flags are used in the native Win32. so we need to map the managed options to those flags
- private const int LINGUISTIC_IGNORECASE = 0x00000010; // linguistically appropriate 'ignore case'
- private const int NORM_IGNORECASE = 0x00000001; // Ignores case. (use LINGUISTIC_IGNORECASE instead)
- private const int NORM_IGNOREKANATYPE = 0x00010000; // Does not differentiate between Hiragana and Katakana characters. Corresponding Hiragana and Katakana will compare as equal.
- private const int LINGUISTIC_IGNOREDIACRITIC = 0x00000020; // linguistically appropriate 'ignore nonspace'
- private const int NORM_IGNORENONSPACE = 0x00000002; // Ignores nonspacing. This flag also removes Japanese accent characters. (use LINGUISTIC_IGNOREDIACRITIC instead)
- private const int NORM_IGNORESYMBOLS = 0x00000004; // Ignores symbols.
- private const int NORM_IGNOREWIDTH = 0x00020000; // Does not differentiate between a single-byte character and the same character as a double-byte character.
- private const int SORT_STRINGSORT = 0x00001000; // Treats punctuation the same as symbols.
- private const int COMPARE_OPTIONS_ORDINAL = 0x40000000; // Ordinal (handled by Comnlsinfo)
- internal const int NORM_LINGUISTIC_CASING = 0x08000000; // use linguistic rules for casing
-
-
- private const int RESERVED_FIND_ASCII_STRING = 0x20000000; // This flag used only to tell the sorting DLL can assume the string characters are in ASCII.
-
- [Pure]
- internal static int GetNativeCompareFlags(CompareOptions options)
- {
- // some NLS VM functions can handle COMPARE_OPTIONS_ORDINAL
- // in which case options should be simply cast to int instead of using this function
- // Does not look like the best approach to me but for now I am going to leave it as it is
- Debug.Assert(options != CompareOptions.OrdinalIgnoreCase, "[CompareInfo.GetNativeCompareFlags]CompareOptions.OrdinalIgnoreCase should be handled separately");
-
- // Use "linguistic casing" by default (load the culture's casing exception tables)
- int nativeCompareFlags = NORM_LINGUISTIC_CASING;
-
- if ((options & CompareOptions.IgnoreCase) != 0) { nativeCompareFlags |= NORM_IGNORECASE; }
- if ((options & CompareOptions.IgnoreKanaType) != 0) { nativeCompareFlags |= NORM_IGNOREKANATYPE; }
- if ((options & CompareOptions.IgnoreNonSpace) != 0) { nativeCompareFlags |= NORM_IGNORENONSPACE; }
- if ((options & CompareOptions.IgnoreSymbols) != 0) { nativeCompareFlags |= NORM_IGNORESYMBOLS; }
- if ((options & CompareOptions.IgnoreWidth) != 0) { nativeCompareFlags |= NORM_IGNOREWIDTH; }
- if ((options & CompareOptions.StringSort) != 0) { nativeCompareFlags |= SORT_STRINGSORT; }
-
- // Suffix & Prefix shouldn't use this, make sure to turn off the NORM_LINGUISTIC_CASING flag
- if (options == CompareOptions.Ordinal) { nativeCompareFlags = COMPARE_OPTIONS_ORDINAL; }
-
- Debug.Assert(((options & ~(CompareOptions.IgnoreCase |
- CompareOptions.IgnoreKanaType |
- CompareOptions.IgnoreNonSpace |
- CompareOptions.IgnoreSymbols |
- CompareOptions.IgnoreWidth |
- CompareOptions.StringSort)) == 0) ||
- (options == CompareOptions.Ordinal), "[CompareInfo.GetNativeCompareFlags]Expected all flags to be handled");
-
- Debug.Assert((nativeCompareFlags & RESERVED_FIND_ASCII_STRING) == 0, "[CompareInfo.GetNativeCompareFlags] RESERVED_FIND_ASCII_STRING shouldn't be set here");
-
- return nativeCompareFlags;
+ return _sortName;
+ }
}
-
////////////////////////////////////////////////////////////////////////
//
// Compare
@@ -381,14 +280,13 @@ namespace System.Globalization {
//
////////////////////////////////////////////////////////////////////////
-
- public virtual int Compare(String string1, String string2)
+ public virtual int Compare(string string1, string string2)
{
return (Compare(string1, string2, CompareOptions.None));
}
- public unsafe virtual int Compare(String string1, String string2, CompareOptions options){
-
+ public unsafe virtual int Compare(string string1, string string2, CompareOptions options)
+ {
if (options == CompareOptions.OrdinalIgnoreCase)
{
return String.Compare(string1, string2, StringComparison.OrdinalIgnoreCase);
@@ -399,29 +297,41 @@ namespace System.Globalization {
{
if (options != CompareOptions.Ordinal)
{
- throw new ArgumentException(Environment.GetResourceString("Argument_CompareOptionOrdinal"), nameof(options));
- }
+ throw new ArgumentException(SR.Argument_CompareOptionOrdinal, nameof(options));
+ }
+
return String.CompareOrdinal(string1, string2);
- }
+ }
if ((options & ValidCompareMaskOffFlags) != 0)
{
- throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), nameof(options));
+ throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options));
}
//Our paradigm is that null sorts less than any other string and
//that two nulls sort as equal.
- if (string1 == null) {
- if (string2 == null) {
+ if (string1 == null)
+ {
+ if (string2 == null)
+ {
return (0); // Equal
}
return (-1); // null < non-null
}
- if (string2 == null) {
+ if (string2 == null)
+ {
return (1); // non-null > null
}
- return InternalCompareString(m_dataHandle, m_handleOrigin, m_sortName, string1, 0, string1.Length, string2, 0, string2.Length, GetNativeCompareFlags(options));
+ if (_invariantMode)
+ {
+ if ((options & CompareOptions.IgnoreCase) != 0)
+ return CompareOrdinalIgnoreCase(string1, 0, string1.Length, string2, 0, string2.Length);
+
+ return String.CompareOrdinal(string1, string2);
+ }
+
+ return CompareString(string1, 0, string1.Length, string2, 0, string2.Length, options);
}
@@ -438,63 +348,63 @@ namespace System.Globalization {
////////////////////////////////////////////////////////////////////////
- public unsafe virtual int Compare(String string1, int offset1, int length1, String string2, int offset2, int length2)
+ public unsafe virtual int Compare(string string1, int offset1, int length1, string string2, int offset2, int length2)
{
return Compare(string1, offset1, length1, string2, offset2, length2, 0);
}
- public unsafe virtual int Compare(String string1, int offset1, String string2, int offset2, CompareOptions options)
+ public virtual int Compare(string string1, int offset1, string string2, int offset2, CompareOptions options)
{
- return Compare(string1, offset1, string1 == null ? 0 : string1.Length-offset1,
- string2, offset2, string2 == null ? 0 : string2.Length-offset2, options);
+ return Compare(string1, offset1, string1 == null ? 0 : string1.Length - offset1,
+ string2, offset2, string2 == null ? 0 : string2.Length - offset2, options);
}
- public unsafe virtual int Compare(String string1, int offset1, String string2, int offset2)
+ public virtual int Compare(string string1, int offset1, string string2, int offset2)
{
return Compare(string1, offset1, string2, offset2, 0);
}
- public unsafe virtual int Compare(String string1, int offset1, int length1, String string2, int offset2, int length2, CompareOptions options)
+ public virtual int Compare(string string1, int offset1, int length1, string string2, int offset2, int length2, CompareOptions options)
{
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 (length1 > length2 ? 1 : -1);
return (result);
}
// Verify inputs
if (length1 < 0 || length2 < 0)
{
- throw new ArgumentOutOfRangeException((length1 < 0) ? nameof(length1) : nameof(length2), Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum"));
+ throw new ArgumentOutOfRangeException((length1 < 0) ? nameof(length1) : nameof(length2), SR.ArgumentOutOfRange_NeedPosNum);
}
if (offset1 < 0 || offset2 < 0)
{
- throw new ArgumentOutOfRangeException((offset1 < 0) ? nameof(offset1) : nameof(offset2), Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum"));
+ throw new ArgumentOutOfRangeException((offset1 < 0) ? nameof(offset1) : nameof(offset2), SR.ArgumentOutOfRange_NeedPosNum);
}
if (offset1 > (string1 == null ? 0 : string1.Length) - length1)
{
- throw new ArgumentOutOfRangeException(nameof(string1), Environment.GetResourceString("ArgumentOutOfRange_OffsetLength"));
+ throw new ArgumentOutOfRangeException(nameof(string1), SR.ArgumentOutOfRange_OffsetLength);
}
if (offset2 > (string2 == null ? 0 : string2.Length) - length2)
{
- throw new ArgumentOutOfRangeException(nameof(string2), Environment.GetResourceString("ArgumentOutOfRange_OffsetLength"));
+ throw new ArgumentOutOfRangeException(nameof(string2), SR.ArgumentOutOfRange_OffsetLength);
}
if ((options & CompareOptions.Ordinal) != 0)
{
if (options != CompareOptions.Ordinal)
{
- throw new ArgumentException(Environment.GetResourceString("Argument_CompareOptionOrdinal"),
+ throw new ArgumentException(SR.Argument_CompareOptionOrdinal,
nameof(options));
}
}
else if ((options & ValidCompareMaskOffFlags) != 0)
{
- throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), nameof(options));
+ throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options));
}
//
@@ -515,12 +425,89 @@ namespace System.Globalization {
if (options == CompareOptions.Ordinal)
{
- return string.CompareOrdinalHelper(string1, offset1, length1, string2, offset2, length2);
+ return CompareOrdinal(string1, offset1, length1,
+ string2, offset2, length2);
+ }
+
+ if (_invariantMode)
+ {
+ if ((options & CompareOptions.IgnoreCase) != 0)
+ return CompareOrdinalIgnoreCase(string1, offset1, length1, string2, offset2, length2);
+
+ return CompareOrdinal(string1, offset1, length1, string2, offset2, length2);
+ }
+
+ return CompareString(string1, offset1, length1,
+ string2, 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);
+ }
+
+ //
+ // CompareOrdinalIgnoreCase compare two string ordinally with ignoring the case.
+ // it assumes the strings are Ascii string till we hit non Ascii character in strA or strB and then we continue the comparison by
+ // calling the OS.
+ //
+ internal static unsafe int CompareOrdinalIgnoreCase(string strA, int indexA, int lengthA, string strB, int indexB, int lengthB)
+ {
+ Debug.Assert(indexA + lengthA <= strA.Length);
+ Debug.Assert(indexB + lengthB <= strB.Length);
+
+ int length = Math.Min(lengthA, lengthB);
+ int range = length;
+
+ fixed (char* ap = strA) fixed (char* bp = strB)
+ {
+ char* a = ap + indexA;
+ char* b = bp + indexB;
+
+ // in InvariantMode we support all range and not only the ascii characters.
+ char maxChar = (char) (GlobalizationMode.Invariant ? 0xFFFF : 0x80);
+
+ while (length != 0 && (*a <= maxChar) && (*b <= maxChar))
+ {
+ int charA = *a;
+ int charB = *b;
+
+ if (charA == charB)
+ {
+ a++; b++;
+ length--;
+ continue;
+ }
+
+ // uppercase both chars - notice that we need just one compare per char
+ if ((uint)(charA - 'a') <= (uint)('z' - 'a')) charA -= 0x20;
+ if ((uint)(charB - 'a') <= (uint)('z' - 'a')) charB -= 0x20;
+
+ // Return the (case-insensitive) difference between them.
+ if (charA != charB)
+ return charA - charB;
+
+ // Next char
+ a++; b++;
+ length--;
+ }
+
+ if (length == 0)
+ return lengthA - lengthB;
+
+ Debug.Assert(!GlobalizationMode.Invariant);
+
+ range -= length;
+
+ return CompareStringOrdinalIgnoreCase(a, lengthA - range, b, lengthB - range);
}
- return InternalCompareString(this.m_dataHandle, this.m_handleOrigin, this.m_sortName,
- string1, offset1, length1,
- string2, offset2, length2,
- GetNativeCompareFlags(options));
}
////////////////////////////////////////////////////////////////////////
@@ -531,22 +518,25 @@ namespace System.Globalization {
// String.Empty, true is returned.
//
////////////////////////////////////////////////////////////////////////
-
-
- public unsafe virtual bool IsPrefix(String source, String prefix, CompareOptions options)
+ public virtual bool IsPrefix(string source, string prefix, CompareOptions options)
{
- if (source == null || prefix == null) {
+ if (source == null || prefix == null)
+ {
throw new ArgumentNullException((source == null ? nameof(source) : nameof(prefix)),
- Environment.GetResourceString("ArgumentNull_String"));
+ SR.ArgumentNull_String);
}
Contract.EndContractBlock();
- int prefixLen = prefix.Length;
- if (prefixLen == 0)
+ if (prefix.Length == 0)
{
return (true);
}
+ if (source.Length == 0)
+ {
+ return false;
+ }
+
if (options == CompareOptions.OrdinalIgnoreCase)
{
return source.StartsWith(prefix, StringComparison.OrdinalIgnoreCase);
@@ -557,26 +547,24 @@ namespace System.Globalization {
return source.StartsWith(prefix, StringComparison.Ordinal);
}
- if ((options & ValidIndexMaskOffFlags) != 0) {
- throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), nameof(options));
+ if ((options & ValidIndexMaskOffFlags) != 0)
+ {
+ throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options));
}
+ if (_invariantMode)
+ {
+ return source.StartsWith(prefix, (options & CompareOptions.IgnoreCase) != 0 ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
+ }
- // to let the sorting DLL do the call optimization in case of Ascii strings, we check if the strings are in Ascii and then send the flag RESERVED_FIND_ASCII_STRING to
- // the sorting DLL API SortFindString so sorting DLL don't have to check if the string is Ascii with every call to SortFindString.
-
- return (InternalFindNLSStringEx(
- m_dataHandle, m_handleOrigin, m_sortName,
- GetNativeCompareFlags(options) | Win32Native.FIND_STARTSWITH | ((source.IsAscii() && prefix.IsAscii()) ? RESERVED_FIND_ASCII_STRING : 0),
- source, source.Length, 0, prefix, prefix.Length) > -1);
+ return StartsWith(source, prefix, options);
}
- public virtual bool IsPrefix(String source, String prefix)
+ public virtual bool IsPrefix(string source, string prefix)
{
return (IsPrefix(source, prefix, 0));
}
-
////////////////////////////////////////////////////////////////////////
//
// IsSuffix
@@ -585,44 +573,50 @@ namespace System.Globalization {
// String.Empty, true is returned.
//
////////////////////////////////////////////////////////////////////////
-
-
- public unsafe virtual bool IsSuffix(String source, String suffix, CompareOptions options)
+ public virtual bool IsSuffix(string source, string suffix, CompareOptions options)
{
- if (source == null || suffix == null) {
+ if (source == null || suffix == null)
+ {
throw new ArgumentNullException((source == null ? nameof(source) : nameof(suffix)),
- Environment.GetResourceString("ArgumentNull_String"));
+ SR.ArgumentNull_String);
}
Contract.EndContractBlock();
- int suffixLen = suffix.Length;
- if (suffixLen == 0)
+ if (suffix.Length == 0)
{
return (true);
}
- if (options == CompareOptions.OrdinalIgnoreCase) {
+ if (source.Length == 0)
+ {
+ return false;
+ }
+
+ if (options == CompareOptions.OrdinalIgnoreCase)
+ {
return source.EndsWith(suffix, StringComparison.OrdinalIgnoreCase);
}
- if (options == CompareOptions.Ordinal) {
+ if (options == CompareOptions.Ordinal)
+ {
return source.EndsWith(suffix, StringComparison.Ordinal);
}
- if ((options & ValidIndexMaskOffFlags) != 0) {
- throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), nameof(options));
+ if ((options & ValidIndexMaskOffFlags) != 0)
+ {
+ throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options));
+ }
+
+ if (_invariantMode)
+ {
+ return source.EndsWith(suffix, (options & CompareOptions.IgnoreCase) != 0 ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
}
- // to let the sorting DLL do the call optimization in case of Ascii strings, we check if the strings are in Ascii and then send the flag RESERVED_FIND_ASCII_STRING to
- // the sorting DLL API SortFindString so sorting DLL don't have to check if the string is Ascii with every call to SortFindString.
- return InternalFindNLSStringEx(
- m_dataHandle, m_handleOrigin, m_sortName,
- GetNativeCompareFlags(options) | Win32Native.FIND_ENDSWITH | ((source.IsAscii() && suffix.IsAscii()) ? RESERVED_FIND_ASCII_STRING : 0),
- source, source.Length, source.Length - 1, suffix, suffix.Length) >= 0;
+ return EndsWith(source, suffix, options);
}
- public virtual bool IsSuffix(String source, String suffix)
+ public virtual bool IsSuffix(string source, string suffix)
{
return (IsSuffix(source, suffix, 0));
}
@@ -641,9 +635,9 @@ namespace System.Globalization {
////////////////////////////////////////////////////////////////////////
- public unsafe virtual int IndexOf(String source, char value)
+ public virtual int IndexOf(string source, char value)
{
- if (source==null)
+ if (source == null)
throw new ArgumentNullException(nameof(source));
Contract.EndContractBlock();
@@ -651,9 +645,9 @@ namespace System.Globalization {
}
- public unsafe virtual int IndexOf(String source, String value)
+ public virtual int IndexOf(string source, string value)
{
- if (source==null)
+ if (source == null)
throw new ArgumentNullException(nameof(source));
Contract.EndContractBlock();
@@ -661,9 +655,9 @@ namespace System.Globalization {
}
- public unsafe virtual int IndexOf(String source, char value, CompareOptions options)
+ public virtual int IndexOf(string source, char value, CompareOptions options)
{
- if (source==null)
+ if (source == null)
throw new ArgumentNullException(nameof(source));
Contract.EndContractBlock();
@@ -671,17 +665,16 @@ namespace System.Globalization {
}
- public unsafe virtual int IndexOf(String source, String value, CompareOptions options)
+ public virtual int IndexOf(string source, string value, CompareOptions options)
{
- if (source==null)
+ if (source == null)
throw new ArgumentNullException(nameof(source));
Contract.EndContractBlock();
return IndexOf(source, value, 0, source.Length, options);
}
-
- public unsafe virtual int IndexOf(String source, char value, int startIndex)
+ public virtual int IndexOf(string source, char value, int startIndex)
{
if (source == null)
throw new ArgumentNullException(nameof(source));
@@ -690,8 +683,7 @@ namespace System.Globalization {
return IndexOf(source, value, startIndex, source.Length - startIndex, CompareOptions.None);
}
-
- public unsafe virtual int IndexOf(String source, String value, int startIndex)
+ public virtual int IndexOf(string source, string value, int startIndex)
{
if (source == null)
throw new ArgumentNullException(nameof(source));
@@ -700,8 +692,7 @@ namespace System.Globalization {
return IndexOf(source, value, startIndex, source.Length - startIndex, CompareOptions.None);
}
-
- public unsafe virtual int IndexOf(String source, char value, int startIndex, CompareOptions options)
+ public virtual int IndexOf(string source, char value, int startIndex, CompareOptions options)
{
if (source == null)
throw new ArgumentNullException(nameof(source));
@@ -711,7 +702,7 @@ namespace System.Globalization {
}
- public unsafe virtual int IndexOf(String source, String value, int startIndex, CompareOptions options)
+ public virtual int IndexOf(string source, string value, int startIndex, CompareOptions options)
{
if (source == null)
throw new ArgumentNullException(nameof(source));
@@ -721,28 +712,28 @@ namespace System.Globalization {
}
- public unsafe virtual int IndexOf(String source, char value, int startIndex, int count)
+ public virtual int IndexOf(string source, char value, int startIndex, int count)
{
return IndexOf(source, value, startIndex, count, CompareOptions.None);
}
- public unsafe virtual int IndexOf(String source, String value, int startIndex, int count)
+ public virtual int IndexOf(string source, string value, int startIndex, int count)
{
return IndexOf(source, value, startIndex, count, CompareOptions.None);
}
- public unsafe virtual int IndexOf(String source, char value, int startIndex, int count, CompareOptions options)
+ public unsafe virtual int IndexOf(string source, char value, int startIndex, int count, CompareOptions options)
{
// Validate inputs
if (source == null)
throw new ArgumentNullException(nameof(source));
if (startIndex < 0 || startIndex > source.Length)
- throw new ArgumentOutOfRangeException(nameof(startIndex), Environment.GetResourceString("ArgumentOutOfRange_Index"));
+ throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index);
if (count < 0 || startIndex > source.Length - count)
- throw new ArgumentOutOfRangeException(nameof(count), Environment.GetResourceString("ArgumentOutOfRange_Count"));
+ throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Count);
Contract.EndContractBlock();
if (options == CompareOptions.OrdinalIgnoreCase)
@@ -753,18 +744,16 @@ namespace System.Globalization {
// Validate CompareOptions
// Ordinal can't be selected with other flags
if ((options & ValidIndexMaskOffFlags) != 0 && (options != CompareOptions.Ordinal))
- throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), nameof(options));
-
- // to let the sorting DLL do the call optimization in case of Ascii strings, we check if the strings are in Ascii and then send the flag RESERVED_FIND_ASCII_STRING to
- // the sorting DLL API SortFindString so sorting DLL don't have to check if the string is Ascii with every call to SortFindString.
- return InternalFindNLSStringEx(
- m_dataHandle, m_handleOrigin, m_sortName,
- GetNativeCompareFlags(options) | Win32Native.FIND_FROMSTART | ((source.IsAscii() && (value <= '\x007f')) ? RESERVED_FIND_ASCII_STRING : 0),
- source, count, startIndex, new String(value, 1), 1);
+ throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options));
+
+ if (_invariantMode)
+ return IndexOfOrdinal(source, new string(value, 1), startIndex, count, ignoreCase: (options & (CompareOptions.IgnoreCase | CompareOptions.OrdinalIgnoreCase)) != 0);
+
+ return IndexOfCore(source, new string(value, 1), startIndex, count, options, null);
}
- public unsafe virtual int IndexOf(String source, String value, int startIndex, int count, CompareOptions options)
+ public unsafe virtual int IndexOf(string source, string value, int startIndex, int count, CompareOptions options)
{
// Validate inputs
if (source == null)
@@ -774,7 +763,7 @@ namespace System.Globalization {
if (startIndex > source.Length)
{
- throw new ArgumentOutOfRangeException(nameof(startIndex), Environment.GetResourceString("ArgumentOutOfRange_Index"));
+ throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index);
}
Contract.EndContractBlock();
@@ -791,28 +780,36 @@ namespace System.Globalization {
if (startIndex < 0)
{
- throw new ArgumentOutOfRangeException(nameof(startIndex), Environment.GetResourceString("ArgumentOutOfRange_Index"));
+ throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index);
}
if (count < 0 || startIndex > source.Length - count)
- throw new ArgumentOutOfRangeException(nameof(count),Environment.GetResourceString("ArgumentOutOfRange_Count"));
+ throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Count);
if (options == CompareOptions.OrdinalIgnoreCase)
{
- return source.IndexOf(value, startIndex, count, StringComparison.OrdinalIgnoreCase);
+ return IndexOfOrdinal(source, value, startIndex, count, ignoreCase: true);
}
// Validate CompareOptions
// Ordinal can't be selected with other flags
if ((options & ValidIndexMaskOffFlags) != 0 && (options != CompareOptions.Ordinal))
- throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), nameof(options));
-
- // to let the sorting DLL do the call optimization in case of Ascii strings, we check if the strings are in Ascii and then send the flag RESERVED_FIND_ASCII_STRING to
- // the sorting DLL API SortFindString so sorting DLL don't have to check if the string is Ascii with every call to SortFindString.
- return InternalFindNLSStringEx(
- m_dataHandle, m_handleOrigin, m_sortName,
- GetNativeCompareFlags(options) | Win32Native.FIND_FROMSTART | ((source.IsAscii() && value.IsAscii()) ? RESERVED_FIND_ASCII_STRING : 0),
- source, count, startIndex, value, value.Length);
+ throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options));
+
+ if (_invariantMode)
+ return IndexOfOrdinal(source, value, startIndex, count, ignoreCase: (options & (CompareOptions.IgnoreCase | CompareOptions.OrdinalIgnoreCase)) != 0);
+
+ return IndexOfCore(source, value, startIndex, count, options, null);
+ }
+
+ internal int IndexOfOrdinal(string source, string value, int startIndex, int count, bool ignoreCase)
+ {
+ if (_invariantMode)
+ {
+ return InvariantIndexOf(source, value, startIndex, count, ignoreCase);
+ }
+
+ return IndexOfOrdinalCore(source, value, startIndex, count, ignoreCase);
}
////////////////////////////////////////////////////////////////////////
@@ -829,21 +826,20 @@ namespace System.Globalization {
////////////////////////////////////////////////////////////////////////
- public unsafe virtual int LastIndexOf(String source, char value)
+ public virtual int LastIndexOf(String source, char value)
{
- if (source==null)
+ if (source == null)
throw new ArgumentNullException(nameof(source));
Contract.EndContractBlock();
// Can't start at negative index, so make sure we check for the length == 0 case.
- return LastIndexOf(source, value, source.Length - 1,
- source.Length, CompareOptions.None);
+ return LastIndexOf(source, value, source.Length - 1, source.Length, CompareOptions.None);
}
- public virtual int LastIndexOf(String source, String value)
+ public virtual int LastIndexOf(string source, string value)
{
- if (source==null)
+ if (source == null)
throw new ArgumentNullException(nameof(source));
Contract.EndContractBlock();
@@ -853,9 +849,9 @@ namespace System.Globalization {
}
- public virtual int LastIndexOf(String source, char value, CompareOptions options)
+ public virtual int LastIndexOf(string source, char value, CompareOptions options)
{
- if (source==null)
+ if (source == null)
throw new ArgumentNullException(nameof(source));
Contract.EndContractBlock();
@@ -864,58 +860,55 @@ namespace System.Globalization {
source.Length, options);
}
- public unsafe virtual int LastIndexOf(String source, String value, CompareOptions options)
+ public virtual int LastIndexOf(string source, string value, CompareOptions options)
{
- if (source==null)
+ if (source == null)
throw new ArgumentNullException(nameof(source));
Contract.EndContractBlock();
// Can't start at negative index, so make sure we check for the length == 0 case.
- return LastIndexOf(source, value, source.Length - 1,
- source.Length, options);
+ return LastIndexOf(source, value, source.Length - 1, source.Length, options);
}
-
- public unsafe virtual int LastIndexOf(String source, char value, int startIndex)
+ public virtual int LastIndexOf(string source, char value, int startIndex)
{
return LastIndexOf(source, value, startIndex, startIndex + 1, CompareOptions.None);
}
- public unsafe virtual int LastIndexOf(String source, String value, int startIndex)
+ public virtual int LastIndexOf(string source, string value, int startIndex)
{
return LastIndexOf(source, value, startIndex, startIndex + 1, CompareOptions.None);
}
-
- public unsafe virtual int LastIndexOf(String source, char value, int startIndex, CompareOptions options)
+ public virtual int LastIndexOf(string source, char value, int startIndex, CompareOptions options)
{
return LastIndexOf(source, value, startIndex, startIndex + 1, options);
}
- public unsafe virtual int LastIndexOf(String source, String value, int startIndex, CompareOptions options)
+ public virtual int LastIndexOf(string source, string value, int startIndex, CompareOptions options)
{
return LastIndexOf(source, value, startIndex, startIndex + 1, options);
}
- public unsafe virtual int LastIndexOf(String source, char value, int startIndex, int count)
+ public virtual int LastIndexOf(string source, char value, int startIndex, int count)
{
return LastIndexOf(source, value, startIndex, count, CompareOptions.None);
}
- public unsafe virtual int LastIndexOf(String source, String value, int startIndex, int count)
+ public virtual int LastIndexOf(string source, string value, int startIndex, int count)
{
return LastIndexOf(source, value, startIndex, count, CompareOptions.None);
}
- public unsafe virtual int LastIndexOf(String source, char value, int startIndex, int count, CompareOptions options)
+ public virtual int LastIndexOf(string source, char value, int startIndex, int count, CompareOptions options)
{
// Verify Arguments
- if (source==null)
+ if (source == null)
throw new ArgumentNullException(nameof(source));
Contract.EndContractBlock();
@@ -924,7 +917,7 @@ namespace System.Globalization {
if ((options & ValidIndexMaskOffFlags) != 0 &&
(options != CompareOptions.Ordinal) &&
(options != CompareOptions.OrdinalIgnoreCase))
- throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), nameof(options));
+ throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options));
// Special case for 0 length input strings
if (source.Length == 0 && (startIndex == -1 || startIndex == 0))
@@ -932,7 +925,7 @@ namespace System.Globalization {
// Make sure we're not out of range
if (startIndex < 0 || startIndex > source.Length)
- throw new ArgumentOutOfRangeException(nameof(startIndex), Environment.GetResourceString("ArgumentOutOfRange_Index"));
+ throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index);
// Make sure that we allow startIndex == source.Length
if (startIndex == source.Length)
@@ -944,23 +937,21 @@ namespace System.Globalization {
// 2nd have of this also catches when startIndex == MAXINT, so MAXINT - 0 + 1 == -1, which is < 0.
if (count < 0 || startIndex - count + 1 < 0)
- throw new ArgumentOutOfRangeException(nameof(count), Environment.GetResourceString("ArgumentOutOfRange_Count"));
+ throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Count);
if (options == CompareOptions.OrdinalIgnoreCase)
{
return source.LastIndexOf(value.ToString(), startIndex, count, StringComparison.OrdinalIgnoreCase);
}
- // to let the sorting DLL do the call optimization in case of Ascii strings, we check if the strings are in Ascii and then send the flag RESERVED_FIND_ASCII_STRING to
- // the sorting DLL API SortFindString so sorting DLL don't have to check if the string is Ascii with every call to SortFindString.
- return InternalFindNLSStringEx(
- m_dataHandle, m_handleOrigin, m_sortName,
- GetNativeCompareFlags(options) | Win32Native.FIND_FROMEND | ((source.IsAscii() && (value <= '\x007f')) ? RESERVED_FIND_ASCII_STRING : 0),
- source, count, startIndex, new String(value, 1), 1);
+ if (_invariantMode)
+ return InvariantLastIndexOf(source, new string(value, 1), startIndex, count, (options & (CompareOptions.IgnoreCase | CompareOptions.OrdinalIgnoreCase)) != 0);
+
+ return LastIndexOfCore(source, value.ToString(), startIndex, count, options);
}
- public unsafe virtual int LastIndexOf(String source, String value, int startIndex, int count, CompareOptions options)
+ public virtual int LastIndexOf(string source, string value, int startIndex, int count, CompareOptions options)
{
// Verify Arguments
if (source == null)
@@ -974,7 +965,7 @@ namespace System.Globalization {
if ((options & ValidIndexMaskOffFlags) != 0 &&
(options != CompareOptions.Ordinal) &&
(options != CompareOptions.OrdinalIgnoreCase))
- throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), nameof(options));
+ throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options));
// Special case for 0 length input strings
if (source.Length == 0 && (startIndex == -1 || startIndex == 0))
@@ -982,7 +973,7 @@ namespace System.Globalization {
// Make sure we're not out of range
if (startIndex < 0 || startIndex > source.Length)
- throw new ArgumentOutOfRangeException(nameof(startIndex), Environment.GetResourceString("ArgumentOutOfRange_Index"));
+ throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index);
// Make sure that we allow startIndex == source.Length
if (startIndex == source.Length)
@@ -998,21 +989,28 @@ namespace System.Globalization {
// 2nd half of this also catches when startIndex == MAXINT, so MAXINT - 0 + 1 == -1, which is < 0.
if (count < 0 || startIndex - count + 1 < 0)
- throw new ArgumentOutOfRangeException(nameof(count), Environment.GetResourceString("ArgumentOutOfRange_Count"));
+ throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Count);
if (options == CompareOptions.OrdinalIgnoreCase)
{
- return source.LastIndexOf(value, startIndex, count, StringComparison.OrdinalIgnoreCase);
+ return LastIndexOfOrdinal(source, value, startIndex, count, ignoreCase: true);
}
- // to let the sorting DLL do the call optimization in case of Ascii strings, we check if the strings are in Ascii and then send the flag RESERVED_FIND_ASCII_STRING to
- // the sorting DLL API SortFindString so sorting DLL don't have to check if the string is Ascii with every call to SortFindString.
- return InternalFindNLSStringEx(
- m_dataHandle, m_handleOrigin, m_sortName,
- GetNativeCompareFlags(options) | Win32Native.FIND_FROMEND | ((source.IsAscii() && value.IsAscii()) ? RESERVED_FIND_ASCII_STRING : 0),
- source, count, startIndex, value, value.Length);
+ if (_invariantMode)
+ return InvariantLastIndexOf(source, value, startIndex, count, (options & (CompareOptions.IgnoreCase | CompareOptions.OrdinalIgnoreCase)) != 0);
+
+ return LastIndexOfCore(source, value, startIndex, count, options);
}
+ internal int LastIndexOfOrdinal(string source, string value, int startIndex, int count, bool ignoreCase)
+ {
+ if (_invariantMode)
+ {
+ return InvariantLastIndexOf(source, value, startIndex, count, ignoreCase);
+ }
+
+ return LastIndexOfOrdinalCore(source, value, startIndex, count, ignoreCase);
+ }
////////////////////////////////////////////////////////////////////////
//
@@ -1021,74 +1019,23 @@ namespace System.Globalization {
// Gets the SortKey for the given string with the given options.
//
////////////////////////////////////////////////////////////////////////
- public unsafe virtual SortKey GetSortKey(String source, CompareOptions options)
+ public virtual SortKey GetSortKey(string source, CompareOptions options)
{
+ if (_invariantMode)
+ return InvariantCreateSortKey(source, options);
+
return CreateSortKey(source, options);
}
- public unsafe virtual SortKey GetSortKey(String source)
+ public virtual SortKey GetSortKey(string source)
{
- return CreateSortKey(source, CompareOptions.None);
- }
+ if (_invariantMode)
+ return InvariantCreateSortKey(source, CompareOptions.None);
- private SortKey CreateSortKey(String source, CompareOptions options)
- {
- if (source==null) { throw new ArgumentNullException(nameof(source)); }
- Contract.EndContractBlock();
-
- // Mask used to check if we have the right flags.
- const CompareOptions ValidSortkeyCtorMaskOffFlags = ~(CompareOptions.IgnoreCase |
- CompareOptions.IgnoreSymbols |
- CompareOptions.IgnoreNonSpace |
- CompareOptions.IgnoreWidth |
- CompareOptions.IgnoreKanaType |
- CompareOptions.StringSort);
-
- if ((options & ValidSortkeyCtorMaskOffFlags) != 0)
- {
- throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), nameof(options));
- }
- byte[] keyData = null;
- // The OS doesn't have quite the same behavior so we have to test for empty inputs
- if (String.IsNullOrEmpty(source))
- {
- // Empty strings get an empty sort key
- keyData = EmptyArray<Byte>.Value;
- // Fake value to test though so we can verify our flags
- source = "\x0000";
- }
-
- int flags = GetNativeCompareFlags(options);
-
- // Go ahead and call the OS
- // First get the count
- int length = InternalGetSortKey(m_dataHandle, m_handleOrigin, m_sortName, flags, source, source.Length, null, 0);
-
- // If there was an error, return an error
- if (length == 0)
- {
- throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), nameof(source));
- }
-
- // If input was empty, return the empty byte[] we made earlier and skip this
- if (keyData == null)
- {
- // Make an appropriate byte array
- keyData = new byte[length];
-
- // Fill up the array
- length = InternalGetSortKey(m_dataHandle, m_handleOrigin, m_sortName, flags, source, source.Length, keyData, keyData.Length);
- }
- else
- {
- source = String.Empty; // back to original
- }
-
- return new SortKey(Name, source, options, keyData);
+ return CreateSortKey(source, CompareOptions.None);
}
-
////////////////////////////////////////////////////////////////////////
//
// Equals
@@ -1129,34 +1076,6 @@ namespace System.Globalization {
return (this.Name.GetHashCode());
}
- //
- // return hash value for the string according to the input CompareOptions
- //
-
- public virtual int GetHashCode(string source, CompareOptions options)
- {
- if (source == null)
- {
- throw new ArgumentNullException(nameof(source));
- }
-
- if (options == CompareOptions.Ordinal)
- {
- return source.GetHashCode();
- }
-
- if (options == CompareOptions.OrdinalIgnoreCase)
- {
- return TextInfo.GetHashCodeOrdinalIgnoreCase(source);
- }
-
- //
- // GetHashCodeOfString does more parameters validation. basically will throw when
- // having Ordinal, OrdinalIgnoreCase and StringSort
- //
-
- return GetHashCodeOfString(source, options, false, 0);
- }
////////////////////////////////////////////////////////////////////////
//
@@ -1184,33 +1103,46 @@ namespace System.Globalization {
////////////////////////////////////////////////////////////////////////
internal int GetHashCodeOfString(string source, CompareOptions options)
{
- return GetHashCodeOfString(source, options, false, 0);
- }
-
- internal int GetHashCodeOfString(string source, CompareOptions options, bool forceRandomizedHashing, long additionalEntropy)
- {
//
// Parameter validation
//
- if(null == source)
+ if (null == source)
{
throw new ArgumentNullException(nameof(source));
}
if ((options & ValidHashCodeOfStringMaskOffFlags) != 0)
{
- throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), nameof(options));
+ throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options));
}
Contract.EndContractBlock();
- if(0 == source.Length)
+ return GetHashCodeOfStringCore(source, options);
+ }
+
+ public virtual int GetHashCode(string source, CompareOptions options)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ if (options == CompareOptions.Ordinal)
{
- return(0);
+ return source.GetHashCode();
}
+ if (options == CompareOptions.OrdinalIgnoreCase)
+ {
+ return TextInfo.GetHashCodeOrdinalIgnoreCase(source);
+ }
+
+ //
+ // GetHashCodeOfString does more parameters validation. basically will throw when
+ // having Ordinal, OrdinalIgnoreCase and StringSort
//
- ////////////////////////////////////////////////////////////////////////
- return (InternalGetGlobalizedHashCode(m_dataHandle, m_handleOrigin, this.m_sortName, source, source.Length, GetNativeCompareFlags(options), forceRandomizedHashing, additionalEntropy));
+
+ return GetHashCodeOfString(source, options);
}
////////////////////////////////////////////////////////////////////////
@@ -1221,82 +1153,41 @@ namespace System.Globalization {
// CompareInfo.
//
////////////////////////////////////////////////////////////////////////
-
-
- public override String ToString()
+ public override string ToString()
{
return ("CompareInfo - " + this.Name);
}
-#if FEATURE_USE_LCID
- public int LCID
+ public SortVersion Version
{
get
{
- return CultureInfo.GetCultureInfo(this.Name).LCID;
- }
- }
-#endif
+ if (_sortVersion == null)
+ {
+ if (_invariantMode)
+ {
+ _sortVersion = new SortVersion(0, CultureInfo.LOCALE_INVARIANT, new Guid(0, 0, 0, 0, 0, 0, 0,
+ (byte) (CultureInfo.LOCALE_INVARIANT >> 24),
+ (byte) ((CultureInfo.LOCALE_INVARIANT & 0x00FF0000) >> 16),
+ (byte) ((CultureInfo.LOCALE_INVARIANT & 0x0000FF00) >> 8),
+ (byte) (CultureInfo.LOCALE_INVARIANT & 0xFF)));
+ }
+ else
+ {
+ _sortVersion = GetSortVersion();
+ }
+ }
- internal static IntPtr InternalInitSortHandle(String localeName, out IntPtr handleOrigin)
- {
- return NativeInternalInitSortHandle(localeName, out handleOrigin);
+ return _sortVersion;
+ }
}
- [OptionalField(VersionAdded = 3)]
- private SortVersion m_SortVersion;
-
- public SortVersion Version
+ public int LCID
{
get
{
- if(m_SortVersion == null)
- {
- Win32Native.NlsVersionInfoEx v = new Win32Native.NlsVersionInfoEx();
- v.dwNLSVersionInfoSize = Marshal.SizeOf(typeof(Win32Native.NlsVersionInfoEx));
- InternalGetNlsVersionEx(m_dataHandle, m_handleOrigin, m_sortName, ref v);
- m_SortVersion = new SortVersion(v.dwNLSVersion, (v.dwEffectiveId != 0) ? v.dwEffectiveId : LCID, v.guidCustomVersion);
- }
-
- return m_SortVersion;
+ return CultureInfo.GetCultureInfo(Name).LCID;
}
}
-
- [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
- [SuppressUnmanagedCodeSecurity]
- [return: MarshalAs(UnmanagedType.Bool)]
- private static extern bool InternalGetNlsVersionEx(IntPtr handle, IntPtr handleOrigin, String localeName, ref Win32Native.NlsVersionInfoEx lpNlsVersionInformation);
-
- [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
- [SuppressUnmanagedCodeSecurity]
- private static extern IntPtr NativeInternalInitSortHandle(String localeName, out IntPtr handleOrigin);
-
- // Get a locale sensitive sort hash code from native code -- COMNlsInfo::InternalGetGlobalizedHashCode
- [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
- [SuppressUnmanagedCodeSecurity]
- private static extern int InternalGetGlobalizedHashCode(IntPtr handle, IntPtr handleOrigin, string localeName, string source, int length, int dwFlags, bool forceRandomizedHashing, long additionalEntropy);
-
- // Use native API calls to see if this string is entirely defined -- COMNlsInfo::InternalIsSortable
- [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
- [SuppressUnmanagedCodeSecurity]
- [return: MarshalAs(UnmanagedType.Bool)]
- private static extern bool InternalIsSortable(IntPtr handle, IntPtr handleOrigin, String localeName, String source, int length);
-
- // Compare a string using the native API calls -- COMNlsInfo::InternalCompareString
- [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
- [SuppressUnmanagedCodeSecurity]
- private static extern int InternalCompareString(IntPtr handle, IntPtr handleOrigin, String localeName, String string1, int offset1, int length1,
- String string2, int offset2, int length2, int flags);
-
- // InternalFindNLSStringEx parameters is not exactly matching kernel32::FindNLSStringEx parameters.
- // Call through to NewApis::FindNLSStringEx so we can get the right behavior
- [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
- [SuppressUnmanagedCodeSecurity]
- private static extern int InternalFindNLSStringEx(IntPtr handle, IntPtr handleOrigin, String localeName, int flags, String source, int sourceCount, int startIndex, string target, int targetCount);
-
- // Call through to NewAPis::LCMapStringEx so we can get appropriate behavior for all platforms
- [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
- [SuppressUnmanagedCodeSecurity]
- private static extern int InternalGetSortKey(IntPtr handle, IntPtr handleOrigin, String localeName, int flags, String source, int sourceCount, byte[] target, int targetCount);
}
}