Merge pull request #3522 from henricm/fix-csharp-compiler-path-windows
[mono.git] / mcs / class / referencesource / mscorlib / system / globalization / compareinfo.cs
1 // ==++==
2 //
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 //
5 // ==--==
6 ////////////////////////////////////////////////////////////////////////////
7 //
8 //  Class:    CompareInfo
9 //
10 //
11 //  Purpose:  This class implements a set of methods for comparing
12 //            strings.
13 //
14 //  Date:     August 12, 1998
15 //
16 ////////////////////////////////////////////////////////////////////////////
17
18 namespace System.Globalization {
19
20     //
21     // We pass all of the sorting calls to the native side, preferrably to the OS to do
22     // the actual work.
23     //
24
25     using System;
26     using System.Collections;
27     using System.Collections.Generic;
28     using System.Reflection;
29     using System.Runtime.Serialization;
30     using System.Runtime.CompilerServices;
31     using System.Runtime.ConstrainedExecution;
32     using System.Runtime.InteropServices;
33     using System.Runtime.Versioning;
34     using System.Threading;
35     using System.Security.Permissions;
36     using Microsoft.Win32;
37     using System.Security;
38     using System.Security.Principal;
39     using System.Diagnostics.Contracts;
40
41     //
42     //  Options can be used during string comparison.
43     //
44     //  Native implementation (COMNlsInfo.cpp & SortingTable.cpp) relies on the values of these,
45     //  If you change the values below, be sure to change the values in native part as well.
46     //
47
48
49 [Serializable]
50     [Flags]
51     [System.Runtime.InteropServices.ComVisible(true)]
52     public enum CompareOptions
53     {
54         None                = 0x00000000,
55         IgnoreCase          = 0x00000001,
56         IgnoreNonSpace      = 0x00000002,
57         IgnoreSymbols       = 0x00000004,
58         IgnoreKanaType      = 0x00000008,   // ignore kanatype
59         IgnoreWidth         = 0x00000010,   // ignore width
60         OrdinalIgnoreCase   = 0x10000000,   // This flag can not be used with other flags.
61         StringSort          = 0x20000000,   // use string sort method
62         Ordinal             = 0x40000000,   // This flag can not be used with other flags.
63
64         // StopOnNull      = 0x10000000,
65
66         // StopOnNull is defined in SortingTable.h, but we didn't enable this option here.
67         // Do not use this value for other flags accidentally.
68     }
69
70
71     [Serializable]
72 [System.Runtime.InteropServices.ComVisible(true)]
73
74     public partial class CompareInfo
75 #if FEATURE_SERIALIZATION
76     : IDeserializationCallback
77 #endif
78     {
79         // Mask used to check if IndexOf()/LastIndexOf()/IsPrefix()/IsPostfix() has the right flags.
80         private const CompareOptions ValidIndexMaskOffFlags =
81             ~(CompareOptions.IgnoreCase | CompareOptions.IgnoreSymbols | CompareOptions.IgnoreNonSpace |
82               CompareOptions.IgnoreWidth | CompareOptions.IgnoreKanaType);
83
84         // Mask used to check if Compare() has the right flags.
85         private const CompareOptions ValidCompareMaskOffFlags =
86             ~(CompareOptions.IgnoreCase | CompareOptions.IgnoreSymbols | CompareOptions.IgnoreNonSpace |
87               CompareOptions.IgnoreWidth | CompareOptions.IgnoreKanaType | CompareOptions.StringSort);
88
89         // Mask used to check if GetHashCodeOfString() has the right flags.
90         private const CompareOptions ValidHashCodeOfStringMaskOffFlags =
91             ~(CompareOptions.IgnoreCase | CompareOptions.IgnoreSymbols | CompareOptions.IgnoreNonSpace |
92               CompareOptions.IgnoreWidth | CompareOptions.IgnoreKanaType);
93
94         //
95         // CompareInfos have an interesting identity.  They are attached to the locale that created them,
96         // ie: en-US would have an en-US sort.  For haw-US (custom), then we serialize it as haw-US.
97         // The interesting part is that since haw-US doesn't have its own sort, it has to point at another
98         // locale, which is what SCOMPAREINFO does.
99
100         [OptionalField(VersionAdded = 2)]
101         private String m_name;  // The name used to construct this CompareInfo
102
103         [NonSerialized] 
104         private String m_sortName; // The name that defines our behavior
105
106 #if !MONO
107         [NonSerialized]
108         private IntPtr m_dataHandle;
109
110         [NonSerialized]
111         private IntPtr m_handleOrigin;
112 #endif
113
114         ////////////////////////////////////////////////////////////////////////
115         //
116         //  CompareInfo Constructor
117         //
118         //
119         ////////////////////////////////////////////////////////////////////////
120         // Constructs an instance that most closely corresponds to the NLS locale
121         // identifier.
122         internal CompareInfo(CultureInfo culture)
123         {
124             this.m_name = culture.m_name;
125             this.m_sortName = culture.SortName;
126
127 #if !MONO
128             IntPtr handleOrigin;
129             this.m_dataHandle = InternalInitSortHandle(m_sortName, out handleOrigin);
130             this.m_handleOrigin = handleOrigin;
131 #endif
132         }
133
134         /*=================================GetCompareInfo==========================
135         **Action: Get the CompareInfo constructed from the data table in the specified assembly for the specified culture.
136         **       Warning: The assembly versioning mechanism is dead!
137         **Returns: The CompareInfo for the specified culture.
138         **Arguments:
139         **   culture     the ID of the culture
140         **   assembly   the assembly which contains the sorting table.
141         **Exceptions:
142         **  ArugmentNullException when the assembly is null
143         **  ArgumentException if culture is invalid.
144         ============================================================================*/
145 #if FEATURE_USE_LCID
146         // 
147         public static CompareInfo GetCompareInfo(int culture, Assembly assembly){
148             // Parameter checking.
149             if (assembly == null) {
150                 throw new ArgumentNullException("assembly");
151             }
152             if (assembly!=typeof(Object).Module.Assembly) {
153                 throw new ArgumentException(Environment.GetResourceString("Argument_OnlyMscorlib"));
154             }
155             Contract.EndContractBlock();
156
157             return GetCompareInfo(culture);
158         }
159 #endif
160
161
162         /*=================================GetCompareInfo==========================
163         **Action: Get the CompareInfo constructed from the data table in the specified assembly for the specified culture.
164         **       The purpose of this method is to provide version for CompareInfo tables.
165         **Returns: The CompareInfo for the specified culture.
166         **Arguments:
167         **   name    the name of the culture
168         **   assembly   the assembly which contains the sorting table.
169         **Exceptions:
170         **  ArugmentNullException when the assembly is null
171         **  ArgumentException if name is invalid.
172         ============================================================================*/
173         // 
174         public static CompareInfo GetCompareInfo(String name, Assembly assembly){
175             if (name == null || assembly == null) {
176                 throw new ArgumentNullException(name == null ? "name" : "assembly");
177             }
178             Contract.EndContractBlock();
179
180             if (assembly!=typeof(Object).Module.Assembly) {
181                 throw new ArgumentException(Environment.GetResourceString("Argument_OnlyMscorlib"));
182             }
183
184             return GetCompareInfo(name);
185         }
186
187         /*=================================GetCompareInfo==========================
188         **Action: Get the CompareInfo for the specified culture.
189         ** This method is provided for ease of integration with NLS-based software.
190         **Returns: The CompareInfo for the specified culture.
191         **Arguments:
192         **   culture    the ID of the culture.
193         **Exceptions:
194         **  ArgumentException if culture is invalid.
195         ============================================================================*/
196
197 #if FEATURE_USE_LCID
198         // People really shouldn't be calling LCID versions, no custom support
199         public static CompareInfo GetCompareInfo(int culture)
200         {
201             if (CultureData.IsCustomCultureId(culture))
202             {
203                 // Customized culture cannot be created by the LCID.
204                 throw new ArgumentException(Environment.GetResourceString("Argument_CustomCultureCannotBePassedByNumber", "culture"));
205             }
206
207             return CultureInfo.GetCultureInfo(culture).CompareInfo;
208         }
209 #endif
210
211         /*=================================GetCompareInfo==========================
212         **Action: Get the CompareInfo for the specified culture.
213         **Returns: The CompareInfo for the specified culture.
214         **Arguments:
215         **   name    the name of the culture.
216         **Exceptions:
217         **  ArgumentException if name is invalid.
218         ============================================================================*/
219
220         public static CompareInfo GetCompareInfo(String name)
221         {
222             if (name == null)
223             {
224                 throw new ArgumentNullException("name");
225             }
226             Contract.EndContractBlock();
227
228             return CultureInfo.GetCultureInfo(name).CompareInfo;
229         }
230
231         [System.Runtime.InteropServices.ComVisible(false)]
232         public static bool IsSortable(char ch) {
233             return(IsSortable(ch.ToString()));
234         }
235
236         [System.Security.SecuritySafeCritical]
237         [System.Runtime.InteropServices.ComVisible(false)]
238         public static bool IsSortable(String text) {
239             if (text == null) {
240                 // A null param is invalid here.
241                 throw new ArgumentNullException("text");
242             }
243
244             if (0 == text.Length) {
245                 // A zero length string is not invalid, but it is also not sortable.
246                 return(false);
247             }
248 #if MONO
249             return Mono.Globalization.Unicode.MSCompatUnicodeTable.IsSortable (text);
250 #else
251             CompareInfo c = CultureInfo.InvariantCulture.CompareInfo;
252
253             return (InternalIsSortable(c.m_dataHandle, c.m_handleOrigin, c.m_sortName, text, text.Length));
254 #endif
255         }
256
257
258 #if FEATURE_SERIALIZATION // Only defined when FEATURE_USE_LCID is also defined
259 #region Serialization
260         // the following fields are defined to keep the compatibility with Whidbey.
261         // don't change/remove the names/types of these fields.
262 #if FEATURE_USE_LCID || MONO
263 #pragma warning disable 169
264                 [OptionalField(VersionAdded = 1)]
265                 private int win32LCID;             // mapped sort culture id of this instance
266 #pragma warning restore
267                 private int culture;               // the culture ID used to create this instance.
268 #endif
269         [OnDeserializing]
270         private void OnDeserializing(StreamingContext ctx)
271         {
272             this.m_name          = null;
273         }
274
275         private void OnDeserialized()
276         {
277             CultureInfo ci;
278             // If we didn't have a name, use the LCID
279             if (this.m_name == null)
280             {
281                 // From whidbey, didn't have a name
282                 ci = CultureInfo.GetCultureInfo(this.culture);
283                 this.m_name = ci.m_name;
284             }
285             else
286             {
287                 ci = CultureInfo.GetCultureInfo(m_name);
288             }
289             this.m_sortName = ci.SortName;
290 #if !MONO
291             IntPtr handleOrigin;
292             this.m_dataHandle = InternalInitSortHandle(m_sortName, out handleOrigin);
293             this.m_handleOrigin = handleOrigin;
294 #endif
295         }
296
297         [OnDeserialized]
298         private void OnDeserialized(StreamingContext ctx)
299         {
300             OnDeserialized();
301         }
302
303         [OnSerializing]
304         private void OnSerializing(StreamingContext ctx)
305         {
306             // This is merely for serialization compatibility with Whidbey/Orcas, it can go away when we don't want that compat any more.
307             culture = CultureInfo.GetCultureInfo(this.Name).LCID; // This is the lcid of the constructing culture (still have to dereference to get target sort)
308             Contract.Assert(m_name != null, "CompareInfo.OnSerializing - expected m_name to be set already");
309         }
310
311         void IDeserializationCallback.OnDeserialization(Object sender)
312         {
313             OnDeserialized();
314         }
315
316 #endregion Serialization
317 #endif // FEATURE_SERIALIZATION
318
319
320         ///////////////////////////----- Name -----/////////////////////////////////
321         //
322         //  Returns the name of the culture (well actually, of the sort).
323         //  Very important for providing a non-LCID way of identifying
324         //  what the sort is.
325         //
326         //  Note that this name isn't dereferenced in case the CompareInfo is a different locale
327         //  which is consistent with the behaviors of earlier versions.  (so if you ask for a sort
328         //  and the locale's changed behavior, then you'll get changed behavior, which is like
329         //  what happens for a version update)
330         //
331         ////////////////////////////////////////////////////////////////////////
332
333         [System.Runtime.InteropServices.ComVisible(false)]
334         public virtual String Name
335         {
336             get
337             {
338                 Contract.Assert(m_name != null, "CompareInfo.Name Expected m_name to be set");
339                 if (m_name == "zh-CHT" || m_name == "zh-CHS")
340                 {
341                     return m_name;
342                 }
343
344                 return (m_sortName);
345             }
346         }
347
348         // These flags are used in the native Win32. so we need to map the managed options to those flags
349         private const int LINGUISTIC_IGNORECASE      = 0x00000010;       // linguistically appropriate 'ignore case'
350         private const int NORM_IGNORECASE            = 0x00000001;       // Ignores case.  (use LINGUISTIC_IGNORECASE instead)
351         private const int NORM_IGNOREKANATYPE        = 0x00010000;       // Does not differentiate between Hiragana and Katakana characters. Corresponding Hiragana and Katakana will compare as equal.
352         private const int LINGUISTIC_IGNOREDIACRITIC = 0x00000020;       // linguistically appropriate 'ignore nonspace'
353         private const int NORM_IGNORENONSPACE        = 0x00000002;       // Ignores nonspacing. This flag also removes Japanese accent characters.  (use LINGUISTIC_IGNOREDIACRITIC instead)
354         private const int NORM_IGNORESYMBOLS         = 0x00000004;       // Ignores symbols.
355         private const int NORM_IGNOREWIDTH           = 0x00020000;       // Does not differentiate between a single-byte character and the same character as a double-byte character.
356         private const int SORT_STRINGSORT            = 0x00001000;       // Treats punctuation the same as symbols.
357         private const int COMPARE_OPTIONS_ORDINAL    = 0x40000000;       // Ordinal (handled by Comnlsinfo)
358         internal const int NORM_LINGUISTIC_CASING    = 0x08000000;       // use linguistic rules for casing
359
360         
361         private const int RESERVED_FIND_ASCII_STRING = 0x20000000;       // This flag used only to tell the sorting DLL can assume the string characters are in ASCII.
362
363         [Pure]
364         internal static int GetNativeCompareFlags(CompareOptions options)
365         {
366             // some NLS VM functions can handle COMPARE_OPTIONS_ORDINAL
367             // in which case options should be simply cast to int instead of using this function
368             // Does not look like the best approach to me but for now I am going to leave it as it is
369             // 
370             Contract.Assert(options != CompareOptions.OrdinalIgnoreCase, "[CompareInfo.GetNativeCompareFlags]CompareOptions.OrdinalIgnoreCase should be handled separately");
371
372             // Use "linguistic casing" by default (load the culture's casing exception tables)
373             int nativeCompareFlags = NORM_LINGUISTIC_CASING;
374
375             if ((options & CompareOptions.IgnoreCase)       != 0) { nativeCompareFlags |= NORM_IGNORECASE;        }
376             if ((options & CompareOptions.IgnoreKanaType)   != 0) { nativeCompareFlags |= NORM_IGNOREKANATYPE;    }
377             if ((options & CompareOptions.IgnoreNonSpace)   != 0) { nativeCompareFlags |= NORM_IGNORENONSPACE;    }
378             if ((options & CompareOptions.IgnoreSymbols)    != 0) { nativeCompareFlags |= NORM_IGNORESYMBOLS;     }
379             if ((options & CompareOptions.IgnoreWidth)      != 0) { nativeCompareFlags |= NORM_IGNOREWIDTH;       }
380             if ((options & CompareOptions.StringSort)       != 0) { nativeCompareFlags |= SORT_STRINGSORT;        }
381
382             // Suffix & Prefix shouldn't use this, make sure to turn off the NORM_LINGUISTIC_CASING flag
383             if (options == CompareOptions.Ordinal)                { nativeCompareFlags = COMPARE_OPTIONS_ORDINAL; }
384
385             Contract.Assert(((options & ~(CompareOptions.IgnoreCase |
386                                           CompareOptions.IgnoreKanaType |
387                                           CompareOptions.IgnoreNonSpace |
388                                           CompareOptions.IgnoreSymbols |
389                                           CompareOptions.IgnoreWidth |
390                                           CompareOptions.StringSort)) == 0) ||
391                              (options == CompareOptions.Ordinal), "[CompareInfo.GetNativeCompareFlags]Expected all flags to be handled");
392
393             Contract.Assert((nativeCompareFlags & RESERVED_FIND_ASCII_STRING) == 0, "[CompareInfo.GetNativeCompareFlags] RESERVED_FIND_ASCII_STRING shouldn't be set here");
394
395             return nativeCompareFlags;
396         }
397
398
399         ////////////////////////////////////////////////////////////////////////
400         //
401         //  Compare
402         //
403         //  Compares the two strings with the given options.  Returns 0 if the
404         //  two strings are equal, a number less than 0 if string1 is less
405         //  than string2, and a number greater than 0 if string1 is greater
406         //  than string2.
407         //
408         ////////////////////////////////////////////////////////////////////////
409
410
411         public virtual int Compare(String string1, String string2)
412         {
413             return (Compare(string1, string2, CompareOptions.None));
414         }
415
416         [System.Security.SecuritySafeCritical]  // auto-generated
417         public unsafe virtual int Compare(String string1, String string2, CompareOptions options){
418
419             if (options == CompareOptions.OrdinalIgnoreCase)
420             {
421                 return String.Compare(string1, string2, StringComparison.OrdinalIgnoreCase);
422             }
423
424             // Verify the options before we do any real comparison.
425             if ((options & CompareOptions.Ordinal) != 0)
426             {
427                 if (options != CompareOptions.Ordinal)
428                 {
429                     throw new ArgumentException(Environment.GetResourceString("Argument_CompareOptionOrdinal"), "options");
430                         }
431                 return String.CompareOrdinal(string1, string2);
432                     }
433
434             if ((options & ValidCompareMaskOffFlags) != 0)
435             {
436                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), "options");
437             }
438
439             //Our paradigm is that null sorts less than any other string and
440             //that two nulls sort as equal.
441             if (string1 == null) {
442                 if (string2 == null) {
443                     return (0);     // Equal
444                 }
445                 return (-1);    // null < non-null
446             }
447             if (string2 == null) {
448                 return (1);     // non-null > null
449             }
450
451 #if MONO
452             return internal_compare_switch (string1, 0, string1.Length, string2, 0, string2.Length, options);
453 #else
454             return InternalCompareString(m_dataHandle, m_handleOrigin, m_sortName, string1, 0, string1.Length, string2, 0, string2.Length, GetNativeCompareFlags(options));
455 #endif
456         }
457
458
459         ////////////////////////////////////////////////////////////////////////
460         //
461         //  Compare
462         //
463         //  Compares the specified regions of the two strings with the given
464         //  options.
465         //  Returns 0 if the two strings are equal, a number less than 0 if
466         //  string1 is less than string2, and a number greater than 0 if
467         //  string1 is greater than string2.
468         //
469         ////////////////////////////////////////////////////////////////////////
470
471
472         public unsafe virtual int Compare(String string1, int offset1, int length1, String string2, int offset2, int length2)
473         {
474             return Compare(string1, offset1, length1, string2, offset2, length2, 0);
475         }
476
477
478         public unsafe virtual int Compare(String string1, int offset1, String string2, int offset2, CompareOptions options)
479         {
480             return Compare(string1, offset1, string1 == null ? 0 : string1.Length-offset1,
481                            string2, offset2, string2 == null ? 0 : string2.Length-offset2, options);
482         }
483
484
485         public unsafe virtual int Compare(String string1, int offset1, String string2, int offset2)
486         {
487             return Compare(string1, offset1, string2, offset2, 0);
488         }
489
490
491         [System.Security.SecuritySafeCritical]  // auto-generated
492         public unsafe virtual int Compare(String string1, int offset1, int length1, String string2, int offset2, int length2, CompareOptions options)
493         {
494             if (options == CompareOptions.OrdinalIgnoreCase)
495             {
496                 int result = String.Compare(string1, offset1, string2, offset2, length1<length2 ? length1 : length2, StringComparison.OrdinalIgnoreCase);
497                 if ((length1 != length2) && result == 0)
498                     return (length1 > length2? 1: -1);
499                 return (result);
500             }
501
502             // Verify inputs
503             if (length1 < 0 || length2 < 0)
504             {
505                 throw new ArgumentOutOfRangeException((length1 < 0) ? "length1" : "length2", Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum"));
506             }
507             if (offset1 < 0 || offset2 < 0)
508             {
509                 throw new ArgumentOutOfRangeException((offset1 < 0) ? "offset1" : "offset2", Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum"));
510             }
511             if (offset1 > (string1 == null ? 0 : string1.Length) - length1)
512             {
513                 throw new ArgumentOutOfRangeException("string1", Environment.GetResourceString("ArgumentOutOfRange_OffsetLength"));
514             }
515             if (offset2 > (string2 == null ? 0 : string2.Length) - length2)
516             {
517                 throw new ArgumentOutOfRangeException("string2", Environment.GetResourceString("ArgumentOutOfRange_OffsetLength"));
518             }
519             if ((options & CompareOptions.Ordinal) != 0)
520             {
521                 if (options != CompareOptions.Ordinal)
522                 {
523                     throw new ArgumentException(Environment.GetResourceString("Argument_CompareOptionOrdinal"),
524                                                 "options");
525                 }
526             }
527             else if ((options & ValidCompareMaskOffFlags) != 0)
528             {
529                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), "options");
530             }
531
532             //
533             // Check for the null case.
534             //
535             if (string1 == null)
536             {
537                 if (string2 == null)
538                 {
539                     return (0);
540                 }
541                 return (-1);
542             }
543             if (string2 == null)
544             {
545                 return (1);
546             }
547
548             if (options == CompareOptions.Ordinal)
549             {
550                 return CompareOrdinal(string1, offset1, length1,
551                                       string2, offset2, length2);
552             }
553 #if MONO
554             return internal_compare_switch (string1, offset1, length1, string2, offset2, length2, options);
555 #else
556             return InternalCompareString(this.m_dataHandle, this.m_handleOrigin, this.m_sortName, 
557                                          string1, offset1, length1, 
558                                          string2, offset2, length2, 
559                                          GetNativeCompareFlags(options));
560 #endif
561         }
562
563         [System.Security.SecurityCritical]
564         private static int CompareOrdinal(string string1, int offset1, int length1, string string2, int offset2, int length2)
565         {
566             int result = String.nativeCompareOrdinalEx(string1, offset1, string2, offset2,
567                                                        (length1 < length2 ? length1 : length2));
568             if ((length1 != length2) && result == 0)
569             {
570                 return (length1 > length2 ? 1 : -1);
571             }
572             return (result);
573         }
574
575         ////////////////////////////////////////////////////////////////////////
576         //
577         //  IsPrefix
578         //
579         //  Determines whether prefix is a prefix of string.  If prefix equals
580         //  String.Empty, true is returned.
581         //
582         ////////////////////////////////////////////////////////////////////////
583
584
585         [System.Security.SecuritySafeCritical]  // auto-generated
586         public unsafe virtual bool IsPrefix(String source, String prefix, CompareOptions options)
587         {
588             if (source == null || prefix == null) {
589                 throw new ArgumentNullException((source == null ? "source" : "prefix"),
590                     Environment.GetResourceString("ArgumentNull_String"));
591             }
592             Contract.EndContractBlock();
593             int prefixLen = prefix.Length;
594
595             if (prefixLen == 0)
596             {
597                 return (true);
598             }
599
600             if (options == CompareOptions.OrdinalIgnoreCase)
601             {
602                 return source.StartsWith(prefix, StringComparison.OrdinalIgnoreCase);
603             }
604
605             if (options == CompareOptions.Ordinal)
606             {
607                 return source.StartsWith(prefix, StringComparison.Ordinal);
608             }
609
610             if ((options & ValidIndexMaskOffFlags) != 0) {
611                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), "options");
612             }
613
614 #if MONO
615             if (UseManagedCollation)
616                 return GetCollator ().IsPrefix (source, prefix, options);
617
618             if(source.Length < prefix.Length)
619                 return false;
620             return Compare (source, 0, prefix.Length, prefix, 0, prefix.Length, options) == 0;
621 #else
622             // to let the sorting DLL do the call optimization in case of Ascii strings, we check if the strings are in Ascii and then send the flag RESERVED_FIND_ASCII_STRING  to 
623             // the sorting DLL API SortFindString so sorting DLL don't have to check if the string is Ascii with every call to SortFindString.
624
625             return (InternalFindNLSStringEx(
626                         m_dataHandle, m_handleOrigin, m_sortName, 
627                         GetNativeCompareFlags(options) | Win32Native.FIND_STARTSWITH | ((source.IsAscii() && prefix.IsAscii()) ? RESERVED_FIND_ASCII_STRING : 0),
628                         source, source.Length, 0, prefix, prefix.Length) > -1);
629 #endif
630         }
631
632         public virtual bool IsPrefix(String source, String prefix)
633         {
634             return (IsPrefix(source, prefix, 0));
635         }
636
637
638         ////////////////////////////////////////////////////////////////////////
639         //
640         //  IsSuffix
641         //
642         //  Determines whether suffix is a suffix of string.  If suffix equals
643         //  String.Empty, true is returned.
644         //
645         ////////////////////////////////////////////////////////////////////////
646
647
648         [System.Security.SecuritySafeCritical]  // auto-generated
649         public unsafe virtual bool IsSuffix(String source, String suffix, CompareOptions options)
650         {
651             if (source == null || suffix == null) {
652                 throw new ArgumentNullException((source == null ? "source" : "suffix"),
653                     Environment.GetResourceString("ArgumentNull_String"));
654             }
655             Contract.EndContractBlock();
656             int suffixLen = suffix.Length;
657
658             if (suffixLen == 0)
659             {
660                 return (true);
661             }
662
663             if (options == CompareOptions.OrdinalIgnoreCase) {
664                 return source.EndsWith(suffix, StringComparison.OrdinalIgnoreCase);
665             }
666
667             if (options == CompareOptions.Ordinal) {
668                 return source.EndsWith(suffix, StringComparison.Ordinal);
669             }
670
671             if ((options & ValidIndexMaskOffFlags) != 0) {
672                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), "options");
673             }
674
675 #if MONO
676             if (UseManagedCollation)
677                 return GetCollator ().IsSuffix (source, suffix, options);
678
679             if (source.Length < suffix.Length)
680                 return false;
681
682             return Compare (source, source.Length - suffix.Length, suffix.Length, suffix, 0, suffix.Length, options) == 0;
683 #else
684
685             // to let the sorting DLL do the call optimization in case of Ascii strings, we check if the strings are in Ascii and then send the flag RESERVED_FIND_ASCII_STRING  to 
686             // the sorting DLL API SortFindString so sorting DLL don't have to check if the string is Ascii with every call to SortFindString.
687             return InternalFindNLSStringEx(
688                         m_dataHandle, m_handleOrigin, m_sortName, 
689                         GetNativeCompareFlags(options) | Win32Native.FIND_ENDSWITH | ((source.IsAscii() && suffix.IsAscii()) ? RESERVED_FIND_ASCII_STRING : 0),
690                         source, source.Length, source.Length - 1, suffix, suffix.Length) >= 0;
691 #endif
692         }
693
694
695         public virtual bool IsSuffix(String source, String suffix)
696         {
697             return (IsSuffix(source, suffix, 0));
698         }
699
700         ////////////////////////////////////////////////////////////////////////
701         //
702         //  IndexOf
703         //
704         //  Returns the first index where value is found in string.  The
705         //  search starts from startIndex and ends at endIndex.  Returns -1 if
706         //  the specified value is not found.  If value equals String.Empty,
707         //  startIndex is returned.  Throws IndexOutOfRange if startIndex or
708         //  endIndex is less than zero or greater than the length of string.
709         //  Throws ArgumentException if value is null.
710         //
711         ////////////////////////////////////////////////////////////////////////
712
713
714         public unsafe virtual int IndexOf(String source, char value)
715         {
716             if (source==null)
717                 throw new ArgumentNullException("source");
718             Contract.EndContractBlock();
719
720             return IndexOf(source, value, 0, source.Length, CompareOptions.None);
721         }
722
723
724         public unsafe virtual int IndexOf(String source, String value)
725         {
726             if (source==null)
727                 throw new ArgumentNullException("source");
728             Contract.EndContractBlock();
729
730             return IndexOf(source, value, 0, source.Length, CompareOptions.None);
731         }
732
733
734         public unsafe virtual int IndexOf(String source, char value, CompareOptions options)
735         {
736             if (source==null)
737                 throw new ArgumentNullException("source");
738             Contract.EndContractBlock();
739
740             return IndexOf(source, value, 0, source.Length, options);
741         }
742
743
744         public unsafe virtual int IndexOf(String source, String value, CompareOptions options)
745         {
746             if (source==null)
747                 throw new ArgumentNullException("source");
748             Contract.EndContractBlock();
749
750             return IndexOf(source, value, 0, source.Length, options);
751         }
752
753
754         public unsafe virtual int IndexOf(String source, char value, int startIndex)
755         {
756             if (source == null)
757                 throw new ArgumentNullException("source");
758             Contract.EndContractBlock();
759
760             return IndexOf(source, value, startIndex, source.Length - startIndex, CompareOptions.None);
761         }
762
763
764         public unsafe virtual int IndexOf(String source, String value, int startIndex)
765         {
766             if (source == null)
767                 throw new ArgumentNullException("source");
768             Contract.EndContractBlock();
769
770             return IndexOf(source, value, startIndex, source.Length - startIndex, CompareOptions.None);
771         }
772
773
774         public unsafe virtual int IndexOf(String source, char value, int startIndex, CompareOptions options)
775         {
776             if (source == null)
777                 throw new ArgumentNullException("source");
778             Contract.EndContractBlock();
779
780             return IndexOf(source, value, startIndex, source.Length - startIndex, options);
781         }
782
783
784         public unsafe virtual int IndexOf(String source, String value, int startIndex, CompareOptions options)
785         {
786             if (source == null)
787                 throw new ArgumentNullException("source");
788             Contract.EndContractBlock();
789
790             return IndexOf(source, value, startIndex, source.Length - startIndex, options);
791         }
792
793
794         public unsafe virtual int IndexOf(String source, char value, int startIndex, int count)
795         {
796             return IndexOf(source, value, startIndex, count, CompareOptions.None);
797         }
798
799
800         public unsafe virtual int IndexOf(String source, String value, int startIndex, int count)
801         {
802             return IndexOf(source, value, startIndex, count, CompareOptions.None);
803         }
804
805         [System.Security.SecuritySafeCritical]  // auto-generated
806         [ResourceExposure(ResourceScope.None)]
807         [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)]
808         public unsafe virtual int IndexOf(String source, char value, int startIndex, int count, CompareOptions options)
809         {
810             // Validate inputs
811             if (source == null)
812                 throw new ArgumentNullException("source");
813
814             if (startIndex < 0 || startIndex > source.Length)
815                 throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index"));
816
817             if (count < 0 || startIndex > source.Length - count)
818                 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_Count"));
819             Contract.EndContractBlock();
820
821             if (options == CompareOptions.OrdinalIgnoreCase)
822             {
823                 // 
824                 return source.IndexOf(value.ToString(), startIndex, count, StringComparison.OrdinalIgnoreCase);
825             }
826
827             // Validate CompareOptions
828             // Ordinal can't be selected with other flags
829             if ((options & ValidIndexMaskOffFlags) != 0 && (options != CompareOptions.Ordinal))
830                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), "options");
831
832 #if MONO
833             return internal_index_switch (source, startIndex, count, value, options, true);
834 #else
835             // to let the sorting DLL do the call optimization in case of Ascii strings, we check if the strings are in Ascii and then send the flag RESERVED_FIND_ASCII_STRING  to 
836             // the sorting DLL API SortFindString so sorting DLL don't have to check if the string is Ascii with every call to SortFindString.
837             return InternalFindNLSStringEx(
838                         m_dataHandle, m_handleOrigin, m_sortName, 
839                         GetNativeCompareFlags(options) | Win32Native.FIND_FROMSTART | ((source.IsAscii() && (value <= '\x007f')) ? RESERVED_FIND_ASCII_STRING : 0),
840                         source, count, startIndex, new String(value, 1), 1);
841 #endif
842         }
843
844
845         [System.Security.SecuritySafeCritical]  // auto-generated
846         public unsafe virtual int IndexOf(String source, String value, int startIndex, int count, CompareOptions options)
847         {
848             // Validate inputs
849             if (source == null)
850                 throw new ArgumentNullException("source");
851             if (value == null)
852                 throw new ArgumentNullException("value");
853
854             if (startIndex > source.Length)
855             {
856                 throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index"));
857             }
858             Contract.EndContractBlock();
859
860             // In Everett we used to return -1 for empty string even if startIndex is negative number so we keeping same behavior here.
861             // We return 0 if both source and value are empty strings for Everett compatibility too.
862             if (source.Length == 0)
863             {
864                 if (value.Length == 0)
865                 {
866                     return 0;
867                 }
868                 return -1;
869             }
870
871             if (startIndex < 0)
872             {
873                 throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index"));
874             }
875
876             if (count < 0 || startIndex > source.Length - count)
877                 throw new ArgumentOutOfRangeException("count",Environment.GetResourceString("ArgumentOutOfRange_Count"));
878
879             if (options == CompareOptions.OrdinalIgnoreCase)
880             {
881                 return source.IndexOf(value, startIndex, count, StringComparison.OrdinalIgnoreCase);
882             }
883
884             // Validate CompareOptions
885             // Ordinal can't be selected with other flags
886             if ((options & ValidIndexMaskOffFlags) != 0 && (options != CompareOptions.Ordinal))
887                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), "options");
888
889 #if MONO
890             return internal_index_switch (source, startIndex, count, value, options, true);
891 #else
892             // to let the sorting DLL do the call optimization in case of Ascii strings, we check if the strings are in Ascii and then send the flag RESERVED_FIND_ASCII_STRING  to 
893             // the sorting DLL API SortFindString so sorting DLL don't have to check if the string is Ascii with every call to SortFindString.
894             return InternalFindNLSStringEx(
895                         m_dataHandle, m_handleOrigin, m_sortName, 
896                         GetNativeCompareFlags(options) | Win32Native.FIND_FROMSTART | ((source.IsAscii() && value.IsAscii()) ? RESERVED_FIND_ASCII_STRING : 0),
897                         source, count, startIndex, value, value.Length);
898 #endif
899         }
900
901         ////////////////////////////////////////////////////////////////////////
902         //
903         //  LastIndexOf
904         //
905         //  Returns the last index where value is found in string.  The
906         //  search starts from startIndex and ends at endIndex.  Returns -1 if
907         //  the specified value is not found.  If value equals String.Empty,
908         //  endIndex is returned.  Throws IndexOutOfRange if startIndex or
909         //  endIndex is less than zero or greater than the length of string.
910         //  Throws ArgumentException if value is null.
911         //
912         ////////////////////////////////////////////////////////////////////////
913
914
915         public unsafe virtual int LastIndexOf(String source, char value)
916         {
917             if (source==null)
918                 throw new ArgumentNullException("source");
919             Contract.EndContractBlock();
920
921             // Can't start at negative index, so make sure we check for the length == 0 case.
922             return LastIndexOf(source, value, source.Length - 1,
923                 source.Length, CompareOptions.None);
924         }
925
926
927         public virtual int LastIndexOf(String source, String value)
928         {
929             if (source==null)
930                 throw new ArgumentNullException("source");
931             Contract.EndContractBlock();
932
933             // Can't start at negative index, so make sure we check for the length == 0 case.
934             return LastIndexOf(source, value, source.Length - 1,
935                 source.Length, CompareOptions.None);
936         }
937
938
939         public virtual int LastIndexOf(String source, char value, CompareOptions options)
940         {
941             if (source==null)
942                 throw new ArgumentNullException("source");
943             Contract.EndContractBlock();
944
945             // Can't start at negative index, so make sure we check for the length == 0 case.
946             return LastIndexOf(source, value, source.Length - 1,
947                 source.Length, options);
948         }
949
950         public unsafe virtual int LastIndexOf(String source, String value, CompareOptions options)
951         {
952             if (source==null)
953                 throw new ArgumentNullException("source");
954             Contract.EndContractBlock();
955
956             // Can't start at negative index, so make sure we check for the length == 0 case.
957             return LastIndexOf(source, value, source.Length - 1,
958                 source.Length, options);
959         }
960
961
962         public unsafe virtual int LastIndexOf(String source, char value, int startIndex)
963         {
964             return LastIndexOf(source, value, startIndex, startIndex + 1, CompareOptions.None);
965         }
966
967
968         public unsafe virtual int LastIndexOf(String source, String value, int startIndex)
969         {
970             return LastIndexOf(source, value, startIndex, startIndex + 1, CompareOptions.None);
971         }
972
973
974         public unsafe virtual int LastIndexOf(String source, char value, int startIndex, CompareOptions options)
975         {
976             return LastIndexOf(source, value, startIndex, startIndex + 1, options);
977         }
978
979
980         public unsafe virtual int LastIndexOf(String source, String value, int startIndex, CompareOptions options)
981         {
982             return LastIndexOf(source, value, startIndex, startIndex + 1, options);
983         }
984
985
986         public unsafe virtual int LastIndexOf(String source, char value, int startIndex, int count)
987         {
988             return LastIndexOf(source, value, startIndex, count, CompareOptions.None);
989         }
990
991
992         public unsafe virtual int LastIndexOf(String source, String value, int startIndex, int count)
993         {
994             return LastIndexOf(source, value, startIndex, count, CompareOptions.None);
995         }
996
997
998         [System.Security.SecuritySafeCritical]  // auto-generated
999         [ResourceExposure(ResourceScope.None)]
1000         [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)]
1001         public unsafe virtual int LastIndexOf(String source, char value, int startIndex, int count, CompareOptions options)
1002         {
1003             // Verify Arguments
1004             if (source==null)
1005                 throw new ArgumentNullException("source");
1006             Contract.EndContractBlock();
1007
1008             // Validate CompareOptions
1009             // Ordinal can't be selected with other flags
1010             if ((options & ValidIndexMaskOffFlags) != 0 &&
1011                 (options != CompareOptions.Ordinal) &&
1012                 (options != CompareOptions.OrdinalIgnoreCase))
1013                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), "options");
1014
1015             // Special case for 0 length input strings
1016             if (source.Length == 0 && (startIndex == -1 || startIndex == 0))
1017                 return -1;
1018
1019             // Make sure we're not out of range
1020             if (startIndex < 0 || startIndex > source.Length)
1021                 throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index"));
1022
1023             // Make sure that we allow startIndex == source.Length
1024             if (startIndex == source.Length)
1025             {
1026                 startIndex--;
1027                 if (count > 0)
1028                     count--;
1029             }
1030
1031             // 2nd have of this also catches when startIndex == MAXINT, so MAXINT - 0 + 1 == -1, which is < 0.
1032             if (count < 0 || startIndex - count + 1 < 0)
1033                 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_Count"));
1034
1035             if (options == CompareOptions.OrdinalIgnoreCase)
1036             {
1037                 // 
1038                 return source.LastIndexOf(value.ToString(), startIndex, count, StringComparison.OrdinalIgnoreCase);
1039             }
1040
1041 #if MONO
1042             return internal_index_switch (source, startIndex, count, value, options, false);
1043 #else
1044             // to let the sorting DLL do the call optimization in case of Ascii strings, we check if the strings are in Ascii and then send the flag RESERVED_FIND_ASCII_STRING  to 
1045             // the sorting DLL API SortFindString so sorting DLL don't have to check if the string is Ascii with every call to SortFindString.
1046             return InternalFindNLSStringEx(
1047                         m_dataHandle, m_handleOrigin, m_sortName, 
1048                         GetNativeCompareFlags(options) | Win32Native.FIND_FROMEND | ((source.IsAscii() && (value <= '\x007f')) ? RESERVED_FIND_ASCII_STRING : 0),
1049                         source, count, startIndex, new String(value, 1), 1);
1050 #endif
1051         }
1052
1053
1054         [System.Security.SecuritySafeCritical]  // auto-generated
1055         public unsafe virtual int LastIndexOf(String source, String value, int startIndex, int count, CompareOptions options)
1056         {
1057             // Verify Arguments
1058             if (source == null)
1059                 throw new ArgumentNullException("source");
1060             if (value == null)
1061                 throw new ArgumentNullException("value");
1062             Contract.EndContractBlock();
1063
1064             // Validate CompareOptions
1065             // Ordinal can't be selected with other flags
1066             if ((options & ValidIndexMaskOffFlags) != 0 &&
1067                 (options != CompareOptions.Ordinal) &&
1068                 (options != CompareOptions.OrdinalIgnoreCase))
1069                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), "options");
1070
1071             // Special case for 0 length input strings
1072             if (source.Length == 0 && (startIndex == -1 || startIndex == 0))
1073                 return (value.Length == 0) ? 0 : -1;
1074
1075             // Make sure we're not out of range
1076             if (startIndex < 0 || startIndex > source.Length)
1077                 throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index"));
1078
1079             // Make sure that we allow startIndex == source.Length
1080             if (startIndex == source.Length)
1081             {
1082                 startIndex--;
1083                 if (count > 0)
1084                     count--;
1085
1086                 // If we are looking for nothing, just return 0
1087                 if (value.Length == 0 && count >= 0 && startIndex - count + 1 >= 0)
1088                     return startIndex;
1089             }
1090
1091             // 2nd half of this also catches when startIndex == MAXINT, so MAXINT - 0 + 1 == -1, which is < 0.
1092             if (count < 0 || startIndex - count + 1 < 0)
1093                 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_Count"));
1094
1095             if (options == CompareOptions.OrdinalIgnoreCase)
1096             {
1097                 return source.LastIndexOf(value, startIndex, count, StringComparison.OrdinalIgnoreCase);
1098             }
1099
1100 #if MONO
1101             return internal_index_switch (source, startIndex, count, value, options, false);
1102 #else
1103             // to let the sorting DLL do the call optimization in case of Ascii strings, we check if the strings are in Ascii and then send the flag RESERVED_FIND_ASCII_STRING  to 
1104             // the sorting DLL API SortFindString so sorting DLL don't have to check if the string is Ascii with every call to SortFindString.
1105             return InternalFindNLSStringEx(
1106                         m_dataHandle, m_handleOrigin, m_sortName, 
1107                         GetNativeCompareFlags(options) | Win32Native.FIND_FROMEND | ((source.IsAscii() && value.IsAscii()) ? RESERVED_FIND_ASCII_STRING : 0),
1108                         source, count, startIndex, value, value.Length);
1109 #endif
1110         }
1111
1112
1113         ////////////////////////////////////////////////////////////////////////
1114         //
1115         //  GetSortKey
1116         //
1117         //  Gets the SortKey for the given string with the given options.
1118         //
1119         ////////////////////////////////////////////////////////////////////////
1120 #if !FEATURE_PAL || MONO
1121         public unsafe virtual SortKey GetSortKey(String source, CompareOptions options)
1122         {
1123             return CreateSortKey(source, options);
1124         }
1125
1126
1127         public unsafe virtual SortKey GetSortKey(String source)
1128         {
1129             return CreateSortKey(source, CompareOptions.None);
1130         }
1131
1132         [System.Security.SecuritySafeCritical]
1133         private SortKey CreateSortKey(String source, CompareOptions options)
1134         {
1135             if (source==null) { throw new ArgumentNullException("source"); }
1136             Contract.EndContractBlock();
1137
1138             // Mask used to check if we have the right flags.
1139             const CompareOptions ValidSortkeyCtorMaskOffFlags = ~(CompareOptions.IgnoreCase |
1140                                                                   CompareOptions.IgnoreSymbols |
1141                                                                   CompareOptions.IgnoreNonSpace |
1142                                                                   CompareOptions.IgnoreWidth |
1143                                                                   CompareOptions.IgnoreKanaType |
1144                                                                   CompareOptions.StringSort);
1145
1146             if ((options & ValidSortkeyCtorMaskOffFlags) != 0)
1147             {
1148                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), "options");
1149             }
1150
1151 #if !MONO            
1152             byte[] keyData = null;
1153 #endif
1154             // The OS doesn't have quite the same behavior so we have to test for empty inputs
1155             if (String.IsNullOrEmpty(source))
1156             {
1157 #if !MONO                
1158                 // Empty strings get an empty sort key
1159                 keyData = EmptyArray<Byte>.Value;
1160 #endif
1161                 // Fake value to test though so we can verify our flags
1162                 source = "\x0000";
1163             }
1164 #if MONO
1165             return CreateSortKeyCore (source, options);
1166 #else
1167             int flags = GetNativeCompareFlags(options);
1168
1169             // Go ahead and call the OS
1170             // First get the count
1171             int length = InternalGetSortKey(m_dataHandle, m_handleOrigin, m_sortName, flags, source, source.Length, null, 0);
1172
1173             // If there was an error, return an error
1174             if (length == 0)
1175             {
1176                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), "source");
1177             }
1178
1179             // If input was empty, return the empty byte[] we made earlier and skip this
1180             if (keyData == null)
1181             {
1182                 // Make an appropriate byte array
1183                 keyData  = new byte[length];
1184
1185                 // Fill up the array
1186                 length = InternalGetSortKey(m_dataHandle, m_handleOrigin, m_sortName, flags, source, source.Length, keyData, keyData.Length);
1187             }
1188             else
1189             {
1190                 source = String.Empty; // back to original
1191             }
1192
1193             return new SortKey(Name, source, options, keyData);
1194 #endif
1195         }
1196
1197 #endif // !FEATURE_PAL
1198
1199
1200         ////////////////////////////////////////////////////////////////////////
1201         //
1202         //  Equals
1203         //
1204         //  Implements Object.Equals().  Returns a boolean indicating whether
1205         //  or not object refers to the same CompareInfo as the current
1206         //  instance.
1207         //
1208         ////////////////////////////////////////////////////////////////////////
1209
1210
1211         public override bool Equals(Object value)
1212         {
1213             CompareInfo that = value as CompareInfo;
1214
1215             if (that != null)
1216             {
1217                 return this.Name == that.Name;
1218             }
1219
1220             return (false);
1221         }
1222
1223
1224         ////////////////////////////////////////////////////////////////////////
1225         //
1226         //  GetHashCode
1227         //
1228         //  Implements Object.GetHashCode().  Returns the hash code for the
1229         //  CompareInfo.  The hash code is guaranteed to be the same for
1230         //  CompareInfo A and B where A.Equals(B) is true.
1231         //
1232         ////////////////////////////////////////////////////////////////////////
1233
1234
1235         public override int GetHashCode()
1236         {
1237             return (this.Name.GetHashCode());
1238         }
1239
1240         //
1241         // return hash value for the string according to the input CompareOptions 
1242         //
1243
1244         public virtual int GetHashCode(string source, CompareOptions options)
1245         {
1246             if (source == null)
1247             {
1248                 throw new ArgumentNullException("source");
1249             }
1250
1251             if (options == CompareOptions.Ordinal)
1252             {
1253                 return source.GetHashCode();
1254             }
1255
1256             if (options == CompareOptions.OrdinalIgnoreCase)
1257             {
1258                 return TextInfo.GetHashCodeOrdinalIgnoreCase(source);
1259             }
1260
1261             //
1262             // GetHashCodeOfString does more parameters validation. basically will throw when  
1263             // having Ordinal, OrdinalIgnoreCase and StringSort
1264             //
1265
1266             return GetHashCodeOfString(source, options, false, 0);
1267         }
1268
1269         ////////////////////////////////////////////////////////////////////////
1270         //
1271         //  GetHashCodeOfString
1272         //
1273         //  This internal method allows a method that allows the equivalent of creating a Sortkey for a
1274         //  string from CompareInfo, and generate a hashcode value from it.  It is not very convenient
1275         //  to use this method as is and it creates an unnecessary Sortkey object that will be GC'ed.
1276         //
1277         //  The hash code is guaranteed to be the same for string A and B where A.Equals(B) is true and both
1278         //  the CompareInfo and the CompareOptions are the same. If two different CompareInfo objects
1279         //  treat the string the same way, this implementation will treat them differently (the same way that
1280         //  Sortkey does at the moment).
1281         //
1282         //  This method will never be made public itself, but public consumers of it could be created, e.g.:
1283         //
1284         //      string.GetHashCode(CultureInfo)
1285         //      string.GetHashCode(CompareInfo)
1286         //      string.GetHashCode(CultureInfo, CompareOptions)
1287         //      string.GetHashCode(CompareInfo, CompareOptions)
1288         //      etc.
1289         //
1290         //  (the methods above that take a CultureInfo would use CultureInfo.CompareInfo)
1291         //
1292         ////////////////////////////////////////////////////////////////////////
1293         internal int GetHashCodeOfString(string source, CompareOptions options)
1294         {
1295             return GetHashCodeOfString(source, options, false, 0);
1296         }
1297
1298         [System.Security.SecuritySafeCritical]  // auto-generated
1299         internal int GetHashCodeOfString(string source, CompareOptions options, bool forceRandomizedHashing, long additionalEntropy)
1300         {
1301             //
1302             //  Parameter validation
1303             //
1304             if(null == source)
1305             {
1306                 throw new ArgumentNullException("source");
1307             }
1308
1309             if ((options & ValidHashCodeOfStringMaskOffFlags) != 0)
1310             {
1311                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), "options");
1312             }
1313             Contract.EndContractBlock();
1314
1315             if(0 == source.Length)
1316             {
1317                 return(0);
1318             }
1319
1320 #if MONO
1321             return GetSortKey (source, options).GetHashCode ();
1322 #else
1323             //
1324             ////////////////////////////////////////////////////////////////////////
1325             return (InternalGetGlobalizedHashCode(m_dataHandle, m_handleOrigin, this.m_sortName, source, source.Length, GetNativeCompareFlags(options), forceRandomizedHashing, additionalEntropy));
1326 #endif
1327         }
1328
1329         ////////////////////////////////////////////////////////////////////////
1330         //
1331         //  ToString
1332         //
1333         //  Implements Object.ToString().  Returns a string describing the
1334         //  CompareInfo.
1335         //
1336         ////////////////////////////////////////////////////////////////////////
1337
1338
1339         public override String ToString()
1340         {
1341             return ("CompareInfo - " + this.Name);
1342         }
1343
1344 #if FEATURE_USE_LCID
1345         public int LCID
1346         {
1347             get
1348             {
1349                 return CultureInfo.GetCultureInfo(this.Name).LCID;
1350             }
1351         }
1352 #endif
1353
1354 #if !MONO
1355         [System.Security.SecuritySafeCritical]
1356         internal static IntPtr InternalInitSortHandle(String localeName, out IntPtr handleOrigin)
1357         {
1358             return NativeInternalInitSortHandle(localeName, out handleOrigin);
1359         }
1360
1361 #if !FEATURE_CORECLR
1362         private const int SORT_VERSION_WHIDBEY = 0x00001000;
1363         private const int SORT_VERSION_V4 = 0x00060101;
1364
1365         internal static bool IsLegacy20SortingBehaviorRequested
1366         {
1367             get
1368             {
1369                 return InternalSortVersion == SORT_VERSION_WHIDBEY;
1370             }
1371         }
1372
1373         private static uint InternalSortVersion
1374         {
1375             [System.Security.SecuritySafeCritical]
1376             get
1377             {
1378                 return InternalGetSortVersion();
1379             }
1380         }
1381
1382         [OptionalField(VersionAdded = 3)]
1383         private SortVersion m_SortVersion;
1384
1385         public SortVersion Version
1386         {
1387             [SecuritySafeCritical]
1388             get
1389             {
1390                 if(m_SortVersion == null) 
1391                 {
1392                     Win32Native.NlsVersionInfoEx v = new Win32Native.NlsVersionInfoEx();
1393                     v.dwNLSVersionInfoSize = Marshal.SizeOf(typeof(Win32Native.NlsVersionInfoEx));
1394                     InternalGetNlsVersionEx(m_dataHandle, m_handleOrigin, m_sortName, ref v);
1395                     m_SortVersion = new SortVersion(v.dwNLSVersion, (v.dwEffectiveId != 0) ? v.dwEffectiveId : LCID, v.guidCustomVersion);
1396                 }
1397
1398                 return m_SortVersion;
1399             }
1400         }
1401         
1402         [System.Security.SecurityCritical]
1403         [ResourceExposure(ResourceScope.None)]
1404         [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
1405         [SuppressUnmanagedCodeSecurity]
1406         [return: MarshalAs(UnmanagedType.Bool)]
1407         private static extern bool InternalGetNlsVersionEx(IntPtr handle, IntPtr handleOrigin, String localeName, ref Win32Native.NlsVersionInfoEx lpNlsVersionInformation);
1408
1409         [System.Security.SecurityCritical]
1410         [ResourceExposure(ResourceScope.None)]
1411         [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
1412         [SuppressUnmanagedCodeSecurity]
1413         private static extern uint InternalGetSortVersion();
1414
1415 #endif
1416         [System.Security.SecurityCritical]
1417         [ResourceExposure(ResourceScope.None)]
1418         [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
1419         [SuppressUnmanagedCodeSecurity]
1420         private static extern IntPtr NativeInternalInitSortHandle(String localeName, out IntPtr handleOrigin);
1421
1422         // Get a locale sensitive sort hash code from native code -- COMNlsInfo::InternalGetGlobalizedHashCode
1423         [System.Security.SecurityCritical]  // auto-generated
1424         [ResourceExposure(ResourceScope.None)]
1425         [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
1426         [SuppressUnmanagedCodeSecurity]
1427         private static extern int InternalGetGlobalizedHashCode(IntPtr handle, IntPtr handleOrigin, string localeName, string source, int length, int dwFlags, bool forceRandomizedHashing, long additionalEntropy);
1428
1429         // Use native API calls to see if this string is entirely defined -- COMNlsInfo::InternalIsSortable
1430         [System.Security.SecurityCritical]  // auto-generated
1431         [ResourceExposure(ResourceScope.None)]
1432         [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
1433         [SuppressUnmanagedCodeSecurity]
1434         [return: MarshalAs(UnmanagedType.Bool)]
1435         private static extern bool InternalIsSortable(IntPtr handle, IntPtr handleOrigin, String localeName, String source, int length);
1436
1437         // Compare a string using the native API calls -- COMNlsInfo::InternalCompareString
1438         [System.Security.SecurityCritical]  // auto-generated
1439         [ResourceExposure(ResourceScope.Process)]
1440         [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
1441         [SuppressUnmanagedCodeSecurity]
1442         private static extern int InternalCompareString(IntPtr handle, IntPtr handleOrigin, String localeName, String string1, int offset1, int length1,
1443                                                                               String string2, int offset2, int length2, int flags);
1444
1445         // InternalFindNLSStringEx parameters is not exactly matching kernel32::FindNLSStringEx parameters.
1446         // Call through to NewApis::FindNLSStringEx so we can get the right behavior
1447         [System.Security.SecurityCritical]  // auto-generated
1448         [ResourceExposure(ResourceScope.None)]
1449         [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
1450         [SuppressUnmanagedCodeSecurity]
1451         private static extern int InternalFindNLSStringEx(IntPtr handle, IntPtr handleOrigin, String localeName, int flags, String source, int sourceCount, int startIndex, string target, int targetCount);
1452
1453         // Call through to NewAPis::LCMapStringEx so we can get appropriate behavior for all platforms
1454         [System.Security.SecurityCritical]  // auto-generated
1455         [ResourceExposure(ResourceScope.None)]
1456         [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
1457         [SuppressUnmanagedCodeSecurity]
1458         private static extern int InternalGetSortKey(IntPtr handle, IntPtr handleOrigin, String localeName, int flags, String source, int sourceCount, byte[] target, int targetCount);
1459 #endif
1460     }
1461 }