Expose missing Global/Encoding APIs in coreclr and remove empty stubs
[platform/upstream/coreclr.git] / src / classlibnative / nls / nlsinfo.cpp
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 ////////////////////////////////////////////////////////////////////////////
5 //
6 //  Class:    NLSInfo
7 //
8
9 //
10 //  Purpose:  This module implements the methods of the COMNlsInfo
11 //            class.  These methods are the helper functions for the
12 //            Locale class.
13 //
14 //  Date:     August 12, 1998
15 //
16 ////////////////////////////////////////////////////////////////////////////
17
18 //
19 //  Include Files.
20 //
21 #include "common.h"
22 #include "object.h"
23 #include "excep.h"
24 #include "vars.hpp"
25 #include "interoputil.h"
26 #include "corhost.h"
27
28 #include <winnls.h>
29
30 #include "utilcode.h"
31 #include "frames.h"
32 #include "field.h"
33 #include "metasig.h"
34 #include "nls.h"
35 #include "nlsinfo.h"
36 #include "nlstable.h"
37
38 //#include <mlang.h>
39 #include "sortversioning.h"
40
41 #include "newapis.h"
42
43 //
44 //  Constant Declarations.
45 //
46
47 #ifndef COMPARE_OPTIONS_IGNORECASE
48 #define COMPARE_OPTIONS_IGNORECASE  0x00000001
49 #endif
50
51 #ifndef LOCALE_SNAME
52 #define LOCALE_SNAME                0x0000005c
53 #endif
54
55 #ifndef LOCALE_SNAN
56 #define LOCALE_SNAN                 0x00000069
57 #endif
58
59 #ifndef LOCALE_SPOSINFINITY
60 #define LOCALE_SPOSINFINITY         0x0000006a
61 #endif
62
63 #ifndef LOCALE_SNEGINFINITY
64 #define LOCALE_SNEGINFINITY         0x0000006b
65 #endif
66
67 #ifndef LOCALE_SPARENT
68 #define LOCALE_SPARENT              0x0000006d
69 #endif
70
71 #ifndef LOCALE_SCONSOLEFALLBACKNAME
72 #define LOCALE_SCONSOLEFALLBACKNAME 0x0000006e   // Fallback name for within the console
73 #endif
74
75 #ifndef LOCALE_SISO3166CTRYNAME2
76 #define LOCALE_SISO3166CTRYNAME2    0x00000068
77 #endif
78
79 #ifndef LOCALE_SISO639LANGNAME2
80 #define LOCALE_SISO639LANGNAME2     0x00000067
81 #endif
82
83 #ifndef LOCALE_SSHORTESTDAYNAME1
84 #define LOCALE_SSHORTESTDAYNAME1    0x00000060
85 #endif
86
87 // Windows 7 LCTypes
88 #ifndef LOCALE_INEUTRAL
89 #define LOCALE_INEUTRAL             0x00000071   // Returns 0 for specific cultures, 1 for neutral cultures.
90 #endif
91
92 #ifndef LCMAP_TITLECASE
93 #define LCMAP_TITLECASE             0x00000300   // Title Case Letters
94 #endif
95
96 // Windows 8 LCTypes
97 #ifndef LCMAP_SORTHANDLE
98 #define LCMAP_SORTHANDLE   0x20000000
99 #endif
100
101 #ifndef LCMAP_HASH
102 #define LCMAP_HASH   0x00040000
103 #endif
104
105 #ifndef LOCALE_REPLACEMENT
106 #define LOCALE_REPLACEMENT          0x00000008   // locales that replace shipped locales (callback flag only)
107 #endif // LOCALE_REPLACEMENT
108
109 #define LOCALE_MAX_STRING_SIZE      530          // maximum sice of LOCALE_SKEYBOARDSTOINSTALL, currently 5 "long" + 2 "short" keyboard signatures (YI + 3).
110
111 #define MAX_STRING_VALUE        512
112
113 // TODO: NLS Arrowhead -Be nice if we could depend more on the OS for this
114 // Language ID for CHT (Taiwan)
115 #define LANGID_ZH_TW            0x0404
116 // Language ID for CHT (Hong-Kong)
117 #define LANGID_ZH_HK            0x0c04
118 #define REGION_NAME_0404 W("\x53f0\x7063")
119 #if BIGENDIAN
120 #define INTERNATIONAL_CURRENCY_SYMBOL W("\x00a4")
121 #else
122 #define INTERNATIONAL_CURRENCY_SYMBOL W("\xa400")
123 #endif
124
125 inline BOOL IsCustomCultureId(LCID lcid)
126 {
127     return (lcid == LOCALE_CUSTOM_DEFAULT || lcid == LOCALE_CUSTOM_UNSPECIFIED);
128 }
129
130 #ifndef FEATURE_COREFX_GLOBALIZATION
131 //
132 // Normalization Implementation
133 //
134 #define NORMALIZATION_DLL       MAKEDLLNAME(W("normalization"))
135 HMODULE COMNlsInfo::m_hNormalization = NULL;
136 PFN_NORMALIZATION_IS_NORMALIZED_STRING COMNlsInfo::m_pfnNormalizationIsNormalizedStringFunc = NULL;
137 PFN_NORMALIZATION_NORMALIZE_STRING COMNlsInfo::m_pfnNormalizationNormalizeStringFunc = NULL;
138 PFN_NORMALIZATION_INIT_NORMALIZATION COMNlsInfo::m_pfnNormalizationInitNormalizationFunc = NULL;
139 #endif // FEATURE_COREFX_GLOBALIZATION
140
141 #if FEATURE_CODEPAGES_FILE
142 /*============================nativeCreateOpenFileMapping============================
143 **Action: Create or open a named memory file mapping.
144 **Returns: Pointer to named section, or NULL if failed
145 **Arguments:
146 **  StringObject*   inSectionName  - name of section to open/create
147 **  int             inBytesToAllocate - desired size of memory section in bytes
148 **                      We use the last 4 bytes (must be aligned, so only choose
149 **                      inBytesToAllocate in multiples of 4) to indicate if the
150 **                      section is set or not.  AFTER section is initialized, set
151 **                      those 4 bytes to non-0, otherwise you'll get get new
152 **                      heap memory all the time.
153 **  HANDLE*         mappedFile - is the handle of the memory mapped file. this is
154 **                      out parameter.
155 **
156 ** NOTE: We'll try to open the same object, so we can share names.  We don't lock
157 **       though, so 2 thread could get the same object, but thread 1 might not
158 **       have initialized it yet.
159 **
160 ** NOTE: For NT you should add a Global\ to the beginning of the name if you
161 **       want to share it machine wide.
162 **
163 ==============================================================================*/
164 FCIMPL3(LPVOID, COMNlsInfo::nativeCreateOpenFileMapping,
165             StringObject* inSectionNameUNSAFE, int inBytesToAllocate, HANDLE *mappedFile)
166 {
167     CONTRACTL
168     {
169         FCALL_CHECK;
170         PRECONDITION(CheckPointer(inSectionNameUNSAFE));
171         PRECONDITION(inBytesToAllocate % 4 == 0);
172         PRECONDITION(inBytesToAllocate > 0);
173         PRECONDITION(CheckPointer(mappedFile));
174     } CONTRACTL_END;
175
176     // Need a place for our result
177     LPVOID pResult = NULL;
178
179     STRINGREF inString(inSectionNameUNSAFE);
180     HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(inString);
181
182     _ASSERTE(inBytesToAllocate % 4 == 0);   // Expected 4 bytes boundaries so we don't get unaligned
183     _ASSERTE(inBytesToAllocate > 0);        // Pointless to have <=0 allocation
184
185     StackSString inNameStackBuffer (inString->GetBuffer());
186     pResult = NLSTable::OpenOrCreateMemoryMapping((LPCWSTR)inNameStackBuffer, inBytesToAllocate, mappedFile);
187
188     // Worst case allocate some memory, use holder
189     //    if (pResult == NULL) pResult = new BYTE[inBytesToAllocate];
190     if (pResult == NULL)
191     {
192         // Need to use a NewHolder
193         NewArrayHolder<BYTE> holder (new BYTE[inBytesToAllocate]);
194         pResult = holder;
195         // Zero out the mapCodePageCached field (an int value, and it's used to check if the section is initialized or not.)
196         BYTE* pByte = (BYTE*)pResult;
197         FillMemory(pByte + inBytesToAllocate - sizeof(int), sizeof(int), 0);
198         holder.SuppressRelease();
199     }
200
201     HELPER_METHOD_FRAME_END();
202
203     return pResult;
204 }
205 FCIMPLEND
206 #endif // FEATURE_CODEPAGES_FILE
207
208 // InternalIsSortable
209 //
210 // Called by CompareInfo.IsSortable() to determine if a string has entirely sortable (ie: defined) code points.
211 BOOL QCALLTYPE COMNlsInfo::InternalIsSortable(INT_PTR handle, INT_PTR handleOrigin, LPCWSTR localeName, LPCWSTR string, INT32 length)
212 {
213     CONTRACTL
214     {
215         QCALL_CHECK;
216         PRECONDITION(CheckPointer(string));
217     } CONTRACTL_END;
218     BOOL result = FALSE;
219     BEGIN_QCALL;
220
221 #ifndef FEATURE_CORECLR
222     AppDomain* curDomain = GetAppDomain();
223
224     if(!(curDomain->m_bUseOsSorting))
225     {
226         handle = EnsureValidSortHandle(handle, handleOrigin, localeName);
227         result = SortVersioning::SortDllIsDefinedString((SortVersioning::PSORTHANDLE) handle, COMPARE_STRING, 0, string, length);
228     }
229     else if(curDomain->m_pCustomSortLibrary != NULL)
230     {
231         result = (curDomain->m_pCustomSortLibrary->pIsNLSDefinedString)(COMPARE_STRING, 0, NULL, string, length);
232     }
233     else
234 #endif
235     {
236         // Function should be COMPARE_STRING, dwFlags should be NULL, lpVersionInfo should be NULL for now
237         result = NewApis::IsNLSDefinedString(COMPARE_STRING, 0, NULL, string, length);
238     }
239
240     END_QCALL;
241     return result;
242 }
243
244 ////////////////////////////////////////////////////////////////////////////
245 //
246 //  InternalGetUserDefaultLocaleName
247 //
248 //  Returns a string with the name of our LCID and returns 0 in LCID.
249 //  If we cant return
250 //
251 ////////////////////////////////////////////////////////////////////////////
252 // This is new to longhorn
253 BOOL QCALLTYPE COMNlsInfo::InternalGetDefaultLocaleName(INT32 langType, QCall::StringHandleOnStack defaultLocaleName)
254 {
255     CONTRACTL
256     {
257         QCALL_CHECK;
258         PRECONDITION((langType == LOCALE_SYSTEM_DEFAULT) || (langType == LOCALE_USER_DEFAULT));
259     } CONTRACTL_END;
260
261     BOOL result;
262     BEGIN_QCALL;
263
264     WCHAR strName[LOCALE_NAME_MAX_LENGTH];
265     int size = 0;
266
267     if (langType == LOCALE_SYSTEM_DEFAULT)
268     {
269         size = NewApis::GetSystemDefaultLocaleName(strName,NumItems(strName));
270     }
271     else
272     {
273         _ASSERT(langType == LOCALE_USER_DEFAULT);
274         size = NewApis::GetUserDefaultLocaleName(strName,NumItems(strName));
275     }
276
277     // Not found, either not longhorn (no LOCALE_SNAME) or not a valid name
278     if (size == 0)
279     {
280         result = false;
281     }
282     else
283     {
284         defaultLocaleName.Set(strName);
285         result = true;
286     }
287     END_QCALL;
288     return result;
289 }
290
291 BOOL QCALLTYPE COMNlsInfo::InternalGetSystemDefaultUILanguage(QCall::StringHandleOnStack systemDefaultUiLanguage)
292 {
293     QCALL_CONTRACT;
294     BOOL result;
295     BEGIN_QCALL;
296
297     WCHAR localeName[LOCALE_NAME_MAX_LENGTH];
298
299     int systemDefaultUiLcid = GetSystemDefaultUILanguage();
300     if(systemDefaultUiLcid == LANGID_ZH_TW)
301     {
302         if (!NewApis::IsZhTwSku())
303         {
304              systemDefaultUiLcid = LANGID_ZH_HK;
305         } 
306     }
307     
308     int length = NewApis::LCIDToLocaleName(systemDefaultUiLcid, localeName, NumItems(localeName), 0);
309     if (length == 0)
310     {
311         result = false;
312     }
313     else
314     {
315         systemDefaultUiLanguage.Set(localeName);
316         result = true;
317     }
318
319     END_QCALL;
320     return result;
321 }
322
323 /*
324  */
325 BOOL QCALLTYPE COMNlsInfo::InternalGetUserDefaultUILanguage(QCall::StringHandleOnStack userDefaultUiLanguage)
326 {
327     QCALL_CONTRACT;
328     BOOL result;
329     BEGIN_QCALL;
330
331     WCHAR wszBuffer[LOCALE_NAME_MAX_LENGTH];
332     LPCWSTR wszLangName=NULL;
333
334     int res= 0;
335     ULONG uLangCount=0;
336     ULONG uBufLen=0;
337     res= NewApis::GetUserPreferredUILanguages (MUI_LANGUAGE_NAME,&uLangCount,NULL,&uBufLen);
338     if (res == 0)
339         ThrowLastError();
340
341
342     NewArrayHolder<WCHAR> sPreferredLanguages(NULL);
343
344     if (uBufLen > 0 && uLangCount > 0 )
345     {
346         sPreferredLanguages = new WCHAR[uBufLen];
347         res= NewApis::GetUserPreferredUILanguages (MUI_LANGUAGE_NAME,&uLangCount,sPreferredLanguages,&uBufLen);
348
349         if (res == 0)
350             ThrowLastError();
351
352          wszLangName=sPreferredLanguages;
353 // Review size_t to int conversion (possible loss of data).
354 #ifdef _MSC_VER
355 #pragma warning(push)
356 #pragma warning(disable:4267)
357 #endif
358         res=wcslen(wszLangName)+1;
359 #ifdef _MSC_VER
360 #pragma warning(pop)
361 #endif
362     }
363     else
364     {
365         res=0;
366     }
367
368     if (res == 0) {
369         res = NewApis::GetUserDefaultLocaleName(wszBuffer,NumItems(wszBuffer));
370         wszLangName=wszBuffer;
371     }
372
373
374     // If not found, either not longhorn (no LOCALE_SNAME) or not a valid name
375     if (res == 0)
376     {
377         // Didn't find string, return an empty string.
378         result = false;
379     }
380     else
381     {
382         userDefaultUiLanguage.Set(wszLangName);
383         result = true;
384     }
385
386     // Return the found language name.  LCID should be found one already.
387     END_QCALL;
388     return result;
389 }
390
391 // Added but disabled from desktop in .NET 4.0, stayed disabled in .NET 4.5
392 #ifdef FEATURE_CORECLR
393 FCIMPL0(Object*, COMNlsInfo::nativeGetResourceFallbackArray)
394 {
395     CONTRACTL
396     {
397         FCALL_CHECK;
398     } CONTRACTL_END;
399
400     DWORD dwFlags = MUI_MERGE_USER_FALLBACK | MUI_MERGE_SYSTEM_FALLBACK;
401     ULONG cchLanguagesBuffer = 0;
402     ULONG ulNumLanguages = 0;
403     BOOL result = FALSE;
404
405     struct _gc
406     {
407         PTRARRAYREF     resourceFallbackArray;
408     } gc;
409
410     gc.resourceFallbackArray = NULL;
411
412     // If the resource lookups we're planning on doing are going to be written to a non-Unicode console,
413     // then we should ideally only return languages that can be displayed correctly on the console.  The 
414     // trick is guessing at whether we're writing this data to the console, which we can't do well.
415     // Instead, we ask new apps to call GetConsoleFallbackUICulture & fall back to en-US.
416     bool disableUserFallback;
417     BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), FCThrow(kStackOverflowException));
418     disableUserFallback = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_Resources_DisableUserPreferredFallback) == 1
419     END_SO_INTOLERANT_CODE;
420
421     if (disableUserFallback)
422         return NULL;
423
424     HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
425
426     // first call with null buffer to get size
427     result = NewApis::GetThreadPreferredUILanguages(dwFlags, &ulNumLanguages, NULL, &cchLanguagesBuffer);
428     if (cchLanguagesBuffer > 0)
429     {
430         NewArrayHolder<WCHAR> stringBuffer = new (nothrow) WCHAR[cchLanguagesBuffer];
431         if (stringBuffer != NULL)
432         {
433             result = NewApis::GetThreadPreferredUILanguages(dwFlags, &ulNumLanguages, stringBuffer, &cchLanguagesBuffer);
434             _ASSERTE(result);
435
436             // now string into strings
437             gc.resourceFallbackArray = (PTRARRAYREF) AllocateObjectArray(ulNumLanguages, g_pStringClass);
438
439             LPCWSTR buffer = stringBuffer;   // Restart @ buffer beginning
440             for(DWORD i = 0; i < ulNumLanguages; i++)
441             {
442                 OBJECTREF o = (OBJECTREF) StringObject::NewString(buffer);
443                 gc.resourceFallbackArray->SetAt(i, o);
444                 buffer += (lstrlenW(buffer) + 1);
445             }
446         }
447     }
448
449     HELPER_METHOD_FRAME_END();
450
451     return OBJECTREFToObject(gc.resourceFallbackArray);
452
453 }
454 FCIMPLEND
455 #endif // FEATURE_CORECLR
456
457 INT32 COMNlsInfo::CallGetUserDefaultUILanguage()
458 {
459     CONTRACTL
460     {
461         NOTHROW;
462         GC_NOTRIGGER;
463         MODE_ANY;
464         SO_TOLERANT;
465     } CONTRACTL_END;
466
467     static INT32 s_lcid = 0;
468
469     // The user UI language cannot change within a process (in fact it cannot change within a logon session),
470     // so we cache it. We dont take a lock while initializing s_lcid for the same reason. If two threads are
471     // racing to initialize s_lcid, the worst thing that'll happen is that one thread will call
472     // GetUserDefaultUILanguage needlessly, but the final result is going to be the same.
473     if (s_lcid == 0)
474     {
475         INT32 s_lcidTemp = GetUserDefaultUILanguage();
476         if (s_lcidTemp == LANGID_ZH_TW)
477         {
478             // If the UI language ID is 0x0404, we need to do extra check to decide
479             // the real UI language, since MUI (in CHT)/HK/TW Windows SKU all uses 0x0404 as their CHT language ID.
480             if (!NewApis::IsZhTwSku())
481             {
482                 s_lcidTemp = LANGID_ZH_HK;
483             }
484         }
485         s_lcid = s_lcidTemp;
486     }
487
488     return s_lcid;
489 }
490
491 INT_PTR COMNlsInfo::EnsureValidSortHandle(INT_PTR handle, INT_PTR handleOrigin, LPCWSTR localeName)
492 {
493     LIMITED_METHOD_CONTRACT;
494
495 #ifndef FEATURE_CORECLR
496     AppDomain* curDomain = GetAppDomain();
497
498     if(!(curDomain->m_bUseOsSorting) && handleOrigin == (INT_PTR) SortVersioning::GetSortHandle && ((SortVersioning::PSORTHANDLE) handle)->dwNLSVersion == curDomain->m_sortVersion)
499     {
500         return handle;
501     }
502
503     if(curDomain->m_bUseOsSorting && curDomain->m_pCustomSortLibrary == NULL && handleOrigin == (INT_PTR) NewApis::LCMapStringEx)
504     {
505         return handle;
506     }
507
508     if(curDomain->m_bUseOsSorting && curDomain->m_pCustomSortLibrary != NULL && handleOrigin == (INT_PTR) curDomain->m_pCustomSortLibrary->pLCMapStringEx)
509     {
510         return handle;
511     }
512
513     // At this point, we can't reuse the sort handle (it has different sort semantics than this domain) so we need to get a new one.
514     INT_PTR newHandleOrigin;
515     return InitSortHandleHelper(localeName, &newHandleOrigin);
516 #else
517     // For CoreCLR, on Windows 8 and up the handle will be valid. on downlevels the handle will be null
518     return handle;
519 #endif
520 }
521
522 #ifdef FEATURE_SYNTHETIC_CULTURES
523 ////////////////////////////////////////////////////////////////////////////
524 //
525 //  WstrToInteger4
526 //
527 ////////////////////////////////////////////////////////////////////////////
528
529 /*=================================WstrToInteger4==================================
530 **Action: Convert a Unicode string to an integer.  Error checking is ignored.
531 **Returns: The integer value of wstr
532 **Arguments:
533 **      wstr: NULL terminated wide string.  Can have character 0'-'9', 'a'-'f', and 'A' - 'F'
534 **      Radix: radix to be used in the conversion.
535 **Exceptions: None.
536 ==============================================================================*/
537
538 INT32 COMNlsInfo::WstrToInteger4(
539     __in_z LPCWSTR wstr,
540     __in int Radix)
541 {
542     CONTRACTL {
543         NOTHROW;
544         GC_NOTRIGGER;
545         MODE_ANY;
546         SO_TOLERANT;
547         PRECONDITION(CheckPointer(wstr));
548         PRECONDITION(Radix > 1 && Radix <= 16);
549     } CONTRACTL_END;
550     INT32 Value = 0;
551     int Base = 1;
552
553     for (int Length = Wszlstrlen(wstr) - 1; Length >= 0; Length--)
554
555     {
556         WCHAR ch = wstr[Length];
557         _ASSERTE((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F'));
558         if (ch >= 'a')
559         {
560             ch = ch - 'a' + 'A';
561         }
562
563         Value += ((ch >= 'A') ? (ch - 'A' + 10) : (ch - '0')) * Base;
564         Base *= Radix;
565     }
566
567     return (Value);
568 }
569 #endif // FEATURE_SYNTHETIC_CULTURES
570
571
572 #ifndef FEATURE_CORECLR
573 FCIMPL1(FC_BOOL_RET, COMNlsInfo::nativeSetThreadLocale, StringObject* localeNameUNSAFE)
574 {
575     CONTRACTL
576     {
577         FCALL_CHECK;
578         PRECONDITION(CheckPointer(localeNameUNSAFE));
579     } CONTRACTL_END;
580
581     LCID lcid = 0;
582
583     // TODO: NLS Arrowhead -A bit scary becausue Set ThreadLocale can't handle custom cultures?
584     STRINGREF localeName = (STRINGREF)localeNameUNSAFE;
585     HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(localeName);
586     lcid=NewApis::LocaleNameToLCID(localeName->GetBuffer(),0);
587     if (lcid == 0)
588     {
589         ThrowHR(HRESULT_FROM_WIN32(GetLastError()));
590     }
591         HELPER_METHOD_FRAME_END();
592
593
594     BOOL result = TRUE;
595
596     // SetThreadLocale doesn't handle names/custom cultures
597 #ifdef _MSC_VER
598 // Get rid of the SetThreadLocale warning in OACR:
599 #pragma warning(push)
600 #pragma warning(disable:38010)
601 #endif
602     result = ::SetThreadLocale(lcid);
603 #ifdef _MSC_VER
604 #pragma warning(pop)
605 #endif
606
607     FC_RETURN_BOOL(result);
608 }
609 FCIMPLEND
610 #endif
611
612
613 FCIMPL2(Object*, COMNlsInfo::nativeGetLocaleInfoEx, StringObject* localeNameUNSAFE, INT32 lcType)
614 {
615     CONTRACTL
616     {
617         FCALL_CHECK;
618         PRECONDITION(CheckPointer(localeNameUNSAFE));
619     } CONTRACTL_END;
620
621     struct _gc
622     {
623         STRINGREF   localeName;
624         STRINGREF   refRetVal;
625     } gc;
626
627     // Dereference our string
628     gc.refRetVal = NULL;
629     gc.localeName = (STRINGREF)localeNameUNSAFE;
630
631     HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
632     StackSString localeNameStackBuffer( gc.localeName->GetBuffer() );
633
634     WCHAR buffer[LOCALE_MAX_STRING_SIZE];
635     int result = NewApis::GetLocaleInfoEx(localeNameStackBuffer, lcType, buffer, NumItems(buffer));
636
637     // Make a string out of it
638     if (result != 0)
639     {
640         // Exclude the NULL char at the end, except that LOCALE_FONTSIGNATURE isn't
641         // really a string, so we need the last character too.
642         gc.refRetVal = StringObject::NewString(buffer, ((lcType & ~LOCALE_NOUSEROVERRIDE) == LOCALE_FONTSIGNATURE) ? result : result-1);
643     }
644     else
645     {
646     }
647     HELPER_METHOD_FRAME_END();
648     return OBJECTREFToObject(gc.refRetVal);
649 }
650 FCIMPLEND
651
652
653 FCIMPL2(INT32, COMNlsInfo::nativeGetLocaleInfoExInt, StringObject* localeNameUNSAFE, INT32 lcType)
654 {
655     CONTRACTL
656     {
657         FCALL_CHECK;
658         PRECONDITION(CheckPointer(localeNameUNSAFE));
659     } CONTRACTL_END;
660
661     INT32 result = 0;
662
663     // Dereference our string
664     STRINGREF localeName = (STRINGREF)localeNameUNSAFE;
665     HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(localeName);
666
667     lcType |= LOCALE_RETURN_NUMBER;
668
669     if (NewApis::GetLocaleInfoEx(localeName->GetBuffer(), lcType, (LPWSTR)&result, sizeof(INT32) / sizeof (WCHAR)) == 0)
670     {
671         // return value of 0 indicates failure and error value is supposed to be set.
672         // shouldn't ever really happen
673         _ASSERTE(!"catastrophic failure calling NewApis::nativeGetLocaleInfoExInt!  This could be a CultureInfo bug (bad localeName string) or maybe a GCHole.");
674     }
675
676     HELPER_METHOD_FRAME_END();
677
678     return result;
679 }
680 FCIMPLEND
681
682
683
684 ////////////////////////////////////////////////////////////////////////
685 //
686 // Call the Win32 GetLocaleInfo() using the specified lcid to retrieve
687 // the native digits, probably from the registry override. The return
688 // indicates whether the call was successful.
689 //
690 // Parameters:
691 //       IN lcid            the LCID to make the Win32 call with
692 //      OUT pOutputStrAry   The output managed string array.
693 //
694 ////////////////////////////////////////////////////////////////////////
695 BOOL COMNlsInfo::GetNativeDigitsFromWin32(LPCWSTR locale, PTRARRAYREF * pOutputStrAry, BOOL useUserOverride)
696 {
697     CONTRACTL
698     {
699         GC_TRIGGERS;
700         MODE_COOPERATIVE;
701         THROWS;
702     } CONTRACTL_END;
703
704     WCHAR buffer[11];
705     int result = 0;
706
707     DWORD lcType = LOCALE_SNATIVEDIGITS;
708     if(!useUserOverride)
709     {
710         lcType |= LOCALE_NOUSEROVERRIDE;
711     }
712     result = NewApis::GetLocaleInfoEx(locale, lcType, buffer, 11);
713     // Be very unforgiving and only support strings of size 10 plus the NULL
714     if (result == 11)
715     {
716         // Break up the unmanaged ten-character ZLS into what NFI wants (a managed
717         // ten-string array).
718         //
719         // Allocate the array of STRINGREFs.  We don't need to check for null because the GC will throw
720         // an OutOfMemoryException if there's not enough memory.
721         //
722         PTRARRAYREF DigitArray = (PTRARRAYREF) AllocateObjectArray(10, g_pStringClass);
723
724         GCPROTECT_BEGIN(DigitArray);
725         for(DWORD i = 0;  i < 10; i++) {
726             OBJECTREF o = (OBJECTREF) StringObject::NewString(buffer + i, 1);
727             DigitArray->SetAt(i, o);
728         }
729         GCPROTECT_END();
730
731         _ASSERTE(pOutputStrAry != NULL);
732         *pOutputStrAry = DigitArray;
733     }
734
735     return (result == 11);
736 }
737
738
739 ////////////////////////////////////////////////////////////////////////
740 //
741 // Call the Win32 GetLocaleInfoEx() using the specified lcid and LCTYPE.
742 // The return value can be INT32 or an allocated managed string object, depending on
743 // which version's called.
744 //
745 // Parameters:
746 //      OUT pOutputInt32    The output int32 value.
747 //      OUT pOutputRef      The output string value.
748 //
749 ////////////////////////////////////////////////////////////////////////
750 BOOL COMNlsInfo::CallGetLocaleInfoEx(LPCWSTR localeName, int lcType, INT32* pOutputInt32, BOOL useUserOverride)
751 {
752     CONTRACTL
753     {
754         GC_NOTRIGGER;
755         MODE_ANY;
756         NOTHROW;
757     } CONTRACTL_END;
758
759     int result = 0;
760
761     _ASSERT((lcType & LOCALE_RETURN_NUMBER) != 0);
762     if(!useUserOverride)
763     {
764         lcType |= LOCALE_NOUSEROVERRIDE;
765     }
766     result = NewApis::GetLocaleInfoEx(localeName, lcType, (LPWSTR)pOutputInt32, sizeof(*pOutputInt32));
767
768     return (result != 0);
769 }
770
771 BOOL COMNlsInfo::CallGetLocaleInfoEx(LPCWSTR localeName, int lcType, STRINGREF* pOutputStrRef, BOOL useUserOverride)
772 {
773     CONTRACTL
774     {
775         THROWS;                 // We can throw since we are allocating managed string.
776         GC_TRIGGERS;
777         MODE_COOPERATIVE;
778     } CONTRACTL_END;
779
780     WCHAR buffer[LOCALE_NAME_MAX_LENGTH];
781     int result = 0;
782
783     _ASSERT((lcType & LOCALE_RETURN_NUMBER) == 0);
784     if(!useUserOverride)
785     {
786         lcType |= LOCALE_NOUSEROVERRIDE;
787     }
788     result = NewApis::GetLocaleInfoEx(localeName, lcType, buffer, LOCALE_NAME_MAX_LENGTH);
789
790     if (result != 0)
791     {
792         _ASSERTE(pOutputStrRef != NULL);
793         *pOutputStrRef = StringObject::NewString(buffer, result - 1);
794     }
795
796     return (result != 0);
797 }
798
799 FCIMPL1(Object*, COMNlsInfo::LCIDToLocaleName, LCID lcid)
800 {
801     FCALL_CONTRACT;
802
803     STRINGREF refRetVal = NULL;
804
805     // The maximum size for locale name is 85 characters.
806     WCHAR localeName[LOCALE_NAME_MAX_LENGTH];
807     int result = 0;
808
809     HELPER_METHOD_FRAME_BEGIN_RET_0();
810
811     // Note that this'll return neutral names (unlike native Vista APIs)
812     result = NewApis::LCIDToLocaleName(lcid, localeName, LOCALE_NAME_MAX_LENGTH, 0);
813
814     if (result != 0)
815     {
816         refRetVal = StringObject::NewString(localeName, result - 1);
817     }
818     else
819     {
820         refRetVal = StringObject::GetEmptyString();
821     }
822
823     HELPER_METHOD_FRAME_END();
824
825     return OBJECTREFToObject(refRetVal);
826 }
827 FCIMPLEND
828
829 FCIMPL1(INT32, COMNlsInfo::LocaleNameToLCID, StringObject* localeNameUNSAFE)
830 {
831     CONTRACTL
832     {
833         FCALL_CHECK;
834         PRECONDITION(CheckPointer(localeNameUNSAFE));
835     } CONTRACTL_END;
836
837     INT32 result = 0;
838
839     // Dereference our string
840     STRINGREF localeName = (STRINGREF)localeNameUNSAFE;
841     HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(localeName);
842
843     // Note that this'll return neutral names (unlike native Vista APIs)
844     result = NewApis::LocaleNameToLCID(localeName->GetBuffer(), 0);
845
846     HELPER_METHOD_FRAME_END();
847
848     return result;
849 }
850 FCIMPLEND
851
852 ////////////////////////////////////////////////////////////////////////
853 //
854 // Implementation of CultureInfo.nativeGetNumberFormatInfoValues.
855 //
856 // Retrieve NumberFormatInfo (NFI) properties from windows
857 //
858 // Parameters:
859 //      IN/OUT pNumfmtUNSAFE
860 //                  The pointer of the managed NumberFormatInfo passed
861 //                  from the managed side.
862 //                  Note that the native NumberFormatInfo* is defined
863 //                  in COMNumber.h
864 // Note:
865 // Managed string will be allocated and assign to the string fields in
866 //      the managed NumberFormatInfo passed in pNumftUNSAFE
867 //
868 ////////////////////////////////////////////////////////////////////////
869
870
871 /*
872     This is the list of the data members in the managed NumberFormatInfo and their
873     corresponding LCTYPE().
874
875     Win32 GetLocaleInfo() constants             Data members in NumberFormatInfo in the defined order.
876     LOCALE_SPOSITIVE                            // String positiveSign
877     LOCALE_SNEGATIVE                            // String negativeSign
878     LOCALE_SDECIMAL                             // String numberDecimalSeparator
879     LOCALE_SGROUPING                            // String numberGroupSeparator
880     LOCALE_SMONGROUPING                         // String currencyGroupSeparator
881     LOCALE_SMONDECIMALSEP                       // String currencyDecimalSeparator
882     LOCALE_SCURRENCY                            // String currencySymbol
883     N/A                                         // String ansiCurrencySymbol
884     N/A                                         // String nanSymbol
885     N/A                                         // String positiveInfinitySymbol
886     N/A                                         // String negativeInfinitySymbol
887     N/A                                         // String percentDecimalSeparator
888     N/A                                         // String percentGroupSeparator
889     N/A                                         // String percentSymbol
890     N/A                                         // String perMilleSymbol
891
892     N/A                                         // int m_dataItem
893
894     LOCALE_IDIGITS | LOCALE_RETURN_NUMBER,      // int numberDecimalDigits
895     LOCALE_ICURRDIGITS | LOCALE_RETURN_NUMBER,  // int currencyDecimalDigits
896     LOCALE_ICURRENCY | LOCALE_RETURN_NUMBER,    // int currencyPositivePattern
897     LOCALE_INEGCURR | LOCALE_RETURN_NUMBER,      // int currencyNegativePattern
898     LOCALE_INEGNUMBER| LOCALE_RETURN_NUMBER,    // int numberNegativePattern
899     N/A                                         // int percentPositivePattern
900     N/A                                         // int percentNegativePattern
901     N/A                                         // int percentDecimalDigits
902     N/A                                         // bool isReadOnly=false;
903     N/A                                         // internal bool m_useUserOverride;
904 */
905 FCIMPL3(FC_BOOL_RET, COMNlsInfo::nativeGetNumberFormatInfoValues,
906         StringObject* localeNameUNSAFE, NumberFormatInfo* pNumfmtUNSAFE, CLR_BOOL useUserOverride) {
907     CONTRACTL
908     {
909         FCALL_CHECK;
910         PRECONDITION(CheckPointer(localeNameUNSAFE));
911     } CONTRACTL_END;
912
913     BOOL ret = TRUE;
914
915     struct _gc
916     {
917         STRINGREF   localeName;
918         STRINGREF   stringResult;
919         NUMFMTREF   numfmt;
920         PTRARRAYREF tempArray;
921     } gc;
922
923     // Dereference our string
924     gc.localeName = (STRINGREF)localeNameUNSAFE;
925     gc.numfmt = (NUMFMTREF) pNumfmtUNSAFE;
926     gc.stringResult = NULL;
927     gc.tempArray = NULL;
928
929     HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
930     StackSString localeNameStackBuffer( gc.localeName->GetBuffer() );
931     
932     // Calling SString::ConvertToUnicode once
933     LPCWSTR pLocaleName = localeNameStackBuffer;
934     
935     //
936     // NOTE: We pass the stringResult allocated in the stack and assign it to the fields
937     // in numfmt after calling CallGetLocaleInfo().  The reason for this is that CallGetLocaleInfo()
938     // allocates a string object, and it may trigger a GC and cause numfmt to be moved.
939     // That's why we use the stringResult allocated in the stack since it will not be moved.
940     // After CallGetLocaleInfo(), we know that numfmt will not be moved, and it's safe to assign
941     // the stringResult to its field.
942     //
943
944     // String values
945     if (CallGetLocaleInfoEx(pLocaleName, LOCALE_SPOSITIVESIGN , &gc.stringResult, useUserOverride)) {
946         SetObjectReference((OBJECTREF*)&(gc.numfmt->sPositive), gc.stringResult, NULL);
947     }
948     else {
949         ret = FALSE;
950         _ASSERT(FALSE);
951     }
952
953     if (CallGetLocaleInfoEx(pLocaleName, LOCALE_SNEGATIVESIGN , &gc.stringResult, useUserOverride)) {
954         SetObjectReference((OBJECTREF*)&(gc.numfmt->sNegative), gc.stringResult, NULL);
955     }
956     else {
957         ret = FALSE;
958         _ASSERT(FALSE);
959     }
960     if (CallGetLocaleInfoEx(pLocaleName, LOCALE_SDECIMAL , &gc.stringResult, useUserOverride)) {
961         SetObjectReference((OBJECTREF*)&(gc.numfmt->sNumberDecimal), gc.stringResult, NULL);
962     }
963     else {
964         ret = FALSE;
965         _ASSERT(FALSE);
966     }
967     if (CallGetLocaleInfoEx(pLocaleName, LOCALE_STHOUSAND , &gc.stringResult, useUserOverride)) {
968         SetObjectReference((OBJECTREF*)&(gc.numfmt->sNumberGroup), gc.stringResult, NULL);
969     }
970     else {
971         ret = FALSE;
972         _ASSERT(FALSE);
973     }
974     if (CallGetLocaleInfoEx(pLocaleName, LOCALE_SMONTHOUSANDSEP , &gc.stringResult, useUserOverride)) {
975         SetObjectReference((OBJECTREF*)&(gc.numfmt->sCurrencyGroup), gc.stringResult, NULL);
976     }
977     else {
978         ret = FALSE;
979         _ASSERT(FALSE);
980     }
981     if (CallGetLocaleInfoEx(pLocaleName, LOCALE_SMONDECIMALSEP , &gc.stringResult, useUserOverride)) {
982         SetObjectReference((OBJECTREF*)&(gc.numfmt->sCurrencyDecimal), gc.stringResult, NULL);
983     }
984     else {
985         ret = FALSE;
986         _ASSERT(FALSE);
987     }
988     if (CallGetLocaleInfoEx(pLocaleName, LOCALE_SCURRENCY , &gc.stringResult, useUserOverride)) {
989         SetObjectReference((OBJECTREF*)&(gc.numfmt->sCurrency), gc.stringResult, NULL);
990     }
991     else {
992         ret = FALSE;
993         _ASSERT(FALSE);
994     }
995
996
997     // Numeric values
998     ret &= CallGetLocaleInfoEx(pLocaleName, LOCALE_IDIGITS | LOCALE_RETURN_NUMBER           , &(gc.numfmt->cNumberDecimals), useUserOverride);
999     _ASSERT(ret == TRUE);
1000     ret &= CallGetLocaleInfoEx(pLocaleName, LOCALE_ICURRDIGITS | LOCALE_RETURN_NUMBER       , &(gc.numfmt->cCurrencyDecimals), useUserOverride);
1001     _ASSERT(ret == TRUE);
1002     ret &= CallGetLocaleInfoEx(pLocaleName, LOCALE_ICURRENCY | LOCALE_RETURN_NUMBER         , &(gc.numfmt->cPosCurrencyFormat), useUserOverride);
1003     _ASSERT(ret == TRUE);
1004     ret &= CallGetLocaleInfoEx(pLocaleName, LOCALE_INEGCURR | LOCALE_RETURN_NUMBER          , &(gc.numfmt->cNegCurrencyFormat), useUserOverride);
1005     _ASSERT(ret == TRUE);
1006     ret &= CallGetLocaleInfoEx(pLocaleName, LOCALE_INEGNUMBER| LOCALE_RETURN_NUMBER         , &(gc.numfmt->cNegativeNumberFormat), useUserOverride);
1007     _ASSERT(ret == TRUE);
1008     ret &= CallGetLocaleInfoEx(pLocaleName, LOCALE_IDIGITSUBSTITUTION | LOCALE_RETURN_NUMBER, &(gc.numfmt->iDigitSubstitution), useUserOverride);
1009     _ASSERT(ret == TRUE);
1010
1011     // LOCALE_SNATIVEDIGITS (gc.tempArray of strings)
1012     if (GetNativeDigitsFromWin32(pLocaleName, &gc.tempArray, useUserOverride)) {
1013         SetObjectReference((OBJECTREF*)&(gc.numfmt->sNativeDigits), gc.tempArray, NULL);
1014     }
1015     else {
1016         ret = FALSE;
1017         _ASSERT(FALSE);
1018     }
1019
1020     HELPER_METHOD_FRAME_END();
1021     FC_RETURN_BOOL(ret);
1022 }
1023 FCIMPLEND
1024
1025
1026 ////////////////////////////////////////////////////////////////////////
1027 //
1028 // Culture enumeration helper functions
1029 //
1030 ////////////////////////////////////////////////////////////////////////
1031
1032 ////////////////////////////////////////////////////////////////////////////
1033 //
1034 // Enum values for System.Globalization.CultureTypes
1035 //
1036 ////////////////////////////////////////////////////////////////////////////
1037
1038 // Neutral cultures are cultures like "en", "de", "zh", etc, for enumeration this includes ALL neutrals regardless of other flags
1039 #define CULTURETYPES_NEUTRALCULTURES              0x0001
1040
1041 // Non-netural cultuers.  Examples are "en-us", "zh-tw", etc., for enumeration this includes ALL specifics regardless of other flags
1042 #define CULTURETYPES_SPECIFICCULTURES             0x0002
1043
1044 // Win32 installed cultures in the system and exists in the framework too., this is effectively all cultures
1045 #define CULTURETYPES_INSTALLEDWIN32CULTURES       0x0004
1046
1047 // User defined custom culture
1048 #define CULTURETYPES_USERCUSTOMCULTURE            0x0008
1049
1050 // User defined replacement custom culture.
1051 #define CULTURETYPES_REPLACEMENTCULTURES          0x0010
1052 // [Obsolete("This value has been deprecated.  Please use other values in CultureTypes.")]
1053 // Culture exists in Win32 but not in the Framework. // TODO: All cultures or no cultures?
1054 #define CULTURETYPES_WINDOWSONLYCULTURES          0x0020
1055 // [Obsolete("This value has been deprecated.  Please use other values in CultureTypes.")]
1056 // the language tag match a culture that ships with the .NET framework, effectively all cultures since we get them from windows
1057 #define CULTURETYPES_FRAMEWORKCULTURES            0x0040
1058
1059
1060 const LPWSTR WHIDBEY_FRAMEWORK_CULTURE_LIST [] =
1061 {
1062     W(""),
1063     W("af"),
1064     W("af-za"),
1065     W("ar"),
1066     W("ar-ae"),
1067     W("ar-bh"),
1068     W("ar-dz"),
1069     W("ar-eg"),
1070     W("ar-iq"),
1071     W("ar-jo"),
1072     W("ar-kw"),
1073     W("ar-lb"),
1074     W("ar-ly"),
1075     W("ar-ma"),
1076     W("ar-om"),
1077     W("ar-qa"),
1078     W("ar-sa"),
1079     W("ar-sy"),
1080     W("ar-tn"),
1081     W("ar-ye"),
1082     W("az"),
1083     W("az-cyrl-az"),
1084     W("az-latn-az"),
1085     W("be"),
1086     W("be-by"),
1087     W("bg"),
1088     W("bg-bg"),
1089     W("ca"),
1090     W("ca-es"),
1091     W("cs"),
1092     W("cs-cz"),
1093     W("da"),
1094     W("da-dk"),
1095     W("de"),
1096     W("de-at"),
1097     W("de-ch"),
1098     W("de-de"),
1099     W("de-li"),
1100     W("de-lu"),
1101     W("dv"),
1102     W("dv-mv"),
1103     W("el"),
1104     W("el-gr"),
1105     W("en"),
1106     W("en-029"),
1107     W("en-au"),
1108     W("en-bz"),
1109     W("en-ca"),
1110     W("en-gb"),
1111     W("en-ie"),
1112     W("en-jm"),
1113     W("en-nz"),
1114     W("en-ph"),
1115     W("en-tt"),
1116     W("en-us"),
1117     W("en-za"),
1118     W("en-zw"),
1119     W("es"),
1120     W("es-ar"),
1121     W("es-bo"),
1122     W("es-cl"),
1123     W("es-co"),
1124     W("es-cr"),
1125     W("es-do"),
1126     W("es-ec"),
1127     W("es-es"),
1128     W("es-gt"),
1129     W("es-hn"),
1130     W("es-mx"),
1131     W("es-ni"),
1132     W("es-pa"),
1133     W("es-pe"),
1134     W("es-pr"),
1135     W("es-py"),
1136     W("es-sv"),
1137     W("es-uy"),
1138     W("es-ve"),
1139     W("et"),
1140     W("et-ee"),
1141     W("eu"),
1142     W("eu-es"),
1143     W("fa"),
1144     W("fa-ir"),
1145     W("fi"),
1146     W("fi-fi"),
1147     W("fo"),
1148     W("fo-fo"),
1149     W("fr"),
1150     W("fr-be"),
1151     W("fr-ca"),
1152     W("fr-ch"),
1153     W("fr-fr"),
1154     W("fr-lu"),
1155     W("fr-mc"),
1156     W("gl"),
1157     W("gl-es"),
1158     W("gu"),
1159     W("gu-in"),
1160     W("he"),
1161     W("he-il"),
1162     W("hi"),
1163     W("hi-in"),
1164     W("hr"),
1165     W("hr-hr"),
1166     W("hu"),
1167     W("hu-hu"),
1168     W("hy"),
1169     W("hy-am"),
1170     W("id"),
1171     W("id-id"),
1172     W("is"),
1173     W("is-is"),
1174     W("it"),
1175     W("it-ch"),
1176     W("it-it"),
1177     W("ja"),
1178     W("ja-jp"),
1179     W("ka"),
1180     W("ka-ge"),
1181     W("kk"),
1182     W("kk-kz"),
1183     W("kn"),
1184     W("kn-in"),
1185     W("ko"),
1186     W("ko-kr"),
1187     W("kok"),
1188     W("kok-in"),
1189     W("ky"),
1190     W("ky-kg"),
1191     W("lt"),
1192     W("lt-lt"),
1193     W("lv"),
1194     W("lv-lv"),
1195     W("mk"),
1196     W("mk-mk"),
1197     W("mn"),
1198     W("mn-mn"),
1199     W("mr"),
1200     W("mr-in"),
1201     W("ms"),
1202     W("ms-bn"),
1203     W("ms-my"),
1204     W("nb-no"),
1205     W("nl"),
1206     W("nl-be"),
1207     W("nl-nl"),
1208     W("nn-no"),
1209     W("no"),
1210     W("pa"),
1211     W("pa-in"),
1212     W("pl"),
1213     W("pl-pl"),
1214     W("pt"),
1215     W("pt-br"),
1216     W("pt-pt"),
1217     W("ro"),
1218     W("ro-ro"),
1219     W("ru"),
1220     W("ru-ru"),
1221     W("sa"),
1222     W("sa-in"),
1223     W("sk"),
1224     W("sk-sk"),
1225     W("sl"),
1226     W("sl-si"),
1227     W("sq"),
1228     W("sq-al"),
1229     W("sr"),
1230     W("sr-cyrl-cs"),
1231     W("sr-latn-cs"),
1232     W("sv"),
1233     W("sv-fi"),
1234     W("sv-se"),
1235     W("sw"),
1236     W("sw-ke"),
1237     W("syr"),
1238     W("syr-sy"),
1239     W("ta"),
1240     W("ta-in"),
1241     W("te"),
1242     W("te-in"),
1243     W("th"),
1244     W("th-th"),
1245     W("tr"),
1246     W("tr-tr"),
1247     W("tt"),
1248     W("tt-ru"),
1249     W("uk"),
1250     W("uk-ua"),
1251     W("ur"),
1252     W("ur-pk"),
1253     W("uz"),
1254     W("uz-cyrl-uz"),
1255     W("uz-latn-uz"),
1256     W("vi"),
1257     W("vi-vn"),
1258     W("zh-chs"),
1259     W("zh-cht"),
1260     W("zh-cn"),
1261     W("zh-hans"),
1262     W("zh-hant"),
1263     W("zh-hk"),
1264     W("zh-mo"),
1265     W("zh-sg"),
1266     W("zh-tw")
1267 };
1268 #define WHIDBEY_FRAMEWORK_CULTURE_LIST_LENGTH (sizeof(WHIDBEY_FRAMEWORK_CULTURE_LIST) / sizeof(WHIDBEY_FRAMEWORK_CULTURE_LIST[0]))
1269
1270 ////////////////////////////////////////////////////////////////////////////
1271 //
1272 //  NlsCompareInvariantNoCase
1273 //
1274 //  This routine does fast caseless comparison without needing the tables.
1275 //  This helps us do the comparisons we need to load the tables :-)
1276 //
1277 //  Returns 0 if identical, <0 if pFirst if first string sorts first.
1278 //
1279 //  This is only intended to help with our locale name comparisons,
1280 //  which are effectively limited to A-Z, 0-9, a-z and - where A-Z and a-z
1281 //  compare as equal.
1282 //
1283 //  WARNING: [\]^_` will be less than A-Z because we make everything lower
1284 //           case before comparing them.
1285 //
1286 //  When bNullEnd is TRUE, both of the strings should be null-terminator to be considered equal.
1287 //  When bNullEnd is FALSE, the strings are considered equal when we reach the number of characters specifed by size
1288 //  or when null terminators are reached, whichever happens first (strncmp-like behavior)
1289 //
1290 ////////////////////////////////////////////////////////////////////////////
1291
1292 int NlsCompareInvariantNoCase(
1293     LPCWSTR pFirst,
1294     LPCWSTR pSecond,
1295     int     size,
1296     BOOL bNullEnd)
1297 {
1298     int i=0;
1299     WCHAR first;
1300     WCHAR second;
1301
1302     for (;
1303          size > 0 && (first = *pFirst) != 0 && (second = *pSecond) != 0;
1304          size--, pFirst++, pSecond++)
1305     {
1306         // Make them lower case
1307         if ((first >= 'A') && (first <= 'Z')) first |= 0x20;
1308         if ((second >= 'A') && (second <= 'Z')) second |= 0x20;
1309
1310         // Get the diff
1311         i = (first - second);
1312
1313         // Are they the same?
1314         if (i == 0)
1315             continue;
1316
1317         // Otherwise the difference.  Remember we made A-Z into lower case, so
1318         // the characters [\]^_` will sort < A-Z and also < a-z.  (Those are the
1319         // characters between A-Z and a-Z in ascii)
1320         return i;
1321     }
1322
1323     // When we are here, one of these holds:
1324     //    size == 0
1325     //    or one of the strings has a null terminator
1326     //    or both of the string reaches null terminator
1327
1328     if (bNullEnd || size != 0)
1329     {
1330         // If bNullEnd is TRUE, always check for null terminator.
1331         // If bNullEnd is FALSE, we still have to check if one of the strings is terminated eariler
1332         // than another (hense the size != 0 check).
1333
1334         // See if one string ended first
1335         if (*pFirst != 0 || *pSecond != 0)
1336         {
1337             // Which one?
1338             return *pFirst == 0 ? -1 : 1;
1339         }
1340     }
1341
1342     // Return our difference (0)
1343     return i;
1344 }
1345
1346 BOOL IsWhidbeyFrameworkCulture(__in LPCWSTR lpLocaleString)
1347 {
1348     int iBottom = 0;
1349     int iTop = WHIDBEY_FRAMEWORK_CULTURE_LIST_LENGTH - 1;
1350
1351     // Do a binary search for our name
1352     while (iBottom <= iTop)
1353     {
1354         int     iMiddle = (iBottom + iTop) / 2;
1355         int     result = NlsCompareInvariantNoCase(lpLocaleString, WHIDBEY_FRAMEWORK_CULTURE_LIST[iMiddle], LOCALE_NAME_MAX_LENGTH, TRUE);
1356         if (result == 0)
1357         {
1358             return TRUE;
1359         }
1360         if (result < 0)
1361         {
1362             // pLocaleName was < pTest
1363             iTop = iMiddle - 1;
1364         }
1365         else
1366         {
1367             // pLocaleName was > pTest
1368             iBottom = iMiddle + 1;
1369         }
1370     }
1371
1372     return FALSE;
1373 }
1374
1375 // Just Check to see if the OS thinks it is a valid locle
1376 BOOL WINAPI IsOSValidLocaleName(__in LPCWSTR  lpLocaleName, bool bIsNeutralLocale)
1377 {
1378 #ifndef ENABLE_DOWNLEVEL_FOR_NLS
1379     return ::IsValidLocaleName(lpLocaleName);
1380 #else
1381     BOOL IsWindows7 = NewApis::IsWindows7Platform();
1382     // if we're < Win7, we didn't know about neutrals or the invariant.
1383     if (!IsWindows7 && (bIsNeutralLocale || (lpLocaleName[0] == 0)))
1384     {
1385         return false;
1386     }
1387
1388     // Work around the name/lcid thingy (can't just link to ::IsValidLocaleName())
1389     LCID lcid = NewApis::LocaleNameToLCID(lpLocaleName, 0);
1390
1391     if (IsCustomCultureId(lcid))
1392     {
1393         return false;
1394     }
1395
1396     if (bIsNeutralLocale)
1397     {
1398         // In this case, we're running on Windows 7.
1399         // For neutral locales, use GetLocaleInfoW.
1400         // If GetLocaleInfoW works, then the OS knows about it.
1401         return (::GetLocaleInfoW(lcid, LOCALE_ILANGUAGE, NULL, 0) != 0);
1402     }
1403
1404     // This is not a custom locale.
1405     // Call IsValidLocale() to check if the LCID is installed.
1406     // IsValidLocale doesn't work for neutral locales.
1407     return IsValidLocale(lcid, LCID_INSTALLED);
1408 #endif
1409 }
1410
1411
1412 ////////////////////////////////////////////////////////////////////////////
1413 //
1414 // Check the dwFlags, which has the 'attributes' of the locale, and decide
1415 // if the locale should be included in the enumeration based on
1416 // the desired CultureTypes.
1417 //
1418 ////////////////////////////////////////////////////////////////////////////
1419
1420 BOOL ShouldIncludeByCultureType(INT32 cultureTypes, LPCWSTR lpLocaleString, INT32 dwFlags)
1421 {
1422
1423     if ((cultureTypes & CULTURETYPES_NEUTRALCULTURES) &&
1424          ((dwFlags & LOCALE_NEUTRALDATA) || (lpLocaleString[0] == 0))) // Invariant culture get enumerated with the neutrals
1425     {
1426         return TRUE;
1427     }
1428
1429     if ((cultureTypes & CULTURETYPES_SPECIFICCULTURES) &&
1430         ((dwFlags & LOCALE_SPECIFICDATA) && (lpLocaleString[0] != 0))) // Invariant culture does not get enumerated with the specifics
1431     {
1432         return TRUE;
1433     }
1434
1435     if (cultureTypes & CULTURETYPES_INSTALLEDWIN32CULTURES)
1436     {
1437         // The user asks for installed Win32 culture, so check
1438         // if this locale is installed. In W7 and above, when ::IsValidLocaleName()
1439         // returns true, it means that it is installed.
1440         // In downlevel (including Vista), we will convert the name to LCID.
1441         // When the LCID is not a custom locale, we will call ::IsValidLocale(.., LCID_INSTALLED)
1442         // to verify if the locale is installed.
1443         // In Vista, we treat custom locale as installed.
1444         if (IsOSValidLocaleName(lpLocaleString, (dwFlags & LOCALE_NEUTRALDATA) == LOCALE_NEUTRALDATA))
1445         {
1446             return TRUE;
1447         }
1448     }
1449
1450     if ((cultureTypes & CULTURETYPES_USERCUSTOMCULTURE) &&
1451         (dwFlags & LOCALE_SUPPLEMENTAL))
1452     {
1453         return TRUE;
1454     }
1455
1456     if ((cultureTypes & CULTURETYPES_REPLACEMENTCULTURES) &&
1457         (dwFlags & LOCALE_REPLACEMENT))
1458     {
1459         return TRUE;
1460     }
1461
1462     if ((cultureTypes & CULTURETYPES_FRAMEWORKCULTURES) &&
1463          IsWhidbeyFrameworkCulture(lpLocaleString))
1464     {
1465         return TRUE;
1466     }
1467
1468     //
1469     // No need to check CULTURETYPES_WINDOWSONLYCULTURES and CULTURETYPES_FRAMEWORKCULTURES
1470     // since they are deprecated, and they are handled in the managed code before calling
1471     // nativeEnumCultureNames.
1472     //
1473
1474     return FALSE;
1475 }
1476
1477 ////////////////////////////////////////////////////////////////////////////
1478 //
1479 // Struct to hold context to be used in the callback for
1480 // EnumLocaleProcessingCallback
1481 //
1482 ////////////////////////////////////////////////////////////////////////////
1483
1484 typedef struct
1485 {
1486     PTRARRAYREF pCultureNamesArray;
1487     INT32 count;
1488     INT32 cultureTypes;
1489 } ENUM_LOCALE_DATA;
1490
1491 ////////////////////////////////////////////////////////////////////////////
1492 //
1493 // Callback for NewApis::EnumSystemLocalesEx to count the number of
1494 // locales to be enumerated.
1495 //
1496 ////////////////////////////////////////////////////////////////////////////
1497
1498 BOOL CALLBACK EnumLocaleCountCallback(__in_z LPCWSTR lpLocaleString, __in DWORD dwFlags, __in LPARAM lParam)
1499 {
1500     ENUM_LOCALE_DATA* pData = (ENUM_LOCALE_DATA*)lParam;
1501
1502     if (ShouldIncludeByCultureType(pData->cultureTypes, lpLocaleString, dwFlags))
1503     {
1504         (pData->count)++;
1505     }
1506     return TRUE;
1507 }
1508
1509
1510 ////////////////////////////////////////////////////////////////////////////
1511 //
1512 // Callback for NewApis::EnumSystemLocalesEx to add the locale name
1513 // into the allocated managed string array.
1514 //
1515 ////////////////////////////////////////////////////////////////////////////
1516
1517 BOOL CALLBACK EnumLocaleProcessingCallback(__in_z LPCWSTR lpLocaleString, __in DWORD dwFlags, __in LPARAM lParam)
1518 {
1519     ENUM_LOCALE_DATA* pData = (ENUM_LOCALE_DATA*)lParam;
1520
1521     if (ShouldIncludeByCultureType(pData->cultureTypes, lpLocaleString, dwFlags))
1522     {
1523         GCX_COOP();
1524
1525         GCPROTECT_BEGIN(pData->pCultureNamesArray);
1526
1527         OBJECTREF cultureString = (OBJECTREF) StringObject::NewString(lpLocaleString);
1528         pData->pCultureNamesArray->SetAt(pData->count, cultureString);
1529         pData->count++;
1530
1531         GCPROTECT_END();
1532     }
1533
1534     return TRUE;
1535 }
1536
1537
1538 ////////////////////////////////////////////////////////////////////////////
1539 //
1540 // Called by CultureData.GetCultures() to enumerate the names of cultures.
1541 // It first calls NewApis::EnumSystemLocalesEx to count the number of
1542 // locales to be enumerated. And it will allocate an managed string
1543 // array with the count. And fill the array with the culture names in
1544 // the 2nd call to NewAPis::EnumSystemLocalesEx.
1545 //
1546 ////////////////////////////////////////////////////////////////////////////
1547
1548
1549 int QCALLTYPE COMNlsInfo::nativeEnumCultureNames(INT32 cultureTypes, QCall::ObjectHandleOnStack retStringArray)
1550 {
1551     CONTRACTL
1552     {
1553         QCALL_CHECK;
1554         // Check CultureTypes.WindowsOnlyCultures and CultureTYpes.FrameworkCultures are deprecated and is
1555         // handled in the managed code side to provide fallback behavior.
1556         //PRECONDITION((cultureTypes & (CULTURETYPES_WINDOWSONLYCULTURES | CULTURETYPES_FRAMEWORKCULTURES) == 0));
1557     } CONTRACTL_END;
1558
1559
1560     int result;
1561     DWORD dwFlags = 0;
1562     PTRARRAYREF cultureNamesArray = NULL;
1563     ENUM_LOCALE_DATA enumData = { NULL, 0, cultureTypes};
1564
1565     BEGIN_QCALL;
1566
1567     //
1568     // if CultureTypes.FrameworkCulture is specified we'll enumerate all cultures
1569     // and filter according to the Whidbey framework culture list (for compatibility)
1570     //
1571
1572     if (cultureTypes & CULTURETYPES_FRAMEWORKCULTURES)
1573     {
1574         dwFlags |= LOCALE_NEUTRALDATA | LOCALE_SPECIFICDATA;
1575     }
1576
1577     // Map CultureTypes to Windows enumeration values.
1578     if (cultureTypes & CULTURETYPES_NEUTRALCULTURES)
1579     {
1580         dwFlags |= LOCALE_NEUTRALDATA;
1581     }
1582
1583     if (cultureTypes & CULTURETYPES_SPECIFICCULTURES)
1584     {
1585         dwFlags |= LOCALE_SPECIFICDATA;
1586     }
1587
1588     if (cultureTypes & CULTURETYPES_INSTALLEDWIN32CULTURES)
1589     {
1590         // Windows 7 knows about neutrals, whereas Vista and lower don't.
1591         if (NewApis::IsWindows7Platform())
1592         {
1593             dwFlags |= LOCALE_SPECIFICDATA | LOCALE_NEUTRALDATA;
1594         }
1595         else
1596         {
1597             dwFlags |= LOCALE_SPECIFICDATA;
1598         }
1599     }
1600
1601     dwFlags |= (cultureTypes & CULTURETYPES_USERCUSTOMCULTURE) ? LOCALE_SUPPLEMENTAL: 0;
1602
1603     // We need special handling for Replacement cultures because Windows does not have a way to enumerate it directly.
1604     // Replacement locale check will be only used when CultureTypes.SpecificCultures is NOT used.
1605     dwFlags |= (cultureTypes & CULTURETYPES_REPLACEMENTCULTURES) ? LOCALE_SPECIFICDATA | LOCALE_NEUTRALDATA: 0;
1606
1607
1608     result = NewApis::EnumSystemLocalesEx((LOCALE_ENUMPROCEX)EnumLocaleCountCallback, dwFlags, (LPARAM)&enumData, NULL) == TRUE ? 1 : 0;
1609
1610     if (result)
1611     {
1612
1613         GCX_COOP();
1614
1615         GCPROTECT_BEGIN(cultureNamesArray);
1616
1617         // Now we need to allocate our culture names string array and populate it
1618         // Get our array object (will throw, don't have to check it)
1619         cultureNamesArray = (PTRARRAYREF) AllocateObjectArray(enumData.count, g_pStringClass);
1620
1621         // In the context struct passed to EnumSystemLocalesEx, reset the count and assign the newly allocated string array
1622         // to hold culture names to be enumerated.
1623         enumData.count = 0;
1624         enumData.pCultureNamesArray = cultureNamesArray;
1625
1626         result = NewApis::EnumSystemLocalesEx((LOCALE_ENUMPROCEX)EnumLocaleProcessingCallback, dwFlags, (LPARAM)&enumData, NULL);
1627
1628         if (result)
1629         {
1630             retStringArray.Set(cultureNamesArray);
1631         }
1632         GCPROTECT_END();
1633     }
1634     END_QCALL
1635
1636     return result;
1637
1638 }
1639
1640 //
1641 // InternalCompareString is used in the managed side to handle the synthetic CompareInfo methods (IndexOf, LastIndexOf, IsPrfix, and IsSuffix)
1642 //
1643 INT32 QCALLTYPE COMNlsInfo::InternalCompareString(
1644     INT_PTR handle,
1645     INT_PTR handleOrigin,
1646     LPCWSTR localeName,
1647     LPCWSTR string1, INT32 offset1, INT32 length1,
1648     LPCWSTR string2, INT32 offset2, INT32 length2,
1649     INT32 flags)
1650 {
1651     CONTRACTL
1652     {
1653         QCALL_CHECK;
1654         PRECONDITION(CheckPointer(string1));
1655         PRECONDITION(CheckPointer(string2));
1656         PRECONDITION(CheckPointer(localeName));
1657     } CONTRACTL_END;
1658
1659     INT32 result = 1;
1660     BEGIN_QCALL;
1661
1662     handle = EnsureValidSortHandle(handle, handleOrigin, localeName);
1663
1664 #ifndef FEATURE_CORECLR
1665     AppDomain* curDomain = GetAppDomain();
1666
1667     if(!(curDomain->m_bUseOsSorting))
1668     {
1669         result = SortVersioning::SortDllCompareString((SortVersioning::PSORTHANDLE) handle, flags, &string1[offset1], length1, &string2[offset2], length2, NULL, 0);
1670     }
1671     else if (curDomain->m_pCustomSortLibrary != NULL) {
1672         result = (curDomain->m_pCustomSortLibrary->pCompareStringEx)(handle != NULL ? NULL : localeName, flags, &string1[offset1], length1, &string2[offset2], length2, NULL, NULL, (LPARAM) handle);
1673     } 
1674     else 
1675 #endif
1676     {
1677         result = NewApis::CompareStringEx(handle != NULL ? NULL : localeName, flags, &string1[offset1], length1, &string2[offset2], length2,NULL,NULL, (LPARAM) handle);
1678     }
1679
1680     switch (result)
1681     {
1682         case CSTR_LESS_THAN:
1683             result = -1;
1684             break;
1685
1686         case CSTR_EQUAL:
1687             result = 0;
1688             break;
1689
1690         case CSTR_GREATER_THAN:
1691             result = 1;
1692             break;
1693
1694         case 0:
1695         default:
1696             _ASSERTE(!"catastrophic failure calling NewApis::CompareStringEx!  This could be a CultureInfo, RegionInfo, or Calendar bug (bad localeName string) or maybe a GCHole.");
1697             break;
1698     }
1699
1700     END_QCALL;
1701     return result;
1702 }
1703
1704 ////////////////////////////////////////////////////////////////////////////
1705 //
1706 //  UseConstantSpaceHashAlgorithm
1707 //  Check for the DWORD "NetFx45_CultureAwareComparerGetHashCode_LongStrings" CLR config option.
1708 //
1709 // .Net 4.5 introduces an opt-in algorithm for determining the hash code of strings that
1710 // uses a constant amount of memory instead of memory proportional to the size of the string
1711 //
1712 // A non-zero value will enable the new algorithm:
1713 //
1714 // 1) Config file (MyApp.exe.config)
1715 //        <?xml version ="1.0"?>
1716 //        <configuration>
1717 //         <runtime>
1718 //          <NetFx45_CultureAwareComparerGetHashCode_LongStrings enabled="1"/>
1719 //         </runtime>
1720 //        </configuration>
1721 // 2) Environment variable
1722 //        set NetFx45_CultureAwareComparerGetHashCode_LongStrings=1
1723 // 3) RegistryKey
1724 //        [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework]
1725 //        "NetFx45_CultureAwareComparerGetHashCode_LongStrings"=dword:00000001
1726 //
1727 ////////////////////////////////////////////////////////////////////////////
1728 BOOL UseConstantSpaceHashAlgorithm()
1729 {
1730     static bool configChecked = false;
1731     static BOOL useConstantSpaceHashAlgorithm = FALSE;
1732
1733     if(!configChecked)
1734     {
1735         BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return false);
1736         useConstantSpaceHashAlgorithm = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_NetFx45_CultureAwareComparerGetHashCode_LongStrings) != 0;
1737         END_SO_INTOLERANT_CODE;
1738
1739         configChecked = true;
1740     }
1741     return useConstantSpaceHashAlgorithm;
1742 }
1743
1744
1745
1746 ////////////////////////////////////////////////////////////////////////////
1747 //
1748 //  InternalGetGlobalizedHashCode
1749 //
1750 ////////////////////////////////////////////////////////////////////////////
1751 INT32 QCALLTYPE COMNlsInfo::InternalGetGlobalizedHashCode(INT_PTR handle, INT_PTR handleOrigin, LPCWSTR localeName, LPCWSTR string, INT32 length, INT32 dwFlagsIn, BOOL bForceRandomizedHashing, INT64 additionalEntropy)
1752 {
1753     CONTRACTL
1754     {
1755         QCALL_CHECK;
1756         PRECONDITION(CheckPointer(localeName));
1757         PRECONDITION(CheckPointer(string, NULL_OK));
1758     } CONTRACTL_END;
1759
1760     INT32  iReturnHash  = 0;
1761     BEGIN_QCALL;
1762
1763     handle = EnsureValidSortHandle(handle, handleOrigin, localeName);
1764     int byteCount = 0;
1765
1766     //
1767     //  Make sure there is a string.
1768     //
1769     if (!string) {
1770         COMPlusThrowArgumentNull(W("string"),W("ArgumentNull_String"));
1771     }
1772
1773 #ifndef FEATURE_CORECLR
1774     AppDomain* curDomain = GetAppDomain();
1775 #endif // FEATURE_CORECLR
1776
1777     if(length > 0 && UseConstantSpaceHashAlgorithm() 
1778     // Note that we can't simply do the hash without the entropy and then try to add it after the fact we need the hash function itself to pass entropy to its inputs.
1779 #ifdef FEATURE_RANDOMIZED_STRING_HASHING
1780          && !bForceRandomizedHashing
1781 #ifndef FEATURE_CORECLR
1782          && !curDomain->m_pNlsHashProvider->GetUseRandomHashing()
1783 #else
1784          && !COMNlsHashProvider::s_NlsHashProvider.GetUseRandomHashing()
1785 #endif // FEATURE_CORECLR
1786 #endif // FEATURE_RANDOMIZED_STRING_HASHING
1787        )
1788     {
1789 #ifndef FEATURE_CORECLR
1790         if(!(curDomain->m_bUseOsSorting))
1791         {
1792             iReturnHash=SortVersioning::SortDllGetHashCode((SortVersioning::PSORTHANDLE) handle, dwFlagsIn, string, length, NULL, 0);
1793         }
1794         else
1795 #endif
1796         {
1797             int iRes = 0;
1798             int iHashValue = 0;
1799
1800 #ifndef FEATURE_CORECLR
1801             if (curDomain->m_pCustomSortLibrary != NULL)
1802             {
1803                 iRes = (curDomain->m_pCustomSortLibrary->pLCMapStringEx)(localeName, dwFlagsIn | LCMAP_HASH, string, length, (LPWSTR) &iHashValue, sizeof(INT32), NULL, NULL, 0);
1804             }
1805             else
1806 #endif
1807             {
1808                 iRes = NewApis::LCMapStringEx(localeName, dwFlagsIn | LCMAP_HASH, string, length, (LPWSTR) &iHashValue, sizeof(INT32), NULL, NULL, 0);
1809             }
1810
1811             if(iRes != 0)
1812             {
1813                 iReturnHash = iHashValue;
1814             }
1815         }
1816     }
1817
1818     if(iReturnHash == 0)
1819     {
1820         DWORD dwFlags = (LCMAP_SORTKEY | dwFlagsIn);
1821
1822         //
1823         // Caller has already verified that the string is not of zero length
1824         //
1825         // Assert if we might hit an AV in LCMapStringEx for the invariant culture.
1826         _ASSERTE(length > 0 || (dwFlags & LCMAP_LINGUISTIC_CASING) == 0);
1827 #ifndef FEATURE_CORECLR
1828         if(!(curDomain->m_bUseOsSorting))
1829         {
1830             byteCount=SortVersioning::SortDllGetSortKey((SortVersioning::PSORTHANDLE) handle, dwFlagsIn, string, length, NULL, 0, NULL, 0);
1831         }
1832         else if (curDomain->m_pCustomSortLibrary != NULL)
1833         {
1834             byteCount = (curDomain->m_pCustomSortLibrary->pLCMapStringEx)(handle != NULL ? NULL : localeName, dwFlags, string, length, NULL, 0, NULL, NULL, (LPARAM) handle);
1835         }
1836         else
1837 #endif
1838         {
1839             byteCount=NewApis::LCMapStringEx(handle != NULL ? NULL : localeName, dwFlags, string, length, NULL, 0, NULL, NULL, (LPARAM) handle);
1840         }
1841
1842         //A count of 0 indicates that we either had an error or had a zero length string originally.
1843         if (byteCount==0) {
1844             COMPlusThrow(kArgumentException, W("Arg_MustBeString"));
1845         }
1846
1847         // We used to use a NewArrayHolder here, but it turns out that hurts our large # process
1848         // scalability in ASP.Net hosting scenarios, using the quick bytes instead mostly stack
1849         // allocates and ups throughput by 8% in 100 process case, 5% in 1000 process case
1850         {
1851             CQuickBytesSpecifySize<MAX_STRING_VALUE * sizeof(WCHAR)> qbBuffer;
1852             BYTE* pByte = (BYTE*)qbBuffer.AllocThrows(byteCount);
1853
1854 #ifndef FEATURE_CORECLR
1855             if(!(curDomain->m_bUseOsSorting))
1856             {
1857                 SortVersioning::SortDllGetSortKey((SortVersioning::PSORTHANDLE) handle, dwFlagsIn, string, length, pByte, byteCount, NULL, 0);
1858             }
1859             else if(curDomain->m_pCustomSortLibrary != NULL)
1860             {
1861                 (curDomain->m_pCustomSortLibrary->pLCMapStringEx)(handle != NULL ? NULL : localeName, dwFlags, string, length, (LPWSTR)pByte, byteCount, NULL, NULL, (LPARAM) handle);
1862             }
1863             else
1864 #endif
1865             {
1866                 NewApis::LCMapStringEx(handle != NULL ? NULL : localeName, dwFlags, string, length, (LPWSTR)pByte, byteCount, NULL,NULL, (LPARAM) handle);
1867             }
1868
1869 #ifndef FEATURE_CORECLR
1870             iReturnHash = curDomain->m_pNlsHashProvider->HashSortKey(pByte, byteCount, bForceRandomizedHashing, additionalEntropy);
1871 #else
1872             iReturnHash = COMNlsHashProvider::s_NlsHashProvider.HashSortKey(pByte, byteCount, bForceRandomizedHashing, additionalEntropy);
1873 #endif // FEATURE_CORECLR
1874         }
1875     }
1876     END_QCALL;
1877     return(iReturnHash);
1878 }
1879
1880 #ifndef FEATURE_CORECLR // FCalls used by System.TimeZone
1881
1882 FCIMPL0(LONG, COMNlsInfo::nativeGetTimeZoneMinuteOffset)
1883 {
1884     FCALL_CONTRACT;
1885
1886     TIME_ZONE_INFORMATION timeZoneInfo;
1887
1888     GetTimeZoneInformation(&timeZoneInfo);
1889
1890     //
1891     // In Win32, UTC = local + offset.  So for Pacific Standard Time, offset = 8.
1892     // In NLS+, Local time = UTC + offset. So for PST, offset = -8.
1893     // So we have to reverse the sign here.
1894     //
1895     return (timeZoneInfo.Bias * -1);
1896 }
1897 FCIMPLEND
1898
1899 FCIMPL0(Object*, COMNlsInfo::nativeGetStandardName)
1900 {
1901     FCALL_CONTRACT;
1902
1903     STRINGREF refRetVal = NULL;
1904     HELPER_METHOD_FRAME_BEGIN_RET_1(refRetVal);
1905
1906     TIME_ZONE_INFORMATION timeZoneInfo;
1907     GetTimeZoneInformation(&timeZoneInfo);
1908
1909     refRetVal = StringObject::NewString(timeZoneInfo.StandardName);
1910
1911     HELPER_METHOD_FRAME_END();
1912     return OBJECTREFToObject(refRetVal);
1913 }
1914 FCIMPLEND
1915
1916 FCIMPL0(Object*, COMNlsInfo::nativeGetDaylightName)
1917 {
1918     FCALL_CONTRACT;
1919
1920     STRINGREF refRetVal = NULL;
1921     HELPER_METHOD_FRAME_BEGIN_RET_1(refRetVal);
1922
1923     TIME_ZONE_INFORMATION timeZoneInfo;
1924     GetTimeZoneInformation(&timeZoneInfo);
1925     // Instead of returning null when daylight saving is not used, now we return the same result as the OS.
1926     //In this case, if daylight saving time is used, the standard name is returned.
1927
1928 #if 0
1929     if (result == TIME_ZONE_ID_UNKNOWN || timeZoneInfo.DaylightDate.wMonth == 0) {
1930         // If daylight saving time is not used in this timezone, return null.
1931         //
1932         // Windows NT/2000: TIME_ZONE_ID_UNKNOWN is returned if daylight saving time is not used in
1933         // the current time zone, because there are no transition dates.
1934         //
1935         // For Windows 9x, a zero in the wMonth in DaylightDate means daylight saving time
1936         // is not specified.
1937         //
1938         // If the current timezone uses daylight saving rule, but user unchekced the
1939         // "Automatically adjust clock for daylight saving changes", the value
1940         // for DaylightBias will be 0.
1941         return (I2ARRAYREF)NULL;
1942     }
1943 #endif  // 0
1944
1945     refRetVal = StringObject::NewString(timeZoneInfo.DaylightName);
1946
1947     HELPER_METHOD_FRAME_END();
1948     return OBJECTREFToObject(refRetVal);
1949 }
1950 FCIMPLEND
1951
1952 FCIMPL1(Object*, COMNlsInfo::nativeGetDaylightChanges, int year)
1953 {
1954     FCALL_CONTRACT;
1955
1956     I2ARRAYREF pResultArray = NULL;
1957     HELPER_METHOD_FRAME_BEGIN_RET_1(pResultArray);
1958
1959     TIME_ZONE_INFORMATION timeZoneInfo;
1960     DWORD result = GetTimeZoneInformation(&timeZoneInfo);
1961
1962     if (result == TIME_ZONE_ID_UNKNOWN || timeZoneInfo.DaylightBias == 0
1963         || timeZoneInfo.DaylightDate.wMonth == 0
1964         ) {
1965         // If daylight saving time is not used in this timezone, return null.
1966         //
1967         // If the current timezone uses daylight saving rule, but user unchekced the
1968         // "Automatically adjust clock for daylight saving changes", the value
1969         // for DaylightBias will be 0.
1970         goto lExit;
1971     }
1972
1973     pResultArray = (I2ARRAYREF)AllocatePrimitiveArray(ELEMENT_TYPE_I2, 17);
1974
1975     //
1976     // The content of timeZoneInfo.StandardDate is 8 words, which
1977     // contains year, month, day, dayOfWeek, hour, minute, second, millisecond.
1978     //
1979     memcpyNoGCRefs(pResultArray->m_Array,
1980             (LPVOID)&timeZoneInfo.DaylightDate,
1981             8 * sizeof(INT16));
1982
1983     //
1984     // The content of timeZoneInfo.DaylightDate is 8 words, which
1985     // contains year, month, day, dayOfWeek, hour, minute, second, millisecond.
1986     //
1987     memcpyNoGCRefs(((INT16*)pResultArray->m_Array) + 8,
1988             (LPVOID)&timeZoneInfo.StandardDate,
1989             8 * sizeof(INT16));
1990
1991     ((INT16*)pResultArray->m_Array)[16] = (INT16)timeZoneInfo.DaylightBias * -1;
1992
1993 lExit: ;
1994     HELPER_METHOD_FRAME_END();
1995     return OBJECTREFToObject(pResultArray);
1996 }
1997 FCIMPLEND
1998
1999 #endif // FEATURE_CORECLR
2000
2001 inline BOOL IsInvariantLocale(STRINGREF localeName)
2002 {
2003    return localeName->GetStringLength() == 0;
2004 }
2005
2006 // InternalChangeCaseChar
2007 //
2008 // Call LCMapStringEx with a char to make it upper or lower case
2009 // Note that if the locale is English or Invariant we'll try just mapping it if its < 0x7f
2010 FCIMPL5(FC_CHAR_RET, COMNlsInfo::InternalChangeCaseChar,
2011         INT_PTR handle, // optional sort handle
2012         INT_PTR handleOrigin, StringObject* localeNameUNSAFE, CLR_CHAR wch, CLR_BOOL bIsToUpper)
2013 {
2014     CONTRACTL
2015     {
2016         FCALL_CHECK;
2017         PRECONDITION(CheckPointer(localeNameUNSAFE));
2018     } CONTRACTL_END;
2019
2020     CLR_CHAR retVal = '\0';
2021     int ret_LCMapStringEx = -1;
2022
2023     // Dereference our string
2024     STRINGREF localeName(localeNameUNSAFE);
2025
2026     HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(localeName);
2027
2028     BOOL isInvariantLocale = IsInvariantLocale(localeName);
2029     // Check for Invariant to avoid A/V in LCMapStringEx
2030     DWORD linguisticCasing = (isInvariantLocale) ? 0 : LCMAP_LINGUISTIC_CASING;
2031
2032     handle = EnsureValidSortHandle(handle, handleOrigin, localeName->GetBuffer());
2033
2034 #ifndef FEATURE_CORECLR
2035     AppDomain* curDomain = GetAppDomain();
2036
2037     //For a versioned sort, Invariant should still use the OS
2038     if(!(curDomain->m_bUseOsSorting) && !isInvariantLocale)
2039     {
2040         ret_LCMapStringEx = SortVersioning::SortDllChangeCase((SortVersioning::PSORTHANDLE) handle,
2041                                     bIsToUpper?LCMAP_UPPERCASE | linguisticCasing:
2042                                                LCMAP_LOWERCASE | linguisticCasing,
2043                                     &wch,
2044                                     1,
2045                                     &retVal,
2046                                     1,
2047                                     NULL, 0);
2048     }
2049     else if(curDomain->m_pCustomSortLibrary != NULL)
2050     {
2051         ret_LCMapStringEx = (curDomain->m_pCustomSortLibrary->pLCMapStringEx)(handle != NULL ? NULL : localeName->GetBuffer(),
2052                                    bIsToUpper?LCMAP_UPPERCASE | linguisticCasing:
2053                                               LCMAP_LOWERCASE | linguisticCasing,
2054                                    &wch,
2055                                    1,
2056                                    &retVal,
2057                                    1,
2058                                    NULL,
2059                                    NULL,
2060                                    (LPARAM) handle);
2061     }
2062     else
2063 #endif    
2064     {
2065         ret_LCMapStringEx = NewApis::LCMapStringEx(handle != NULL ? NULL : localeName->GetBuffer(),
2066                                    bIsToUpper?LCMAP_UPPERCASE | linguisticCasing:
2067                                               LCMAP_LOWERCASE | linguisticCasing,
2068                                    &wch,
2069                                    1,
2070                                    &retVal,
2071                                    1,
2072                                    NULL,
2073                                    NULL,
2074                                    (LPARAM) handle);
2075     }
2076
2077     if (0 == ret_LCMapStringEx)
2078     {
2079         // return value of 0 indicates failure and error value is supposed to be set.
2080         // shouldn't ever really happen
2081         _ASSERTE(!"catastrophic failure calling NewApis::InternalChangeCaseChar!  This could be a CultureInfo or CompareInfo bug (bad localeName string) or maybe a GCHole.");
2082     }
2083
2084     HELPER_METHOD_FRAME_END(); // localeName is now unprotected
2085     return retVal;
2086 }
2087 FCIMPLEND
2088
2089 // InternalChangeCaseString
2090 //
2091 // Call LCMapStringEx with a string to make it upper or lower case
2092 // Note that if the locale is English or Invariant we'll try just mapping it if its < 0x7f
2093 //
2094 // We typically expect the output string to be the same size as the input.  If not
2095 // we have to count, reallocate the output buffer, and try again.
2096 FCIMPL5(Object*, COMNlsInfo::InternalChangeCaseString,
2097         INT_PTR handle, // optional sort handle
2098         INT_PTR handleOrigin, StringObject* localeNameUNSAFE, StringObject* pStringUNSAFE, CLR_BOOL bIsToUpper)
2099 {
2100     CONTRACTL
2101     {
2102         FCALL_CHECK;
2103         PRECONDITION(CheckPointer(pStringUNSAFE));
2104         PRECONDITION(CheckPointer(localeNameUNSAFE));
2105     } CONTRACTL_END;
2106
2107     struct _gc
2108     {
2109         STRINGREF pResult;
2110         STRINGREF pString;
2111         STRINGREF pLocale;
2112     } gc;
2113
2114     gc.pResult = NULL;
2115     gc.pString = ObjectToSTRINGREF(pStringUNSAFE);
2116     gc.pLocale = ObjectToSTRINGREF(localeNameUNSAFE);
2117
2118     HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc)
2119
2120     handle = EnsureValidSortHandle(handle, handleOrigin, gc.pLocale->GetBuffer());
2121
2122     //
2123     //  Get the length of the string.
2124     //
2125     int nLengthInput = gc.pString->GetStringLength();
2126     int nLengthOutput = nLengthInput; //  initially we assume the string length does not change.
2127
2128     BOOL isInvariantLocale = IsInvariantLocale(gc.pLocale);
2129     // Check for Invariant to avoid A/V in LCMapStringEx
2130     DWORD linguisticCasing = (isInvariantLocale) ? 0 : LCMAP_LINGUISTIC_CASING;
2131     // Check for Invariant to avoid A/V in LCMapStringEx
2132
2133     //
2134     //  Check if we have the empty string.
2135     //
2136     if (nLengthInput == 0)
2137     {
2138         gc.pResult = ObjectToSTRINGREF(gc.pString);
2139     }
2140     else
2141     {
2142         //
2143         //  Create the result string.
2144         //
2145         gc.pResult = StringObject::NewString(nLengthOutput);
2146         LPWSTR pResultStr = gc.pResult->GetBuffer();
2147
2148         int result;
2149 #ifndef FEATURE_CORECLR
2150         AppDomain* curDomain = GetAppDomain();
2151
2152         //Invariant should always use OS
2153         if(!(curDomain->m_bUseOsSorting) && !isInvariantLocale)
2154         {
2155             result = SortVersioning::SortDllChangeCase((SortVersioning::PSORTHANDLE) handle,
2156                                         bIsToUpper?LCMAP_UPPERCASE | linguisticCasing:
2157                                                    LCMAP_LOWERCASE | linguisticCasing,
2158                                         gc.pString->GetBuffer(),
2159                                         nLengthInput,
2160                                         pResultStr,
2161                                         nLengthOutput,
2162                                         NULL, 0);
2163         }
2164         else if(curDomain->m_pCustomSortLibrary != NULL)
2165         {
2166             result = (curDomain->m_pCustomSortLibrary->pLCMapStringEx)(handle != NULL ? NULL : (gc.pLocale)->GetBuffer(),
2167                                        bIsToUpper?LCMAP_UPPERCASE | linguisticCasing :
2168                                                   LCMAP_LOWERCASE | linguisticCasing,
2169                                        gc.pString->GetBuffer(),
2170                                        nLengthInput,
2171                                        pResultStr,
2172                                        nLengthOutput,
2173                                        NULL,
2174                                        NULL,
2175                                        (LPARAM) handle);
2176         }
2177         else
2178 #endif
2179         {
2180             result = NewApis::LCMapStringEx(handle != NULL ? NULL : (gc.pLocale)->GetBuffer(),
2181                                        bIsToUpper?LCMAP_UPPERCASE | linguisticCasing :
2182                                                   LCMAP_LOWERCASE | linguisticCasing,
2183                                        gc.pString->GetBuffer(),
2184                                        nLengthInput,
2185                                        pResultStr,
2186                                        nLengthOutput,
2187                                        NULL,
2188                                        NULL,
2189                                        (LPARAM) handle);
2190         }
2191
2192         if(0 == result)
2193         {
2194             // Failure: Detect if that's due to insufficient buffer
2195             if (GetLastError()!= ERROR_INSUFFICIENT_BUFFER)
2196             {
2197                 ThrowLastError();
2198             }
2199             // need to update buffer
2200 #ifndef FEATURE_CORECLR
2201             //Invariant should always use OS
2202             if(!(curDomain->m_bUseOsSorting) && !IsInvariantLocale(gc.pLocale))
2203             {
2204                 nLengthOutput = SortVersioning::SortDllChangeCase((SortVersioning::PSORTHANDLE) handle,
2205                                             bIsToUpper?LCMAP_UPPERCASE | linguisticCasing:
2206                                                        LCMAP_LOWERCASE | linguisticCasing,
2207                                             gc.pString->GetBuffer(),
2208                                             nLengthInput,
2209                                             NULL,
2210                                             0,
2211                                             NULL, 0);
2212             }
2213             else if(curDomain->m_pCustomSortLibrary != NULL)
2214             {
2215                 nLengthOutput = (curDomain->m_pCustomSortLibrary->pLCMapStringEx)(handle != NULL ? NULL : (gc.pLocale)->GetBuffer(),
2216                                                         bIsToUpper?LCMAP_UPPERCASE | linguisticCasing :
2217                                                                    LCMAP_LOWERCASE | linguisticCasing,
2218                                                         gc.pString->GetBuffer(),
2219                                                         nLengthInput,
2220                                                         NULL,
2221                                                         0,
2222                                                         NULL,
2223                                                         NULL,
2224                                                         (LPARAM) handle);
2225             }
2226             else
2227 #endif
2228             {
2229                 nLengthOutput = NewApis::LCMapStringEx(handle != NULL ? NULL : (gc.pLocale)->GetBuffer(),
2230                                                         bIsToUpper?LCMAP_UPPERCASE | linguisticCasing :
2231                                                                    LCMAP_LOWERCASE | linguisticCasing,
2232                                                         gc.pString->GetBuffer(),
2233                                                         nLengthInput,
2234                                                         NULL,
2235                                                         0,
2236                                                         NULL,
2237                                                         NULL,
2238                                                         (LPARAM) handle);
2239             }
2240             if (nLengthOutput == 0)
2241             {
2242                 // return value of 0 indicates failure and error value is supposed to be set.
2243                 // shouldn't ever really happen
2244                 _ASSERTE(!"catastrophic failure calling NewApis::InternalChangeCaseString!  This could be a CultureInfo or CompareInfo bug (bad localeName string) or maybe a GCHole.");
2245             }
2246             _ASSERTE(nLengthOutput > 0);
2247             // NOTE: The length of the required buffer does not include the terminating null character.
2248             // So it can be used as-is for our calculations -- the length we pass in to NewString also does
2249             // not include the terminating null character.
2250             // MSDN documentation could be interpreted to mean that the length returned includes the terminating
2251             // NULL character, but that's not the case.
2252
2253             // NOTE: Also note that we let the GC take care of the previously allocated pResult.
2254
2255             gc.pResult = StringObject::NewString(nLengthOutput);
2256             pResultStr = gc.pResult->GetBuffer();
2257 #ifndef FEATURE_CORECLR
2258             //Invariant should always use OS
2259             if(!(curDomain->m_bUseOsSorting) && !IsInvariantLocale(gc.pLocale))
2260             {
2261                 result = SortVersioning::SortDllChangeCase((SortVersioning::PSORTHANDLE) handle,
2262                                             bIsToUpper?LCMAP_UPPERCASE | linguisticCasing:
2263                                                        LCMAP_LOWERCASE | linguisticCasing,
2264                                             gc.pString->GetBuffer(),
2265                                             nLengthInput,
2266                                             pResultStr,
2267                                             nLengthOutput,
2268                                             NULL, 0);
2269             }
2270             else if(curDomain->m_pCustomSortLibrary != NULL)
2271             {
2272                 result = (curDomain->m_pCustomSortLibrary->pLCMapStringEx)(handle != NULL ? NULL : (gc.pLocale)->GetBuffer(),
2273                                            bIsToUpper?LCMAP_UPPERCASE | linguisticCasing :
2274                                                       LCMAP_LOWERCASE | linguisticCasing,
2275                                            gc.pString->GetBuffer(),
2276                                            nLengthInput,
2277                                            pResultStr,
2278                                            nLengthOutput,
2279                                            NULL,
2280                                            NULL,
2281                                            (LPARAM) handle);
2282             }
2283             else
2284 #endif
2285             {
2286                 result = NewApis::LCMapStringEx(handle != NULL ? NULL : (gc.pLocale)->GetBuffer(),
2287                                            bIsToUpper?LCMAP_UPPERCASE | linguisticCasing :
2288                                                       LCMAP_LOWERCASE | linguisticCasing,
2289                                            gc.pString->GetBuffer(),
2290                                            nLengthInput,
2291                                            pResultStr,
2292                                            nLengthOutput,
2293                                            NULL,
2294                                            NULL,
2295                                            (LPARAM) handle);
2296             }
2297
2298             if(0 == result)
2299             {
2300                 // return value of 0 indicates failure and error value is supposed to be set.
2301                 // shouldn't ever really happen
2302                 _ASSERTE(!"catastrophic failure calling NewApis::InternalChangeCaseString!  This could be a CultureInfo or CompareInfo bug (bad localeName string) or maybe a GCHole.");
2303             }
2304         }
2305
2306         pResultStr[nLengthOutput] = 0;
2307     }
2308
2309     HELPER_METHOD_FRAME_END();
2310
2311     return OBJECTREFToObject(gc.pResult);
2312 }
2313 FCIMPLEND
2314
2315 /*================================InternalGetCaseInsHash================================
2316 **Action:
2317 **Returns:
2318 **Arguments:
2319 **Exceptions:
2320 ==============================================================================*/
2321 FCIMPL6(INT32, COMNlsInfo::InternalGetCaseInsHash,
2322         INT_PTR handle, // optional sort handle
2323         INT_PTR handleOrigin, StringObject* localeNameUNSAFE, LPVOID pvStrA, CLR_BOOL bForceRandomizedHashing, INT64 additionalEntropy)
2324 {
2325     CONTRACTL
2326     {
2327         FCALL_CHECK;
2328         PRECONDITION(CheckPointer(localeNameUNSAFE));
2329         PRECONDITION(CheckPointer(pvStrA));
2330     } CONTRACTL_END;
2331
2332     STRINGREF localeName = ObjectToSTRINGREF(localeNameUNSAFE);
2333     STRINGREF strA;
2334
2335     INT32 result;
2336
2337     BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), FCThrow(kStackOverflowException))
2338
2339     *((LPVOID *)&strA)=pvStrA;
2340
2341 #ifndef FEATURE_CORECLR
2342     AppDomain* curDomain = GetAppDomain();
2343 #endif
2344
2345     //
2346     // If we know that we don't have any high characters (the common case) we can
2347     // call a hash function that knows how to do a very fast case conversion.  If
2348     // we find characters above 0x80, it's much faster to convert the entire string
2349     // to Uppercase and then call the standard hash function on it.
2350     //
2351     // TODO: NLS Arrowhead -We aren't consistent with the fast casing cultures (any en?  fr? de?)
2352     if (IsCultureEnglishOrInvariant((localeName)->GetBuffer()) &&           // If we're en-US or Invariant
2353         ((IS_STRING_STATE_UNDETERMINED(strA->GetHighCharState()) &&          // and we're undetermined
2354            IS_FAST_CASING(strA->InternalCheckHighChars())) ||       // and its fast casing when determined
2355         IS_FAST_CASING(strA->GetHighCharState())))                         // or we're fast casing that's already determined
2356     {
2357         // Notice that for Turkish and Azeri we don't get here and shouldn't use this
2358         // fast path because of their special Latin casing rules.
2359 #ifndef FEATURE_CORECLR
2360         result = curDomain->m_pNlsHashProvider->HashiStringKnownLower80(strA->GetBuffer(), strA->GetStringLength(), bForceRandomizedHashing, additionalEntropy);
2361 #else
2362         result = COMNlsHashProvider::s_NlsHashProvider.HashiStringKnownLower80(strA->GetBuffer(), strA->GetStringLength(), bForceRandomizedHashing, additionalEntropy);
2363 #endif // FEATURE_CORECLR
2364     }
2365     else
2366     {
2367         handle = EnsureValidSortHandle(handle, handleOrigin, localeName->GetBuffer());
2368
2369         // Make it upper case
2370         CQuickBytes newBuffer;
2371         INT32 length = strA->GetStringLength();
2372         WCHAR *pNewStr = (WCHAR *)newBuffer.AllocThrows((length + 1) * sizeof(WCHAR));
2373
2374         // Work around an A/V in LCMapStringEx for the invariant culture.
2375         // Revisit this after Vista SP2 has been deployed everywhere. 
2376         DWORD linguisticCasing = 0;
2377         if (localeName->GetStringLength() > 0)  // if not the invariant culture...
2378         {
2379             linguisticCasing = LCMAP_LINGUISTIC_CASING;
2380         }
2381
2382         int lcmapResult;
2383 #ifndef FEATURE_CORECLR
2384         if(!(curDomain->m_bUseOsSorting))
2385         {
2386             lcmapResult = SortVersioning::SortDllChangeCase((SortVersioning::PSORTHANDLE) handle,
2387                                                     LCMAP_UPPERCASE | linguisticCasing,
2388                                                     strA->GetBuffer(),
2389                                                     length,
2390                                                     pNewStr,
2391                                                     length,
2392                                                     NULL, 0);
2393         }
2394         else if(curDomain->m_pCustomSortLibrary != NULL)
2395         {
2396             lcmapResult = (curDomain->m_pCustomSortLibrary->pLCMapStringEx)(handle != NULL ? NULL : localeName->GetBuffer(),
2397                                                    LCMAP_UPPERCASE | linguisticCasing,
2398                                                    strA->GetBuffer(),
2399                                                    length,
2400                                                    pNewStr,
2401                                                    length,
2402                                                    NULL, NULL, (LPARAM) handle);
2403         }
2404         else
2405 #endif
2406         {
2407             lcmapResult = NewApis::LCMapStringEx(handle != NULL ? NULL : localeName->GetBuffer(),
2408                                                    LCMAP_UPPERCASE | linguisticCasing,
2409                                                    strA->GetBuffer(),
2410                                                    length,
2411                                                    pNewStr,
2412                                                    length,
2413                                                    NULL, NULL, (LPARAM) handle);
2414         }
2415
2416         if (lcmapResult == 0)
2417         {
2418             // return value of 0 indicates failure and error value is supposed to be set.
2419             // shouldn't ever really happen
2420             _ASSERTE(!"catastrophic failure calling NewApis::InternalGetCaseInsHash!  This could be a CultureInfo or CompareInfo bug (bad localeName string) or maybe a GCHole.");
2421         }
2422         pNewStr[length]='\0';
2423
2424         // Get hash for the upper case of the new string
2425
2426 #ifndef FEATURE_CORECLR
2427         result = curDomain->m_pNlsHashProvider->HashString(pNewStr, length, (BOOL)bForceRandomizedHashing, additionalEntropy);
2428 #else
2429         result = COMNlsHashProvider::s_NlsHashProvider.HashString(pNewStr, length, (BOOL)bForceRandomizedHashing, additionalEntropy);
2430 #endif // FEATURE_CORECLR
2431     }
2432
2433     END_SO_INTOLERANT_CODE
2434
2435     return result;
2436 }
2437 FCIMPLEND
2438
2439 // Fast path for finding a String using OrdinalIgnoreCase rules
2440 // returns true if the fast path succeeded, with foundIndex set to the location where the String was found or -1
2441 // Returns false when FindStringOrdinal isn't handled (we don't have our own general version of this function to fall back on)
2442 // Note for future optimizations: kernel32!FindStringOrdinal(ignoreCase=TRUE) uses per-character table lookup
2443 // to map to upper case before comparison, but isn't otherwise optimized
2444 BOOL QCALLTYPE COMNlsInfo::InternalTryFindStringOrdinalIgnoreCase(
2445     __in                   DWORD       dwFindNLSStringFlags, // mutually exclusive flags: FIND_FROMSTART, FIND_STARTSWITH, FIND_FROMEND, FIND_ENDSWITH
2446     __in_ecount(cchSource) LPCWSTR     lpStringSource,       // the string we search in
2447     __in                   int         cchSource,            // number of characters lpStringSource after sourceIndex
2448     __in                   int         sourceIndex,          // index from where the search will start in lpStringSource
2449     __in_ecount(cchValue)  LPCWSTR     lpStringValue,        // the string we search for
2450     __in                   int         cchValue,
2451     __out                  int*        foundIndex)           // the index in lpStringSource where we found lpStringValue
2452 {
2453     CONTRACTL
2454     {
2455         QCALL_CHECK;
2456         PRECONDITION(lpStringSource != NULL);
2457         PRECONDITION(lpStringValue != NULL);
2458         PRECONDITION(cchSource>=0);
2459         PRECONDITION(cchValue>=0);
2460         PRECONDITION((dwFindNLSStringFlags & FIND_NLS_STRING_FLAGS_NEGATION) == 0);
2461     } CONTRACTL_END;
2462
2463     BOOL result = FALSE;
2464
2465     BEGIN_QCALL;
2466
2467     LPCWSTR lpSearchStart = NULL;
2468     if (dwFindNLSStringFlags & (FIND_FROMEND | FIND_ENDSWITH))
2469     {
2470         lpSearchStart = &lpStringSource[sourceIndex - cchSource + 1];
2471     }
2472     else {
2473         lpSearchStart = &lpStringSource[sourceIndex];
2474     }
2475 #ifndef FEATURE_CORECLR
2476     AppDomain* curDomain = GetAppDomain();
2477
2478     // Check if the default sorting is overridden
2479     if (curDomain->m_pCustomSortLibrary != NULL)
2480     {
2481         *foundIndex = (curDomain->m_pCustomSortLibrary->pFindStringOrdinal)(
2482             dwFindNLSStringFlags,
2483             lpSearchStart,
2484             cchSource,
2485             lpStringValue,
2486             cchValue,
2487             TRUE);
2488         result = TRUE;
2489     }
2490     else
2491 #endif
2492     {
2493 #ifndef FEATURE_CORESYSTEM
2494         // kernel function pointer
2495         typedef int (WINAPI *PFNFindStringOrdinal)(DWORD, LPCWSTR, INT, LPCWSTR, INT, BOOL);
2496         static PFNFindStringOrdinal FindStringOrdinal = NULL;
2497
2498         // initizalize kernel32!FindStringOrdinal
2499         if (FindStringOrdinal == NULL)
2500         {
2501             PFNFindStringOrdinal result  = NULL;
2502
2503             HMODULE hMod=WszGetModuleHandle(WINDOWS_KERNEL32_DLLNAME_W);
2504             if(hMod != NULL)
2505                 result=(PFNFindStringOrdinal)GetProcAddress(hMod,"FindStringOrdinal");
2506             
2507             FindStringOrdinal = (result != NULL) ? result : (PFNFindStringOrdinal)-1;
2508         }
2509
2510         // call into the kernel
2511         if (FindStringOrdinal != (PFNFindStringOrdinal)-1)
2512 #endif
2513         {
2514             *foundIndex = FindStringOrdinal(
2515                 dwFindNLSStringFlags,
2516                 lpSearchStart,
2517                 cchSource,
2518                 lpStringValue,
2519                 cchValue,
2520                 TRUE);
2521             result = TRUE;
2522         }
2523     }
2524     // if we found the pattern string, fixup the index before we return
2525     if (*foundIndex >= 0)
2526     {
2527         if (dwFindNLSStringFlags & (FIND_FROMEND | FIND_ENDSWITH))
2528             *foundIndex += (sourceIndex - cchSource + 1);
2529         else
2530             *foundIndex += sourceIndex;
2531     }
2532     END_QCALL;
2533
2534     return result;
2535 }
2536
2537
2538 // InternalCompareStringOrdinalIgnoreCase
2539 //
2540 // Call ::CompareStringOrdinal for native ordinal behavior
2541 INT32 QCALLTYPE COMNlsInfo::InternalCompareStringOrdinalIgnoreCase(
2542     LPCWSTR string1, INT32 index1,
2543     LPCWSTR string2, INT32 index2,
2544     INT32 length1,
2545     INT32 length2)
2546 {
2547     CONTRACTL
2548     {
2549         QCALL_CHECK;
2550         PRECONDITION(CheckPointer(string1));
2551         PRECONDITION(CheckPointer(string2));
2552     } CONTRACTL_END;
2553
2554     INT32 result = 0;
2555
2556     BEGIN_QCALL;
2557     //
2558     //  Get the arguments.
2559     //  We assume the caller checked them before calling us
2560     //
2561
2562     // We don't allow the -1 that native code allows
2563     _ASSERT(length1 >= 0);
2564     _ASSERT(length2 >= 0);
2565
2566     // Do the comparison
2567 #ifndef FEATURE_CORECLR
2568     AppDomain* curDomain = GetAppDomain();
2569     
2570     if (curDomain->m_pCustomSortLibrary != NULL) {
2571         result = (curDomain->m_pCustomSortLibrary->pCompareStringOrdinal)(string1 + index1, length1, string2 + index2, length2, TRUE);
2572     } 
2573     else 
2574 #endif
2575     {
2576         result = NewApis::CompareStringOrdinal(string1 + index1, length1, string2 + index2, length2, TRUE);
2577     }
2578
2579     // The native call shouldn't fail
2580     _ASSERT(result != 0);
2581     if (result == 0)
2582     {
2583         // return value of 0 indicates failure and error value is supposed to be set.
2584         // shouldn't ever really happen
2585         _ASSERTE(!"catastrophic failure calling NewApis::CompareStringOrdinal!  This is usually due to bad arguments.");
2586     }
2587
2588     // Adjust the result to the expected -1, 0, 1 result
2589     result -= 2;
2590
2591     END_QCALL;
2592
2593     return result;
2594 }
2595
2596 /**
2597  * This function returns a pointer to this table that we use in System.Globalization.EncodingTable.
2598  * No error checking of any sort is performed.  Range checking is entirely the responsibility of the managed
2599  * code.
2600  */
2601 FCIMPL0(EncodingDataItem *, COMNlsInfo::nativeGetEncodingTableDataPointer)
2602 {
2603     LIMITED_METHOD_CONTRACT;
2604     STATIC_CONTRACT_SO_TOLERANT;
2605
2606     return (EncodingDataItem *)EncodingDataTable;
2607 }
2608 FCIMPLEND
2609
2610 /**
2611  * This function returns a pointer to this table that we use in System.Globalization.EncodingTable.
2612  * No error checking of any sort is performed.  Range checking is entirely the responsibility of the managed
2613  * code.
2614  */
2615 FCIMPL0(CodePageDataItem *, COMNlsInfo::nativeGetCodePageTableDataPointer)
2616 {
2617     LIMITED_METHOD_CONTRACT;
2618
2619     STATIC_CONTRACT_SO_TOLERANT;
2620
2621     return ((CodePageDataItem*) CodePageDataTable);
2622 }
2623 FCIMPLEND
2624
2625
2626 #ifndef FEATURE_COREFX_GLOBALIZATION
2627 //
2628 // Normalization
2629 //
2630
2631 FCIMPL6(int, COMNlsInfo::nativeNormalizationNormalizeString,
2632             int NormForm, int& iError,
2633             StringObject* inChars, int inLength,
2634             CHARArray* outChars, int outLength )
2635 {
2636     CONTRACTL
2637     {
2638         FCALL_CHECK;
2639         PRECONDITION(CheckPointer(inChars));
2640         PRECONDITION(CheckPointer(outChars, NULL_OK));
2641     } CONTRACTL_END;
2642
2643     // Dereference our string
2644     STRINGREF inString(inChars);
2645     LPWSTR inCharsBuffer = inString->GetBuffer();
2646
2647     CHARARRAYREF outCharArray(outChars);
2648     LPWSTR outCharsBuffer = (outCharArray != NULL) ? ((LPWSTR) (outCharArray->GetDirectPointerToNonObjectElements())) : NULL;
2649
2650     // The OS APIs do not always set last error in success, so we have to do it explicitly
2651     SetLastError(ERROR_SUCCESS);
2652
2653     int iResult = m_pfnNormalizationNormalizeStringFunc(
2654             NormForm, inCharsBuffer, inLength, outCharsBuffer, outLength);
2655
2656     // Get our error if necessary
2657     if (iResult <= 0)
2658     {
2659         // if the length is <= 0 there was an error
2660         iError = GetLastError();
2661
2662         // Go ahead and return positive lengths/indexes so we don't get confused
2663         iResult = -iResult;
2664     }
2665     else
2666     {
2667         iError = 0; // ERROR_SUCCESS
2668     }
2669
2670     return iResult;
2671 }
2672 FCIMPLEND
2673
2674 FCIMPL4( FC_BOOL_RET, COMNlsInfo::nativeNormalizationIsNormalizedString,
2675             int NormForm, int& iError,
2676             StringObject* chars, int inLength )
2677 {
2678     CONTRACTL
2679     {
2680         FCALL_CHECK;
2681         PRECONDITION(CheckPointer(chars));
2682     } CONTRACTL_END;
2683
2684     STRINGREF inString(chars);
2685     LPWSTR charsBuffer = inString->GetBuffer();
2686
2687     // The OS APIs do not always set last error in success, so we have to do it explicitly
2688     SetLastError(ERROR_SUCCESS);
2689
2690     // Ask if its normalized
2691     BOOL bResult = m_pfnNormalizationIsNormalizedStringFunc( NormForm, charsBuffer, inLength);
2692
2693     // May need an error
2694     if (bResult == false)
2695     {
2696         // If its false there may have been an error
2697         iError = GetLastError();
2698     }
2699     else
2700     {
2701         iError = 0; // ERROR_SUCCESS
2702     }
2703
2704     FC_RETURN_BOOL(bResult);
2705 }
2706 FCIMPLEND
2707
2708 void QCALLTYPE COMNlsInfo::nativeNormalizationInitNormalization(int NormForm, BYTE* pTableData)
2709 {
2710     QCALL_CONTRACT;
2711
2712     BEGIN_QCALL;
2713
2714     if (m_hNormalization == NULL)
2715     {
2716         HMODULE hNormalization = NULL;
2717
2718         if (pTableData == NULL)
2719         {
2720             // Use OS implementation
2721             hNormalization = GetModuleHandleW(W("kernel32.dll"));
2722             if (!hNormalization)
2723                ThrowLastError();
2724         }
2725 #ifndef FEATURE_CORECLR
2726         // in coreclr we should always find the normalization in kernel32 as it supports Win7 and up 
2727         else
2728         {
2729             HRESULT hr = g_pCLRRuntime->LoadLibrary(NORMALIZATION_DLL, &hNormalization);
2730             if (FAILED(hr))
2731                 ThrowHR(hr);
2732         }
2733 #endif // FEATURE_CORECLR
2734
2735         _ASSERTE(hNormalization != NULL);
2736         m_hNormalization = hNormalization;
2737     }
2738
2739     if (m_pfnNormalizationIsNormalizedStringFunc == NULL)
2740     {
2741         FARPROC pfn = GetProcAddress(m_hNormalization, "IsNormalizedString");
2742         if (pfn == NULL)
2743             ThrowLastError();
2744         m_pfnNormalizationIsNormalizedStringFunc = (PFN_NORMALIZATION_IS_NORMALIZED_STRING)pfn;
2745     }
2746
2747     if (m_pfnNormalizationNormalizeStringFunc == NULL)
2748     {
2749         FARPROC pfn = GetProcAddress(m_hNormalization, "NormalizeString");
2750         if (pfn == NULL)
2751             ThrowLastError();
2752         m_pfnNormalizationNormalizeStringFunc = (PFN_NORMALIZATION_NORMALIZE_STRING)pfn;
2753     }
2754
2755     if (pTableData != NULL)
2756     {
2757         if (m_pfnNormalizationInitNormalizationFunc == NULL)
2758         {
2759             FARPROC pfn = GetProcAddress(m_hNormalization, "InitNormalization");
2760             if (pfn == NULL)
2761                 ThrowLastError();
2762             m_pfnNormalizationInitNormalizationFunc = (PFN_NORMALIZATION_INIT_NORMALIZATION)pfn;
2763         }
2764
2765         BYTE* pResult = m_pfnNormalizationInitNormalizationFunc( NormForm, pTableData);
2766         if (pResult == NULL)
2767             ThrowOutOfMemory();
2768     }
2769
2770     END_QCALL;
2771 }
2772
2773 #endif // FEATURE_COREFX_GLOBALIZATION
2774
2775
2776 //
2777 // This table should be sorted using case-insensitive ordinal order.
2778 // In the managed code, String.CompareStringOrdinalWC() is used to sort this.
2779 //
2780
2781
2782 /**
2783  * This function returns the number of items in EncodingDataTable.
2784  */
2785 FCIMPL0(INT32, COMNlsInfo::nativeGetNumEncodingItems)
2786 {
2787     LIMITED_METHOD_CONTRACT;
2788     STATIC_CONTRACT_SO_TOLERANT;
2789
2790     return (m_nEncodingDataTableItems);
2791 }
2792 FCIMPLEND
2793
2794
2795
2796 typedef CultureDataBaseObject* CULTUREDATAREF;
2797
2798 // nativeInitCultureData checks with the OS to see if this is a valid culture.
2799 // If so we populate a limited number of fields.  If its not valid we return false.
2800 //
2801 // The fields we populate:
2802 //
2803 // sWindowsName -- The name that windows thinks this culture is, ie:
2804 //                            en-US if you pass in en-US
2805 //                            de-DE_phoneb if you pass in de-DE_phoneb
2806 //                            fj-FJ if you pass in fj (neutral, on a pre-Windows 7 machine)
2807 //                            fj if you pass in fj (neutral, post-Windows 7 machine)
2808 //
2809 // sRealName -- The name you used to construct the culture, in pretty form
2810 //                       en-US if you pass in EN-us
2811 //                       en if you pass in en
2812 //                       de-DE_phoneb if you pass in de-DE_phoneb
2813 //
2814 // sSpecificCulture -- The specific culture for this culture
2815 //                             en-US for en-US
2816 //                             en-US for en
2817 //                             de-DE_phoneb for alt sort
2818 //                             fj-FJ for fj (neutral)
2819 //
2820 // sName -- The IETF name of this culture (ie: no sort info, could be neutral)
2821 //                en-US if you pass in en-US
2822 //                en if you pass in en
2823 //                de-DE if you pass in de-DE_phoneb
2824 //
2825 // bNeutral -- TRUE if it is a neutral locale
2826 //
2827 // For a neutral we just populate the neutral name, but we leave the windows name pointing to the
2828 // windows locale that's going to provide data for us.
2829 //
2830 FCIMPL1(FC_BOOL_RET, COMNlsInfo::nativeInitCultureData, CultureDataBaseObject *cultureDataUNSAFE)
2831 {
2832     FCALL_CONTRACT;
2833
2834     BOOL        success=FALSE;
2835
2836     struct _gc
2837     {
2838         STRINGREF stringResult;
2839         CULTUREDATAREF cultureData;
2840     } gc;
2841
2842     gc.stringResult = NULL;
2843     gc.cultureData  = (CULTUREDATAREF) cultureDataUNSAFE;
2844     HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
2845
2846     WCHAR       buffer[LOCALE_NAME_MAX_LENGTH];
2847     int         result;
2848
2849     StackSString realNameBuffer( ((STRINGREF)gc.cultureData->sRealName)->GetBuffer() );
2850
2851     // Call GetLocaleInfoEx and see if the OS knows about it.
2852     // Note that GetLocaleInfoEx has variations:
2853     // * Pre-Vista it fails and has to go downlevel
2854     // * Vista succeeds, but not for neutrals
2855     // * Win7 succeeds for all locales.
2856     // * Mac does ???
2857     // The differences should be handled by the NewApis wrapper
2858     result = NewApis::GetLocaleInfoEx(realNameBuffer, LOCALE_SNAME, buffer, NumItems(buffer));
2859
2860     // Did it fail?
2861     if (result == 0)
2862     {
2863         // Not a real locale, fail
2864         goto Exit;
2865     }
2866
2867     // It worked, note that the name is the locale name, so use that (even for neutrals)
2868     // We need to clean up our "real" name, which should look like the windows name right now
2869     // so overwrite the input with the cleaned up name
2870     gc.stringResult = StringObject::NewString(buffer, result-1);
2871     SetObjectReference((OBJECTREF*)&(gc.cultureData->sRealName), gc.stringResult, NULL);
2872
2873     // Check for neutrality, don't expect to fail
2874     // (buffer has our name in it, so we don't have to do the gc. stuff)
2875     DWORD bNeutral;
2876     if (0 == NewApis::GetLocaleInfoEx(buffer, LOCALE_INEUTRAL | LOCALE_RETURN_NUMBER, (LPWSTR)&bNeutral, sizeof(bNeutral)/sizeof(WCHAR)))
2877         goto Exit;
2878
2879     // Remember our neutrality
2880     gc.cultureData->bNeutral = (bNeutral != 0);
2881
2882     gc.cultureData->bWin32Installed = (IsOSValidLocaleName(buffer, gc.cultureData->bNeutral) != 0);
2883     gc.cultureData->bFramework = (IsWhidbeyFrameworkCulture(buffer) != 0);
2884
2885
2886     // Note: Parents will be set dynamically
2887
2888     // Start by assuming the windows name'll be the same as the specific name since windows knows
2889     // about specifics on all versions.  For macs it also works.  Only for downlevel Neutral locales
2890     // does this have to change.
2891     gc.stringResult = StringObject::NewString(buffer, result-1);
2892     SetObjectReference((OBJECTREF*)&(gc.cultureData->sWindowsName), gc.stringResult, NULL);
2893
2894     // Neutrals and non-neutrals are slightly different
2895     if (gc.cultureData->bNeutral)
2896     {
2897         // Neutral Locale
2898
2899         // IETF name looks like neutral name
2900         gc.stringResult = StringObject::NewString(buffer, result-1);
2901         SetObjectReference((OBJECTREF*)&(gc.cultureData->sName), gc.stringResult, NULL);
2902
2903         // Specific locale name is whatever ResolveLocaleName (win7+) returns.
2904         // (Buffer has our name in it, and we can recycle that because windows resolves it before writing to the buffer)
2905         result = NewApis::ResolveLocaleName(buffer, buffer, NumItems(buffer));
2906
2907         // 0 is failure, 1 is invariant (""), which we expect
2908         if (result < 1) goto Exit;
2909
2910         // We found a locale name, so use it.
2911         // In vista this should look like a sort name (de-DE_phoneb) or a specific culture (en-US) and be in the "pretty" form
2912         gc.stringResult = StringObject::NewString(buffer, result - 1);
2913         SetObjectReference((OBJECTREF*)&(gc.cultureData->sSpecificCulture), gc.stringResult, NULL);
2914
2915 #ifdef FEATURE_CORECLR
2916         if (!IsWindows7())
2917         {
2918             // For neutrals on Windows 7 + the neutral windows name can be the same as the neutral name,
2919             // but on pre windows 7 names it has to be the specific, so we have to fix it in that case.
2920             gc.stringResult = StringObject::NewString(buffer, result - 1);
2921             SetObjectReference((OBJECTREF*)&(gc.cultureData->sWindowsName), gc.stringResult, NULL);
2922         }
2923 #endif
2924
2925
2926     }
2927     else
2928     {
2929         // Specific Locale
2930
2931         // Specific culture's the same as the locale name since we know its not neutral
2932         // On mac we'll use this as well, even for neutrals. There's no obvious specific
2933         // culture to use and this isn't exposed, but behaviorally this is correct on mac.
2934         // Note that specifics include the sort name (de-DE_phoneb)
2935         gc.stringResult = StringObject::NewString(buffer, result-1);
2936         SetObjectReference((OBJECTREF*)&(gc.cultureData->sSpecificCulture), gc.stringResult, NULL);
2937
2938         // We need the IETF name (sname)
2939         // If we aren't an alt sort locale then this is the same as the windows name.
2940         // If we are an alt sort locale then this is the same as the part before the _ in the windows name
2941         // This is for like de-DE_phoneb and es-ES_tradnl that hsouldn't have the _ part
2942
2943         int localeNameLength = result - 1;
2944
2945         LCID lcid = NewApis::LocaleNameToLCID(buffer, 0);
2946         if (!IsCustomCultureId(lcid))
2947         {
2948             LPCWSTR index = wcschr(buffer, W('_'));
2949             if(index)                               // Not a custom culture and looks like an alt sort name
2950             {
2951                 // Looks like an alt sort, and has a appropriate sort LCID (so not custom), make it smaller for the RFC 4646 style name
2952                 localeNameLength = static_cast<int>(index - buffer);
2953             }
2954         }
2955
2956         gc.stringResult = StringObject::NewString(buffer, localeNameLength);
2957         _ASSERTE(gc.stringResult != NULL);
2958
2959         // Now use that name
2960         SetObjectReference((OBJECTREF*)&(gc.cultureData->sName), gc.stringResult, NULL);
2961     }
2962
2963 #ifdef FEATURE_CORECLR
2964     // For Silverlight make sure that the sorting tables are available (< Vista may not have east asian installed)
2965     result = NewApis::CompareStringEx(((STRINGREF)gc.cultureData->sWindowsName)->GetBuffer(),
2966                                       0, W("A"), 1, W("B"), 1, NULL, NULL, 0);
2967     if (result == 0) goto Exit;
2968 #endif
2969
2970     // It succeeded.
2971     success = TRUE;
2972
2973 Exit: {}
2974
2975     HELPER_METHOD_FRAME_END();
2976
2977     FC_RETURN_BOOL(success);
2978 }
2979 FCIMPLEND
2980
2981
2982 // Return true if we're on Windows 7 (ie: if we have neutral native support)
2983 BOOL COMNlsInfo::IsWindows7()
2984 {
2985     static BOOL bChecked=FALSE;
2986     static BOOL bIsWindows7=FALSE;
2987
2988     if (!bChecked)
2989     {
2990         // LOCALE_INEUTRAL is first supported on Windows 7
2991         if (GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_INEUTRAL, NULL, 0) != 0)
2992         {
2993             // Success, we're win7
2994             bIsWindows7 = TRUE;
2995         }
2996
2997         // Either way we checked now
2998         bChecked = TRUE;
2999     }
3000
3001     return bIsWindows7;
3002 }
3003
3004 //
3005 // QCall implementation
3006 //
3007 int QCALLTYPE COMNlsInfo::InternalFindNLSStringEx(
3008     __in_opt               INT_PTR     handle,               // optional sort handle
3009     __in_opt               INT_PTR     handleOrigin,
3010     __in_z                 LPCWSTR     lpLocaleName,         // locale name
3011     __in                   int         dwFindNLSStringFlags, // search falg
3012     __in_ecount(cchSource) LPCWSTR     lpStringSource,       // the string we search in
3013     __in                   int         cchSource,            // number of characters lpStringSource after sourceIndex
3014     __in                   int         sourceIndex,          // index from where the search will start in lpStringSource
3015     __in_ecount(cchValue)  LPCWSTR     lpStringValue,        // the string we search for
3016     __in                   int         cchValue)             // length of the string we search for
3017 {
3018     CONTRACTL {
3019         QCALL_CHECK;
3020         PRECONDITION(lpLocaleName != NULL);
3021         PRECONDITION(lpStringSource != NULL);
3022         PRECONDITION(lpStringValue != NULL);
3023         PRECONDITION(cchSource>=0);
3024         PRECONDITION(cchValue>=0);
3025     } CONTRACTL_END;
3026
3027     int retValue = -1;
3028
3029     BEGIN_QCALL;
3030
3031 #ifndef FEATURE_CORECLR
3032     AppDomain* curDomain = GetAppDomain();
3033     handle = EnsureValidSortHandle(handle, handleOrigin, lpLocaleName);
3034 #endif
3035
3036     #define RESERVED_FIND_ASCII_STRING 0x20000000       // This flag used only to tell the sorting DLL can assume the string characters are in ASCII.
3037
3038 #ifndef FEATURE_CORECLR
3039     int asciiFlag = (dwFindNLSStringFlags & RESERVED_FIND_ASCII_STRING);
3040 #endif // FEATURE_CORECLR
3041
3042     dwFindNLSStringFlags &= ~RESERVED_FIND_ASCII_STRING;
3043
3044     if (cchValue == 0)
3045     {
3046         retValue = sourceIndex;       // keep Whidbey compatibility
3047         goto lExit;
3048     }
3049
3050     if (sourceIndex<0 || cchSource<0 ||
3051         ((dwFindNLSStringFlags & (FIND_FROMEND | FIND_ENDSWITH)) && (sourceIndex+1<cchSource)))
3052     {
3053         goto lExit;
3054     }
3055
3056     if (dwFindNLSStringFlags & COMPARE_OPTIONS_ORDINAL)
3057     {
3058         if (dwFindNLSStringFlags & (FIND_FROMEND | FIND_ENDSWITH))
3059         {
3060             retValue = NewApis::LastIndexOfString(
3061                         lpLocaleName,
3062                         &lpStringSource[sourceIndex - cchSource + 1],
3063                         cchSource,
3064                         lpStringValue,
3065                         cchValue,
3066                         dwFindNLSStringFlags & FIND_NLS_STRING_FLAGS_NEGATION,
3067                         dwFindNLSStringFlags & FIND_ENDSWITH);
3068             if (retValue >= 0)
3069             {
3070                 retValue += sourceIndex - cchSource + 1;
3071             }
3072         }
3073         else
3074         {
3075             retValue = NewApis::IndexOfString(
3076                         lpLocaleName,
3077                         &lpStringSource[sourceIndex],
3078                         cchSource,
3079                         lpStringValue,
3080                         cchValue,
3081                         dwFindNLSStringFlags & FIND_NLS_STRING_FLAGS_NEGATION,
3082                         dwFindNLSStringFlags & FIND_STARTSWITH);
3083
3084             if  (retValue >= 0)
3085             {
3086                 retValue += sourceIndex;
3087             }
3088         }
3089     }
3090     else
3091     {
3092         if (dwFindNLSStringFlags & (FIND_FROMEND | FIND_ENDSWITH))
3093         {
3094 #ifndef FEATURE_CORECLR
3095             if(!(curDomain->m_bUseOsSorting))
3096             {
3097                 retValue = SortVersioning::SortDllFindString((SortVersioning::PSORTHANDLE) handle,
3098                                                   dwFindNLSStringFlags | asciiFlag,
3099                                                   &lpStringSource[sourceIndex - cchSource + 1],
3100                                                   cchSource,
3101                                                   lpStringValue,
3102                                                   cchValue,
3103                                                   NULL, NULL, 0);
3104             }
3105             else if(curDomain->m_pCustomSortLibrary != NULL)
3106             {
3107                 retValue = (curDomain->m_pCustomSortLibrary->pFindNLSStringEx)(
3108                                         handle != NULL ? NULL : lpLocaleName,
3109                                         dwFindNLSStringFlags,
3110                                         &lpStringSource[sourceIndex - cchSource + 1],
3111                                         cchSource,
3112                                         lpStringValue,
3113                                         cchValue, NULL, NULL, NULL, (LPARAM) handle);
3114             }   
3115             else
3116 #endif
3117             {
3118                 retValue = NewApis::FindNLSStringEx(
3119                                         handle != NULL ? NULL : lpLocaleName,
3120                                         dwFindNLSStringFlags,
3121                                         &lpStringSource[sourceIndex - cchSource + 1],
3122                                         cchSource,
3123                                         lpStringValue,
3124                                         cchValue, NULL, NULL, NULL, (LPARAM) handle);
3125             }
3126
3127             if (retValue >= 0)
3128             {
3129                 retValue += sourceIndex - cchSource + 1;
3130             }
3131         }
3132         else
3133         {
3134 #ifndef FEATURE_CORECLR
3135             if(!(curDomain->m_bUseOsSorting))
3136             {
3137                 retValue = SortVersioning::SortDllFindString((SortVersioning::PSORTHANDLE) handle,
3138                                                   dwFindNLSStringFlags | asciiFlag,
3139                                                   &lpStringSource[sourceIndex],
3140                                                   cchSource,
3141                                                   lpStringValue,
3142                                                   cchValue,
3143                                                   NULL, NULL, 0);
3144             }
3145             else if(curDomain->m_pCustomSortLibrary != NULL)
3146             {
3147                 retValue = (curDomain->m_pCustomSortLibrary->pFindNLSStringEx)(
3148                                         handle != NULL ? NULL : lpLocaleName,
3149                                         dwFindNLSStringFlags,
3150                                         &lpStringSource[sourceIndex],
3151                                         cchSource,
3152                                         lpStringValue,
3153                                         cchValue, NULL, NULL, NULL, (LPARAM) handle);
3154             }
3155             else
3156 #endif
3157             {
3158                 retValue = NewApis::FindNLSStringEx(
3159                                         handle != NULL ? NULL : lpLocaleName,
3160                                         dwFindNLSStringFlags,
3161                                         &lpStringSource[sourceIndex],
3162                                         cchSource,
3163                                         lpStringValue,
3164                                         cchValue, NULL, NULL, NULL, (LPARAM) handle);
3165             }
3166
3167             if (retValue >= 0)
3168             {
3169                 retValue += sourceIndex;
3170             }
3171         }
3172     }
3173
3174 lExit:
3175
3176     END_QCALL;
3177
3178     return retValue;
3179 }
3180
3181
3182 int QCALLTYPE COMNlsInfo::InternalGetSortKey(
3183     __in_opt               INT_PTR handle,        // PSORTHANDLE
3184     __in_opt               INT_PTR handleOrigin,
3185     __in_z                 LPCWSTR pLocaleName,   // locale name
3186     __in                   int     flags,         // flags
3187     __in_ecount(cchSource) LPCWSTR pStringSource, // Source string
3188     __in                   int     cchSource,     // number of characters in lpStringSource
3189     __in_ecount(cchTarget) PBYTE   pTarget,       // Target data buffer (may be null to count)
3190     __in                   int     cchTarget)     // Character count for target buffer
3191 {
3192     CONTRACTL {
3193         QCALL_CHECK;
3194         PRECONDITION(pLocaleName != NULL);
3195         PRECONDITION(pStringSource != NULL);
3196 //        PRECONDITION(pTarget != NULL);
3197         PRECONDITION(cchSource>=0);
3198 //        PRECONDITION(cchTarget>=0);
3199     } CONTRACTL_END;
3200
3201     int retValue = 0;
3202
3203     BEGIN_QCALL;
3204
3205
3206 #ifndef FEATURE_CORECLR
3207     handle = EnsureValidSortHandle(handle, handleOrigin, pLocaleName);
3208
3209     AppDomain* curDomain = GetAppDomain();
3210
3211     if(!(curDomain->m_bUseOsSorting))
3212     {
3213         retValue = SortVersioning::SortDllGetSortKey((SortVersioning::PSORTHANDLE) handle,
3214                                                   flags,
3215                                                   pStringSource,
3216                                                   cchSource,
3217                                                   pTarget,
3218                                                   cchTarget,
3219                                                   NULL, 0);
3220     }
3221     else if(curDomain->m_pCustomSortLibrary != NULL)
3222     {
3223         retValue = (curDomain->m_pCustomSortLibrary->pLCMapStringEx)(handle != NULL ? NULL : pLocaleName,
3224                                           flags | LCMAP_SORTKEY,
3225                                           pStringSource,
3226                                           cchSource,
3227                                           (LPWSTR)pTarget,
3228                                           cchTarget,
3229                                           NULL,
3230                                           NULL,
3231                                           (LPARAM) handle);
3232     }
3233     else
3234 #endif
3235     {
3236         // Just call NewApis::LCMapStringEx to do our work
3237         retValue = NewApis::LCMapStringEx(handle != NULL ? NULL : pLocaleName,
3238                                           flags | LCMAP_SORTKEY,
3239                                           pStringSource,
3240                                           cchSource,
3241                                           (LPWSTR)pTarget,
3242                                           cchTarget,
3243                                           NULL,
3244                                           NULL,
3245                                           (LPARAM) handle);
3246     }
3247     END_QCALL;
3248
3249     return retValue;
3250 }
3251
3252
3253 // We allow InternalInitSortHandle to return a NULL value
3254 // this is the case for Silverlight or when the appdomain has custom sorting.
3255 // So all the methods that take a SortHandle, also have to
3256 // be able to just call the slower api that looks up the tables based on the locale name
3257 INT_PTR QCALLTYPE COMNlsInfo::InternalInitSortHandle(LPCWSTR localeName, __out INT_PTR* handleOrigin)
3258 {
3259     CONTRACTL {
3260         QCALL_CHECK;
3261         PRECONDITION(localeName != NULL);
3262     } CONTRACTL_END;
3263     
3264     INT_PTR pSort = NULL;
3265
3266     BEGIN_QCALL;
3267
3268     pSort = InitSortHandleHelper(localeName, handleOrigin);
3269
3270     END_QCALL;
3271
3272     return pSort;
3273 }
3274
3275 INT_PTR COMNlsInfo::InitSortHandleHelper(LPCWSTR localeName, __out INT_PTR* handleOrigin)
3276 {
3277     CONTRACTL {
3278         NOTHROW;
3279         GC_NOTRIGGER;
3280         MODE_ANY;
3281         CANNOT_TAKE_LOCK;
3282         PRECONDITION(localeName != NULL);
3283     } CONTRACTL_END;
3284
3285     INT_PTR pSort = NULL;
3286
3287 #ifndef FEATURE_CORECLR
3288     AppDomain* curDomain = GetAppDomain();
3289
3290 #if _DEBUG
3291     _ASSERTE(curDomain->m_bSortingInitialized);
3292 #endif
3293
3294     if(curDomain->m_bUseOsSorting)
3295     {
3296         pSort = InternalInitOsSortHandle(localeName, handleOrigin);
3297     }
3298     else
3299     {
3300         pSort = InternalInitVersionedSortHandle(localeName, handleOrigin);
3301     }
3302 #else
3303     // coreclr will try to initialize the handle and if running on downlevel it'll just return null
3304     pSort = InternalInitOsSortHandle(localeName, handleOrigin);
3305 #endif // FEATURE_CORECLR
3306     return pSort;
3307 }
3308
3309 #ifndef FEATURE_CORECLR
3310 INT_PTR COMNlsInfo::InternalInitVersionedSortHandle(LPCWSTR localeName, __out INT_PTR* handleOrigin)
3311 {
3312     CONTRACTL {
3313         NOTHROW;
3314         GC_NOTRIGGER;
3315         MODE_ANY;
3316         CANNOT_TAKE_LOCK;
3317         PRECONDITION(localeName != NULL);
3318     } CONTRACTL_END;
3319
3320     AppDomain* curDomain = GetAppDomain();
3321
3322     if(curDomain->m_pCustomSortLibrary != NULL)
3323     {
3324         return NULL;
3325     }
3326
3327     return InternalInitVersionedSortHandle(localeName, handleOrigin, curDomain->m_sortVersion);
3328 }
3329
3330 INT_PTR COMNlsInfo::InternalInitVersionedSortHandle(LPCWSTR localeName, __out INT_PTR* handleOrigin, DWORD sortVersion)
3331 {
3332     CONTRACTL {
3333         NOTHROW;
3334         GC_NOTRIGGER;
3335         MODE_ANY;
3336         CANNOT_TAKE_LOCK;
3337         PRECONDITION(localeName != NULL);
3338     } CONTRACTL_END;
3339     
3340     INT_PTR pSort = NULL;
3341
3342     _ASSERTE(NewApis::NotLeakingFrameworkOnlyCultures(localeName));
3343
3344     *handleOrigin = (INT_PTR) SortVersioning::GetSortHandle;
3345
3346     // try the requested version
3347     if(sortVersion != DEFAULT_SORT_VERSION)
3348     {
3349         NLSVERSIONINFO  sortVersionInfo;
3350         sortVersionInfo.dwNLSVersionInfoSize = sizeof(NLSVERSIONINFO);
3351         sortVersionInfo.dwNLSVersion = sortVersion;
3352         sortVersionInfo.dwDefinedVersion = sortVersion;
3353         pSort = (INT_PTR) SortVersioning::GetSortHandle(localeName, &sortVersionInfo);
3354     }
3355
3356     // fallback to default version
3357     if(pSort == NULL)
3358     {
3359         pSort = (INT_PTR) SortVersioning::GetSortHandle(localeName, NULL);
3360     }
3361
3362     _ASSERTE(RunningOnWin8() || pSort != NULL);
3363
3364     return pSort;
3365 }
3366 #endif //FEATURE_CORECLR
3367
3368 // Can return NULL
3369 INT_PTR COMNlsInfo::InternalInitOsSortHandle(LPCWSTR localeName, __out INT_PTR* handleOrigin)
3370 {
3371     CONTRACTL {
3372         NOTHROW;
3373         GC_NOTRIGGER;
3374         MODE_ANY;
3375         CANNOT_TAKE_LOCK;
3376         PRECONDITION(localeName != NULL);
3377     } CONTRACTL_END;
3378     
3379     INT_PTR pSort = NULL;
3380
3381     AppDomain* curDomain = GetAppDomain();
3382
3383 #ifndef FEATURE_CORECLR
3384     if (RunningOnWin8() || curDomain->m_pCustomSortLibrary != NULL)
3385 #else
3386     if (RunningOnWin8())
3387 #endif //FEATURE_CORECLR 
3388     {
3389         LPARAM lSortHandle;
3390         int ret;
3391
3392 #ifndef FEATURE_CORECLR
3393         if (curDomain->m_pCustomSortLibrary != NULL)
3394         {
3395             ret = (curDomain->m_pCustomSortLibrary->pLCMapStringEx)(localeName, LCMAP_SORTHANDLE, NULL, 0, (LPWSTR) &lSortHandle, sizeof(LPARAM), NULL, NULL, 0);
3396             *handleOrigin = (INT_PTR) curDomain->m_pCustomSortLibrary->pLCMapStringEx;
3397         }
3398         else
3399 #endif //FEATURE_CORECLR
3400         {
3401             ret = NewApis::LCMapStringEx(localeName, LCMAP_SORTHANDLE, NULL, 0, (LPWSTR) &lSortHandle, sizeof(LPARAM), NULL, NULL, 0);
3402             *handleOrigin = (INT_PTR) NewApis::LCMapStringEx;
3403         }
3404
3405         if (ret != 0)
3406         {
3407             pSort = (INT_PTR) lSortHandle;
3408         }
3409     }
3410
3411     return pSort;
3412 }
3413
3414 #ifndef FEATURE_CORECLR
3415 BOOL QCALLTYPE COMNlsInfo::InternalGetNlsVersionEx(INT_PTR handle, INT_PTR handleOrigin, LPCWSTR lpLocaleName, NLSVERSIONINFOEX * lpVersionInformation)
3416 {
3417     CONTRACTL {
3418         QCALL_CHECK;
3419     } CONTRACTL_END;
3420
3421     BOOL ret = FALSE;
3422
3423     BEGIN_QCALL;
3424     
3425     AppDomain* curDomain = GetAppDomain();
3426
3427     if(curDomain->m_bUseOsSorting)
3428     {
3429         if(curDomain->m_pCustomSortLibrary != NULL)
3430         {
3431             ret = (curDomain->m_pCustomSortLibrary->pGetNLSVersionEx)(COMPARE_STRING, lpLocaleName, lpVersionInformation);
3432         }
3433         else
3434         {
3435             ret = GetNLSVersionEx(COMPARE_STRING, lpLocaleName, lpVersionInformation);
3436         }
3437     }
3438     else
3439     {
3440         handle = EnsureValidSortHandle(handle, handleOrigin, lpLocaleName);
3441
3442         NLSVERSIONINFO nlsVersion;
3443         nlsVersion.dwNLSVersionInfoSize = sizeof(NLSVERSIONINFO);
3444
3445         ret = SortVersioning::SortGetNLSVersion((SortVersioning::PSORTHANDLE) handle, COMPARE_STRING, &nlsVersion);
3446
3447         lpVersionInformation->dwNLSVersion = nlsVersion.dwNLSVersion;
3448         lpVersionInformation->dwDefinedVersion = nlsVersion.dwDefinedVersion;
3449         lpVersionInformation->dwEffectiveId = 0;
3450         ZeroMemory(&(lpVersionInformation->guidCustomVersion), sizeof(GUID));                
3451     }
3452     
3453     END_QCALL;
3454  
3455     return ret;
3456 }
3457
3458 DWORD QCALLTYPE COMNlsInfo::InternalGetSortVersion()
3459 {
3460     CONTRACTL {
3461         QCALL_CHECK;
3462     } CONTRACTL_END;
3463
3464     DWORD version = DEFAULT_SORT_VERSION;
3465
3466     BEGIN_QCALL;
3467
3468     AppDomain* curDomain = GetAppDomain();
3469     version = curDomain->m_sortVersion;
3470
3471     END_QCALL;
3472
3473     return version;
3474 }
3475
3476 #endif //FEATURE_CORECLR