CompareInfo.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / clr / src / BCL / System / Globalization / CompareInfo.cs / 1305376 / CompareInfo.cs

                            // ==++== 
//
//   Copyright (c) Microsoft Corporation.  All rights reserved.
//
// ==--== 
////////////////////////////////////////////////////////////////////////////
// 
//  Class:    CompareInfo 
//
// 
//  Purpose:  This class implements a set of methods for comparing
//            strings.
//
//  Date:     August 12, 1998 
//
//////////////////////////////////////////////////////////////////////////// 
 
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 System.Security.Permissions; 
    using Microsoft.Win32;
    using System.Security; 
    using System.Security.Principal;
    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]
    [Flags] 
    [System.Runtime.InteropServices.ComVisible(true)]
    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.
    } 

 
    [Serializable] 
[System.Runtime.InteropServices.ComVisible(true)]
 
    public class CompareInfo
#if FEATURE_SERIALIZATION
    : IDeserializationCallback
#endif 
#if NOT_DEFINED // to kepp Thinner happy
    , Object 
#endif 

    { 
        // Mask used to check if IndexOf()/LastIndexOf()/IsPrefix()/IsPostfix() has the right flags.
        private const CompareOptions ValidIndexMaskOffFlags =
            ~(CompareOptions.IgnoreCase | CompareOptions.IgnoreSymbols | CompareOptions.IgnoreNonSpace |
              CompareOptions.IgnoreWidth | CompareOptions.IgnoreKanaType); 

        // Mask used to check if Compare() has the right flags. 
        private const CompareOptions ValidCompareMaskOffFlags = 
            ~(CompareOptions.IgnoreCase | CompareOptions.IgnoreSymbols | CompareOptions.IgnoreNonSpace |
              CompareOptions.IgnoreWidth | CompareOptions.IgnoreKanaType | CompareOptions.StringSort); 

        // Mask used to check if GetHashCodeOfString() has the right flags.
        private const CompareOptions ValidHashCodeOfStringMaskOffFlags =
            ~(CompareOptions.IgnoreCase | CompareOptions.IgnoreSymbols | CompareOptions.IgnoreNonSpace | 
              CompareOptions.IgnoreWidth | CompareOptions.IgnoreKanaType);
 
        // 
        // 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. 
        // The interesting part is that since haw-US doesn't have its own sort, it has to point at another
        // locale, which is what SCOMPAREINFO does.

        [OptionalField(VersionAdded = 2)] 
        private String m_name;  // The name used to construct this CompareInfo
 
        [NonSerialized] 
        private String m_sortName; // The name that defines our behavior
 
        [NonSerialized]
        private IntPtr m_dataHandle;

        //////////////////////////////////////////////////////////////////////// 
        //
        //  CompareInfo Constructor 
        // 
        //
        //////////////////////////////////////////////////////////////////////// 
        // Constructs an instance that most closely corresponds to the NLS locale
        // identifier.
        [System.Security.SecuritySafeCritical]
        internal CompareInfo(CultureInfo culture) 
        {
            this.m_name = culture.m_name; 
            this.m_sortName = culture.SortName; 

#if !FEATURE_CORECLR 
            this.m_dataHandle = InternalInitSortHandle(m_sortName);
#endif
        }
 
        /*=================================GetCompareInfo==========================
        **Action: Get the CompareInfo constructed from the data table in the specified assembly for the specified culture. 
        **       Warning: The assembly versioning mechanism is dead! 
        **Returns: The CompareInfo for the specified culture.
        **Arguments: 
        **   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 
        //
        public static CompareInfo GetCompareInfo(int culture, Assembly assembly){ 
            // Parameter checking.
            if (assembly == null) {
                throw new ArgumentNullException("assembly");
            } 
            if (assembly!=typeof(Object).Module.Assembly) {
                throw new ArgumentException(Environment.GetResourceString("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.
        **Exceptions: 
        **  ArugmentNullException when the assembly is null
        **  ArgumentException if name is invalid. 
        ============================================================================*/ 
        //
        public static CompareInfo GetCompareInfo(String name, Assembly assembly){ 
            if (name == null || assembly == null) {
                throw new ArgumentNullException(name == null ? "name" : "assembly");
            }
            Contract.EndContractBlock(); 

            if (assembly!=typeof(Object).Module.Assembly) { 
                throw new ArgumentException(Environment.GetResourceString("Argument_OnlyMscorlib")); 
            }
 
            return GetCompareInfo(name);
        }

        /*=================================GetCompareInfo========================== 
        **Action: Get the CompareInfo for the specified culture.
        ** This method is provided for ease of integration with NLS-based software. 
        **Returns: The CompareInfo for the specified culture. 
        **Arguments:
        **   culture    the ID of the culture. 
        **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", "culture"));
            } 

            return CultureInfo.GetCultureInfo(culture).CompareInfo; 
        } 
#endif
 
        /*=================================GetCompareInfo==========================
        **Action: Get the CompareInfo for the specified culture.
        **Returns: The CompareInfo for the specified culture.
        **Arguments: 
        **   name    the name of the culture.
        **Exceptions: 
        **  ArgumentException if name is invalid. 
        ============================================================================*/
 
        [System.Security.SecuritySafeCritical]  // auto-generated
        public static CompareInfo GetCompareInfo(String name)
        {
            if (name == null) 
            {
                throw new ArgumentNullException("name"); 
            } 
            Contract.EndContractBlock();
 
            return CultureInfo.GetCultureInfo(name).CompareInfo;
        }

        [System.Runtime.InteropServices.ComVisible(false)] 
        public static bool IsSortable(char ch) {
            return(IsSortable(ch.ToString())); 
        } 

        [System.Security.SecuritySafeCritical] 
        [System.Runtime.InteropServices.ComVisible(false)]
        public static bool IsSortable(String text) {
            if (text == null) {
                // A null param is invalid here. 
                throw new ArgumentNullException("text");
            } 
 
            if (0 == text.Length) {
                // A zero length string is not invalid, but it is also not sortable. 
                return(false);
            }

            return (InternalIsSortable(CultureInfo.InvariantCulture.CompareInfo.m_dataHandle, text, text.Length)); 
        }
 
 
#if FEATURE_SERIALIZATION // Only defined when FEATURE_USE_LCID is also defined
#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;
        } 

        [System.Security.SecuritySafeCritical] 
        private void OnDeserialized() 
        {
            CultureInfo ci; 
            // If we didn't have a name, use the LCID
            if (this.m_name == null)
            {
                // From whidbey, didn't have a name 
                ci = CultureInfo.GetCultureInfo(this.culture);
                this.m_name = ci.m_name; 
            } 
            else
            { 
                ci = CultureInfo.GetCultureInfo(m_name);
            }
            this.m_sortName = ci.SortName;
#if !FEATURE_CORECLR 
            this.m_dataHandle = InternalInitSortHandle(m_sortName);
#endif 
        } 

        [OnDeserialized] 
        private void OnDeserialized(StreamingContext ctx)
        {
            OnDeserialized();
        } 

        [OnSerializing] 
        private void OnSerializing(StreamingContext ctx) 
        {
            // 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)
            Contract.Assert(m_name != null, "CompareInfo.OnSerializing - expected m_name to be set already");
        }
 
        void IDeserializationCallback.OnDeserialization(Object sender)
        { 
            OnDeserialized(); 
        }
 
#endregion Serialization
#endif // FEATURE_SERIALIZATION

 
        ///////////////////////////----- Name -----/////////////////////////////////
        // 
        //  Returns the name of the culture (well actually, of the sort). 
        //  Very important for providing a non-LCID way of identifying
        //  what the sort is. 
        //
        //  Note that this name isn't dereferenced in case the CompareInfo is a different locale
        //  which is consistent with the behaviors of earlier versions.  (so if you ask for a sort
        //  and the locale's changed behavior, then you'll get changed behavior, which is like 
        //  what happens for a version update)
        // 
        //////////////////////////////////////////////////////////////////////// 

        [System.Runtime.InteropServices.ComVisible(false)] 
        public virtual String Name
        {
            get
            { 
                Contract.Assert(m_name != null, "CompareInfo.Name Expected m_name to be set");
                if (m_name == "zh-CHT" || m_name == "zh-CHS") 
                { 
                    return m_name;
                } 

                return (m_sortName);
            }
        } 

        // 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 

        [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
            // 
            Contract.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; }

            Contract.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");

            return nativeCompareFlags;
        } 

 
        //////////////////////////////////////////////////////////////////////// 
        //
        //  Compare 
        //
        //  Compares the two strings with the given options.  Returns 0 if the
        //  two strings are equal, a number less than 0 if string1 is less
        //  than string2, and a number greater than 0 if string1 is greater 
        //  than string2.
        // 
        //////////////////////////////////////////////////////////////////////// 

 
        public virtual int Compare(String string1, String string2)
        {
            return (Compare(string1, string2, CompareOptions.None));
        } 

        [System.Security.SecuritySafeCritical]  // auto-generated 
        public unsafe virtual int Compare(String string1, String string2, CompareOptions options){ 

            if (options == CompareOptions.OrdinalIgnoreCase) 
            {
                return String.Compare(string1, string2, StringComparison.OrdinalIgnoreCase);
            }
 
            // Verify the options before we do any real comparison.
            if ((options & CompareOptions.Ordinal) != 0) 
            { 
                if (options != CompareOptions.Ordinal)
                { 
                    throw new ArgumentException(Environment.GetResourceString("Argument_CompareOptionOrdinal"), "options");
                        }
                return String.CompareOrdinal(string1, string2);
                    } 

            if ((options & ValidCompareMaskOffFlags) != 0) 
            { 
                throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), "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) {
                    return (0);     // Equal 
                } 
                return (-1);    // null < non-null
            } 
            if (string2 == null) {
                return (1);     // non-null > null
            }
 
            return InternalCompareString(m_dataHandle, m_sortName, string1, 0, string1.Length, string2, 0, string2.Length, GetNativeCompareFlags(options));
        } 
 

        //////////////////////////////////////////////////////////////////////// 
        //
        //  Compare
        //
        //  Compares the specified regions of the two strings with the given 
        //  options.
        //  Returns 0 if the two strings are equal, a number less than 0 if 
        //  string1 is less than string2, and a number greater than 0 if 
        //  string1 is greater than string2.
        // 
        ////////////////////////////////////////////////////////////////////////


        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)
        {
            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)
        { 
            return Compare(string1, offset1, string2, offset2, 0);
        }

 
        [System.Security.SecuritySafeCritical]  // auto-generated
        public unsafe 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? 1: -1);
                return (result); 
            }
 
            // Verify inputs 
            if (length1 < 0 || length2 < 0)
            { 
                throw new ArgumentOutOfRangeException((length1 < 0) ? "length1" : "length2", Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum"));
            }
            if (offset1 < 0 || offset2 < 0)
            { 
                throw new ArgumentOutOfRangeException((offset1 < 0) ? "offset1" : "offset2", Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum"));
            } 
            if (offset1 > (string1 == null ? 0 : string1.Length) - length1) 
            {
                throw new ArgumentOutOfRangeException("string1", Environment.GetResourceString("ArgumentOutOfRange_OffsetLength")); 
            }
            if (offset2 > (string2 == null ? 0 : string2.Length) - length2)
            {
                throw new ArgumentOutOfRangeException("string2", Environment.GetResourceString("ArgumentOutOfRange_OffsetLength")); 
            }
            if ((options & CompareOptions.Ordinal) != 0) 
            { 
                if (options != CompareOptions.Ordinal)
                { 
                    throw new ArgumentException(Environment.GetResourceString("Argument_CompareOptionOrdinal"),
                                                "options");
                }
            } 
            else if ((options & ValidCompareMaskOffFlags) != 0)
            { 
                throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), "options"); 
            }
 
            //
            // Check for the null case.
            //
            if (string1 == null) 
            {
                if (string2 == null) 
                { 
                    return (0);
                } 
                return (-1);
            }
            if (string2 == null)
            { 
                return (1);
            } 
 
            if (options == CompareOptions.Ordinal)
            { 
                return CompareOrdinal(string1, offset1, length1,
                                      string2, offset2, length2);
            }
            return InternalCompareString(this.m_dataHandle, this.m_sortName, 
                                         string1, offset1, length1,
                                         string2, offset2, length2, 
                                         GetNativeCompareFlags(options)); 
        }
 
        [System.Security.SecurityCritical]
        private static int CompareOrdinal(string string1, int offset1, int length1, string string2, int offset2, int length2)
        {
            int result = String.nativeCompareOrdinalEx(string1, offset1, string2, offset2, 
                                                       (length1 < length2 ? length1 : length2));
            if ((length1 != length2) && result == 0) 
            { 
                return (length1 > length2 ? 1 : -1);
            } 
            return (result);
        }

        //////////////////////////////////////////////////////////////////////// 
        //
        //  IsPrefix 
        // 
        //  Determines whether prefix is a prefix of string.  If prefix equals
        //  String.Empty, true is returned. 
        //
        ////////////////////////////////////////////////////////////////////////

 
        [System.Security.SecuritySafeCritical]  // auto-generated
        public unsafe virtual bool IsPrefix(String source, String prefix, CompareOptions options) 
        { 
            if (source == null || prefix == null) {
                throw new ArgumentNullException((source == null ? "source" : "prefix"), 
                    Environment.GetResourceString("ArgumentNull_String"));
            }
            Contract.EndContractBlock();
            int prefixLen = prefix.Length; 

            if (prefixLen == 0) 
            { 
                return (true);
            } 

            if (options == CompareOptions.OrdinalIgnoreCase)
            {
                return source.StartsWith(prefix, StringComparison.OrdinalIgnoreCase); 
            }
 
            if (options == CompareOptions.Ordinal) 
            {
                return source.StartsWith(prefix, StringComparison.Ordinal); 
            }

            if ((options & ValidIndexMaskOffFlags) != 0) {
                throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), "options"); 
            }
 
            return (InternalFindNLSStringEx(m_dataHandle, m_sortName, GetNativeCompareFlags(options) | Win32Native.FIND_STARTSWITH, source, source.Length, 0, prefix, prefix.Length) > -1); 
        }
 
        public virtual bool IsPrefix(String source, String prefix)
        {
            return (IsPrefix(source, prefix, 0));
        } 

 
        //////////////////////////////////////////////////////////////////////// 
        //
        //  IsSuffix 
        //
        //  Determines whether suffix is a suffix of string.  If suffix equals
        //  String.Empty, true is returned.
        // 
        ////////////////////////////////////////////////////////////////////////
 
 
        [System.Security.SecuritySafeCritical]  // auto-generated
        public unsafe virtual bool IsSuffix(String source, String suffix, CompareOptions options) 
        {
            if (source == null || suffix == null) {
                throw new ArgumentNullException((source == null ? "source" : "suffix"),
                    Environment.GetResourceString("ArgumentNull_String")); 
            }
            Contract.EndContractBlock(); 
            int suffixLen = suffix.Length; 

            if (suffixLen == 0) 
            {
                return (true);
            }
 
            if (options == CompareOptions.OrdinalIgnoreCase) {
                return source.EndsWith(suffix, StringComparison.OrdinalIgnoreCase); 
            } 

            if (options == CompareOptions.Ordinal) { 
                return source.EndsWith(suffix, StringComparison.Ordinal);
            }

            if ((options & ValidIndexMaskOffFlags) != 0) { 
                throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), "options");
            } 
 
            return InternalFindNLSStringEx(m_dataHandle, m_sortName, GetNativeCompareFlags(options) | Win32Native.FIND_ENDSWITH, source, source.Length, source.Length - 1, suffix, suffix.Length) >= 0;
        } 


        public virtual bool IsSuffix(String source, String suffix)
        { 
            return (IsSuffix(source, suffix, 0));
        } 
 
        ////////////////////////////////////////////////////////////////////////
        // 
        //  IndexOf
        //
        //  Returns the first index where value is found in string.  The
        //  search starts from startIndex and ends at endIndex.  Returns -1 if 
        //  the specified value is not found.  If value equals String.Empty,
        //  startIndex is returned.  Throws IndexOutOfRange if startIndex or 
        //  endIndex is less than zero or greater than the length of string. 
        //  Throws ArgumentException if value is null.
        // 
        ////////////////////////////////////////////////////////////////////////


        public unsafe virtual int IndexOf(String source, char value) 
        {
            if (source==null) 
                throw new ArgumentNullException("source"); 
            Contract.EndContractBlock();
 
            return IndexOf(source, value, 0, source.Length, CompareOptions.None);
        }

 
        public unsafe virtual int IndexOf(String source, String value)
        { 
            if (source==null) 
                throw new ArgumentNullException("source");
            Contract.EndContractBlock(); 

            return IndexOf(source, value, 0, source.Length, CompareOptions.None);
        }
 

        public unsafe virtual int IndexOf(String source, char value, CompareOptions options) 
        { 
            if (source==null)
                throw new ArgumentNullException("source"); 
            Contract.EndContractBlock();

            return IndexOf(source, value, 0, source.Length, options);
        } 

 
        public unsafe virtual int IndexOf(String source, String value, CompareOptions options) 
        {
            if (source==null) 
                throw new ArgumentNullException("source");
            Contract.EndContractBlock();

            return IndexOf(source, value, 0, source.Length, options); 
        }
 
 
        public unsafe virtual int IndexOf(String source, char value, int startIndex)
        { 
            if (source == null)
                throw new ArgumentNullException("source");
            Contract.EndContractBlock();
 
            return IndexOf(source, value, startIndex, source.Length - startIndex, CompareOptions.None);
        } 
 

        public unsafe virtual int IndexOf(String source, String value, int startIndex) 
        {
            if (source == null)
                throw new ArgumentNullException("source");
            Contract.EndContractBlock(); 

            return IndexOf(source, value, startIndex, source.Length - startIndex, CompareOptions.None); 
        } 

 
        public unsafe virtual int IndexOf(String source, char value, int startIndex, CompareOptions options)
        {
            if (source == null)
                throw new ArgumentNullException("source"); 
            Contract.EndContractBlock();
 
            return IndexOf(source, value, startIndex, source.Length - startIndex, options); 
        }
 

        public unsafe virtual int IndexOf(String source, String value, int startIndex, CompareOptions options)
        {
            if (source == null) 
                throw new ArgumentNullException("source");
            Contract.EndContractBlock(); 
 
            return IndexOf(source, value, startIndex, source.Length - startIndex, options);
        } 


        public unsafe 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) 
        {
            return IndexOf(source, value, startIndex, count, CompareOptions.None);
        }
 
        [System.Security.SecuritySafeCritical]  // auto-generated
        [ResourceExposure(ResourceScope.None)] 
        [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)] 
        public unsafe virtual int IndexOf(String source, char value, int startIndex, int count, CompareOptions options)
        { 
            // Validate inputs
            if (source == null)
                throw new ArgumentNullException("source");
 
            if (startIndex < 0 || startIndex > source.Length)
                throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index")); 
 
            if (count < 0 || startIndex > source.Length - count)
                throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_Count")); 
            Contract.EndContractBlock();

            if (options == CompareOptions.OrdinalIgnoreCase)
            { 
                //
                return TextInfo.IndexOfStringOrdinalIgnoreCase(source, value.ToString(), startIndex, count); 
            } 

            // Validate CompareOptions 
            // Ordinal can't be selected with other flags
            if ((options & ValidIndexMaskOffFlags) != 0 && (options != CompareOptions.Ordinal))
                throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), "options");
 
            return InternalFindNLSStringEx(m_dataHandle, m_sortName, GetNativeCompareFlags(options) | Win32Native.FIND_FROMSTART, source, count, startIndex, new String(value, 1), 1);
        } 
 

        [System.Security.SecuritySafeCritical]  // auto-generated 
        public unsafe virtual int IndexOf(String source, String value, int startIndex, int count, CompareOptions options)
        {
            // Validate inputs
            if (source == null) 
                throw new ArgumentNullException("source");
            if (value == null) 
                throw new ArgumentNullException("value"); 

            if (startIndex > source.Length) 
            {
                throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index"));
            }
            Contract.EndContractBlock(); 

            // In Everett we used to return -1 for empty string even if startIndex is negative number so we keeping same behavior here. 
            // We return 0 if both source and value are empty strings for Everett compatibility too. 
            if (source.Length == 0)
            { 
                if (value.Length == 0)
                {
                    return 0;
                } 
                return -1;
            } 
 
            if (startIndex < 0)
            { 
                throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index"));
            }

            if (count < 0 || startIndex > source.Length - count) 
                throw new ArgumentOutOfRangeException("count",Environment.GetResourceString("ArgumentOutOfRange_Count"));
 
            if (options == CompareOptions.OrdinalIgnoreCase) 
            {
                return TextInfo.IndexOfStringOrdinalIgnoreCase(source, value, startIndex, count); 
            }

            // Validate CompareOptions
            // Ordinal can't be selected with other flags 
            if ((options & ValidIndexMaskOffFlags) != 0 && (options != CompareOptions.Ordinal))
                throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), "options"); 
 
            return InternalFindNLSStringEx(m_dataHandle, m_sortName, GetNativeCompareFlags(options) | Win32Native.FIND_FROMSTART, source, count, startIndex, value, value.Length);
        } 

        ////////////////////////////////////////////////////////////////////////
        //
        //  LastIndexOf 
        //
        //  Returns the last index where value is found in string.  The 
        //  search starts from startIndex and ends at endIndex.  Returns -1 if 
        //  the specified value is not found.  If value equals String.Empty,
        //  endIndex is returned.  Throws IndexOutOfRange if startIndex or 
        //  endIndex is less than zero or greater than the length of string.
        //  Throws ArgumentException if value is null.
        //
        //////////////////////////////////////////////////////////////////////// 

 
        public unsafe virtual int LastIndexOf(String source, char value) 
        {
            if (source==null) 
                throw new ArgumentNullException("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); 
        } 

 
        [System.Security.SecuritySafeCritical]  // auto-generated
        public unsafe virtual int LastIndexOf(String source, String value)
        {
            if (source==null) 
                throw new ArgumentNullException("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);
        }

 
        public unsafe virtual int LastIndexOf(String source, char value, CompareOptions options)
        { 
            if (source==null) 
                throw new ArgumentNullException("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); 
        }
 
        [System.Security.SecuritySafeCritical]  // auto-generated 
        public unsafe virtual int LastIndexOf(String source, String value, CompareOptions options)
        { 
            if (source==null)
                throw new ArgumentNullException("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); 
        }
 

        public unsafe 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)
        { 
            return LastIndexOf(source, value, startIndex, startIndex + 1, CompareOptions.None);
        }

 
        public unsafe 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)
        {
            return LastIndexOf(source, value, startIndex, startIndex + 1, options); 
        }
 
 
        public unsafe 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)
        { 
            return LastIndexOf(source, value, startIndex, count, CompareOptions.None); 
        }
 

        [System.Security.SecuritySafeCritical]  // auto-generated
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)] 
        public unsafe virtual int LastIndexOf(String source, char value, int startIndex, int count, CompareOptions options)
        { 
            // Verify Arguments 
            if (source==null)
                throw new ArgumentNullException("source"); 
            Contract.EndContractBlock();

            // Validate CompareOptions
            // Ordinal can't be selected with other flags 
            if ((options & ValidIndexMaskOffFlags) != 0 &&
                (options != CompareOptions.Ordinal) && 
                (options != CompareOptions.OrdinalIgnoreCase)) 
                throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), "options");
 
            // Special case for 0 length input strings
            if (source.Length == 0 && (startIndex == -1 || startIndex == 0))
                return -1;
 
            // Make sure we're not out of range
            if (startIndex < 0 || startIndex > source.Length) 
                throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index")); 

            // Make sure that we allow startIndex == source.Length 
            if (startIndex == source.Length)
            {
                startIndex--;
                if (count > 0) 
                    count--;
            } 
 
            // 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("count", Environment.GetResourceString("ArgumentOutOfRange_Count"));

            if (options == CompareOptions.OrdinalIgnoreCase)
            { 
                //
                return TextInfo.LastIndexOfStringOrdinalIgnoreCase(source, value.ToString(), startIndex, count); 
            } 

            return InternalFindNLSStringEx(m_dataHandle, m_sortName, GetNativeCompareFlags(options) | Win32Native.FIND_FROMEND, source, count, startIndex, new String(value, 1), 1); 
        }


        [System.Security.SecuritySafeCritical]  // auto-generated 
        public unsafe virtual int LastIndexOf(String source, String value, int startIndex, int count, CompareOptions options)
        { 
            // Verify Arguments 
            if (source == null)
                throw new ArgumentNullException("source"); 
            if (value == null)
                throw new ArgumentNullException("value");
            Contract.EndContractBlock();
 
            // Validate CompareOptions
            // Ordinal can't be selected with other flags 
            if ((options & ValidIndexMaskOffFlags) != 0 && 
                (options != CompareOptions.Ordinal) &&
                (options != CompareOptions.OrdinalIgnoreCase)) 
                throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), "options");

            // Special case for 0 length input strings
            if (source.Length == 0 && (startIndex == -1 || startIndex == 0)) 
                return (value.Length == 0) ? 0 : -1;
 
            // Make sure we're not out of range 
            if (startIndex < 0 || startIndex > source.Length)
                throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index")); 

            // Make sure that we allow startIndex == source.Length
            if (startIndex == source.Length)
            { 
                startIndex--;
                if (count > 0) 
                    count--; 

                // If we are looking for nothing, just return 0 
                if (value.Length == 0 && count >= 0 && startIndex - count + 1 >= 0)
                    return startIndex;
            }
 
            // 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("count", Environment.GetResourceString("ArgumentOutOfRange_Count")); 

            if (options == CompareOptions.OrdinalIgnoreCase) 
            {
                return TextInfo.LastIndexOfStringOrdinalIgnoreCase(source, value, startIndex, count);
            }
 
            return InternalFindNLSStringEx(m_dataHandle, m_sortName, GetNativeCompareFlags(options) | Win32Native.FIND_FROMEND, source, count, startIndex, value, value.Length);
        } 
 

        //////////////////////////////////////////////////////////////////////// 
        //
        //  GetSortKey
        //
        //  Gets the SortKey for the given string with the given options. 
        //
        //////////////////////////////////////////////////////////////////////// 
#if !FEATURE_PAL 
        public unsafe virtual SortKey GetSortKey(String source, CompareOptions options)
        { 
            return CreateSortKey(source, options);
        }

 
        public unsafe virtual SortKey GetSortKey(String source)
        { 
            return CreateSortKey(source, CompareOptions.None); 
        }
 
        [System.Security.SecuritySafeCritical]
        private SortKey CreateSortKey(String source, CompareOptions options)
        {
            if (source==null) { throw new ArgumentNullException("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"), "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 = new byte[0]; 
                // 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_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"), "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_sortName, flags, source, source.Length, keyData, keyData.Length);
            } 
            else
            { 
                source = String.Empty; // back to original 
            }
 
            return new SortKey(Name, source, options, keyData);
        }

#endif // !FEATURE_PAL 

 
        //////////////////////////////////////////////////////////////////////// 
        //
        //  Equals 
        //
        //  Implements Object.Equals().  Returns a boolean indicating whether
        //  or not object refers to the same CompareInfo as the current
        //  instance. 
        //
        //////////////////////////////////////////////////////////////////////// 
 

        public override bool Equals(Object value) 
        {
            CompareInfo that = value as CompareInfo;

            if (that != null) 
            {
                return this.Name == that.Name; 
            } 

            return (false); 
        }


        //////////////////////////////////////////////////////////////////////// 
        //
        //  GetHashCode 
        // 
        //  Implements Object.GetHashCode().  Returns the hash code for the
        //  CompareInfo.  The hash code is guaranteed to be the same for 
        //  CompareInfo A and B where A.Equals(B) is true.
        //
        ////////////////////////////////////////////////////////////////////////
 

        public override int GetHashCode() 
        { 
            return (this.Name.GetHashCode());
        } 

        ////////////////////////////////////////////////////////////////////////
        //
        //  GetHashCodeOfString 
        //
        //  This internal method allows a method that allows the equivalent of creating a Sortkey for a 
        //  string from CompareInfo, and generate a hashcode value from it.  It is not very convenient 
        //  to use this method as is and it creates an unnecessary Sortkey object that will be GC'ed.
        // 
        //  The hash code is guaranteed to be the same for string A and B where A.Equals(B) is true and both
        //  the CompareInfo and the CompareOptions are the same. If two different CompareInfo objects
        //  treat the string the same way, this implementation will treat them differently (the same way that
        //  Sortkey does at the moment). 
        //
        //  This method will never be made public itself, but public consumers of it could be created, e.g.: 
        // 
        //      string.GetHashCode(CultureInfo)
        //      string.GetHashCode(CompareInfo) 
        //      string.GetHashCode(CultureInfo, CompareOptions)
        //      string.GetHashCode(CompareInfo, CompareOptions)
        //      etc.
        // 
        //  (the methods above that take a CultureInfo would use CultureInfo.CompareInfo)
        // 
        //////////////////////////////////////////////////////////////////////// 
        [System.Security.SecuritySafeCritical]  // auto-generated
        internal int GetHashCodeOfString(string source, CompareOptions options) 
        {
            //
            //  Parameter validation
            // 
            if(null == source)
            { 
                throw new ArgumentNullException("source"); 
            }
 
            if ((options & ValidHashCodeOfStringMaskOffFlags) != 0)
            {
                throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), "options");
            } 
            Contract.EndContractBlock();
 
            if(0 == source.Length) 
            {
                return(0); 
            }

            //
            //////////////////////////////////////////////////////////////////////// 
            return (InternalGetGlobalizedHashCode(m_dataHandle, this.m_sortName, source, source.Length, GetNativeCompareFlags(options)));
        } 
 
        ////////////////////////////////////////////////////////////////////////
        // 
        //  ToString
        //
        //  Implements Object.ToString().  Returns a string describing the
        //  CompareInfo. 
        //
        //////////////////////////////////////////////////////////////////////// 
 

        public override String ToString() 
        {
            return ("CompareInfo - " + this.Name);
        }
 
#if FEATURE_USE_LCID
        public int LCID 
        { 
            get
            { 
                return CultureInfo.GetCultureInfo(this.Name).LCID;
            }
        }
#endif 

#if !FEATURE_CORECLR 
        [System.Security.SecuritySafeCritical] 
        internal static IntPtr InternalInitSortHandle(String localeName)
        { 
            return InternalInitSortHandle(localeName, Version);
        }

        private const int SORT_VERSION_WHIDBEY = 0x00001000; 

        internal static bool IsLegacy20SortingBehaviorRequested 
        { 
            get
            { 
                return Version == SORT_VERSION_WHIDBEY;
            }
        }
 
        private static uint Version
        { 
            [System.Security.SecuritySafeCritical] 
            get
            { 
                Nullable compatSwitch = AppDomain.CurrentDomain.IsCompatibilitySwitchSet("NetFx40_Legacy20SortingBehavior");

                if (compatSwitch.HasValue && compatSwitch.Value)
                { 
                    return SORT_VERSION_WHIDBEY;
                } 
 
                return InternalGetSortVersion();
            } 
        }

        [System.Security.SecurityCritical]
        [ResourceExposure(ResourceScope.None)] 
        [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
        [SuppressUnmanagedCodeSecurity] 
        private static extern uint InternalGetSortVersion(); 

        [System.Security.SecurityCritical] 
        [ResourceExposure(ResourceScope.None)]
        [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
        [SuppressUnmanagedCodeSecurity]
        private static extern IntPtr InternalInitSortHandle(String localeName, uint version); 
#endif
 
        // Get a locale sensitive sort hash code from native code -- COMNlsInfo::InternalGetGlobalizedHashCode 
        [System.Security.SecurityCritical]  // auto-generated
        [ResourceExposure(ResourceScope.None)] 
        [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
        [SuppressUnmanagedCodeSecurity]
        private static extern int InternalGetGlobalizedHashCode(IntPtr handle, string localeName, string source, int length, int dwFlags);
 
        // Use native API calls to see if this string is entirely defined -- COMNlsInfo::InternalIsSortable
        [System.Security.SecurityCritical]  // auto-generated 
        [ResourceExposure(ResourceScope.None)] 
        [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
        [SuppressUnmanagedCodeSecurity] 
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool InternalIsSortable(IntPtr handle, String source, int length);

        // Compare a string using the native API calls -- COMNlsInfo::InternalCompareString 
        [System.Security.SecurityCritical]  // auto-generated
        [ResourceExposure(ResourceScope.Process)] 
        [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] 
        [SuppressUnmanagedCodeSecurity]
        private static extern int InternalCompareString(IntPtr handle, 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 
        [System.Security.SecurityCritical]  // auto-generated
        [ResourceExposure(ResourceScope.None)] 
        [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] 
        [SuppressUnmanagedCodeSecurity]
        private static extern int InternalFindNLSStringEx(IntPtr handle, 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
        [System.Security.SecurityCritical]  // auto-generated
        [ResourceExposure(ResourceScope.None)] 
        [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
        [SuppressUnmanagedCodeSecurity] 
        private static extern int InternalGetSortKey(IntPtr handle, String localeName, int flags, String source, int sourceCount, byte[] target, int targetCount); 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// ==++== 
//
//   Copyright (c) Microsoft Corporation.  All rights reserved.
//
// ==--== 
////////////////////////////////////////////////////////////////////////////
// 
//  Class:    CompareInfo 
//
// 
//  Purpose:  This class implements a set of methods for comparing
//            strings.
//
//  Date:     August 12, 1998 
//
//////////////////////////////////////////////////////////////////////////// 
 
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 System.Security.Permissions; 
    using Microsoft.Win32;
    using System.Security; 
    using System.Security.Principal;
    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]
    [Flags] 
    [System.Runtime.InteropServices.ComVisible(true)]
    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.
    } 

 
    [Serializable] 
[System.Runtime.InteropServices.ComVisible(true)]
 
    public class CompareInfo
#if FEATURE_SERIALIZATION
    : IDeserializationCallback
#endif 
#if NOT_DEFINED // to kepp Thinner happy
    , Object 
#endif 

    { 
        // Mask used to check if IndexOf()/LastIndexOf()/IsPrefix()/IsPostfix() has the right flags.
        private const CompareOptions ValidIndexMaskOffFlags =
            ~(CompareOptions.IgnoreCase | CompareOptions.IgnoreSymbols | CompareOptions.IgnoreNonSpace |
              CompareOptions.IgnoreWidth | CompareOptions.IgnoreKanaType); 

        // Mask used to check if Compare() has the right flags. 
        private const CompareOptions ValidCompareMaskOffFlags = 
            ~(CompareOptions.IgnoreCase | CompareOptions.IgnoreSymbols | CompareOptions.IgnoreNonSpace |
              CompareOptions.IgnoreWidth | CompareOptions.IgnoreKanaType | CompareOptions.StringSort); 

        // Mask used to check if GetHashCodeOfString() has the right flags.
        private const CompareOptions ValidHashCodeOfStringMaskOffFlags =
            ~(CompareOptions.IgnoreCase | CompareOptions.IgnoreSymbols | CompareOptions.IgnoreNonSpace | 
              CompareOptions.IgnoreWidth | CompareOptions.IgnoreKanaType);
 
        // 
        // 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. 
        // The interesting part is that since haw-US doesn't have its own sort, it has to point at another
        // locale, which is what SCOMPAREINFO does.

        [OptionalField(VersionAdded = 2)] 
        private String m_name;  // The name used to construct this CompareInfo
 
        [NonSerialized] 
        private String m_sortName; // The name that defines our behavior
 
        [NonSerialized]
        private IntPtr m_dataHandle;

        //////////////////////////////////////////////////////////////////////// 
        //
        //  CompareInfo Constructor 
        // 
        //
        //////////////////////////////////////////////////////////////////////// 
        // Constructs an instance that most closely corresponds to the NLS locale
        // identifier.
        [System.Security.SecuritySafeCritical]
        internal CompareInfo(CultureInfo culture) 
        {
            this.m_name = culture.m_name; 
            this.m_sortName = culture.SortName; 

#if !FEATURE_CORECLR 
            this.m_dataHandle = InternalInitSortHandle(m_sortName);
#endif
        }
 
        /*=================================GetCompareInfo==========================
        **Action: Get the CompareInfo constructed from the data table in the specified assembly for the specified culture. 
        **       Warning: The assembly versioning mechanism is dead! 
        **Returns: The CompareInfo for the specified culture.
        **Arguments: 
        **   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 
        //
        public static CompareInfo GetCompareInfo(int culture, Assembly assembly){ 
            // Parameter checking.
            if (assembly == null) {
                throw new ArgumentNullException("assembly");
            } 
            if (assembly!=typeof(Object).Module.Assembly) {
                throw new ArgumentException(Environment.GetResourceString("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.
        **Exceptions: 
        **  ArugmentNullException when the assembly is null
        **  ArgumentException if name is invalid. 
        ============================================================================*/ 
        //
        public static CompareInfo GetCompareInfo(String name, Assembly assembly){ 
            if (name == null || assembly == null) {
                throw new ArgumentNullException(name == null ? "name" : "assembly");
            }
            Contract.EndContractBlock(); 

            if (assembly!=typeof(Object).Module.Assembly) { 
                throw new ArgumentException(Environment.GetResourceString("Argument_OnlyMscorlib")); 
            }
 
            return GetCompareInfo(name);
        }

        /*=================================GetCompareInfo========================== 
        **Action: Get the CompareInfo for the specified culture.
        ** This method is provided for ease of integration with NLS-based software. 
        **Returns: The CompareInfo for the specified culture. 
        **Arguments:
        **   culture    the ID of the culture. 
        **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", "culture"));
            } 

            return CultureInfo.GetCultureInfo(culture).CompareInfo; 
        } 
#endif
 
        /*=================================GetCompareInfo==========================
        **Action: Get the CompareInfo for the specified culture.
        **Returns: The CompareInfo for the specified culture.
        **Arguments: 
        **   name    the name of the culture.
        **Exceptions: 
        **  ArgumentException if name is invalid. 
        ============================================================================*/
 
        [System.Security.SecuritySafeCritical]  // auto-generated
        public static CompareInfo GetCompareInfo(String name)
        {
            if (name == null) 
            {
                throw new ArgumentNullException("name"); 
            } 
            Contract.EndContractBlock();
 
            return CultureInfo.GetCultureInfo(name).CompareInfo;
        }

        [System.Runtime.InteropServices.ComVisible(false)] 
        public static bool IsSortable(char ch) {
            return(IsSortable(ch.ToString())); 
        } 

        [System.Security.SecuritySafeCritical] 
        [System.Runtime.InteropServices.ComVisible(false)]
        public static bool IsSortable(String text) {
            if (text == null) {
                // A null param is invalid here. 
                throw new ArgumentNullException("text");
            } 
 
            if (0 == text.Length) {
                // A zero length string is not invalid, but it is also not sortable. 
                return(false);
            }

            return (InternalIsSortable(CultureInfo.InvariantCulture.CompareInfo.m_dataHandle, text, text.Length)); 
        }
 
 
#if FEATURE_SERIALIZATION // Only defined when FEATURE_USE_LCID is also defined
#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;
        } 

        [System.Security.SecuritySafeCritical] 
        private void OnDeserialized() 
        {
            CultureInfo ci; 
            // If we didn't have a name, use the LCID
            if (this.m_name == null)
            {
                // From whidbey, didn't have a name 
                ci = CultureInfo.GetCultureInfo(this.culture);
                this.m_name = ci.m_name; 
            } 
            else
            { 
                ci = CultureInfo.GetCultureInfo(m_name);
            }
            this.m_sortName = ci.SortName;
#if !FEATURE_CORECLR 
            this.m_dataHandle = InternalInitSortHandle(m_sortName);
#endif 
        } 

        [OnDeserialized] 
        private void OnDeserialized(StreamingContext ctx)
        {
            OnDeserialized();
        } 

        [OnSerializing] 
        private void OnSerializing(StreamingContext ctx) 
        {
            // 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)
            Contract.Assert(m_name != null, "CompareInfo.OnSerializing - expected m_name to be set already");
        }
 
        void IDeserializationCallback.OnDeserialization(Object sender)
        { 
            OnDeserialized(); 
        }
 
#endregion Serialization
#endif // FEATURE_SERIALIZATION

 
        ///////////////////////////----- Name -----/////////////////////////////////
        // 
        //  Returns the name of the culture (well actually, of the sort). 
        //  Very important for providing a non-LCID way of identifying
        //  what the sort is. 
        //
        //  Note that this name isn't dereferenced in case the CompareInfo is a different locale
        //  which is consistent with the behaviors of earlier versions.  (so if you ask for a sort
        //  and the locale's changed behavior, then you'll get changed behavior, which is like 
        //  what happens for a version update)
        // 
        //////////////////////////////////////////////////////////////////////// 

        [System.Runtime.InteropServices.ComVisible(false)] 
        public virtual String Name
        {
            get
            { 
                Contract.Assert(m_name != null, "CompareInfo.Name Expected m_name to be set");
                if (m_name == "zh-CHT" || m_name == "zh-CHS") 
                { 
                    return m_name;
                } 

                return (m_sortName);
            }
        } 

        // 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 

        [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
            // 
            Contract.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; }

            Contract.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");

            return nativeCompareFlags;
        } 

 
        //////////////////////////////////////////////////////////////////////// 
        //
        //  Compare 
        //
        //  Compares the two strings with the given options.  Returns 0 if the
        //  two strings are equal, a number less than 0 if string1 is less
        //  than string2, and a number greater than 0 if string1 is greater 
        //  than string2.
        // 
        //////////////////////////////////////////////////////////////////////// 

 
        public virtual int Compare(String string1, String string2)
        {
            return (Compare(string1, string2, CompareOptions.None));
        } 

        [System.Security.SecuritySafeCritical]  // auto-generated 
        public unsafe virtual int Compare(String string1, String string2, CompareOptions options){ 

            if (options == CompareOptions.OrdinalIgnoreCase) 
            {
                return String.Compare(string1, string2, StringComparison.OrdinalIgnoreCase);
            }
 
            // Verify the options before we do any real comparison.
            if ((options & CompareOptions.Ordinal) != 0) 
            { 
                if (options != CompareOptions.Ordinal)
                { 
                    throw new ArgumentException(Environment.GetResourceString("Argument_CompareOptionOrdinal"), "options");
                        }
                return String.CompareOrdinal(string1, string2);
                    } 

            if ((options & ValidCompareMaskOffFlags) != 0) 
            { 
                throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), "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) {
                    return (0);     // Equal 
                } 
                return (-1);    // null < non-null
            } 
            if (string2 == null) {
                return (1);     // non-null > null
            }
 
            return InternalCompareString(m_dataHandle, m_sortName, string1, 0, string1.Length, string2, 0, string2.Length, GetNativeCompareFlags(options));
        } 
 

        //////////////////////////////////////////////////////////////////////// 
        //
        //  Compare
        //
        //  Compares the specified regions of the two strings with the given 
        //  options.
        //  Returns 0 if the two strings are equal, a number less than 0 if 
        //  string1 is less than string2, and a number greater than 0 if 
        //  string1 is greater than string2.
        // 
        ////////////////////////////////////////////////////////////////////////


        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)
        {
            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)
        { 
            return Compare(string1, offset1, string2, offset2, 0);
        }

 
        [System.Security.SecuritySafeCritical]  // auto-generated
        public unsafe 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? 1: -1);
                return (result); 
            }
 
            // Verify inputs 
            if (length1 < 0 || length2 < 0)
            { 
                throw new ArgumentOutOfRangeException((length1 < 0) ? "length1" : "length2", Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum"));
            }
            if (offset1 < 0 || offset2 < 0)
            { 
                throw new ArgumentOutOfRangeException((offset1 < 0) ? "offset1" : "offset2", Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum"));
            } 
            if (offset1 > (string1 == null ? 0 : string1.Length) - length1) 
            {
                throw new ArgumentOutOfRangeException("string1", Environment.GetResourceString("ArgumentOutOfRange_OffsetLength")); 
            }
            if (offset2 > (string2 == null ? 0 : string2.Length) - length2)
            {
                throw new ArgumentOutOfRangeException("string2", Environment.GetResourceString("ArgumentOutOfRange_OffsetLength")); 
            }
            if ((options & CompareOptions.Ordinal) != 0) 
            { 
                if (options != CompareOptions.Ordinal)
                { 
                    throw new ArgumentException(Environment.GetResourceString("Argument_CompareOptionOrdinal"),
                                                "options");
                }
            } 
            else if ((options & ValidCompareMaskOffFlags) != 0)
            { 
                throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), "options"); 
            }
 
            //
            // Check for the null case.
            //
            if (string1 == null) 
            {
                if (string2 == null) 
                { 
                    return (0);
                } 
                return (-1);
            }
            if (string2 == null)
            { 
                return (1);
            } 
 
            if (options == CompareOptions.Ordinal)
            { 
                return CompareOrdinal(string1, offset1, length1,
                                      string2, offset2, length2);
            }
            return InternalCompareString(this.m_dataHandle, this.m_sortName, 
                                         string1, offset1, length1,
                                         string2, offset2, length2, 
                                         GetNativeCompareFlags(options)); 
        }
 
        [System.Security.SecurityCritical]
        private static int CompareOrdinal(string string1, int offset1, int length1, string string2, int offset2, int length2)
        {
            int result = String.nativeCompareOrdinalEx(string1, offset1, string2, offset2, 
                                                       (length1 < length2 ? length1 : length2));
            if ((length1 != length2) && result == 0) 
            { 
                return (length1 > length2 ? 1 : -1);
            } 
            return (result);
        }

        //////////////////////////////////////////////////////////////////////// 
        //
        //  IsPrefix 
        // 
        //  Determines whether prefix is a prefix of string.  If prefix equals
        //  String.Empty, true is returned. 
        //
        ////////////////////////////////////////////////////////////////////////

 
        [System.Security.SecuritySafeCritical]  // auto-generated
        public unsafe virtual bool IsPrefix(String source, String prefix, CompareOptions options) 
        { 
            if (source == null || prefix == null) {
                throw new ArgumentNullException((source == null ? "source" : "prefix"), 
                    Environment.GetResourceString("ArgumentNull_String"));
            }
            Contract.EndContractBlock();
            int prefixLen = prefix.Length; 

            if (prefixLen == 0) 
            { 
                return (true);
            } 

            if (options == CompareOptions.OrdinalIgnoreCase)
            {
                return source.StartsWith(prefix, StringComparison.OrdinalIgnoreCase); 
            }
 
            if (options == CompareOptions.Ordinal) 
            {
                return source.StartsWith(prefix, StringComparison.Ordinal); 
            }

            if ((options & ValidIndexMaskOffFlags) != 0) {
                throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), "options"); 
            }
 
            return (InternalFindNLSStringEx(m_dataHandle, m_sortName, GetNativeCompareFlags(options) | Win32Native.FIND_STARTSWITH, source, source.Length, 0, prefix, prefix.Length) > -1); 
        }
 
        public virtual bool IsPrefix(String source, String prefix)
        {
            return (IsPrefix(source, prefix, 0));
        } 

 
        //////////////////////////////////////////////////////////////////////// 
        //
        //  IsSuffix 
        //
        //  Determines whether suffix is a suffix of string.  If suffix equals
        //  String.Empty, true is returned.
        // 
        ////////////////////////////////////////////////////////////////////////
 
 
        [System.Security.SecuritySafeCritical]  // auto-generated
        public unsafe virtual bool IsSuffix(String source, String suffix, CompareOptions options) 
        {
            if (source == null || suffix == null) {
                throw new ArgumentNullException((source == null ? "source" : "suffix"),
                    Environment.GetResourceString("ArgumentNull_String")); 
            }
            Contract.EndContractBlock(); 
            int suffixLen = suffix.Length; 

            if (suffixLen == 0) 
            {
                return (true);
            }
 
            if (options == CompareOptions.OrdinalIgnoreCase) {
                return source.EndsWith(suffix, StringComparison.OrdinalIgnoreCase); 
            } 

            if (options == CompareOptions.Ordinal) { 
                return source.EndsWith(suffix, StringComparison.Ordinal);
            }

            if ((options & ValidIndexMaskOffFlags) != 0) { 
                throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), "options");
            } 
 
            return InternalFindNLSStringEx(m_dataHandle, m_sortName, GetNativeCompareFlags(options) | Win32Native.FIND_ENDSWITH, source, source.Length, source.Length - 1, suffix, suffix.Length) >= 0;
        } 


        public virtual bool IsSuffix(String source, String suffix)
        { 
            return (IsSuffix(source, suffix, 0));
        } 
 
        ////////////////////////////////////////////////////////////////////////
        // 
        //  IndexOf
        //
        //  Returns the first index where value is found in string.  The
        //  search starts from startIndex and ends at endIndex.  Returns -1 if 
        //  the specified value is not found.  If value equals String.Empty,
        //  startIndex is returned.  Throws IndexOutOfRange if startIndex or 
        //  endIndex is less than zero or greater than the length of string. 
        //  Throws ArgumentException if value is null.
        // 
        ////////////////////////////////////////////////////////////////////////


        public unsafe virtual int IndexOf(String source, char value) 
        {
            if (source==null) 
                throw new ArgumentNullException("source"); 
            Contract.EndContractBlock();
 
            return IndexOf(source, value, 0, source.Length, CompareOptions.None);
        }

 
        public unsafe virtual int IndexOf(String source, String value)
        { 
            if (source==null) 
                throw new ArgumentNullException("source");
            Contract.EndContractBlock(); 

            return IndexOf(source, value, 0, source.Length, CompareOptions.None);
        }
 

        public unsafe virtual int IndexOf(String source, char value, CompareOptions options) 
        { 
            if (source==null)
                throw new ArgumentNullException("source"); 
            Contract.EndContractBlock();

            return IndexOf(source, value, 0, source.Length, options);
        } 

 
        public unsafe virtual int IndexOf(String source, String value, CompareOptions options) 
        {
            if (source==null) 
                throw new ArgumentNullException("source");
            Contract.EndContractBlock();

            return IndexOf(source, value, 0, source.Length, options); 
        }
 
 
        public unsafe virtual int IndexOf(String source, char value, int startIndex)
        { 
            if (source == null)
                throw new ArgumentNullException("source");
            Contract.EndContractBlock();
 
            return IndexOf(source, value, startIndex, source.Length - startIndex, CompareOptions.None);
        } 
 

        public unsafe virtual int IndexOf(String source, String value, int startIndex) 
        {
            if (source == null)
                throw new ArgumentNullException("source");
            Contract.EndContractBlock(); 

            return IndexOf(source, value, startIndex, source.Length - startIndex, CompareOptions.None); 
        } 

 
        public unsafe virtual int IndexOf(String source, char value, int startIndex, CompareOptions options)
        {
            if (source == null)
                throw new ArgumentNullException("source"); 
            Contract.EndContractBlock();
 
            return IndexOf(source, value, startIndex, source.Length - startIndex, options); 
        }
 

        public unsafe virtual int IndexOf(String source, String value, int startIndex, CompareOptions options)
        {
            if (source == null) 
                throw new ArgumentNullException("source");
            Contract.EndContractBlock(); 
 
            return IndexOf(source, value, startIndex, source.Length - startIndex, options);
        } 


        public unsafe 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) 
        {
            return IndexOf(source, value, startIndex, count, CompareOptions.None);
        }
 
        [System.Security.SecuritySafeCritical]  // auto-generated
        [ResourceExposure(ResourceScope.None)] 
        [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)] 
        public unsafe virtual int IndexOf(String source, char value, int startIndex, int count, CompareOptions options)
        { 
            // Validate inputs
            if (source == null)
                throw new ArgumentNullException("source");
 
            if (startIndex < 0 || startIndex > source.Length)
                throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index")); 
 
            if (count < 0 || startIndex > source.Length - count)
                throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_Count")); 
            Contract.EndContractBlock();

            if (options == CompareOptions.OrdinalIgnoreCase)
            { 
                //
                return TextInfo.IndexOfStringOrdinalIgnoreCase(source, value.ToString(), startIndex, count); 
            } 

            // Validate CompareOptions 
            // Ordinal can't be selected with other flags
            if ((options & ValidIndexMaskOffFlags) != 0 && (options != CompareOptions.Ordinal))
                throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), "options");
 
            return InternalFindNLSStringEx(m_dataHandle, m_sortName, GetNativeCompareFlags(options) | Win32Native.FIND_FROMSTART, source, count, startIndex, new String(value, 1), 1);
        } 
 

        [System.Security.SecuritySafeCritical]  // auto-generated 
        public unsafe virtual int IndexOf(String source, String value, int startIndex, int count, CompareOptions options)
        {
            // Validate inputs
            if (source == null) 
                throw new ArgumentNullException("source");
            if (value == null) 
                throw new ArgumentNullException("value"); 

            if (startIndex > source.Length) 
            {
                throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index"));
            }
            Contract.EndContractBlock(); 

            // In Everett we used to return -1 for empty string even if startIndex is negative number so we keeping same behavior here. 
            // We return 0 if both source and value are empty strings for Everett compatibility too. 
            if (source.Length == 0)
            { 
                if (value.Length == 0)
                {
                    return 0;
                } 
                return -1;
            } 
 
            if (startIndex < 0)
            { 
                throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index"));
            }

            if (count < 0 || startIndex > source.Length - count) 
                throw new ArgumentOutOfRangeException("count",Environment.GetResourceString("ArgumentOutOfRange_Count"));
 
            if (options == CompareOptions.OrdinalIgnoreCase) 
            {
                return TextInfo.IndexOfStringOrdinalIgnoreCase(source, value, startIndex, count); 
            }

            // Validate CompareOptions
            // Ordinal can't be selected with other flags 
            if ((options & ValidIndexMaskOffFlags) != 0 && (options != CompareOptions.Ordinal))
                throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), "options"); 
 
            return InternalFindNLSStringEx(m_dataHandle, m_sortName, GetNativeCompareFlags(options) | Win32Native.FIND_FROMSTART, source, count, startIndex, value, value.Length);
        } 

        ////////////////////////////////////////////////////////////////////////
        //
        //  LastIndexOf 
        //
        //  Returns the last index where value is found in string.  The 
        //  search starts from startIndex and ends at endIndex.  Returns -1 if 
        //  the specified value is not found.  If value equals String.Empty,
        //  endIndex is returned.  Throws IndexOutOfRange if startIndex or 
        //  endIndex is less than zero or greater than the length of string.
        //  Throws ArgumentException if value is null.
        //
        //////////////////////////////////////////////////////////////////////// 

 
        public unsafe virtual int LastIndexOf(String source, char value) 
        {
            if (source==null) 
                throw new ArgumentNullException("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); 
        } 

 
        [System.Security.SecuritySafeCritical]  // auto-generated
        public unsafe virtual int LastIndexOf(String source, String value)
        {
            if (source==null) 
                throw new ArgumentNullException("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);
        }

 
        public unsafe virtual int LastIndexOf(String source, char value, CompareOptions options)
        { 
            if (source==null) 
                throw new ArgumentNullException("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); 
        }
 
        [System.Security.SecuritySafeCritical]  // auto-generated 
        public unsafe virtual int LastIndexOf(String source, String value, CompareOptions options)
        { 
            if (source==null)
                throw new ArgumentNullException("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); 
        }
 

        public unsafe 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)
        { 
            return LastIndexOf(source, value, startIndex, startIndex + 1, CompareOptions.None);
        }

 
        public unsafe 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)
        {
            return LastIndexOf(source, value, startIndex, startIndex + 1, options); 
        }
 
 
        public unsafe 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)
        { 
            return LastIndexOf(source, value, startIndex, count, CompareOptions.None); 
        }
 

        [System.Security.SecuritySafeCritical]  // auto-generated
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)] 
        public unsafe virtual int LastIndexOf(String source, char value, int startIndex, int count, CompareOptions options)
        { 
            // Verify Arguments 
            if (source==null)
                throw new ArgumentNullException("source"); 
            Contract.EndContractBlock();

            // Validate CompareOptions
            // Ordinal can't be selected with other flags 
            if ((options & ValidIndexMaskOffFlags) != 0 &&
                (options != CompareOptions.Ordinal) && 
                (options != CompareOptions.OrdinalIgnoreCase)) 
                throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), "options");
 
            // Special case for 0 length input strings
            if (source.Length == 0 && (startIndex == -1 || startIndex == 0))
                return -1;
 
            // Make sure we're not out of range
            if (startIndex < 0 || startIndex > source.Length) 
                throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index")); 

            // Make sure that we allow startIndex == source.Length 
            if (startIndex == source.Length)
            {
                startIndex--;
                if (count > 0) 
                    count--;
            } 
 
            // 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("count", Environment.GetResourceString("ArgumentOutOfRange_Count"));

            if (options == CompareOptions.OrdinalIgnoreCase)
            { 
                //
                return TextInfo.LastIndexOfStringOrdinalIgnoreCase(source, value.ToString(), startIndex, count); 
            } 

            return InternalFindNLSStringEx(m_dataHandle, m_sortName, GetNativeCompareFlags(options) | Win32Native.FIND_FROMEND, source, count, startIndex, new String(value, 1), 1); 
        }


        [System.Security.SecuritySafeCritical]  // auto-generated 
        public unsafe virtual int LastIndexOf(String source, String value, int startIndex, int count, CompareOptions options)
        { 
            // Verify Arguments 
            if (source == null)
                throw new ArgumentNullException("source"); 
            if (value == null)
                throw new ArgumentNullException("value");
            Contract.EndContractBlock();
 
            // Validate CompareOptions
            // Ordinal can't be selected with other flags 
            if ((options & ValidIndexMaskOffFlags) != 0 && 
                (options != CompareOptions.Ordinal) &&
                (options != CompareOptions.OrdinalIgnoreCase)) 
                throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), "options");

            // Special case for 0 length input strings
            if (source.Length == 0 && (startIndex == -1 || startIndex == 0)) 
                return (value.Length == 0) ? 0 : -1;
 
            // Make sure we're not out of range 
            if (startIndex < 0 || startIndex > source.Length)
                throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index")); 

            // Make sure that we allow startIndex == source.Length
            if (startIndex == source.Length)
            { 
                startIndex--;
                if (count > 0) 
                    count--; 

                // If we are looking for nothing, just return 0 
                if (value.Length == 0 && count >= 0 && startIndex - count + 1 >= 0)
                    return startIndex;
            }
 
            // 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("count", Environment.GetResourceString("ArgumentOutOfRange_Count")); 

            if (options == CompareOptions.OrdinalIgnoreCase) 
            {
                return TextInfo.LastIndexOfStringOrdinalIgnoreCase(source, value, startIndex, count);
            }
 
            return InternalFindNLSStringEx(m_dataHandle, m_sortName, GetNativeCompareFlags(options) | Win32Native.FIND_FROMEND, source, count, startIndex, value, value.Length);
        } 
 

        //////////////////////////////////////////////////////////////////////// 
        //
        //  GetSortKey
        //
        //  Gets the SortKey for the given string with the given options. 
        //
        //////////////////////////////////////////////////////////////////////// 
#if !FEATURE_PAL 
        public unsafe virtual SortKey GetSortKey(String source, CompareOptions options)
        { 
            return CreateSortKey(source, options);
        }

 
        public unsafe virtual SortKey GetSortKey(String source)
        { 
            return CreateSortKey(source, CompareOptions.None); 
        }
 
        [System.Security.SecuritySafeCritical]
        private SortKey CreateSortKey(String source, CompareOptions options)
        {
            if (source==null) { throw new ArgumentNullException("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"), "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 = new byte[0]; 
                // 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_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"), "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_sortName, flags, source, source.Length, keyData, keyData.Length);
            } 
            else
            { 
                source = String.Empty; // back to original 
            }
 
            return new SortKey(Name, source, options, keyData);
        }

#endif // !FEATURE_PAL 

 
        //////////////////////////////////////////////////////////////////////// 
        //
        //  Equals 
        //
        //  Implements Object.Equals().  Returns a boolean indicating whether
        //  or not object refers to the same CompareInfo as the current
        //  instance. 
        //
        //////////////////////////////////////////////////////////////////////// 
 

        public override bool Equals(Object value) 
        {
            CompareInfo that = value as CompareInfo;

            if (that != null) 
            {
                return this.Name == that.Name; 
            } 

            return (false); 
        }


        //////////////////////////////////////////////////////////////////////// 
        //
        //  GetHashCode 
        // 
        //  Implements Object.GetHashCode().  Returns the hash code for the
        //  CompareInfo.  The hash code is guaranteed to be the same for 
        //  CompareInfo A and B where A.Equals(B) is true.
        //
        ////////////////////////////////////////////////////////////////////////
 

        public override int GetHashCode() 
        { 
            return (this.Name.GetHashCode());
        } 

        ////////////////////////////////////////////////////////////////////////
        //
        //  GetHashCodeOfString 
        //
        //  This internal method allows a method that allows the equivalent of creating a Sortkey for a 
        //  string from CompareInfo, and generate a hashcode value from it.  It is not very convenient 
        //  to use this method as is and it creates an unnecessary Sortkey object that will be GC'ed.
        // 
        //  The hash code is guaranteed to be the same for string A and B where A.Equals(B) is true and both
        //  the CompareInfo and the CompareOptions are the same. If two different CompareInfo objects
        //  treat the string the same way, this implementation will treat them differently (the same way that
        //  Sortkey does at the moment). 
        //
        //  This method will never be made public itself, but public consumers of it could be created, e.g.: 
        // 
        //      string.GetHashCode(CultureInfo)
        //      string.GetHashCode(CompareInfo) 
        //      string.GetHashCode(CultureInfo, CompareOptions)
        //      string.GetHashCode(CompareInfo, CompareOptions)
        //      etc.
        // 
        //  (the methods above that take a CultureInfo would use CultureInfo.CompareInfo)
        // 
        //////////////////////////////////////////////////////////////////////// 
        [System.Security.SecuritySafeCritical]  // auto-generated
        internal int GetHashCodeOfString(string source, CompareOptions options) 
        {
            //
            //  Parameter validation
            // 
            if(null == source)
            { 
                throw new ArgumentNullException("source"); 
            }
 
            if ((options & ValidHashCodeOfStringMaskOffFlags) != 0)
            {
                throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), "options");
            } 
            Contract.EndContractBlock();
 
            if(0 == source.Length) 
            {
                return(0); 
            }

            //
            //////////////////////////////////////////////////////////////////////// 
            return (InternalGetGlobalizedHashCode(m_dataHandle, this.m_sortName, source, source.Length, GetNativeCompareFlags(options)));
        } 
 
        ////////////////////////////////////////////////////////////////////////
        // 
        //  ToString
        //
        //  Implements Object.ToString().  Returns a string describing the
        //  CompareInfo. 
        //
        //////////////////////////////////////////////////////////////////////// 
 

        public override String ToString() 
        {
            return ("CompareInfo - " + this.Name);
        }
 
#if FEATURE_USE_LCID
        public int LCID 
        { 
            get
            { 
                return CultureInfo.GetCultureInfo(this.Name).LCID;
            }
        }
#endif 

#if !FEATURE_CORECLR 
        [System.Security.SecuritySafeCritical] 
        internal static IntPtr InternalInitSortHandle(String localeName)
        { 
            return InternalInitSortHandle(localeName, Version);
        }

        private const int SORT_VERSION_WHIDBEY = 0x00001000; 

        internal static bool IsLegacy20SortingBehaviorRequested 
        { 
            get
            { 
                return Version == SORT_VERSION_WHIDBEY;
            }
        }
 
        private static uint Version
        { 
            [System.Security.SecuritySafeCritical] 
            get
            { 
                Nullable compatSwitch = AppDomain.CurrentDomain.IsCompatibilitySwitchSet("NetFx40_Legacy20SortingBehavior");

                if (compatSwitch.HasValue && compatSwitch.Value)
                { 
                    return SORT_VERSION_WHIDBEY;
                } 
 
                return InternalGetSortVersion();
            } 
        }

        [System.Security.SecurityCritical]
        [ResourceExposure(ResourceScope.None)] 
        [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
        [SuppressUnmanagedCodeSecurity] 
        private static extern uint InternalGetSortVersion(); 

        [System.Security.SecurityCritical] 
        [ResourceExposure(ResourceScope.None)]
        [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
        [SuppressUnmanagedCodeSecurity]
        private static extern IntPtr InternalInitSortHandle(String localeName, uint version); 
#endif
 
        // Get a locale sensitive sort hash code from native code -- COMNlsInfo::InternalGetGlobalizedHashCode 
        [System.Security.SecurityCritical]  // auto-generated
        [ResourceExposure(ResourceScope.None)] 
        [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
        [SuppressUnmanagedCodeSecurity]
        private static extern int InternalGetGlobalizedHashCode(IntPtr handle, string localeName, string source, int length, int dwFlags);
 
        // Use native API calls to see if this string is entirely defined -- COMNlsInfo::InternalIsSortable
        [System.Security.SecurityCritical]  // auto-generated 
        [ResourceExposure(ResourceScope.None)] 
        [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
        [SuppressUnmanagedCodeSecurity] 
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool InternalIsSortable(IntPtr handle, String source, int length);

        // Compare a string using the native API calls -- COMNlsInfo::InternalCompareString 
        [System.Security.SecurityCritical]  // auto-generated
        [ResourceExposure(ResourceScope.Process)] 
        [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] 
        [SuppressUnmanagedCodeSecurity]
        private static extern int InternalCompareString(IntPtr handle, 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 
        [System.Security.SecurityCritical]  // auto-generated
        [ResourceExposure(ResourceScope.None)] 
        [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] 
        [SuppressUnmanagedCodeSecurity]
        private static extern int InternalFindNLSStringEx(IntPtr handle, 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
        [System.Security.SecurityCritical]  // auto-generated
        [ResourceExposure(ResourceScope.None)] 
        [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
        [SuppressUnmanagedCodeSecurity] 
        private static extern int InternalGetSortKey(IntPtr handle, String localeName, int flags, String source, int sourceCount, byte[] target, int targetCount); 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK