diff options
Diffstat (limited to 'src/mscorlib/src/System/Globalization/CompareInfo.cs')
-rw-r--r-- | src/mscorlib/src/System/Globalization/CompareInfo.cs | 897 |
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); } } |