From: Tarek Mahmoud Sayed Date: Thu, 2 Mar 2017 02:20:18 +0000 (-0800) Subject: Cleanup the native globalization code (dotnet/coreclr#9887) X-Git-Tag: submit/tizen/20210909.063632~11030^2~7887 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=a5bd335bc7fb932c3b675876cb1a465910f27047;p=platform%2Fupstream%2Fdotnet%2Fruntime.git Cleanup the native globalization code (dotnet/coreclr#9887) * Cleanup the native globalization code After we merged the globalization code used for Windows and Linux, this change is cleaning up the native VM globalization code * Added a comment to the normalization code Commit migrated from https://github.com/dotnet/coreclr/commit/8597adf44830c451281ec42f0dbc2389e3395cfa --- diff --git a/src/coreclr/src/classlibnative/inc/calendardata.h b/src/coreclr/src/classlibnative/inc/calendardata.h deleted file mode 100644 index f30660e..0000000 --- a/src/coreclr/src/classlibnative/inc/calendardata.h +++ /dev/null @@ -1,165 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. -//////////////////////////////////////////////////////////////////////////// -// -// Class: CalendarData -// - -// -// Purpose: This module implements the methods of the CalendarData -// class. These methods are the helper functions for the -// Locale class. -// -// Date: July 4, 2007 -// -//////////////////////////////////////////////////////////////////////////// - -#ifndef _CALENDARDATA_H -#define _CALENDARDATA_H - -// -// Data store for the calendar data. -// -class CalendarData : Object -{ - - // - // WARNING: These properties should stay in-sync with CalendarData.cs - // -private: - // Identity - STRINGREF sNativeName ; // Calendar Name for the locale (fallback for calendar only records) - - // Formats - PTRARRAYREF saShortDates ; // Short Data format, default first - PTRARRAYREF saYearMonths ; // Year/Month Data format, default first - PTRARRAYREF saLongDates ; // Long Data format, default first - STRINGREF sMonthDay ; // Month/Day format - - // Calendar Parts Names - PTRARRAYREF saEraNames ; // Names of Eras - PTRARRAYREF saAbbrevEraNames ; // Abbreviated Era Names - PTRARRAYREF saAbbrevEnglishEraNames ; // Abbreviated Era Names in English - PTRARRAYREF saDayNames ; // Day Names, null to use locale data, starts on Sunday - PTRARRAYREF saAbbrevDayNames ; // Abbrev Day Names, null to use locale data, starts on Sunday - PTRARRAYREF saSuperShortDayNames ; // Super short Day of week names - PTRARRAYREF saMonthNames ; // Month Names (13) - PTRARRAYREF saAbbrevMonthNames ; // Abbrev Month Names (13) - PTRARRAYREF saMonthGenitiveNames ; // Genitive Month Names (13) - PTRARRAYREF saAbbrevMonthGenitiveNames; // Genitive Abbrev Month Names (13) - PTRARRAYREF saLeapYearMonthNames ; // Multiple strings for the month names in a leap year. - - // Year, digits have to be at end to make marshaller happy? - INT32 iTwoDigitYearMax ; // Max 2 digit year (for Y2K bug data entry) - INT32 iCurrentEra ; // our current era # - - // Use overrides? - CLR_BOOL bUseUserOverrides ; // True if we want user overrides. - - // - // Helpers - // - static BOOL CallGetCalendarInfoEx(LPCWSTR localeName, int calendar, int calType, STRINGREF* pOutputStrRef); - static BOOL CallGetCalendarInfoEx(LPCWSTR localeName, int calendar, int calType, INT32* pOutputInt32); - static BOOL GetCalendarDayInfo(LPCWSTR localeName, int calendar, int calType, PTRARRAYREF* pOutputStrings); - static BOOL GetCalendarMonthInfo(LPCWSTR localeName, int calendar, int calType, PTRARRAYREF* pOutputStrings); -// TODO: NLS Arrowhead -Windows 7 If the OS had data this could use it, but Windows doesn't expose data for eras in enough detail -// static BOOL GetCalendarEraInfo(LPCWSTR localeName, int calendar, PTRARRAYREF* pOutputEras); - static BOOL CallEnumCalendarInfo(__in_z LPCWSTR localeName, __in int calendar, __in int calType, - __in int lcType, __inout PTRARRAYREF* pOutputStrings); - - static void CheckSpecialCalendar(INT32* pCalendarInt, StackSString* pLocaleNameStackBuffer); -public: - // - // ecall function for methods in CalendarData - // - static FCDECL1(INT32, nativeGetTwoDigitYearMax, INT32 nValue); - static FCDECL3(FC_BOOL_RET, nativeGetCalendarData, CalendarData* calendarDataUNSAFE, StringObject* pLocaleNameUNSAFE, INT32 calendar); - static FCDECL3(INT32, nativeGetCalendars, StringObject* pLocaleNameUNSAFE, CLR_BOOL bUseOverrides, I4Array* calendars); - static FCDECL3(Object*, nativeEnumTimeFormats, StringObject* pLocaleNameUNSAFE, INT32 dwFlags, CLR_BOOL useUserOverride); -}; - -typedef CalendarData* CALENDARDATAREF; - -#ifndef LOCALE_RETURN_GENITIVE_NAMES -#define LOCALE_RETURN_GENITIVE_NAMES 0x10000000 //Flag to return the Genitive forms of month names -#endif - -#ifndef CAL_RETURN_GENITIVE_NAMES -#define CAL_RETURN_GENITIVE_NAMES LOCALE_RETURN_GENITIVE_NAMES // return genitive forms of month names -#endif - -#ifndef CAL_SERASTRING -#define CAL_SERASTRING 0x00000004 // era name for IYearOffsetRanges, eg A.D. -#endif - -#ifndef CAL_SMONTHDAY -#define CAL_SMONTHDAY 0x00000038 // Month/day pattern (reserve for potential inclusion in a future version) -#define CAL_SABBREVERASTRING 0x00000039 // Abbreviated era string (eg: AD) -#endif - -#define RESERVED_CAL_JULIAN 13 // Julian calendar (data looks like GREGORIAN_US) -#define RESERVED_CAL_JAPANESELUNISOLAR 14 // Japaenese Lunisolar calendar (data looks like CAL_JAPANESE) -#define RESERVED_CAL_CHINESELUNISOLAR 15 // Algorithmic -#define RESERVED_CAL_SAKA 16 // reserved to match Office but not implemented in our code -#define RESERVED_CAL_LUNAR_ETO_CHN 17 // reserved to match Office but not implemented in our code -#define RESERVED_CAL_LUNAR_ETO_KOR 18 // reserved to match Office but not implemented in our code -#define RESERVED_CAL_LUNAR_ETO_ROKUYOU 19 // reserved to match Office but not implemented in our code -#define RESERVED_CAL_KOREANLUNISOLAR 20 // Algorithmic -#define RESERVED_CAL_TAIWANLUNISOLAR 21 // Algorithmic -#define RESERVED_CAL_PERSIAN 22 // Algorithmic - -// These are vista properties -#ifndef CAL_UMALQURA -#define CAL_UMALQURA 23 -#endif - -#ifndef CAL_SSHORTESTDAYNAME1 -#define CAL_SSHORTESTDAYNAME1 0x00000031 -#define CAL_SSHORTESTDAYNAME2 0x00000032 -#define CAL_SSHORTESTDAYNAME3 0x00000033 -#define CAL_SSHORTESTDAYNAME4 0x00000034 -#define CAL_SSHORTESTDAYNAME5 0x00000035 -#define CAL_SSHORTESTDAYNAME6 0x00000036 -#define CAL_SSHORTESTDAYNAME7 0x00000037 -#endif - -#ifndef CAL_ITWODIGITYEARMAX - #define CAL_ITWODIGITYEARMAX 0x00000030 // two digit year max -#endif // CAL_ITWODIGITYEARMAX -#ifndef CAL_RETURN_NUMBER - #define CAL_RETURN_NUMBER 0x20000000 // return number instead of string -#endif // CAL_RETURN_NUMBER - -#ifndef LOCALE_SNAME -#define LOCALE_SNAME 0x0000005c // locale name (ie: en-us) -#define LOCALE_SDURATION 0x0000005d // time duration format -#define LOCALE_SKEYBOARDSTOINSTALL 0x0000005e -#define LOCALE_SSHORTESTDAYNAME1 0x00000060 // Shortest day name for Monday -#define LOCALE_SSHORTESTDAYNAME2 0x00000061 // Shortest day name for Tuesday -#define LOCALE_SSHORTESTDAYNAME3 0x00000062 // Shortest day name for Wednesday -#define LOCALE_SSHORTESTDAYNAME4 0x00000063 // Shortest day name for Thursday -#define LOCALE_SSHORTESTDAYNAME5 0x00000064 // Shortest day name for Friday -#define LOCALE_SSHORTESTDAYNAME6 0x00000065 // Shortest day name for Saturday -#define LOCALE_SSHORTESTDAYNAME7 0x00000066 // Shortest day name for Sunday -#define LOCALE_SISO639LANGNAME2 0x00000067 // 3 character ISO abbreviated language name -#define LOCALE_SISO3166CTRYNAME2 0x00000068 // 3 character ISO country name -#define LOCALE_SNAN 0x00000069 // Not a Number -#define LOCALE_SPOSINFINITY 0x0000006a // + Infinity -#define LOCALE_SNEGINFINITY 0x0000006b // - Infinity -#define LOCALE_SSCRIPTS 0x0000006c // Typical scripts in the locale -#define LOCALE_SPARENT 0x0000006d // Fallback name for resources -#define LOCALE_SCONSOLEFALLBACKNAME 0x0000006e // Fallback name for within the console -#define LOCALE_SLANGDISPLAYNAME 0x0000006f // Lanugage Display Name for a language -#endif // LOCALE_SNAME -#ifndef LOCALE_SSHORTTIME -#define LOCALE_SSHORTTIME 0x00000079 // short time format (ie: no seconds, just h:mm) -#endif // LOCALE_SSHORTTIME - -#ifndef TIME_NOSECONDS -#define TIME_NOSECONDS 0x00000002 // do not use seconds -#endif - -#endif - diff --git a/src/coreclr/src/classlibnative/inc/nlsinfo.h b/src/coreclr/src/classlibnative/inc/nlsinfo.h index 3373f1d..6c32fed 100644 --- a/src/coreclr/src/classlibnative/inc/nlsinfo.h +++ b/src/coreclr/src/classlibnative/inc/nlsinfo.h @@ -18,10 +18,6 @@ #ifndef _NLSINFO_H_ #define _NLSINFO_H_ -#define DEFAULT_SORT_VERSION 0 -#define SORT_VERSION_WHIDBEY 0x00001000 -#define SORT_VERSION_V4 0x00060101 - // //This structure must map 1-for-1 with the InternalDataItem structure in //System.Globalization.EncodingTable. @@ -53,56 +49,23 @@ typedef int (*PFN_NORMALIZATION_NORMALIZE_STRING) typedef BYTE* (*PFN_NORMALIZATION_INIT_NORMALIZATION) ( int NormForm, BYTE* pTableData); -//////////////////////////////////////////////////////////////////////////// -// -// Forward declarations -// -//////////////////////////////////////////////////////////////////////////// - -class CharTypeTable; -class CasingTable; -class SortingTable; -class NativeTextInfo; - -class COMNlsInfo { - +class COMNlsInfo +{ public: - static INT32 GetCHTLanguage(); - static INT32 CallGetSystemDefaultUILanguage(); static INT32 CallGetUserDefaultUILanguage(); - static LANGID GetDownLevelSystemDefaultUILanguage(); - - // - // Native helper functions for methods in CultureInfo. - // - static BOOL QCALLTYPE InternalGetDefaultLocaleName(INT32 langType, QCall::StringHandleOnStack defaultLocaleName); - static BOOL QCALLTYPE InternalGetUserDefaultUILanguage(QCall::StringHandleOnStack userDefaultUiLanguage); - static BOOL QCALLTYPE InternalGetSystemDefaultUILanguage(QCall::StringHandleOnStack systemDefaultUiLanguage); // // Native helper functions for methods in DateTimeFormatInfo // static FCDECL1(FC_BOOL_RET, nativeSetThreadLocale, StringObject* localeNameUNSAFE); - static FCDECL2(Object*, nativeGetLocaleInfoEx, StringObject* localeNameUNSAFE, INT32 lcType); - static FCDECL2(INT32, nativeGetLocaleInfoExInt, StringObject* localeNameUNSAFE, INT32 lcType); // // Native helper functions for CultureData // - static FCDECL3(FC_BOOL_RET, nativeGetNumberFormatInfoValues, StringObject* localeNameUNSAFE, NumberFormatInfo* nfi, CLR_BOOL useUserOverride); - static FCDECL1(Object*, LCIDToLocaleName, LCID lcid); - static FCDECL1(INT32, LocaleNameToLCID, StringObject* localeNameUNSAFE); - static INT32 QCALLTYPE InternalCompareString (INT_PTR handle, INT_PTR handleOrigin, LPCWSTR localeName, LPCWSTR string1, INT32 offset1, INT32 length1, LPCWSTR string2, INT32 offset2, INT32 length2, INT32 flags); static INT32 QCALLTYPE InternalGetGlobalizedHashCode(INT_PTR handle, LPCWSTR localeName, LPCWSTR pString, INT32 length, INT32 dwFlagsIn, INT64 additionalEntropy); - static BOOL QCALLTYPE InternalIsSortable(INT_PTR handle, INT_PTR handleOrigin, LPCWSTR localeName, LPCWSTR pString, INT32 length); - static INT_PTR QCALLTYPE InternalInitSortHandle(LPCWSTR localeName, INT_PTR* handleOrigin); - static INT_PTR InitSortHandleHelper(LPCWSTR localeName, INT_PTR* handleOrigin); - static INT_PTR InternalInitOsSortHandle(LPCWSTR localeName, INT_PTR* handleOrigin); - static BOOL QCALLTYPE InternalGetNlsVersionEx(INT_PTR handle, INT_PTR handleOrigin, LPCWSTR lpLocaleName, NLSVERSIONINFOEX * lpVersionInformation); - // // Native helper function for methods in EncodingTable // @@ -111,30 +74,12 @@ public: static FCDECL0(CodePageDataItem *, nativeGetCodePageTableDataPointer); // - // Native helper function for methods in CharacterInfo - // - static FCDECL0(void, AllocateCharTypeTable); - - // - // Native helper function for methods in TextInfo - // - static FCDECL5(FC_CHAR_RET, InternalChangeCaseChar, INT_PTR handle, INT_PTR handleOrigin, StringObject* localeNameUNSAFE, CLR_CHAR wch, CLR_BOOL bIsToUpper); - static FCDECL5(Object*, InternalChangeCaseString, INT_PTR handle, INT_PTR handleOrigin, StringObject* localeNameUNSAFE, StringObject* pString, CLR_BOOL bIsToUpper); - static FCDECL6(INT32, InternalGetCaseInsHash, INT_PTR handle, INT_PTR handleOrigin, StringObject* localeNameUNSAFE, LPVOID strA, CLR_BOOL bForceRandomizedHashing, INT64 additionalEntropy); - static INT32 QCALLTYPE InternalCompareStringOrdinalIgnoreCase(LPCWSTR string1, INT32 index1, LPCWSTR string2, INT32 index2, INT32 length1, INT32 length2); - - static BOOL QCALLTYPE InternalTryFindStringOrdinalIgnoreCase( - __in DWORD dwFindNLSStringFlags, // mutually exclusive flags: FIND_FROMSTART, FIND_STARTSWITH, FIND_FROMEND, FIND_ENDSWITH - __in_ecount(cchSource) LPCWSTR lpStringSource, // the string we search in - __in int cchSource, // number of characters lpStringSource after sourceIndex - __in int sourceIndex, // index from where the search will start in lpStringSource - __in_ecount(cchValue) LPCWSTR lpStringValue, // the string we search for - __in int cchValue, - __out int* foundIndex); // the index in lpStringSource where we found lpStringValue - - // // Native helper function for methods in Normalization // + // On Windows 7 we use the normalization data embedded inside the corelib to get better results. + // On Windows 8 and up we use the OS for normalization. + // That is why we need to keep these fcalls and not doing it through pinvokes. + static FCDECL6(int, nativeNormalizationNormalizeString, int NormForm, int& iError, StringObject* inString, int inLength, @@ -145,53 +90,7 @@ public: static void QCALLTYPE nativeNormalizationInitNormalization(int NormForm, BYTE* pTableData); - // - // QCalls prototype - // - - static int QCALLTYPE nativeEnumCultureNames(INT32 cultureTypes, QCall::ObjectHandleOnStack retStringArray); - - static int QCALLTYPE InternalFindNLSStringEx( - __in_opt INT_PTR handle, // optional sort handle - __in_opt INT_PTR handleOrigin, // optional pointer to the native function that created the sort handle - __in_z LPCWSTR lpLocaleName, // locale name - __in int dwFindNLSStringFlags, // search falg - __in_ecount(cchSource) LPCWSTR lpStringSource, // the string we search in - __in int cchSource, // number of characters lpStringSource after sourceIndex - __in int sourceIndex, // index from where the search will start in lpStringSource - __in_ecount(cchValue) LPCWSTR lpStringValue, // the string we search for - __in int cchValue, // length of the string we search for - __out_opt LPINT pcchFound); // length of the string we found in source - - static int QCALLTYPE InternalGetSortKey( - __in_opt INT_PTR handle, // PSORTHANDLE - __in_opt INT_PTR handleOrigin, // optional pointer to the native function that created the sort handle - __in_z LPCWSTR pLocaleName, // locale name - __in int flags, // flags - __in_ecount(cchSource) LPCWSTR pStringSource, // Source string - __in int cchSource, // number of characters in lpStringSource - __in_ecount(cchTarget) PBYTE pTarget, // Target data buffer (may be null to count) - __in int cchTarget); // Character count for target buffer - - private: - - // - // Internal helper functions. - // - static LPVOID internalEnumSystemLocales(DWORD dwFlags); - static INT32 CompareOrdinal(__in_ecount(Length1) WCHAR* strAChars, int Length1, __in_ecount(Length2) WCHAR* strBChars, int Length2 ); - static INT32 FastIndexOfString(__in WCHAR *sourceString, INT32 startIndex, INT32 endIndex, __in_ecount(patternLength) WCHAR *pattern, INT32 patternLength); - static INT32 FastIndexOfStringInsensitive(__in WCHAR *sourceString, INT32 startIndex, INT32 endIndex, __in_ecount(patternLength) WCHAR *pattern, INT32 patternLength); - static INT32 FastLastIndexOfString(__in WCHAR *sourceString, INT32 startIndex, INT32 endIndex, __in_ecount(patternLength) WCHAR *pattern, INT32 patternLength); - static INT32 FastLastIndexOfStringInsensitive(__in WCHAR *sourceString, INT32 startIndex, INT32 endIndex, __in_ecount(patternLength) WCHAR *pattern, INT32 patternLength); - - static BOOL GetNativeDigitsFromWin32(LPCWSTR locale, PTRARRAYREF* pOutputStrAry, BOOL useUserOverride); - static BOOL CallGetLocaleInfoEx(LPCWSTR locale, int lcType, STRINGREF* pOutputStrRef, BOOL useUserOverride); - static BOOL CallGetLocaleInfoEx(LPCWSTR locale, int lcType, INT32* pOutputInt32, BOOL useUserOverride); - - static BOOL IsWindows7(); - // // Definitions. // diff --git a/src/coreclr/src/classlibnative/nls/CMakeLists.txt b/src/coreclr/src/classlibnative/nls/CMakeLists.txt index 546566e..d6451b9 100644 --- a/src/coreclr/src/classlibnative/nls/CMakeLists.txt +++ b/src/coreclr/src/classlibnative/nls/CMakeLists.txt @@ -1,5 +1,4 @@ set( COMMLS_WKS_SOURCES - calendardata.cpp encodingdata.cpp nlsinfo.cpp ) diff --git a/src/coreclr/src/classlibnative/nls/calendardata.cpp b/src/coreclr/src/classlibnative/nls/calendardata.cpp deleted file mode 100644 index 95d071c..0000000 --- a/src/coreclr/src/classlibnative/nls/calendardata.cpp +++ /dev/null @@ -1,985 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. -//////////////////////////////////////////////////////////////////////////// -// -// Class: CalendarData -// - -// -// Purpose: This module implements the methods of the CalendarData -// class. These methods are the helper functions for the -// Locale class. -// -// Date: July 4, 2007 -// -//////////////////////////////////////////////////////////////////////////// - -#include "common.h" -#include "object.h" -#include "excep.h" -#include "vars.hpp" -#include "interoputil.h" -#include "corhost.h" - -#include - -#include "calendardata.h" -#include "nlsinfo.h" -#include "newapis.h" - -//////////////////////////////////////////////////////////////////////// -// -// Call the Win32 GetCalendarInfoEx() using the specified calendar and LCTYPE. -// The return value can be INT32 or an allocated managed string object, depending on -// which version's called. -// -// Parameters: -// OUT pOutputInt32 The output int32 value. -// OUT pOutputRef The output string value. -// -//////////////////////////////////////////////////////////////////////// -BOOL CalendarData::CallGetCalendarInfoEx(LPCWSTR localeName, int calendar, int calType, INT32* pOutputInt32) -{ - CONTRACTL - { - GC_NOTRIGGER; - MODE_ANY; - SO_TOLERANT; - } CONTRACTL_END; - - int result = 0; - - BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), return result; ) - - // Just stick it right into the output int - _ASSERT((calType & CAL_RETURN_NUMBER) != 0); - result = NewApis::GetCalendarInfoEx(localeName, calendar, NULL, calType, NULL, 0, (LPDWORD)pOutputInt32); - - END_SO_INTOLERANT_CODE - - return (result != 0); -} - -BOOL CalendarData::CallGetCalendarInfoEx(LPCWSTR localeName, int calendar, int calType, STRINGREF* pOutputStrRef) -{ - CONTRACTL - { - THROWS; // We can throw since we are allocating managed string. - DISABLED(GC_TRIGGERS); // Disabled 'cause it don't work right now - MODE_ANY; - SO_TOLERANT; - } CONTRACTL_END; - - // The maximum size for values returned from GetLocaleInfo is 80 characters. - WCHAR buffer[80]; - int result = 0; - - BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), return result; ) - - _ASSERT((calType & CAL_RETURN_NUMBER) == 0); - result = NewApis::GetCalendarInfoEx(localeName, calendar, NULL, calType, buffer, 80, NULL); - - if (result != 0) - { - _ASSERTE(pOutputStrRef != NULL); - *pOutputStrRef = StringObject::NewString(buffer, result - 1); - } - - END_SO_INTOLERANT_CODE - - return (result != 0); -} - -//////////////////////////////////////////////////////////////////////// -// -// Get the native day names -// -// NOTE: There's a disparity between .Net & windows day orders, the input day should -// start with Sunday -// -// Parameters: -// OUT pOutputStrings The output string[] value. -// -//////////////////////////////////////////////////////////////////////// -BOOL CalendarData::GetCalendarDayInfo(LPCWSTR localeName, int calendar, int calType, PTRARRAYREF* pOutputStrings) -{ - CONTRACTL - { - THROWS; // We can throw since we are allocating managed string. - INJECT_FAULT(COMPlusThrowOM()); - GC_TRIGGERS; - MODE_COOPERATIVE; - SO_TOLERANT; - } CONTRACTL_END; - - // The maximum size for values returned from GetLocaleInfo is 80 characters. - WCHAR buffer[80]; - int result = 0; - - _ASSERT((calType & CAL_RETURN_NUMBER) == 0); - - BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), return result;) - - // - // We'll need a new array of 7 items - // - // Allocate the array of STRINGREFs. We don't need to check for null because the GC will throw - // an OutOfMemoryException if there's not enough memory. - // - PTRARRAYREF ResultArray = (PTRARRAYREF)AllocateObjectArray(7, g_pStringClass); - - GCPROTECT_BEGIN(ResultArray); - - // Get each one of them - for (int i = 0; i < 7; i++, calType++) - { - result = NewApis::GetCalendarInfoEx(localeName, calendar, NULL, calType, buffer, 80, NULL); - - // Note that the returned string is null terminated, so even an empty string will be 1 - if (result != 0) - { - // Make a string for this entry - STRINGREF stringResult = StringObject::NewString(buffer, result - 1); - ResultArray->SetAt(i, (OBJECTREF)stringResult); - } - - // On the first iteration we need to go from CAL_SDAYNAME7 to CAL_SDAYNAME1, so subtract 7 before the ++ happens - // This is because the framework starts on sunday and windows starts on monday when counting days - if (i == 0) calType -= 7; - } - GCPROTECT_END(); - - _ASSERTE(pOutputStrings != NULL); - *pOutputStrings = ResultArray; - - END_SO_INTOLERANT_CODE - - return (result != 0); -} - - - -//////////////////////////////////////////////////////////////////////// -// -// Get the native month names -// -// Parameters: -// OUT pOutputStrings The output string[] value. -// -//////////////////////////////////////////////////////////////////////// -BOOL CalendarData::GetCalendarMonthInfo(LPCWSTR localeName, int calendar, int calType, PTRARRAYREF* pOutputStrings) -{ - CONTRACTL - { - THROWS; // We can throw since we are allocating managed string. - INJECT_FAULT(COMPlusThrowOM()); - GC_TRIGGERS; - MODE_COOPERATIVE; - SO_TOLERANT; - } CONTRACTL_END; - - // The maximum size for values returned from GetLocaleInfo is 80 characters. - WCHAR buffer[80]; - int result = 0; - - _ASSERT((calType & CAL_RETURN_NUMBER) == 0); - - BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), return result;) - - // - // We'll need a new array of 13 items - // - // Allocate the array of STRINGREFs. We don't need to check for null because the GC will throw - // an OutOfMemoryException if there's not enough memory. - // - PTRARRAYREF ResultArray = (PTRARRAYREF)AllocateObjectArray(13, g_pStringClass); - - GCPROTECT_BEGIN(ResultArray); - - // Get each one of them - for (int i = 0; i < 13; i++, calType++) - { - result = NewApis::GetCalendarInfoEx(localeName, calendar, NULL, calType, buffer, 80, NULL); - - // If we still have failure, then mark as empty string - if (result == 0) - { - buffer[0] = W('0'); - result = 1; - - - } - - // Note that the returned string is null terminated, so even an empty string will be 1 - // Make a string for this entry - STRINGREF stringResult = StringObject::NewString(buffer, result - 1); - ResultArray->SetAt(i, (OBJECTREF)stringResult); - } - GCPROTECT_END(); - - _ASSERTE(pOutputStrings != NULL); - *pOutputStrings = ResultArray; - - END_SO_INTOLERANT_CODE - - return (result != 0); -} - -// -// struct to help our calendar data enumaration callback -// -struct enumData -{ - int count; // # of strings found so far - LPWSTR userOverride; // pointer to user override string if used - LPWSTR stringsBuffer; // pointer to a buffer to use for the strings. - LPWSTR endOfBuffer; // pointer to the end of the stringsBuffer ( must be < this to write) -}; - -// -// callback itself -// -BOOL CALLBACK EnumCalendarInfoCallback(__in_z LPWSTR lpCalendarInfoString, __in CALID Calendar, __in_opt LPWSTR pReserved, __in LPARAM lParam) -{ - CONTRACTL - { - GC_NOTRIGGER; - MODE_COOPERATIVE; - SO_TOLERANT; - PRECONDITION(CheckPointer(lpCalendarInfoString)); - PRECONDITION(CheckPointer((LPVOID)lParam)); - } CONTRACTL_END; - - // Cast our data to the right type - enumData* pData = (enumData*)lParam; - - // If we had a user override, check to make sure this differs - if (pData->userOverride == NULL || - wcscmp(pData->userOverride, lpCalendarInfoString) != 0) - { - // They're different, add it to our buffer - LPWSTR pStart = pData->stringsBuffer; - LPCWSTR pEnd = pData->endOfBuffer; - while (pStart < pEnd && *lpCalendarInfoString != 0) - { - *(pStart++) = *(lpCalendarInfoString++); - } - - // Add a \0 - if (pStart < pEnd) - { - *(pStart++) = 0; - - // Did it finish? - if (pStart <= pEnd) - { - // It finished, use it - pData->count++; - pData->stringsBuffer = pStart; - } - } - } - - return TRUE; -} - -// -// CallEnumCalendarInfo -// -// Get the list of whichever calendar property from the OS. If user override is passed in, then check GetLocaleInfo as well -// to see if a user override is set. -// -// We build a list of strings, first calling getlocaleinfo if necessary, and then the enums. The strings are null terminated, -// with a double null ending the list. Once we have the list we can allocate our COMStrings and arrays from the count. -// -// We need a helper structure to pass as an lParam -// -BOOL CalendarData::CallEnumCalendarInfo(__in_z LPCWSTR localeName, __in int calendar, __in int calType, - __in int lcType, __inout PTRARRAYREF* pOutputStrings) -{ - CONTRACTL - { - THROWS; // We can throw since we are allocating managed string. - INJECT_FAULT(COMPlusThrowOM()); - DISABLED(GC_TRIGGERS); // Disabled 'cause it don't work right now - MODE_COOPERATIVE; - SO_TOLERANT; - } CONTRACTL_END; - - BOOL result = TRUE; - - // Our longest string in culture.xml is shorter than this and it has lots of \x type characters, so this should be long enough by far. - WCHAR stringBuffer[512]; - - struct enumData data; - data.count = 0; - data.userOverride = NULL; - data.stringsBuffer = stringBuffer; - data.endOfBuffer = stringBuffer + 512; // We're adding WCHAR sizes - - // First call GetLocaleInfo if necessary - if ((lcType && ((lcType & LOCALE_NOUSEROVERRIDE) == 0)) && - // Get user locale, see if it matches localeName. - // Note that they should match exactly, including letter case - NewApis::GetUserDefaultLocaleName(stringBuffer, 512) && wcscmp(localeName, stringBuffer) == 0) - { - // They want user overrides, see if the user calendar matches the input calendar - CALID userCalendar = 0; - NewApis::GetLocaleInfoEx(localeName, LOCALE_ICALENDARTYPE | LOCALE_RETURN_NUMBER, (LPWSTR)&userCalendar, - sizeof(userCalendar) / sizeof(WCHAR) ); - - // If the calendars were the same, see if the locales were the same - if ((int)userCalendar == calendar) // todo: cast to compile on MAC - { - // They matched, get the user override since locale & calendar match - int i = NewApis::GetLocaleInfoEx(localeName, lcType, stringBuffer, 512); - - // if it succeeded, advance the pointer and remember the override for the later callers - if (i > 0) - { - // Remember this was the override (so we can look for duplicates later in the enum function) - data.userOverride = data.stringsBuffer; - - // Advance to the next free spot (i includes counting the \0) - data.stringsBuffer += i; - - // And our count... - data.count++; - } - } - } - - // Now call the enumeration API. Work is done by our callback function - NewApis::EnumCalendarInfoExEx(EnumCalendarInfoCallback, localeName, calendar, calType, (LPARAM)&data); - - // Now we have a list of data, fail if we didn't find anything. - if (data.count == 0) return FALSE; - - // Now we need to allocate our stringarray and populate it - STATIC_CONTRACT_SO_TOLERANT; - - BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), return (result); ) - - // Get our array object (will throw, don't have to check it) - PTRARRAYREF dataArray = (PTRARRAYREF) AllocateObjectArray(data.count, g_pStringClass); - - GCPROTECT_BEGIN(dataArray); - LPCWSTR buffer = stringBuffer; // Restart @ buffer beginning - for(DWORD i = 0; i < (DWORD)data.count; i++) - { - OBJECTREF o = (OBJECTREF) StringObject::NewString(buffer); - - if (calType == CAL_SABBREVERASTRING || calType == CAL_SERASTRING) - { - // Eras are enumerated backwards. (oldest era name first, but - // Japanese calendar has newest era first in array, and is only - // calendar with multiple eras) - dataArray->SetAt((DWORD)data.count - i - 1, o); - } - else - { - dataArray->SetAt(i, o); - } - - buffer += (lstrlenW(buffer) + 1); - } - GCPROTECT_END(); - - _ASSERTE(pOutputStrings != NULL); - *pOutputStrings = dataArray; - - END_SO_INTOLERANT_CODE - - return result; -} - -//////////////////////////////////////////////////////////////////////// -// -// For calendars like Gregorain US/Taiwan/UmAlQura, they are not available -// in all OS or all localized versions of OS. -// If OS does not support these calendars, we will fallback by using the -// appropriate fallback calendar and locale combination to retrieve data from OS. -// -// Parameters: -// __deref_inout pCalendarInt: -// Pointer to the calendar ID. This will be updated to new fallback calendar ID if needed. -// __in_out pLocaleNameStackBuffer -// Pointer to the StackSString object which holds the locale name to be checked. -// This will be updated to new fallback locale name if needed. -// -//////////////////////////////////////////////////////////////////////// - -void CalendarData::CheckSpecialCalendar(INT32* pCalendarInt, StackSString* pLocaleNameStackBuffer) -{ - // Gregorian-US isn't always available in the OS, however it is the same for all locales - switch (*pCalendarInt) - { - case CAL_GREGORIAN_US: - // See if this works - if (0 == NewApis::GetCalendarInfoEx(*pLocaleNameStackBuffer, *pCalendarInt, NULL, CAL_SCALNAME, NULL, 0, NULL)) - { - // Failed, set it to a locale (fa-IR) that's alway has Gregorian US available in the OS - pLocaleNameStackBuffer->Set(W("fa-IR"), 5); - } - // See if that works - if (0 == NewApis::GetCalendarInfoEx(*pLocaleNameStackBuffer, *pCalendarInt, NULL, CAL_SCALNAME, NULL, 0, NULL)) - { - // Failed again, just use en-US with the gregorian calendar - pLocaleNameStackBuffer->Set(W("en-US"), 5); - *pCalendarInt = CAL_GREGORIAN; - } - break; - case CAL_TAIWAN: - // Taiwan calendar data is not always in all language version of OS due to Geopolical reasons. - // It is only available in zh-TW localized versions of Windows. - // Let's check if OS supports it. If not, fallback to Greogrian localized for Taiwan calendar. - if (0 == NewApis::GetCalendarInfoEx(*pLocaleNameStackBuffer, *pCalendarInt, NULL, CAL_SCALNAME, NULL, 0, NULL)) - { - *pCalendarInt = CAL_GREGORIAN; - } - break; - case CAL_UMALQURA: - // UmAlQura is only available in Vista and above, so we will need to fallback to Hijri if it is not available in the OS. - if (0 == NewApis::GetCalendarInfoEx(*pLocaleNameStackBuffer, *pCalendarInt, NULL, CAL_SCALNAME, NULL, 0, NULL)) - { - // There are no differences in DATA between UmAlQura and Hijri, and - // UmAlQura isn't available before Vista, so just use Hijri.. - *pCalendarInt = CAL_HIJRI; - } - break; - } -} - -//////////////////////////////////////////////////////////////////////// -// -// Implementation for CalendarInfo.nativeGetCalendarData -// -// Retrieve calendar properties from the native side -// -// Parameters: -// pCalendarData: This is passed from a managed structure CalendarData.cs -// pLocaleNameUNSAFE: Locale name associated with the locale for this calendar -// calendar: Calendar ID -// -// NOTE: Calendars depend on the locale name that creates it. Only a few -// properties are available without locales using CalendarData.GetCalendar(int) -// -//////////////////////////////////////////////////////////////////////// - -FCIMPL3(FC_BOOL_RET, CalendarData::nativeGetCalendarData, CalendarData* calendarDataUNSAFE, StringObject* pLocaleNameUNSAFE, INT32 calendar) -{ - CONTRACTL - { - FCALL_CHECK; - PRECONDITION(CheckPointer(pLocaleNameUNSAFE)); - } CONTRACTL_END; - - - // The maximum allowed string length in GetLocaleInfo is 80 WCHARs. - BOOL ret = TRUE; - - struct _gc - { - STRINGREF localeName; - CALENDARDATAREF calendarData; - } gc; - - // Dereference our gc objects - gc.localeName = (STRINGREF)pLocaleNameUNSAFE; - gc.calendarData = (CALENDARDATAREF)calendarDataUNSAFE; - - // Need to set up the frame since we will be allocating managed strings. - HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc); - - // create a local copy of the string in order to pass it to helper methods that trigger GCs like GetCalendarDayInfo and GetCalendarMonthInfo - StackSString localeNameStackBuffer( gc.localeName->GetBuffer() ); - - // Conveniently this is the same as LOCALE_NOUSEROVERRIDE, so we can use this for both - int useOverrides = (gc.calendarData->bUseUserOverrides) ? 0 : CAL_NOUSEROVERRIDE; - - // Helper string - STRINGREF stringResult = NULL; - - // - // Windows doesn't support some calendars right now, so remap those. - // - if (calendar >= 13 && calendar < 23) - { - switch (calendar) - { - case RESERVED_CAL_PERSIAN: // don't change if we have Persian calendar - break; - - case RESERVED_CAL_JAPANESELUNISOLAR: // Data looks like Japanese - calendar=CAL_JAPAN; - break; - case RESERVED_CAL_JULIAN: // Data looks like gregorian US - case RESERVED_CAL_CHINESELUNISOLAR: // Algorithmic, so actual data is irrelevent - case RESERVED_CAL_SAKA: // reserved to match Office but not implemented in our code, so data is irrelevent - case RESERVED_CAL_LUNAR_ETO_CHN: // reserved to match Office but not implemented in our code, so data is irrelevent - case RESERVED_CAL_LUNAR_ETO_KOR: // reserved to match Office but not implemented in our code, so data is irrelevent - case RESERVED_CAL_LUNAR_ETO_ROKUYOU: // reserved to match Office but not implemented in our code, so data is irrelevent - case RESERVED_CAL_KOREANLUNISOLAR: // Algorithmic, so actual data is irrelevent - case RESERVED_CAL_TAIWANLUNISOLAR: // Algorithmic, so actual data is irrelevent - default: - calendar = CAL_GREGORIAN_US; - break; - } - } - - // - // Speical handling for some special calendar due to OS limitation. - // This includes calendar like Taiwan calendar, UmAlQura calendar, etc. - // - CheckSpecialCalendar(&calendar, &localeNameStackBuffer); - - - // Numbers - ret &= CallGetCalendarInfoEx(localeNameStackBuffer, calendar, - CAL_ITWODIGITYEARMAX | CAL_RETURN_NUMBER | useOverrides, - &(gc.calendarData->iTwoDigitYearMax)); - - if (ret == FALSE) // failed call - { - _ASSERTE(!"nativeGetCalendarData could not read CAL_ITWODIGITYEARMAX"); - } - - _ASSERTE(ret == TRUE); - - // Strings - if (CallGetCalendarInfoEx(localeNameStackBuffer, calendar, CAL_SCALNAME , &stringResult)) - SetObjectReference((OBJECTREF*)&(gc.calendarData->sNativeName), stringResult, NULL); - else - { - _ASSERTE(!"nativeGetCalendarData could not read CAL_SCALNAME"); - ret = FALSE; - } - if (CallGetCalendarInfoEx(localeNameStackBuffer, calendar, CAL_SMONTHDAY | useOverrides, &stringResult)) - SetObjectReference((OBJECTREF*)&(gc.calendarData->sMonthDay), stringResult, NULL); - else - { - _ASSERTE(!"nativeGetCalendarData could not read RESERVED_CAL_SMONTHDAY"); - ret = FALSE; - } - - // String Arrays - // Formats - PTRARRAYREF array = NULL; - if (CallEnumCalendarInfo(localeNameStackBuffer, calendar, CAL_SSHORTDATE, LOCALE_SSHORTDATE | useOverrides, &array)) - SetObjectReference((OBJECTREF*)&(gc.calendarData->saShortDates), array, NULL); - else - { - _ASSERTE(!"nativeGetCalendarData could not read CAL_SSHORTDATE"); - ret = FALSE; - } - if (CallEnumCalendarInfo(localeNameStackBuffer, calendar, CAL_SLONGDATE, LOCALE_SLONGDATE | useOverrides, &array)) - SetObjectReference((OBJECTREF*)&(gc.calendarData->saLongDates), array, NULL); - else - { - _ASSERTE(!"nativeGetCalendarData could not read CAL_SLONGDATE"); - ret = FALSE; - } - - // Get the YearMonth pattern. - // Before Windows Vista, NLS would not write the Year/Month pattern into the reg key. - // This causes GetLocaleInfo() to retrieve the Gregorian localized calendar pattern even when the calendar is not Gregorian localized. - // So we will call GetLocaleInfo() only when the reg key for sYearMonth is there. - // - // If the key does not exist, leave yearMonthPattern to be null, so that we will pick up the default table value. - - int useOverridesForYearMonthPattern = useOverrides; - if (useOverridesForYearMonthPattern == 0) - { - HKEY hkey = NULL; - useOverridesForYearMonthPattern = CAL_NOUSEROVERRIDE; - if (WszRegOpenKeyEx(HKEY_CURRENT_USER, W("Control Panel\\International"), 0, KEY_READ, &hkey) == ERROR_SUCCESS) - { - if (WszRegQueryValueEx(hkey, W("sYearMonth"), 0, NULL, NULL, NULL) == ERROR_SUCCESS) - { - // The sYearMonth key exists. Call GetLocaleInfo() to read it. - useOverridesForYearMonthPattern = 0; // now we can use the overrides - } - RegCloseKey(hkey); - } - } - - if (CallEnumCalendarInfo(localeNameStackBuffer, calendar, CAL_SYEARMONTH, LOCALE_SYEARMONTH | useOverridesForYearMonthPattern, &array)) - SetObjectReference((OBJECTREF*)&(gc.calendarData->saYearMonths), array, NULL); - else - { - _ASSERTE(!"nativeGetCalendarData could not read CAL_SYEARMONTH"); - ret = FALSE; - } - - // Day & Month Names - // These are all single calType entries, 1 per day, so we have to make 7 or 13 calls to collect all the names - - // Day - // Note that we're off-by-one since managed starts on sunday and windows starts on monday - if (GetCalendarDayInfo(localeNameStackBuffer, calendar, CAL_SDAYNAME7, &array)) - SetObjectReference((OBJECTREF*)&(gc.calendarData->saDayNames), array, NULL); - else - { - _ASSERTE(!"nativeGetCalendarData could not read CAL_SDAYNAME7"); - ret = FALSE; - } - if (GetCalendarDayInfo(localeNameStackBuffer, calendar, CAL_SABBREVDAYNAME7, &array)) - SetObjectReference((OBJECTREF*)&(gc.calendarData->saAbbrevDayNames), array, NULL); - else - { - _ASSERTE(!"nativeGetCalendarData could not read CAL_SABBREVDAYNAME7"); - ret = FALSE; - } - - // Month names - if (GetCalendarMonthInfo(localeNameStackBuffer, calendar, CAL_SMONTHNAME1, &array)) - SetObjectReference((OBJECTREF*)&(gc.calendarData->saMonthNames), array, NULL); - else - { - _ASSERTE(!"nativeGetCalendarData could not read CAL_SMONTHNAME1"); - ret = FALSE; - } - - if (GetCalendarMonthInfo(localeNameStackBuffer, calendar, CAL_SABBREVMONTHNAME1, &array)) - SetObjectReference((OBJECTREF*)&(gc.calendarData->saAbbrevMonthNames), array, NULL); - else - { - _ASSERTE(!"nativeGetCalendarData could not read CAL_SABBREVMONTHNAME1"); - ret = FALSE; - } - - // - // The following LCTYPE are not supported in some platforms. If the call fails, - // don't return a failure. - // - if (GetCalendarDayInfo(localeNameStackBuffer, calendar, CAL_SSHORTESTDAYNAME7, &array)) - SetObjectReference((OBJECTREF*)&(gc.calendarData->saSuperShortDayNames), array, NULL); - - - // Gregorian may have genitive month names - if (calendar == CAL_GREGORIAN) - { - if (GetCalendarMonthInfo(localeNameStackBuffer, calendar, CAL_SMONTHNAME1 | CAL_RETURN_GENITIVE_NAMES, &array)) - SetObjectReference((OBJECTREF*)&(gc.calendarData->saMonthGenitiveNames), array, NULL); - // else we ignore the error and let managed side copy the normal month names - if (GetCalendarMonthInfo(localeNameStackBuffer, calendar, CAL_SABBREVMONTHNAME1 | CAL_RETURN_GENITIVE_NAMES, &array)) - SetObjectReference((OBJECTREF*)&(gc.calendarData->saAbbrevMonthGenitiveNames), array, NULL); - // else we ignore the error and let managed side copy the normal month names - } - -// leap year names are only different for month 6 in Hebrew calendar -// PTRARRAYREF saLeapYearMonthNames ; // Multiple strings for the month names in a leap year. (Hebrew's the only one that has these) - - // Calendar Parts Names - if (CallEnumCalendarInfo(localeNameStackBuffer, calendar, CAL_SERASTRING, NULL, &array)) - SetObjectReference((OBJECTREF*)&(gc.calendarData->saEraNames), array, NULL); - // else we set the era in managed code - if (CallEnumCalendarInfo(localeNameStackBuffer, calendar, CAL_SABBREVERASTRING, NULL, &array)) - SetObjectReference((OBJECTREF*)&(gc.calendarData->saAbbrevEraNames), array, NULL); - // else we set the era in managed code - - // PTRARRAYREF saAbbrevEnglishEraNames ; // Abbreviated Era Names in English - - // - // Calendar Era Info - // Note that calendar era data (offsets, etc) is hard coded for each calendar since this - // data is implementation specific and not dynamic (except perhaps Japanese) - // - - HELPER_METHOD_FRAME_END(); - - FC_RETURN_BOOL(ret); -} -FCIMPLEND - -// -// Get the system two digit year max value for the specified calendar -// -FCIMPL1(INT32, CalendarData::nativeGetTwoDigitYearMax, INT32 calendar) -{ - FCALL_CONTRACT; - - DWORD dwTwoDigitYearMax = (DWORD) -1; - - HELPER_METHOD_FRAME_BEGIN_RET_0(); - - WCHAR strName[LOCALE_NAME_MAX_LENGTH]; - - // Really we should just be able to pass NULL for the locale name since that - // causes the OS to look up the user default locale name. The downlevel APIS could - // emulate this as necessary - if (NewApis::GetUserDefaultLocaleName(strName,NumItems(strName)) == 0 || - NewApis::GetCalendarInfoEx(strName, calendar, NULL, CAL_ITWODIGITYEARMAX | CAL_RETURN_NUMBER, NULL, 0,&dwTwoDigitYearMax) == 0) - { - dwTwoDigitYearMax = (DWORD) -1; - goto lExit; - } - -lExit: ; - HELPER_METHOD_FRAME_END(); - - return (dwTwoDigitYearMax); -} -FCIMPLEND - -// -// nativeGetCalendars -// -// Get the list of acceptable calendars for this user/locale -// -// Might be a better way to marshal the int[] for calendars -// We expect the input array to be 23 ints long. We then fill up the first "count" ints and return the count. -// The caller should then make it a smaller array. -// - -// Perhaps we could do something more like this... -//U1ARRAYREF rgbOut = (U1ARRAYREF) AllocatePrimitiveArray(ELEMENT_TYPE_U1, cb); -//memcpyNoGCRefs(rgbOut->GetDirectPointerToNonObjectElements(), rgbKey, cb * sizeof(BYTE)); -// -//refRetVal = rgbOut; -// -//HELPER_METHOD_FRAME_END(); -//return (U1Array*) OBJECTREFToObject(refRetVal); - -// -// struct to help our calendar data enumaration callback -// -struct enumCalendarsData -{ - int count; // # of strings found so far - CALID userOverride; // user override value (if found) - INT32* calendarList; // list of calendars found so far -}; - -// -// callback itself -// -BOOL CALLBACK EnumCalendarsCallback(__in_z LPWSTR lpCalendarInfoString, __in CALID Calendar, __in_opt LPWSTR pReserved, __in LPARAM lParam) -{ - CONTRACTL - { - GC_NOTRIGGER; - MODE_COOPERATIVE; - SO_TOLERANT; - PRECONDITION(CheckPointer(lpCalendarInfoString)); - PRECONDITION(CheckPointer((LPVOID)lParam)); - } CONTRACTL_END; - - // Cast our data to the right type - enumCalendarsData* pData = (enumCalendarsData*)lParam; - - // If we had a user override, check to make sure this differs - if (pData->userOverride == Calendar) - { - // Its the same, just return - return TRUE; - } - - // They're different, add it to our buffer, check we have room - if (pData->count < 23) - { - pData->calendarList[pData->count++] = Calendar; - } - - return TRUE; -} - -FCIMPL3(INT32, CalendarData::nativeGetCalendars, StringObject* pLocaleNameUNSAFE, CLR_BOOL useOverrides, I4Array* calendarsUNSAFE) -{ - CONTRACTL - { - FCALL_CHECK; - PRECONDITION(CheckPointer(pLocaleNameUNSAFE)); - } CONTRACTL_END; - - int ret = 0; - - struct _gc - { - STRINGREF localeName; - I4ARRAYREF calendarsRef; - } gc; - - // Dereference our string - gc.localeName = (STRINGREF)pLocaleNameUNSAFE; - gc.calendarsRef = (I4ARRAYREF)calendarsUNSAFE; - HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc); - - int calendarBuffer[23]; - struct enumCalendarsData data; - data.count = 0; - data.userOverride = 0; - data.calendarList = calendarBuffer; - - // First call GetLocaleInfo if necessary - if (useOverrides) - { - // They want user overrides, see if the user calendar matches the input calendar - - CALID userCalendar = 0; - NewApis::GetLocaleInfoEx( gc.localeName->GetBuffer(), LOCALE_ICALENDARTYPE | LOCALE_RETURN_NUMBER, - (LPWSTR)&userCalendar, sizeof(userCalendar) / sizeof(WCHAR) ); - - // If we got a default, then use it as the first calendar - if (userCalendar != 0) - { - data.userOverride = userCalendar; - data.calendarList[data.count++] = userCalendar; - } - } - - // Now call the enumeration API. Work is done by our callback function - NewApis::EnumCalendarInfoExEx(EnumCalendarsCallback, gc.localeName->GetBuffer(), ENUM_ALL_CALENDARS, CAL_ICALINTVALUE, (LPARAM)&(data)); - - // Copy to the output array - for (int i = 0; i < data.count; i++) - { - (gc.calendarsRef->GetDirectPointerToNonObjectElements())[i] = calendarBuffer[i]; - } - - ret = data.count; - HELPER_METHOD_FRAME_END(); - - // Now we have a list of data, return the count - return ret; -} -FCIMPLEND - -// -// nativeEnumTimeFormats -// -// Enumerate all of the time formats (long times) on the system. -// Windows only has 1 time format so there's nothing like an LCTYPE here. -// -// Note that if the locale is the user default locale windows ALWAYS returns the user override value first. -// (ie: there's no no-user-override option for this API) -// -// We reuse the enumData structure since it works for us. -// - -// -// callback itself -// -BOOL CALLBACK EnumTimeFormatsCallback(__in_z LPCWSTR lpTimeFormatString, __in LPARAM lParam) -{ - CONTRACTL - { - GC_NOTRIGGER; - MODE_COOPERATIVE; - SO_TOLERANT; - PRECONDITION(CheckPointer(lpTimeFormatString)); - PRECONDITION(CheckPointer((LPVOID)lParam)); - } CONTRACTL_END; - - // Cast our data to the right type - enumData* pData = (enumData*)lParam; - - // Don't have to worry about user overrides (the enum adds them) - // add it to our buffer - LPWSTR pStart = pData->stringsBuffer; - LPCWSTR pEnd = pData->endOfBuffer; - while (pStart < pEnd && *lpTimeFormatString != 0) - { - *(pStart++) = *(lpTimeFormatString++); - } - - // Add a \0 - if (pStart < pEnd) - { - *(pStart++) = 0; - - // Did it finish? - if (pStart <= pEnd) - { - // It finished, use it - pData->count++; - pData->stringsBuffer = pStart; - } - } - - return TRUE; -} - -// -// nativeEnumTimeFormats that calls the callback above -// -FCIMPL3(Object*, CalendarData::nativeEnumTimeFormats, - StringObject* pLocaleNameUNSAFE, INT32 dwFlags, CLR_BOOL useUserOverride) -{ - CONTRACTL - { - FCALL_CHECK; - PRECONDITION(CheckPointer(pLocaleNameUNSAFE)); - } CONTRACTL_END; - - - struct _gc - { - STRINGREF localeName; - PTRARRAYREF timeFormatsArray; - } gc; - - // Dereference our gc objects - gc.localeName = (STRINGREF)pLocaleNameUNSAFE; - gc.timeFormatsArray = NULL; - - HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc); - - // Our longest string in culture.xml is shorter than this and it has lots of \x type characters, so this should be long enough by far. - WCHAR stringBuffer[512]; - struct enumData data; - data.count = 0; - data.userOverride = NULL; - data.stringsBuffer = stringBuffer; - data.endOfBuffer = stringBuffer + 512; // We're adding WCHAR sizes - - // Now call the enumeration API. Work is done by our callback function - NewApis::EnumTimeFormatsEx((TIMEFMT_ENUMPROCEX)EnumTimeFormatsCallback, gc.localeName->GetBuffer(), dwFlags, (LPARAM)&data); - - if (data.count > 0) - { - // Now we need to allocate our stringarray and populate it - // Get our array object (will throw, don't have to check it) - gc.timeFormatsArray = (PTRARRAYREF) AllocateObjectArray(data.count, g_pStringClass); - - LPCWSTR buffer = stringBuffer; // Restart @ buffer beginning - for(DWORD i = 0; i < (DWORD)data.count; i++) // todo: cast to compile on Mac - { - OBJECTREF o = (OBJECTREF) StringObject::NewString(buffer); - gc.timeFormatsArray->SetAt(i, o); - - buffer += (lstrlenW(buffer) + 1); - } - - if(!useUserOverride && data.count > 1) - { - // Since there is no "NoUserOverride" aware EnumTimeFormatsEx, we always get an override - // The override is the first entry if it is overriden. - // We can check if we have overrides by checking the GetLocaleInfo with no override - // If we do have an override, we don't know if it is a user defined override or if the - // user has just selected one of the predefined formats so we can't just remove it - // but we can move it down. - WCHAR timeFormatNoUserOverride[LOCALE_NAME_MAX_LENGTH]; - DWORD lcType = (dwFlags == TIME_NOSECONDS) ? LOCALE_SSHORTTIME : LOCALE_STIMEFORMAT; - lcType |= LOCALE_NOUSEROVERRIDE; - int result = NewApis::GetLocaleInfoEx(gc.localeName->GetBuffer(), lcType, timeFormatNoUserOverride, LOCALE_NAME_MAX_LENGTH); - if(result != 0) - { - STRINGREF firstTimeFormat = (STRINGREF)gc.timeFormatsArray->GetAt(0); - if(wcscmp(timeFormatNoUserOverride, firstTimeFormat->GetBuffer())!=0) - { - gc.timeFormatsArray->SetAt(0, gc.timeFormatsArray->GetAt(1)); - gc.timeFormatsArray->SetAt(1, firstTimeFormat); - } - } - } - - } - - HELPER_METHOD_FRAME_END(); - return OBJECTREFToObject(gc.timeFormatsArray); -} -FCIMPLEND - - diff --git a/src/coreclr/src/classlibnative/nls/nlsinfo.cpp b/src/coreclr/src/classlibnative/nls/nlsinfo.cpp index 7afe116..b1d65e3 100644 --- a/src/coreclr/src/classlibnative/nls/nlsinfo.cpp +++ b/src/coreclr/src/classlibnative/nls/nlsinfo.cpp @@ -43,1896 +43,117 @@ // Constant Declarations. // -#ifndef COMPARE_OPTIONS_IGNORECASE -#define COMPARE_OPTIONS_IGNORECASE 0x00000001 -#endif - -#ifndef LOCALE_SNAME -#define LOCALE_SNAME 0x0000005c -#endif - -#ifndef LOCALE_SNAN -#define LOCALE_SNAN 0x00000069 -#endif - -#ifndef LOCALE_SPOSINFINITY -#define LOCALE_SPOSINFINITY 0x0000006a -#endif - -#ifndef LOCALE_SNEGINFINITY -#define LOCALE_SNEGINFINITY 0x0000006b -#endif - -#ifndef LOCALE_SPARENT -#define LOCALE_SPARENT 0x0000006d -#endif - -#ifndef LOCALE_SCONSOLEFALLBACKNAME -#define LOCALE_SCONSOLEFALLBACKNAME 0x0000006e // Fallback name for within the console -#endif - -#ifndef LOCALE_SISO3166CTRYNAME2 -#define LOCALE_SISO3166CTRYNAME2 0x00000068 -#endif - -#ifndef LOCALE_SISO639LANGNAME2 -#define LOCALE_SISO639LANGNAME2 0x00000067 -#endif - -#ifndef LOCALE_SSHORTESTDAYNAME1 -#define LOCALE_SSHORTESTDAYNAME1 0x00000060 -#endif - -// Windows 7 LCTypes -#ifndef LOCALE_INEUTRAL -#define LOCALE_INEUTRAL 0x00000071 // Returns 0 for specific cultures, 1 for neutral cultures. -#endif - -#ifndef LCMAP_TITLECASE -#define LCMAP_TITLECASE 0x00000300 // Title Case Letters -#endif - -// Windows 8 LCTypes -#ifndef LCMAP_SORTHANDLE -#define LCMAP_SORTHANDLE 0x20000000 -#endif - -#ifndef LCMAP_HASH -#define LCMAP_HASH 0x00040000 -#endif - -#ifndef LOCALE_REPLACEMENT -#define LOCALE_REPLACEMENT 0x00000008 // locales that replace shipped locales (callback flag only) -#endif // LOCALE_REPLACEMENT - -#define LOCALE_MAX_STRING_SIZE 530 // maximum sice of LOCALE_SKEYBOARDSTOINSTALL, currently 5 "long" + 2 "short" keyboard signatures (YI + 3). - #define MAX_STRING_VALUE 512 // TODO: NLS Arrowhead -Be nice if we could depend more on the OS for this -// Language ID for CHT (Taiwan) -#define LANGID_ZH_TW 0x0404 -// Language ID for CHT (Hong-Kong) -#define LANGID_ZH_HK 0x0c04 -#define REGION_NAME_0404 W("\x53f0\x7063") -#if BIGENDIAN -#define INTERNATIONAL_CURRENCY_SYMBOL W("\x00a4") -#else -#define INTERNATIONAL_CURRENCY_SYMBOL W("\xa400") -#endif - -inline BOOL IsCustomCultureId(LCID lcid) -{ - return (lcid == LOCALE_CUSTOM_DEFAULT || lcid == LOCALE_CUSTOM_UNSPECIFIED); -} - -#ifndef FEATURE_COREFX_GLOBALIZATION -// -// Normalization Implementation -// -#define NORMALIZATION_DLL MAKEDLLNAME(W("normalization")) -HMODULE COMNlsInfo::m_hNormalization = NULL; -PFN_NORMALIZATION_IS_NORMALIZED_STRING COMNlsInfo::m_pfnNormalizationIsNormalizedStringFunc = NULL; -PFN_NORMALIZATION_NORMALIZE_STRING COMNlsInfo::m_pfnNormalizationNormalizeStringFunc = NULL; -PFN_NORMALIZATION_INIT_NORMALIZATION COMNlsInfo::m_pfnNormalizationInitNormalizationFunc = NULL; -#endif // FEATURE_COREFX_GLOBALIZATION - - -// InternalIsSortable -// -// Called by CompareInfo.IsSortable() to determine if a string has entirely sortable (ie: defined) code points. -BOOL QCALLTYPE COMNlsInfo::InternalIsSortable(INT_PTR handle, INT_PTR handleOrigin, LPCWSTR localeName, LPCWSTR string, INT32 length) -{ - CONTRACTL - { - QCALL_CHECK; - PRECONDITION(CheckPointer(string)); - } CONTRACTL_END; - BOOL result = FALSE; - BEGIN_QCALL; - - { - // Function should be COMPARE_STRING, dwFlags should be NULL, lpVersionInfo should be NULL for now - result = NewApis::IsNLSDefinedString(COMPARE_STRING, 0, NULL, string, length); - } - - END_QCALL; - return result; -} - -//////////////////////////////////////////////////////////////////////////// -// -// InternalGetUserDefaultLocaleName -// -// Returns a string with the name of our LCID and returns 0 in LCID. -// If we cant return -// -//////////////////////////////////////////////////////////////////////////// -// This is new to longhorn -BOOL QCALLTYPE COMNlsInfo::InternalGetDefaultLocaleName(INT32 langType, QCall::StringHandleOnStack defaultLocaleName) -{ - CONTRACTL - { - QCALL_CHECK; - PRECONDITION((langType == LOCALE_SYSTEM_DEFAULT) || (langType == LOCALE_USER_DEFAULT)); - } CONTRACTL_END; - - BOOL result; - BEGIN_QCALL; - - WCHAR strName[LOCALE_NAME_MAX_LENGTH]; - int size = 0; - - if (langType == LOCALE_SYSTEM_DEFAULT) - { - size = NewApis::GetSystemDefaultLocaleName(strName,NumItems(strName)); - } - else - { - _ASSERT(langType == LOCALE_USER_DEFAULT); - size = NewApis::GetUserDefaultLocaleName(strName,NumItems(strName)); - } - - // Not found, either not longhorn (no LOCALE_SNAME) or not a valid name - if (size == 0) - { - result = false; - } - else - { - defaultLocaleName.Set(strName); - result = true; - } - END_QCALL; - return result; -} - -BOOL QCALLTYPE COMNlsInfo::InternalGetSystemDefaultUILanguage(QCall::StringHandleOnStack systemDefaultUiLanguage) -{ - QCALL_CONTRACT; - BOOL result; - BEGIN_QCALL; - - WCHAR localeName[LOCALE_NAME_MAX_LENGTH]; - - int systemDefaultUiLcid = GetSystemDefaultUILanguage(); - if(systemDefaultUiLcid == LANGID_ZH_TW) - { - if (!NewApis::IsZhTwSku()) - { - systemDefaultUiLcid = LANGID_ZH_HK; - } - } - - int length = NewApis::LCIDToLocaleName(systemDefaultUiLcid, localeName, NumItems(localeName), 0); - if (length == 0) - { - result = false; - } - else - { - systemDefaultUiLanguage.Set(localeName); - result = true; - } - - END_QCALL; - return result; -} - -/* - */ -BOOL QCALLTYPE COMNlsInfo::InternalGetUserDefaultUILanguage(QCall::StringHandleOnStack userDefaultUiLanguage) -{ - QCALL_CONTRACT; - BOOL result; - BEGIN_QCALL; - - WCHAR wszBuffer[LOCALE_NAME_MAX_LENGTH]; - LPCWSTR wszLangName=NULL; - - int res= 0; - ULONG uLangCount=0; - ULONG uBufLen=0; - res= NewApis::GetUserPreferredUILanguages (MUI_LANGUAGE_NAME,&uLangCount,NULL,&uBufLen); - if (res == 0) - ThrowLastError(); - - - NewArrayHolder sPreferredLanguages(NULL); - - if (uBufLen > 0 && uLangCount > 0 ) - { - sPreferredLanguages = new WCHAR[uBufLen]; - res= NewApis::GetUserPreferredUILanguages (MUI_LANGUAGE_NAME,&uLangCount,sPreferredLanguages,&uBufLen); - - if (res == 0) - ThrowLastError(); - - wszLangName=sPreferredLanguages; -// Review size_t to int conversion (possible loss of data). -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable:4267) -#endif - res=wcslen(wszLangName)+1; -#ifdef _MSC_VER -#pragma warning(pop) -#endif - } - else - { - res=0; - } - - if (res == 0) { - res = NewApis::GetUserDefaultLocaleName(wszBuffer,NumItems(wszBuffer)); - wszLangName=wszBuffer; - } - - - // If not found, either not longhorn (no LOCALE_SNAME) or not a valid name - if (res == 0) - { - // Didn't find string, return an empty string. - result = false; - } - else - { - userDefaultUiLanguage.Set(wszLangName); - result = true; - } - - // Return the found language name. LCID should be found one already. - END_QCALL; - return result; -} - -INT32 COMNlsInfo::CallGetUserDefaultUILanguage() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - SO_TOLERANT; - } CONTRACTL_END; - - static INT32 s_lcid = 0; - - // The user UI language cannot change within a process (in fact it cannot change within a logon session), - // so we cache it. We dont take a lock while initializing s_lcid for the same reason. If two threads are - // racing to initialize s_lcid, the worst thing that'll happen is that one thread will call - // GetUserDefaultUILanguage needlessly, but the final result is going to be the same. - if (s_lcid == 0) - { - INT32 s_lcidTemp = GetUserDefaultUILanguage(); - if (s_lcidTemp == LANGID_ZH_TW) - { - // If the UI language ID is 0x0404, we need to do extra check to decide - // the real UI language, since MUI (in CHT)/HK/TW Windows SKU all uses 0x0404 as their CHT language ID. - if (!NewApis::IsZhTwSku()) - { - s_lcidTemp = LANGID_ZH_HK; - } - } - s_lcid = s_lcidTemp; - } - - return s_lcid; -} - - -FCIMPL2(Object*, COMNlsInfo::nativeGetLocaleInfoEx, StringObject* localeNameUNSAFE, INT32 lcType) -{ - CONTRACTL - { - FCALL_CHECK; - PRECONDITION(CheckPointer(localeNameUNSAFE)); - } CONTRACTL_END; - - struct _gc - { - STRINGREF localeName; - STRINGREF refRetVal; - } gc; - - // Dereference our string - gc.refRetVal = NULL; - gc.localeName = (STRINGREF)localeNameUNSAFE; - - HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc); - StackSString localeNameStackBuffer( gc.localeName->GetBuffer() ); - - WCHAR buffer[LOCALE_MAX_STRING_SIZE]; - int result = NewApis::GetLocaleInfoEx(localeNameStackBuffer, lcType, buffer, NumItems(buffer)); - - // Make a string out of it - if (result != 0) - { - // Exclude the NULL char at the end, except that LOCALE_FONTSIGNATURE isn't - // really a string, so we need the last character too. - gc.refRetVal = StringObject::NewString(buffer, ((lcType & ~LOCALE_NOUSEROVERRIDE) == LOCALE_FONTSIGNATURE) ? result : result-1); - } - else - { - } - HELPER_METHOD_FRAME_END(); - return OBJECTREFToObject(gc.refRetVal); -} -FCIMPLEND - - -FCIMPL2(INT32, COMNlsInfo::nativeGetLocaleInfoExInt, StringObject* localeNameUNSAFE, INT32 lcType) -{ - CONTRACTL - { - FCALL_CHECK; - PRECONDITION(CheckPointer(localeNameUNSAFE)); - } CONTRACTL_END; - - INT32 result = 0; - - // Dereference our string - STRINGREF localeName = (STRINGREF)localeNameUNSAFE; - HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(localeName); - - lcType |= LOCALE_RETURN_NUMBER; - - if (NewApis::GetLocaleInfoEx(localeName->GetBuffer(), lcType, (LPWSTR)&result, sizeof(INT32) / sizeof (WCHAR)) == 0) - { - // return value of 0 indicates failure and error value is supposed to be set. - // shouldn't ever really happen - _ASSERTE(!"catastrophic failure calling NewApis::nativeGetLocaleInfoExInt! This could be a CultureInfo bug (bad localeName string) or maybe a GCHole."); - } - - HELPER_METHOD_FRAME_END(); - - return result; -} -FCIMPLEND - - - -//////////////////////////////////////////////////////////////////////// -// -// Call the Win32 GetLocaleInfo() using the specified lcid to retrieve -// the native digits, probably from the registry override. The return -// indicates whether the call was successful. -// -// Parameters: -// IN lcid the LCID to make the Win32 call with -// OUT pOutputStrAry The output managed string array. -// -//////////////////////////////////////////////////////////////////////// -BOOL COMNlsInfo::GetNativeDigitsFromWin32(LPCWSTR locale, PTRARRAYREF * pOutputStrAry, BOOL useUserOverride) -{ - CONTRACTL - { - GC_TRIGGERS; - MODE_COOPERATIVE; - THROWS; - } CONTRACTL_END; - - WCHAR buffer[11]; - int result = 0; - - DWORD lcType = LOCALE_SNATIVEDIGITS; - if(!useUserOverride) - { - lcType |= LOCALE_NOUSEROVERRIDE; - } - result = NewApis::GetLocaleInfoEx(locale, lcType, buffer, 11); - // Be very unforgiving and only support strings of size 10 plus the NULL - if (result == 11) - { - // Break up the unmanaged ten-character ZLS into what NFI wants (a managed - // ten-string array). - // - // Allocate the array of STRINGREFs. We don't need to check for null because the GC will throw - // an OutOfMemoryException if there's not enough memory. - // - PTRARRAYREF DigitArray = (PTRARRAYREF) AllocateObjectArray(10, g_pStringClass); - - GCPROTECT_BEGIN(DigitArray); - for(DWORD i = 0; i < 10; i++) { - OBJECTREF o = (OBJECTREF) StringObject::NewString(buffer + i, 1); - DigitArray->SetAt(i, o); - } - GCPROTECT_END(); - - _ASSERTE(pOutputStrAry != NULL); - *pOutputStrAry = DigitArray; - } - - return (result == 11); -} - - -//////////////////////////////////////////////////////////////////////// -// -// Call the Win32 GetLocaleInfoEx() using the specified lcid and LCTYPE. -// The return value can be INT32 or an allocated managed string object, depending on -// which version's called. -// -// Parameters: -// OUT pOutputInt32 The output int32 value. -// OUT pOutputRef The output string value. -// -//////////////////////////////////////////////////////////////////////// -BOOL COMNlsInfo::CallGetLocaleInfoEx(LPCWSTR localeName, int lcType, INT32* pOutputInt32, BOOL useUserOverride) -{ - CONTRACTL - { - GC_NOTRIGGER; - MODE_ANY; - NOTHROW; - } CONTRACTL_END; - - int result = 0; - - _ASSERT((lcType & LOCALE_RETURN_NUMBER) != 0); - if(!useUserOverride) - { - lcType |= LOCALE_NOUSEROVERRIDE; - } - result = NewApis::GetLocaleInfoEx(localeName, lcType, (LPWSTR)pOutputInt32, sizeof(*pOutputInt32)); - - return (result != 0); -} - -BOOL COMNlsInfo::CallGetLocaleInfoEx(LPCWSTR localeName, int lcType, STRINGREF* pOutputStrRef, BOOL useUserOverride) -{ - CONTRACTL - { - THROWS; // We can throw since we are allocating managed string. - GC_TRIGGERS; - MODE_COOPERATIVE; - } CONTRACTL_END; - - WCHAR buffer[LOCALE_NAME_MAX_LENGTH]; - int result = 0; - - _ASSERT((lcType & LOCALE_RETURN_NUMBER) == 0); - if(!useUserOverride) - { - lcType |= LOCALE_NOUSEROVERRIDE; - } - result = NewApis::GetLocaleInfoEx(localeName, lcType, buffer, LOCALE_NAME_MAX_LENGTH); - - if (result != 0) - { - _ASSERTE(pOutputStrRef != NULL); - *pOutputStrRef = StringObject::NewString(buffer, result - 1); - } - - return (result != 0); -} - -FCIMPL1(Object*, COMNlsInfo::LCIDToLocaleName, LCID lcid) -{ - FCALL_CONTRACT; - - STRINGREF refRetVal = NULL; - - // The maximum size for locale name is 85 characters. - WCHAR localeName[LOCALE_NAME_MAX_LENGTH]; - int result = 0; - - HELPER_METHOD_FRAME_BEGIN_RET_0(); - - // Note that this'll return neutral names (unlike native Vista APIs) - result = NewApis::LCIDToLocaleName(lcid, localeName, LOCALE_NAME_MAX_LENGTH, 0); - - if (result != 0) - { - refRetVal = StringObject::NewString(localeName, result - 1); - } - else - { - refRetVal = StringObject::GetEmptyString(); - } - - HELPER_METHOD_FRAME_END(); - - return OBJECTREFToObject(refRetVal); -} -FCIMPLEND - -FCIMPL1(INT32, COMNlsInfo::LocaleNameToLCID, StringObject* localeNameUNSAFE) -{ - CONTRACTL - { - FCALL_CHECK; - PRECONDITION(CheckPointer(localeNameUNSAFE)); - } CONTRACTL_END; - - INT32 result = 0; - - // Dereference our string - STRINGREF localeName = (STRINGREF)localeNameUNSAFE; - HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(localeName); - - // Note that this'll return neutral names (unlike native Vista APIs) - result = NewApis::LocaleNameToLCID(localeName->GetBuffer(), 0); - - HELPER_METHOD_FRAME_END(); - - return result; -} -FCIMPLEND - -//////////////////////////////////////////////////////////////////////// -// -// Implementation of CultureInfo.nativeGetNumberFormatInfoValues. -// -// Retrieve NumberFormatInfo (NFI) properties from windows -// -// Parameters: -// IN/OUT pNumfmtUNSAFE -// The pointer of the managed NumberFormatInfo passed -// from the managed side. -// Note that the native NumberFormatInfo* is defined -// in COMNumber.h -// Note: -// Managed string will be allocated and assign to the string fields in -// the managed NumberFormatInfo passed in pNumftUNSAFE -// -//////////////////////////////////////////////////////////////////////// - - -/* - This is the list of the data members in the managed NumberFormatInfo and their - corresponding LCTYPE(). - - Win32 GetLocaleInfo() constants Data members in NumberFormatInfo in the defined order. - LOCALE_SPOSITIVE // String positiveSign - LOCALE_SNEGATIVE // String negativeSign - LOCALE_SDECIMAL // String numberDecimalSeparator - LOCALE_SGROUPING // String numberGroupSeparator - LOCALE_SMONGROUPING // String currencyGroupSeparator - LOCALE_SMONDECIMALSEP // String currencyDecimalSeparator - LOCALE_SCURRENCY // String currencySymbol - N/A // String ansiCurrencySymbol - N/A // String nanSymbol - N/A // String positiveInfinitySymbol - N/A // String negativeInfinitySymbol - N/A // String percentDecimalSeparator - N/A // String percentGroupSeparator - N/A // String percentSymbol - N/A // String perMilleSymbol - - N/A // int m_dataItem - - LOCALE_IDIGITS | LOCALE_RETURN_NUMBER, // int numberDecimalDigits - LOCALE_ICURRDIGITS | LOCALE_RETURN_NUMBER, // int currencyDecimalDigits - LOCALE_ICURRENCY | LOCALE_RETURN_NUMBER, // int currencyPositivePattern - LOCALE_INEGCURR | LOCALE_RETURN_NUMBER, // int currencyNegativePattern - LOCALE_INEGNUMBER| LOCALE_RETURN_NUMBER, // int numberNegativePattern - N/A // int percentPositivePattern - N/A // int percentNegativePattern - N/A // int percentDecimalDigits - N/A // bool isReadOnly=false; - N/A // internal bool m_useUserOverride; -*/ -FCIMPL3(FC_BOOL_RET, COMNlsInfo::nativeGetNumberFormatInfoValues, - StringObject* localeNameUNSAFE, NumberFormatInfo* pNumfmtUNSAFE, CLR_BOOL useUserOverride) { - CONTRACTL - { - FCALL_CHECK; - PRECONDITION(CheckPointer(localeNameUNSAFE)); - } CONTRACTL_END; - - BOOL ret = TRUE; - - struct _gc - { - STRINGREF localeName; - STRINGREF stringResult; - NUMFMTREF numfmt; - PTRARRAYREF tempArray; - } gc; - - // Dereference our string - gc.localeName = (STRINGREF)localeNameUNSAFE; - gc.numfmt = (NUMFMTREF) pNumfmtUNSAFE; - gc.stringResult = NULL; - gc.tempArray = NULL; - - HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc); - StackSString localeNameStackBuffer( gc.localeName->GetBuffer() ); - - // Calling SString::ConvertToUnicode once - LPCWSTR pLocaleName = localeNameStackBuffer; - - // - // NOTE: We pass the stringResult allocated in the stack and assign it to the fields - // in numfmt after calling CallGetLocaleInfo(). The reason for this is that CallGetLocaleInfo() - // allocates a string object, and it may trigger a GC and cause numfmt to be moved. - // That's why we use the stringResult allocated in the stack since it will not be moved. - // After CallGetLocaleInfo(), we know that numfmt will not be moved, and it's safe to assign - // the stringResult to its field. - // - - // String values - if (CallGetLocaleInfoEx(pLocaleName, LOCALE_SPOSITIVESIGN , &gc.stringResult, useUserOverride)) { - SetObjectReference((OBJECTREF*)&(gc.numfmt->sPositive), gc.stringResult, NULL); - } - else { - ret = FALSE; - _ASSERT(FALSE); - } - - if (CallGetLocaleInfoEx(pLocaleName, LOCALE_SNEGATIVESIGN , &gc.stringResult, useUserOverride)) { - SetObjectReference((OBJECTREF*)&(gc.numfmt->sNegative), gc.stringResult, NULL); - } - else { - ret = FALSE; - _ASSERT(FALSE); - } - if (CallGetLocaleInfoEx(pLocaleName, LOCALE_SDECIMAL , &gc.stringResult, useUserOverride)) { - SetObjectReference((OBJECTREF*)&(gc.numfmt->sNumberDecimal), gc.stringResult, NULL); - } - else { - ret = FALSE; - _ASSERT(FALSE); - } - if (CallGetLocaleInfoEx(pLocaleName, LOCALE_STHOUSAND , &gc.stringResult, useUserOverride)) { - SetObjectReference((OBJECTREF*)&(gc.numfmt->sNumberGroup), gc.stringResult, NULL); - } - else { - ret = FALSE; - _ASSERT(FALSE); - } - if (CallGetLocaleInfoEx(pLocaleName, LOCALE_SMONTHOUSANDSEP , &gc.stringResult, useUserOverride)) { - SetObjectReference((OBJECTREF*)&(gc.numfmt->sCurrencyGroup), gc.stringResult, NULL); - } - else { - ret = FALSE; - _ASSERT(FALSE); - } - if (CallGetLocaleInfoEx(pLocaleName, LOCALE_SMONDECIMALSEP , &gc.stringResult, useUserOverride)) { - SetObjectReference((OBJECTREF*)&(gc.numfmt->sCurrencyDecimal), gc.stringResult, NULL); - } - else { - ret = FALSE; - _ASSERT(FALSE); - } - if (CallGetLocaleInfoEx(pLocaleName, LOCALE_SCURRENCY , &gc.stringResult, useUserOverride)) { - SetObjectReference((OBJECTREF*)&(gc.numfmt->sCurrency), gc.stringResult, NULL); - } - else { - ret = FALSE; - _ASSERT(FALSE); - } - - - // Numeric values - ret &= CallGetLocaleInfoEx(pLocaleName, LOCALE_IDIGITS | LOCALE_RETURN_NUMBER , &(gc.numfmt->cNumberDecimals), useUserOverride); - _ASSERT(ret == TRUE); - ret &= CallGetLocaleInfoEx(pLocaleName, LOCALE_ICURRDIGITS | LOCALE_RETURN_NUMBER , &(gc.numfmt->cCurrencyDecimals), useUserOverride); - _ASSERT(ret == TRUE); - ret &= CallGetLocaleInfoEx(pLocaleName, LOCALE_ICURRENCY | LOCALE_RETURN_NUMBER , &(gc.numfmt->cPosCurrencyFormat), useUserOverride); - _ASSERT(ret == TRUE); - ret &= CallGetLocaleInfoEx(pLocaleName, LOCALE_INEGCURR | LOCALE_RETURN_NUMBER , &(gc.numfmt->cNegCurrencyFormat), useUserOverride); - _ASSERT(ret == TRUE); - ret &= CallGetLocaleInfoEx(pLocaleName, LOCALE_INEGNUMBER| LOCALE_RETURN_NUMBER , &(gc.numfmt->cNegativeNumberFormat), useUserOverride); - _ASSERT(ret == TRUE); - ret &= CallGetLocaleInfoEx(pLocaleName, LOCALE_IDIGITSUBSTITUTION | LOCALE_RETURN_NUMBER, &(gc.numfmt->iDigitSubstitution), useUserOverride); - _ASSERT(ret == TRUE); - - // LOCALE_SNATIVEDIGITS (gc.tempArray of strings) - if (GetNativeDigitsFromWin32(pLocaleName, &gc.tempArray, useUserOverride)) { - SetObjectReference((OBJECTREF*)&(gc.numfmt->sNativeDigits), gc.tempArray, NULL); - } - else { - ret = FALSE; - _ASSERT(FALSE); - } - - HELPER_METHOD_FRAME_END(); - FC_RETURN_BOOL(ret); -} -FCIMPLEND - - -//////////////////////////////////////////////////////////////////////// -// -// Culture enumeration helper functions -// -//////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////// -// -// Enum values for System.Globalization.CultureTypes -// -//////////////////////////////////////////////////////////////////////////// - -// Neutral cultures are cultures like "en", "de", "zh", etc, for enumeration this includes ALL neutrals regardless of other flags -#define CULTURETYPES_NEUTRALCULTURES 0x0001 - -// Non-netural cultuers. Examples are "en-us", "zh-tw", etc., for enumeration this includes ALL specifics regardless of other flags -#define CULTURETYPES_SPECIFICCULTURES 0x0002 - -// Win32 installed cultures in the system and exists in the framework too., this is effectively all cultures -#define CULTURETYPES_INSTALLEDWIN32CULTURES 0x0004 - -// User defined custom culture -#define CULTURETYPES_USERCUSTOMCULTURE 0x0008 - -// User defined replacement custom culture. -#define CULTURETYPES_REPLACEMENTCULTURES 0x0010 -// [Obsolete("This value has been deprecated. Please use other values in CultureTypes.")] -// Culture exists in Win32 but not in the Framework. // TODO: All cultures or no cultures? -#define CULTURETYPES_WINDOWSONLYCULTURES 0x0020 -// [Obsolete("This value has been deprecated. Please use other values in CultureTypes.")] -// the language tag match a culture that ships with the .NET framework, effectively all cultures since we get them from windows -#define CULTURETYPES_FRAMEWORKCULTURES 0x0040 - - -const LPCWSTR WHIDBEY_FRAMEWORK_CULTURE_LIST [] = -{ - W(""), - W("af"), - W("af-za"), - W("ar"), - W("ar-ae"), - W("ar-bh"), - W("ar-dz"), - W("ar-eg"), - W("ar-iq"), - W("ar-jo"), - W("ar-kw"), - W("ar-lb"), - W("ar-ly"), - W("ar-ma"), - W("ar-om"), - W("ar-qa"), - W("ar-sa"), - W("ar-sy"), - W("ar-tn"), - W("ar-ye"), - W("az"), - W("az-cyrl-az"), - W("az-latn-az"), - W("be"), - W("be-by"), - W("bg"), - W("bg-bg"), - W("ca"), - W("ca-es"), - W("cs"), - W("cs-cz"), - W("da"), - W("da-dk"), - W("de"), - W("de-at"), - W("de-ch"), - W("de-de"), - W("de-li"), - W("de-lu"), - W("dv"), - W("dv-mv"), - W("el"), - W("el-gr"), - W("en"), - W("en-029"), - W("en-au"), - W("en-bz"), - W("en-ca"), - W("en-gb"), - W("en-ie"), - W("en-jm"), - W("en-nz"), - W("en-ph"), - W("en-tt"), - W("en-us"), - W("en-za"), - W("en-zw"), - W("es"), - W("es-ar"), - W("es-bo"), - W("es-cl"), - W("es-co"), - W("es-cr"), - W("es-do"), - W("es-ec"), - W("es-es"), - W("es-gt"), - W("es-hn"), - W("es-mx"), - W("es-ni"), - W("es-pa"), - W("es-pe"), - W("es-pr"), - W("es-py"), - W("es-sv"), - W("es-uy"), - W("es-ve"), - W("et"), - W("et-ee"), - W("eu"), - W("eu-es"), - W("fa"), - W("fa-ir"), - W("fi"), - W("fi-fi"), - W("fo"), - W("fo-fo"), - W("fr"), - W("fr-be"), - W("fr-ca"), - W("fr-ch"), - W("fr-fr"), - W("fr-lu"), - W("fr-mc"), - W("gl"), - W("gl-es"), - W("gu"), - W("gu-in"), - W("he"), - W("he-il"), - W("hi"), - W("hi-in"), - W("hr"), - W("hr-hr"), - W("hu"), - W("hu-hu"), - W("hy"), - W("hy-am"), - W("id"), - W("id-id"), - W("is"), - W("is-is"), - W("it"), - W("it-ch"), - W("it-it"), - W("ja"), - W("ja-jp"), - W("ka"), - W("ka-ge"), - W("kk"), - W("kk-kz"), - W("kn"), - W("kn-in"), - W("ko"), - W("ko-kr"), - W("kok"), - W("kok-in"), - W("ky"), - W("ky-kg"), - W("lt"), - W("lt-lt"), - W("lv"), - W("lv-lv"), - W("mk"), - W("mk-mk"), - W("mn"), - W("mn-mn"), - W("mr"), - W("mr-in"), - W("ms"), - W("ms-bn"), - W("ms-my"), - W("nb-no"), - W("nl"), - W("nl-be"), - W("nl-nl"), - W("nn-no"), - W("no"), - W("pa"), - W("pa-in"), - W("pl"), - W("pl-pl"), - W("pt"), - W("pt-br"), - W("pt-pt"), - W("ro"), - W("ro-ro"), - W("ru"), - W("ru-ru"), - W("sa"), - W("sa-in"), - W("sk"), - W("sk-sk"), - W("sl"), - W("sl-si"), - W("sq"), - W("sq-al"), - W("sr"), - W("sr-cyrl-cs"), - W("sr-latn-cs"), - W("sv"), - W("sv-fi"), - W("sv-se"), - W("sw"), - W("sw-ke"), - W("syr"), - W("syr-sy"), - W("ta"), - W("ta-in"), - W("te"), - W("te-in"), - W("th"), - W("th-th"), - W("tr"), - W("tr-tr"), - W("tt"), - W("tt-ru"), - W("uk"), - W("uk-ua"), - W("ur"), - W("ur-pk"), - W("uz"), - W("uz-cyrl-uz"), - W("uz-latn-uz"), - W("vi"), - W("vi-vn"), - W("zh-chs"), - W("zh-cht"), - W("zh-cn"), - W("zh-hans"), - W("zh-hant"), - W("zh-hk"), - W("zh-mo"), - W("zh-sg"), - W("zh-tw") -}; -#define WHIDBEY_FRAMEWORK_CULTURE_LIST_LENGTH (sizeof(WHIDBEY_FRAMEWORK_CULTURE_LIST) / sizeof(WHIDBEY_FRAMEWORK_CULTURE_LIST[0])) - -//////////////////////////////////////////////////////////////////////////// -// -// NlsCompareInvariantNoCase -// -// This routine does fast caseless comparison without needing the tables. -// This helps us do the comparisons we need to load the tables :-) -// -// Returns 0 if identical, <0 if pFirst if first string sorts first. -// -// This is only intended to help with our locale name comparisons, -// which are effectively limited to A-Z, 0-9, a-z and - where A-Z and a-z -// compare as equal. -// -// WARNING: [\]^_` will be less than A-Z because we make everything lower -// case before comparing them. -// -// When bNullEnd is TRUE, both of the strings should be null-terminator to be considered equal. -// When bNullEnd is FALSE, the strings are considered equal when we reach the number of characters specifed by size -// or when null terminators are reached, whichever happens first (strncmp-like behavior) -// -//////////////////////////////////////////////////////////////////////////// - -int NlsCompareInvariantNoCase( - LPCWSTR pFirst, - LPCWSTR pSecond, - int size, - BOOL bNullEnd) -{ - int i=0; - WCHAR first; - WCHAR second; - - for (; - size > 0 && (first = *pFirst) != 0 && (second = *pSecond) != 0; - size--, pFirst++, pSecond++) - { - // Make them lower case - if ((first >= 'A') && (first <= 'Z')) first |= 0x20; - if ((second >= 'A') && (second <= 'Z')) second |= 0x20; - - // Get the diff - i = (first - second); - - // Are they the same? - if (i == 0) - continue; - - // Otherwise the difference. Remember we made A-Z into lower case, so - // the characters [\]^_` will sort < A-Z and also < a-z. (Those are the - // characters between A-Z and a-Z in ascii) - return i; - } - - // When we are here, one of these holds: - // size == 0 - // or one of the strings has a null terminator - // or both of the string reaches null terminator - - if (bNullEnd || size != 0) - { - // If bNullEnd is TRUE, always check for null terminator. - // If bNullEnd is FALSE, we still have to check if one of the strings is terminated eariler - // than another (hense the size != 0 check). - - // See if one string ended first - if (*pFirst != 0 || *pSecond != 0) - { - // Which one? - return *pFirst == 0 ? -1 : 1; - } - } - - // Return our difference (0) - return i; -} - -BOOL IsWhidbeyFrameworkCulture(__in LPCWSTR lpLocaleString) -{ - int iBottom = 0; - int iTop = WHIDBEY_FRAMEWORK_CULTURE_LIST_LENGTH - 1; - - // Do a binary search for our name - while (iBottom <= iTop) - { - int iMiddle = (iBottom + iTop) / 2; - int result = NlsCompareInvariantNoCase(lpLocaleString, WHIDBEY_FRAMEWORK_CULTURE_LIST[iMiddle], LOCALE_NAME_MAX_LENGTH, TRUE); - if (result == 0) - { - return TRUE; - } - if (result < 0) - { - // pLocaleName was < pTest - iTop = iMiddle - 1; - } - else - { - // pLocaleName was > pTest - iBottom = iMiddle + 1; - } - } - - return FALSE; -} - -// Just Check to see if the OS thinks it is a valid locle -BOOL WINAPI IsOSValidLocaleName(__in LPCWSTR lpLocaleName, bool bIsNeutralLocale) -{ -#ifndef ENABLE_DOWNLEVEL_FOR_NLS - return ::IsValidLocaleName(lpLocaleName); -#else - BOOL IsWindows7 = NewApis::IsWindows7Platform(); - // if we're < Win7, we didn't know about neutrals or the invariant. - if (!IsWindows7 && (bIsNeutralLocale || (lpLocaleName[0] == 0))) - { - return false; - } - - // Work around the name/lcid thingy (can't just link to ::IsValidLocaleName()) - LCID lcid = NewApis::LocaleNameToLCID(lpLocaleName, 0); - - if (IsCustomCultureId(lcid)) - { - return false; - } - - if (bIsNeutralLocale) - { - // In this case, we're running on Windows 7. - // For neutral locales, use GetLocaleInfoW. - // If GetLocaleInfoW works, then the OS knows about it. - return (::GetLocaleInfoW(lcid, LOCALE_ILANGUAGE, NULL, 0) != 0); - } - - // This is not a custom locale. - // Call IsValidLocale() to check if the LCID is installed. - // IsValidLocale doesn't work for neutral locales. - return IsValidLocale(lcid, LCID_INSTALLED); -#endif -} - - -//////////////////////////////////////////////////////////////////////////// -// -// Check the dwFlags, which has the 'attributes' of the locale, and decide -// if the locale should be included in the enumeration based on -// the desired CultureTypes. -// -//////////////////////////////////////////////////////////////////////////// - -BOOL ShouldIncludeByCultureType(INT32 cultureTypes, LPCWSTR lpLocaleString, INT32 dwFlags) -{ - - if ((cultureTypes & CULTURETYPES_NEUTRALCULTURES) && - ((dwFlags & LOCALE_NEUTRALDATA) || (lpLocaleString[0] == 0))) // Invariant culture get enumerated with the neutrals - { - return TRUE; - } - - if ((cultureTypes & CULTURETYPES_SPECIFICCULTURES) && - ((dwFlags & LOCALE_SPECIFICDATA) && (lpLocaleString[0] != 0))) // Invariant culture does not get enumerated with the specifics - { - return TRUE; - } - - if (cultureTypes & CULTURETYPES_INSTALLEDWIN32CULTURES) - { - // The user asks for installed Win32 culture, so check - // if this locale is installed. In W7 and above, when ::IsValidLocaleName() - // returns true, it means that it is installed. - // In downlevel (including Vista), we will convert the name to LCID. - // When the LCID is not a custom locale, we will call ::IsValidLocale(.., LCID_INSTALLED) - // to verify if the locale is installed. - // In Vista, we treat custom locale as installed. - if (IsOSValidLocaleName(lpLocaleString, (dwFlags & LOCALE_NEUTRALDATA) == LOCALE_NEUTRALDATA)) - { - return TRUE; - } - } - - if ((cultureTypes & CULTURETYPES_USERCUSTOMCULTURE) && - (dwFlags & LOCALE_SUPPLEMENTAL)) - { - return TRUE; - } - - if ((cultureTypes & CULTURETYPES_REPLACEMENTCULTURES) && - (dwFlags & LOCALE_REPLACEMENT)) - { - return TRUE; - } - - if ((cultureTypes & CULTURETYPES_FRAMEWORKCULTURES) && - IsWhidbeyFrameworkCulture(lpLocaleString)) - { - return TRUE; - } - - // - // No need to check CULTURETYPES_WINDOWSONLYCULTURES and CULTURETYPES_FRAMEWORKCULTURES - // since they are deprecated, and they are handled in the managed code before calling - // nativeEnumCultureNames. - // - - return FALSE; -} - -//////////////////////////////////////////////////////////////////////////// -// -// Struct to hold context to be used in the callback for -// EnumLocaleProcessingCallback -// -//////////////////////////////////////////////////////////////////////////// - -typedef struct -{ - PTRARRAYREF pCultureNamesArray; - INT32 count; - INT32 cultureTypes; -} ENUM_LOCALE_DATA; - -//////////////////////////////////////////////////////////////////////////// -// -// Callback for NewApis::EnumSystemLocalesEx to count the number of -// locales to be enumerated. -// -//////////////////////////////////////////////////////////////////////////// - -BOOL CALLBACK EnumLocaleCountCallback(__in_z LPCWSTR lpLocaleString, __in DWORD dwFlags, __in LPARAM lParam) -{ - ENUM_LOCALE_DATA* pData = (ENUM_LOCALE_DATA*)lParam; - - if (ShouldIncludeByCultureType(pData->cultureTypes, lpLocaleString, dwFlags)) - { - (pData->count)++; - } - return TRUE; -} - - -//////////////////////////////////////////////////////////////////////////// -// -// Callback for NewApis::EnumSystemLocalesEx to add the locale name -// into the allocated managed string array. -// -//////////////////////////////////////////////////////////////////////////// - -BOOL CALLBACK EnumLocaleProcessingCallback(__in_z LPCWSTR lpLocaleString, __in DWORD dwFlags, __in LPARAM lParam) -{ - ENUM_LOCALE_DATA* pData = (ENUM_LOCALE_DATA*)lParam; - - if (ShouldIncludeByCultureType(pData->cultureTypes, lpLocaleString, dwFlags)) - { - GCX_COOP(); - - GCPROTECT_BEGIN(pData->pCultureNamesArray); - - OBJECTREF cultureString = (OBJECTREF) StringObject::NewString(lpLocaleString); - pData->pCultureNamesArray->SetAt(pData->count, cultureString); - pData->count++; - - GCPROTECT_END(); - } - - return TRUE; -} - - -//////////////////////////////////////////////////////////////////////////// -// -// Called by CultureData.GetCultures() to enumerate the names of cultures. -// It first calls NewApis::EnumSystemLocalesEx to count the number of -// locales to be enumerated. And it will allocate an managed string -// array with the count. And fill the array with the culture names in -// the 2nd call to NewAPis::EnumSystemLocalesEx. -// -//////////////////////////////////////////////////////////////////////////// - - -int QCALLTYPE COMNlsInfo::nativeEnumCultureNames(INT32 cultureTypes, QCall::ObjectHandleOnStack retStringArray) -{ - CONTRACTL - { - QCALL_CHECK; - // Check CultureTypes.WindowsOnlyCultures and CultureTYpes.FrameworkCultures are deprecated and is - // handled in the managed code side to provide fallback behavior. - //PRECONDITION((cultureTypes & (CULTURETYPES_WINDOWSONLYCULTURES | CULTURETYPES_FRAMEWORKCULTURES) == 0)); - } CONTRACTL_END; - - - int result; - DWORD dwFlags = 0; - PTRARRAYREF cultureNamesArray = NULL; - ENUM_LOCALE_DATA enumData = { NULL, 0, cultureTypes}; - - BEGIN_QCALL; - - // - // if CultureTypes.FrameworkCulture is specified we'll enumerate all cultures - // and filter according to the Whidbey framework culture list (for compatibility) - // - - if (cultureTypes & CULTURETYPES_FRAMEWORKCULTURES) - { - dwFlags |= LOCALE_NEUTRALDATA | LOCALE_SPECIFICDATA; - } - - // Map CultureTypes to Windows enumeration values. - if (cultureTypes & CULTURETYPES_NEUTRALCULTURES) - { - dwFlags |= LOCALE_NEUTRALDATA; - } - - if (cultureTypes & CULTURETYPES_SPECIFICCULTURES) - { - dwFlags |= LOCALE_SPECIFICDATA; - } - - if (cultureTypes & CULTURETYPES_INSTALLEDWIN32CULTURES) - { - // Windows 7 knows about neutrals, whereas Vista and lower don't. - if (NewApis::IsWindows7Platform()) - { - dwFlags |= LOCALE_SPECIFICDATA | LOCALE_NEUTRALDATA; - } - else - { - dwFlags |= LOCALE_SPECIFICDATA; - } - } - - dwFlags |= (cultureTypes & CULTURETYPES_USERCUSTOMCULTURE) ? LOCALE_SUPPLEMENTAL: 0; - - // We need special handling for Replacement cultures because Windows does not have a way to enumerate it directly. - // Replacement locale check will be only used when CultureTypes.SpecificCultures is NOT used. - dwFlags |= (cultureTypes & CULTURETYPES_REPLACEMENTCULTURES) ? LOCALE_SPECIFICDATA | LOCALE_NEUTRALDATA: 0; - - - result = NewApis::EnumSystemLocalesEx((LOCALE_ENUMPROCEX)EnumLocaleCountCallback, dwFlags, (LPARAM)&enumData, NULL) == TRUE ? 1 : 0; - - if (result) - { - - GCX_COOP(); - - GCPROTECT_BEGIN(cultureNamesArray); - - // Now we need to allocate our culture names string array and populate it - // Get our array object (will throw, don't have to check it) - cultureNamesArray = (PTRARRAYREF) AllocateObjectArray(enumData.count, g_pStringClass); - - // In the context struct passed to EnumSystemLocalesEx, reset the count and assign the newly allocated string array - // to hold culture names to be enumerated. - enumData.count = 0; - enumData.pCultureNamesArray = cultureNamesArray; - - result = NewApis::EnumSystemLocalesEx((LOCALE_ENUMPROCEX)EnumLocaleProcessingCallback, dwFlags, (LPARAM)&enumData, NULL); - - if (result) - { - retStringArray.Set(cultureNamesArray); - } - GCPROTECT_END(); - } - END_QCALL - - return result; - -} - -// -// InternalCompareString is used in the managed side to handle the synthetic CompareInfo methods (IndexOf, LastIndexOf, IsPrfix, and IsSuffix) -// -INT32 QCALLTYPE COMNlsInfo::InternalCompareString( - INT_PTR handle, - INT_PTR handleOrigin, - LPCWSTR localeName, - LPCWSTR string1, INT32 offset1, INT32 length1, - LPCWSTR string2, INT32 offset2, INT32 length2, - INT32 flags) -{ - CONTRACTL - { - QCALL_CHECK; - PRECONDITION(CheckPointer(string1)); - PRECONDITION(CheckPointer(string2)); - PRECONDITION(CheckPointer(localeName)); - } CONTRACTL_END; - - INT32 result = 1; - BEGIN_QCALL; - - { - result = NewApis::CompareStringEx(handle != NULL ? NULL : localeName, flags, &string1[offset1], length1, &string2[offset2], length2,NULL,NULL, (LPARAM) handle); - } - - switch (result) - { - case CSTR_LESS_THAN: - result = -1; - break; - - case CSTR_EQUAL: - result = 0; - break; - - case CSTR_GREATER_THAN: - result = 1; - break; - - case 0: - default: - _ASSERTE(!"catastrophic failure calling NewApis::CompareStringEx! This could be a CultureInfo, RegionInfo, or Calendar bug (bad localeName string) or maybe a GCHole."); - break; - } - - END_QCALL; - return result; -} - -//////////////////////////////////////////////////////////////////////////// -// -// UseConstantSpaceHashAlgorithm -// Check for the DWORD "NetFx45_CultureAwareComparerGetHashCode_LongStrings" CLR config option. -// -// .Net 4.5 introduces an opt-in algorithm for determining the hash code of strings that -// uses a constant amount of memory instead of memory proportional to the size of the string -// -// A non-zero value will enable the new algorithm: -// -// 1) Config file (MyApp.exe.config) -// -// -// -// -// -// -// 2) Environment variable -// set NetFx45_CultureAwareComparerGetHashCode_LongStrings=1 -// 3) RegistryKey -// [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework] -// "NetFx45_CultureAwareComparerGetHashCode_LongStrings"=dword:00000001 -// -//////////////////////////////////////////////////////////////////////////// -BOOL UseConstantSpaceHashAlgorithm() -{ - static bool configChecked = false; - static BOOL useConstantSpaceHashAlgorithm = FALSE; - - if(!configChecked) - { - BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return false); - useConstantSpaceHashAlgorithm = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_NetFx45_CultureAwareComparerGetHashCode_LongStrings) != 0; - END_SO_INTOLERANT_CODE; - - configChecked = true; - } - return useConstantSpaceHashAlgorithm; -} - - - -//////////////////////////////////////////////////////////////////////////// -// -// InternalGetGlobalizedHashCode -// -//////////////////////////////////////////////////////////////////////////// -INT32 QCALLTYPE COMNlsInfo::InternalGetGlobalizedHashCode(INT_PTR handle, LPCWSTR localeName, LPCWSTR string, INT32 length, INT32 dwFlagsIn, INT64 additionalEntropy) -{ - CONTRACTL - { - QCALL_CHECK; - PRECONDITION(CheckPointer(localeName)); - PRECONDITION(CheckPointer(string, NULL_OK)); - } CONTRACTL_END; - - INT32 iReturnHash = 0; - BEGIN_QCALL; - - int byteCount = 0; - - // - // Make sure there is a string. - // - if (!string) { - COMPlusThrowArgumentNull(W("string"),W("ArgumentNull_String")); - } - - DWORD dwFlags = (LCMAP_SORTKEY | dwFlagsIn); - - // - // Caller has already verified that the string is not of zero length - // - // Assert if we might hit an AV in LCMapStringEx for the invariant culture. - _ASSERTE(length > 0 || (dwFlags & LCMAP_LINGUISTIC_CASING) == 0); - { - byteCount=NewApis::LCMapStringEx(handle != NULL ? NULL : localeName, dwFlags, string, length, NULL, 0, NULL, NULL, (LPARAM) handle); - } - - //A count of 0 indicates that we either had an error or had a zero length string originally. - if (byteCount==0) - { - COMPlusThrow(kArgumentException, W("Arg_MustBeString")); - } - - // We used to use a NewArrayHolder here, but it turns out that hurts our large # process - // scalability in ASP.Net hosting scenarios, using the quick bytes instead mostly stack - // allocates and ups throughput by 8% in 100 process case, 5% in 1000 process case - { - CQuickBytesSpecifySize qbBuffer; - BYTE* pByte = (BYTE*)qbBuffer.AllocThrows(byteCount); - - { - NewApis::LCMapStringEx(handle != NULL ? NULL : localeName, dwFlags, string, length, (LPWSTR)pByte, byteCount, NULL,NULL, (LPARAM) handle); - } - - iReturnHash = COMNlsHashProvider::s_NlsHashProvider.HashSortKey(pByte, byteCount, true, additionalEntropy); - } - END_QCALL; - return(iReturnHash); -} - -inline BOOL IsInvariantLocale(STRINGREF localeName) -{ - return localeName->GetStringLength() == 0; -} - -// InternalChangeCaseChar -// -// Call LCMapStringEx with a char to make it upper or lower case -// Note that if the locale is English or Invariant we'll try just mapping it if its < 0x7f -FCIMPL5(FC_CHAR_RET, COMNlsInfo::InternalChangeCaseChar, - INT_PTR handle, // optional sort handle - INT_PTR handleOrigin, StringObject* localeNameUNSAFE, CLR_CHAR wch, CLR_BOOL bIsToUpper) -{ - CONTRACTL - { - FCALL_CHECK; - PRECONDITION(CheckPointer(localeNameUNSAFE)); - } CONTRACTL_END; - - CLR_CHAR retVal = '\0'; - int ret_LCMapStringEx = -1; - - // Dereference our string - STRINGREF localeName(localeNameUNSAFE); - - HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(localeName); - - BOOL isInvariantLocale = IsInvariantLocale(localeName); - // Check for Invariant to avoid A/V in LCMapStringEx - DWORD linguisticCasing = (isInvariantLocale) ? 0 : LCMAP_LINGUISTIC_CASING; - - { - ret_LCMapStringEx = NewApis::LCMapStringEx(handle != NULL ? NULL : localeName->GetBuffer(), - bIsToUpper?LCMAP_UPPERCASE | linguisticCasing: - LCMAP_LOWERCASE | linguisticCasing, - &wch, - 1, - &retVal, - 1, - NULL, - NULL, - (LPARAM) handle); - } - - if (0 == ret_LCMapStringEx) - { - // return value of 0 indicates failure and error value is supposed to be set. - // shouldn't ever really happen - _ASSERTE(!"catastrophic failure calling NewApis::InternalChangeCaseChar! This could be a CultureInfo or CompareInfo bug (bad localeName string) or maybe a GCHole."); - } - - HELPER_METHOD_FRAME_END(); // localeName is now unprotected - return retVal; -} -FCIMPLEND - -// InternalChangeCaseString -// -// Call LCMapStringEx with a string to make it upper or lower case -// Note that if the locale is English or Invariant we'll try just mapping it if its < 0x7f -// -// We typically expect the output string to be the same size as the input. If not -// we have to count, reallocate the output buffer, and try again. -FCIMPL5(Object*, COMNlsInfo::InternalChangeCaseString, - INT_PTR handle, // optional sort handle - INT_PTR handleOrigin, StringObject* localeNameUNSAFE, StringObject* pStringUNSAFE, CLR_BOOL bIsToUpper) -{ - CONTRACTL - { - FCALL_CHECK; - PRECONDITION(CheckPointer(pStringUNSAFE)); - PRECONDITION(CheckPointer(localeNameUNSAFE)); - } CONTRACTL_END; - - struct _gc - { - STRINGREF pResult; - STRINGREF pString; - STRINGREF pLocale; - } gc; - - gc.pResult = NULL; - gc.pString = ObjectToSTRINGREF(pStringUNSAFE); - gc.pLocale = ObjectToSTRINGREF(localeNameUNSAFE); - - HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc) - - // - // Get the length of the string. - // - int nLengthInput = gc.pString->GetStringLength(); - int nLengthOutput = nLengthInput; // initially we assume the string length does not change. - - BOOL isInvariantLocale = IsInvariantLocale(gc.pLocale); - // Check for Invariant to avoid A/V in LCMapStringEx - DWORD linguisticCasing = (isInvariantLocale) ? 0 : LCMAP_LINGUISTIC_CASING; - // Check for Invariant to avoid A/V in LCMapStringEx - - // - // Check if we have the empty string. - // - if (nLengthInput == 0) - { - gc.pResult = ObjectToSTRINGREF(gc.pString); - } - else - { - // - // Create the result string. - // - gc.pResult = StringObject::NewString(nLengthOutput); - LPWSTR pResultStr = gc.pResult->GetBuffer(); - - int result; - { - result = NewApis::LCMapStringEx(handle != NULL ? NULL : (gc.pLocale)->GetBuffer(), - bIsToUpper?LCMAP_UPPERCASE | linguisticCasing : - LCMAP_LOWERCASE | linguisticCasing, - gc.pString->GetBuffer(), - nLengthInput, - pResultStr, - nLengthOutput, - NULL, - NULL, - (LPARAM) handle); - } - - if(0 == result) - { - // Failure: Detect if that's due to insufficient buffer - if (GetLastError()!= ERROR_INSUFFICIENT_BUFFER) - { - ThrowLastError(); - } - // need to update buffer - { - nLengthOutput = NewApis::LCMapStringEx(handle != NULL ? NULL : (gc.pLocale)->GetBuffer(), - bIsToUpper?LCMAP_UPPERCASE | linguisticCasing : - LCMAP_LOWERCASE | linguisticCasing, - gc.pString->GetBuffer(), - nLengthInput, - NULL, - 0, - NULL, - NULL, - (LPARAM) handle); - } - if (nLengthOutput == 0) - { - // return value of 0 indicates failure and error value is supposed to be set. - // shouldn't ever really happen - _ASSERTE(!"catastrophic failure calling NewApis::InternalChangeCaseString! This could be a CultureInfo or CompareInfo bug (bad localeName string) or maybe a GCHole."); - } - _ASSERTE(nLengthOutput > 0); - // NOTE: The length of the required buffer does not include the terminating null character. - // So it can be used as-is for our calculations -- the length we pass in to NewString also does - // not include the terminating null character. - // MSDN documentation could be interpreted to mean that the length returned includes the terminating - // NULL character, but that's not the case. - - // NOTE: Also note that we let the GC take care of the previously allocated pResult. - - gc.pResult = StringObject::NewString(nLengthOutput); - pResultStr = gc.pResult->GetBuffer(); - { - result = NewApis::LCMapStringEx(handle != NULL ? NULL : (gc.pLocale)->GetBuffer(), - bIsToUpper?LCMAP_UPPERCASE | linguisticCasing : - LCMAP_LOWERCASE | linguisticCasing, - gc.pString->GetBuffer(), - nLengthInput, - pResultStr, - nLengthOutput, - NULL, - NULL, - (LPARAM) handle); - } - - if(0 == result) - { - // return value of 0 indicates failure and error value is supposed to be set. - // shouldn't ever really happen - _ASSERTE(!"catastrophic failure calling NewApis::InternalChangeCaseString! This could be a CultureInfo or CompareInfo bug (bad localeName string) or maybe a GCHole."); - } - } - - pResultStr[nLengthOutput] = 0; - } - - HELPER_METHOD_FRAME_END(); +// Language ID for CHT (Taiwan) +#define LANGID_ZH_TW 0x0404 +// Language ID for CHT (Hong-Kong) +#define LANGID_ZH_HK 0x0c04 - return OBJECTREFToObject(gc.pResult); -} -FCIMPLEND +#ifndef FEATURE_COREFX_GLOBALIZATION +// +// Normalization Implementation +// +#define NORMALIZATION_DLL MAKEDLLNAME(W("normalization")) +HMODULE COMNlsInfo::m_hNormalization = NULL; +PFN_NORMALIZATION_IS_NORMALIZED_STRING COMNlsInfo::m_pfnNormalizationIsNormalizedStringFunc = NULL; +PFN_NORMALIZATION_NORMALIZE_STRING COMNlsInfo::m_pfnNormalizationNormalizeStringFunc = NULL; +PFN_NORMALIZATION_INIT_NORMALIZATION COMNlsInfo::m_pfnNormalizationInitNormalizationFunc = NULL; +#endif // FEATURE_COREFX_GLOBALIZATION -/*================================InternalGetCaseInsHash================================ -**Action: -**Returns: -**Arguments: -**Exceptions: -==============================================================================*/ -FCIMPL6(INT32, COMNlsInfo::InternalGetCaseInsHash, - INT_PTR handle, // optional sort handle - INT_PTR handleOrigin, StringObject* localeNameUNSAFE, LPVOID pvStrA, CLR_BOOL bForceRandomizedHashing, INT64 additionalEntropy) +INT32 COMNlsInfo::CallGetUserDefaultUILanguage() { CONTRACTL { - FCALL_CHECK; - PRECONDITION(CheckPointer(localeNameUNSAFE)); - PRECONDITION(CheckPointer(pvStrA)); + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + SO_TOLERANT; } CONTRACTL_END; - STRINGREF localeName = ObjectToSTRINGREF(localeNameUNSAFE); - STRINGREF strA; - - INT32 result; - - BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), FCThrow(kStackOverflowException)) - - *((LPVOID *)&strA)=pvStrA; - + static INT32 s_lcid = 0; - // - // If we know that we don't have any high characters (the common case) we can - // call a hash function that knows how to do a very fast case conversion. If - // we find characters above 0x80, it's much faster to convert the entire string - // to Uppercase and then call the standard hash function on it. - // - // TODO: NLS Arrowhead -We aren't consistent with the fast casing cultures (any en? fr? de?) - if (IsCultureEnglishOrInvariant((localeName)->GetBuffer()) && // If we're en-US or Invariant - ((IS_STRING_STATE_UNDETERMINED(strA->GetHighCharState()) && // and we're undetermined - IS_FAST_CASING(strA->InternalCheckHighChars())) || // and its fast casing when determined - IS_FAST_CASING(strA->GetHighCharState()))) // or we're fast casing that's already determined - { - // Notice that for Turkish and Azeri we don't get here and shouldn't use this - // fast path because of their special Latin casing rules. - result = COMNlsHashProvider::s_NlsHashProvider.HashiStringKnownLower80(strA->GetBuffer(), strA->GetStringLength(), bForceRandomizedHashing, additionalEntropy); - } - else + // The user UI language cannot change within a process (in fact it cannot change within a logon session), + // so we cache it. We dont take a lock while initializing s_lcid for the same reason. If two threads are + // racing to initialize s_lcid, the worst thing that'll happen is that one thread will call + // GetUserDefaultUILanguage needlessly, but the final result is going to be the same. + if (s_lcid == 0) { - // Make it upper case - CQuickBytes newBuffer; - INT32 length = strA->GetStringLength(); - WCHAR *pNewStr = (WCHAR *)newBuffer.AllocThrows((length + 1) * sizeof(WCHAR)); - - // Work around an A/V in LCMapStringEx for the invariant culture. - // Revisit this after Vista SP2 has been deployed everywhere. - DWORD linguisticCasing = 0; - if (localeName->GetStringLength() > 0) // if not the invariant culture... - { - linguisticCasing = LCMAP_LINGUISTIC_CASING; - } - - int lcmapResult; - { - lcmapResult = NewApis::LCMapStringEx(handle != NULL ? NULL : localeName->GetBuffer(), - LCMAP_UPPERCASE | linguisticCasing, - strA->GetBuffer(), - length, - pNewStr, - length, - NULL, NULL, (LPARAM) handle); - } - - if (lcmapResult == 0) + INT32 s_lcidTemp = GetUserDefaultUILanguage(); + if (s_lcidTemp == LANGID_ZH_TW) { - // return value of 0 indicates failure and error value is supposed to be set. - // shouldn't ever really happen - _ASSERTE(!"catastrophic failure calling NewApis::InternalGetCaseInsHash! This could be a CultureInfo or CompareInfo bug (bad localeName string) or maybe a GCHole."); + // If the UI language ID is 0x0404, we need to do extra check to decide + // the real UI language, since MUI (in CHT)/HK/TW Windows SKU all uses 0x0404 as their CHT language ID. + if (!NewApis::IsZhTwSku()) + { + s_lcidTemp = LANGID_ZH_HK; + } } - pNewStr[length]='\0'; - - // Get hash for the upper case of the new string - - result = COMNlsHashProvider::s_NlsHashProvider.HashString(pNewStr, length, (BOOL)bForceRandomizedHashing, additionalEntropy); + s_lcid = s_lcidTemp; } - END_SO_INTOLERANT_CODE - - return result; + return s_lcid; } -FCIMPLEND -// Fast path for finding a String using OrdinalIgnoreCase rules -// returns true if the fast path succeeded, with foundIndex set to the location where the String was found or -1 -// Returns false when FindStringOrdinal isn't handled (we don't have our own general version of this function to fall back on) -// Note for future optimizations: kernel32!FindStringOrdinal(ignoreCase=TRUE) uses per-character table lookup -// to map to upper case before comparison, but isn't otherwise optimized -BOOL QCALLTYPE COMNlsInfo::InternalTryFindStringOrdinalIgnoreCase( - __in DWORD dwFindNLSStringFlags, // mutually exclusive flags: FIND_FROMSTART, FIND_STARTSWITH, FIND_FROMEND, FIND_ENDSWITH - __in_ecount(cchSource) LPCWSTR lpStringSource, // the string we search in - __in int cchSource, // number of characters lpStringSource after sourceIndex - __in int sourceIndex, // index from where the search will start in lpStringSource - __in_ecount(cchValue) LPCWSTR lpStringValue, // the string we search for - __in int cchValue, - __out int* foundIndex) // the index in lpStringSource where we found lpStringValue +//////////////////////////////////////////////////////////////////////////// +// +// InternalGetGlobalizedHashCode +// +//////////////////////////////////////////////////////////////////////////// +INT32 QCALLTYPE COMNlsInfo::InternalGetGlobalizedHashCode(INT_PTR handle, LPCWSTR localeName, LPCWSTR string, INT32 length, INT32 dwFlagsIn, INT64 additionalEntropy) { CONTRACTL { QCALL_CHECK; - PRECONDITION(lpStringSource != NULL); - PRECONDITION(lpStringValue != NULL); - PRECONDITION(cchSource>=0); - PRECONDITION(cchValue>=0); - PRECONDITION((dwFindNLSStringFlags & FIND_NLS_STRING_FLAGS_NEGATION) == 0); + PRECONDITION(CheckPointer(localeName)); + PRECONDITION(CheckPointer(string, NULL_OK)); } CONTRACTL_END; - BOOL result = FALSE; - + INT32 iReturnHash = 0; BEGIN_QCALL; - LPCWSTR lpSearchStart = NULL; - if (dwFindNLSStringFlags & (FIND_FROMEND | FIND_ENDSWITH)) - { - lpSearchStart = &lpStringSource[sourceIndex - cchSource + 1]; - } - else { - lpSearchStart = &lpStringSource[sourceIndex]; - } - { -#ifndef FEATURE_CORESYSTEM - // kernel function pointer - typedef int (WINAPI *PFNFindStringOrdinal)(DWORD, LPCWSTR, INT, LPCWSTR, INT, BOOL); - static PFNFindStringOrdinal FindStringOrdinal = NULL; - - // initizalize kernel32!FindStringOrdinal - if (FindStringOrdinal == NULL) - { - PFNFindStringOrdinal result = NULL; - - HMODULE hMod=WszGetModuleHandle(WINDOWS_KERNEL32_DLLNAME_W); - if(hMod != NULL) - result=(PFNFindStringOrdinal)GetProcAddress(hMod,"FindStringOrdinal"); - - FindStringOrdinal = (result != NULL) ? result : (PFNFindStringOrdinal)-1; - } + int byteCount = 0; - // call into the kernel - if (FindStringOrdinal != (PFNFindStringOrdinal)-1) -#endif - { - *foundIndex = FindStringOrdinal( - dwFindNLSStringFlags, - lpSearchStart, - cchSource, - lpStringValue, - cchValue, - TRUE); - result = TRUE; - } - } - // if we found the pattern string, fixup the index before we return - if (*foundIndex >= 0) - { - if (dwFindNLSStringFlags & (FIND_FROMEND | FIND_ENDSWITH)) - *foundIndex += (sourceIndex - cchSource + 1); - else - *foundIndex += sourceIndex; + // + // Make sure there is a string. + // + if (!string) { + COMPlusThrowArgumentNull(W("string"),W("ArgumentNull_String")); } - END_QCALL; - - return result; -} - - -// InternalCompareStringOrdinalIgnoreCase -// -// Call ::CompareStringOrdinal for native ordinal behavior -INT32 QCALLTYPE COMNlsInfo::InternalCompareStringOrdinalIgnoreCase( - LPCWSTR string1, INT32 index1, - LPCWSTR string2, INT32 index2, - INT32 length1, - INT32 length2) -{ - CONTRACTL - { - QCALL_CHECK; - PRECONDITION(CheckPointer(string1)); - PRECONDITION(CheckPointer(string2)); - } CONTRACTL_END; - INT32 result = 0; + DWORD dwFlags = (LCMAP_SORTKEY | dwFlagsIn); - BEGIN_QCALL; // - // Get the arguments. - // We assume the caller checked them before calling us + // Caller has already verified that the string is not of zero length // - - // We don't allow the -1 that native code allows - _ASSERT(length1 >= 0); - _ASSERT(length2 >= 0); - - // Do the comparison + // Assert if we might hit an AV in LCMapStringEx for the invariant culture. + _ASSERTE(length > 0 || (dwFlags & LCMAP_LINGUISTIC_CASING) == 0); { - result = NewApis::CompareStringOrdinal(string1 + index1, length1, string2 + index2, length2, TRUE); + byteCount=NewApis::LCMapStringEx(handle != NULL ? NULL : localeName, dwFlags, string, length, NULL, 0, NULL, NULL, (LPARAM) handle); } - // The native call shouldn't fail - _ASSERT(result != 0); - if (result == 0) + //A count of 0 indicates that we either had an error or had a zero length string originally. + if (byteCount==0) { - // return value of 0 indicates failure and error value is supposed to be set. - // shouldn't ever really happen - _ASSERTE(!"catastrophic failure calling NewApis::CompareStringOrdinal! This is usually due to bad arguments."); + COMPlusThrow(kArgumentException, W("Arg_MustBeString")); } - // Adjust the result to the expected -1, 0, 1 result - result -= 2; + // We used to use a NewArrayHolder here, but it turns out that hurts our large # process + // scalability in ASP.Net hosting scenarios, using the quick bytes instead mostly stack + // allocates and ups throughput by 8% in 100 process case, 5% in 1000 process case + { + CQuickBytesSpecifySize qbBuffer; + BYTE* pByte = (BYTE*)qbBuffer.AllocThrows(byteCount); + + { + NewApis::LCMapStringEx(handle != NULL ? NULL : localeName, dwFlags, string, length, (LPWSTR)pByte, byteCount, NULL,NULL, (LPARAM) handle); + } + iReturnHash = COMNlsHashProvider::s_NlsHashProvider.HashSortKey(pByte, byteCount, true, additionalEntropy); + } END_QCALL; - - return result; + return(iReturnHash); } /** @@ -1964,7 +185,6 @@ FCIMPL0(CodePageDataItem *, COMNlsInfo::nativeGetCodePageTableDataPointer) } FCIMPLEND - #ifndef FEATURE_COREFX_GLOBALIZATION // // Normalization @@ -2105,13 +325,6 @@ void QCALLTYPE COMNlsInfo::nativeNormalizationInitNormalization(int NormForm, BY #endif // FEATURE_COREFX_GLOBALIZATION - -// -// This table should be sorted using case-insensitive ordinal order. -// In the managed code, String.CompareStringOrdinalWC() is used to sort this. -// - - /** * This function returns the number of items in EncodingDataTable. */ @@ -2123,289 +336,3 @@ FCIMPL0(INT32, COMNlsInfo::nativeGetNumEncodingItems) return (m_nEncodingDataTableItems); } FCIMPLEND - -// Return true if we're on Windows 7 (ie: if we have neutral native support) -BOOL COMNlsInfo::IsWindows7() -{ - static BOOL bChecked=FALSE; - static BOOL bIsWindows7=FALSE; - - if (!bChecked) - { - // LOCALE_INEUTRAL is first supported on Windows 7 - if (GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_INEUTRAL, NULL, 0) != 0) - { - // Success, we're win7 - bIsWindows7 = TRUE; - } - - // Either way we checked now - bChecked = TRUE; - } - - return bIsWindows7; -} - -// -// QCall implementation -// -int QCALLTYPE COMNlsInfo::InternalFindNLSStringEx( - __in_opt INT_PTR handle, // optional sort handle - __in_opt INT_PTR handleOrigin, - __in_z LPCWSTR lpLocaleName, // locale name - __in int dwFindNLSStringFlags, // search falg - __in_ecount(cchSource) LPCWSTR lpStringSource, // the string we search in - __in int cchSource, // number of characters lpStringSource after sourceIndex - __in int sourceIndex, // index from where the search will start in lpStringSource - __in_ecount(cchValue) LPCWSTR lpStringValue, // the string we search for - __in int cchValue, // length of the string we search for - __out_opt LPINT pcchFound) // length of the string we found in source -{ - CONTRACTL { - QCALL_CHECK; - PRECONDITION(lpLocaleName != NULL); - PRECONDITION(lpStringSource != NULL); - PRECONDITION(lpStringValue != NULL); - PRECONDITION(cchSource>=0); - PRECONDITION(cchValue>=0); - } CONTRACTL_END; - - int retValue = -1; - - BEGIN_QCALL; - - - #define RESERVED_FIND_ASCII_STRING 0x20000000 // This flag used only to tell the sorting DLL can assume the string characters are in ASCII. - - - dwFindNLSStringFlags &= ~RESERVED_FIND_ASCII_STRING; - - if (cchValue == 0) - { - retValue = sourceIndex; // keep Whidbey compatibility - goto lExit; - } - - if (sourceIndex<0 || cchSource<0 || - ((dwFindNLSStringFlags & (FIND_FROMEND | FIND_ENDSWITH)) && (sourceIndex+1= 0) - { - retValue += sourceIndex - cchSource + 1; - } - } - else - { - retValue = NewApis::IndexOfString( - lpLocaleName, - &lpStringSource[sourceIndex], - cchSource, - lpStringValue, - cchValue, - dwFindNLSStringFlags & FIND_NLS_STRING_FLAGS_NEGATION, - dwFindNLSStringFlags & FIND_STARTSWITH, - pcchFound); - - if (retValue >= 0) - { - retValue += sourceIndex; - } - } - } - else - { - if (dwFindNLSStringFlags & (FIND_FROMEND | FIND_ENDSWITH)) - { - { - retValue = NewApis::FindNLSStringEx( - handle != NULL ? NULL : lpLocaleName, - dwFindNLSStringFlags, - &lpStringSource[sourceIndex - cchSource + 1], - cchSource, - lpStringValue, - cchValue, - pcchFound, - NULL, NULL, (LPARAM) handle); - } - - if (retValue >= 0) - { - retValue += sourceIndex - cchSource + 1; - } - } - else - { - { - retValue = NewApis::FindNLSStringEx( - handle != NULL ? NULL : lpLocaleName, - dwFindNLSStringFlags, - &lpStringSource[sourceIndex], - cchSource, - lpStringValue, - cchValue, - pcchFound, - NULL, NULL, (LPARAM) handle); - } - - if (retValue >= 0) - { - retValue += sourceIndex; - } - } - } - -lExit: - - END_QCALL; - - return retValue; -} - - -int QCALLTYPE COMNlsInfo::InternalGetSortKey( - __in_opt INT_PTR handle, // PSORTHANDLE - __in_opt INT_PTR handleOrigin, - __in_z LPCWSTR pLocaleName, // locale name - __in int flags, // flags - __in_ecount(cchSource) LPCWSTR pStringSource, // Source string - __in int cchSource, // number of characters in lpStringSource - __in_ecount(cchTarget) PBYTE pTarget, // Target data buffer (may be null to count) - __in int cchTarget) // Character count for target buffer -{ - CONTRACTL { - QCALL_CHECK; - PRECONDITION(pLocaleName != NULL); - PRECONDITION(pStringSource != NULL); -// PRECONDITION(pTarget != NULL); - PRECONDITION(cchSource>=0); -// PRECONDITION(cchTarget>=0); - } CONTRACTL_END; - - int retValue = 0; - - BEGIN_QCALL; - - - { - // Just call NewApis::LCMapStringEx to do our work - retValue = NewApis::LCMapStringEx(handle != NULL ? NULL : pLocaleName, - flags | LCMAP_SORTKEY, - pStringSource, - cchSource, - (LPWSTR)pTarget, - cchTarget, - NULL, - NULL, - (LPARAM) handle); - } - END_QCALL; - - return retValue; -} - - -// We allow InternalInitSortHandle to return a NULL value -// this is the case for Silverlight or when the appdomain has custom sorting. -// So all the methods that take a SortHandle, also have to -// be able to just call the slower api that looks up the tables based on the locale name -INT_PTR QCALLTYPE COMNlsInfo::InternalInitSortHandle(LPCWSTR localeName, __out INT_PTR* handleOrigin) -{ - CONTRACTL { - QCALL_CHECK; - PRECONDITION(localeName != NULL); - } CONTRACTL_END; - - INT_PTR pSort = NULL; - - BEGIN_QCALL; - - pSort = InitSortHandleHelper(localeName, handleOrigin); - - END_QCALL; - - return pSort; -} - -INT_PTR COMNlsInfo::InitSortHandleHelper(LPCWSTR localeName, __out INT_PTR* handleOrigin) -{ - CONTRACTL { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - CANNOT_TAKE_LOCK; - PRECONDITION(localeName != NULL); - } CONTRACTL_END; - - INT_PTR pSort = NULL; - - // coreclr will try to initialize the handle and if running on downlevel it'll just return null - pSort = InternalInitOsSortHandle(localeName, handleOrigin); - return pSort; -} - - -// Can return NULL -INT_PTR COMNlsInfo::InternalInitOsSortHandle(LPCWSTR localeName, __out INT_PTR* handleOrigin) -{ - CONTRACTL { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - CANNOT_TAKE_LOCK; - PRECONDITION(localeName != NULL); - } CONTRACTL_END; - - INT_PTR pSort = NULL; - - AppDomain* curDomain = GetAppDomain(); - - if (RunningOnWin8()) - { - LPARAM lSortHandle; - int ret; - - { - ret = NewApis::LCMapStringEx(localeName, LCMAP_SORTHANDLE, NULL, 0, (LPWSTR) &lSortHandle, sizeof(LPARAM), NULL, NULL, 0); - *handleOrigin = (INT_PTR) NewApis::LCMapStringEx; - } - - if (ret != 0) - { - pSort = (INT_PTR) lSortHandle; - } - } - - return pSort; -} - -BOOL QCALLTYPE COMNlsInfo::InternalGetNlsVersionEx(INT_PTR handle, INT_PTR handleOrigin, LPCWSTR lpLocaleName, NLSVERSIONINFOEX * lpVersionInformation) -{ - CONTRACTL { - QCALL_CHECK; - } CONTRACTL_END; - - BOOL ret = FALSE; - - BEGIN_QCALL; - ret = GetNLSVersionEx(COMPARE_STRING, lpLocaleName, lpVersionInformation); - END_QCALL; - - return ret; -} - diff --git a/src/coreclr/src/mscorlib/src/System/Globalization/Calendar.cs b/src/coreclr/src/mscorlib/src/System/Globalization/Calendar.cs index 8d10353..422b72a 100644 --- a/src/coreclr/src/mscorlib/src/System/Globalization/Calendar.cs +++ b/src/coreclr/src/mscorlib/src/System/Globalization/Calendar.cs @@ -846,7 +846,6 @@ namespace System.Globalization internal static int GetSystemTwoDigitYearSetting(CalendarId CalID, int defaultYearValue) { - // Call nativeGetTwoDigitYearMax int twoDigitYearMax = CalendarData.GetTwoDigitYearMax(CalID); if (twoDigitYearMax < 0) { diff --git a/src/coreclr/src/vm/ecalllist.h b/src/coreclr/src/vm/ecalllist.h index 008a0e7..a420c52 100644 --- a/src/coreclr/src/vm/ecalllist.h +++ b/src/coreclr/src/vm/ecalllist.h @@ -867,12 +867,6 @@ FCFuncEnd() #if !defined(FEATURE_COREFX_GLOBALIZATION) FCFuncStart(gCompareInfoFuncs) QCFuncElement("InternalGetGlobalizedHashCode", COMNlsInfo::InternalGetGlobalizedHashCode) - QCFuncElement("InternalCompareString", COMNlsInfo::InternalCompareString) - QCFuncElement("InternalFindNLSStringEx", COMNlsInfo::InternalFindNLSStringEx) - QCFuncElement("NativeInternalInitSortHandle", COMNlsInfo::InternalInitSortHandle) - QCFuncElement("InternalIsSortable", COMNlsInfo::InternalIsSortable) - QCFuncElement("InternalGetSortKey", COMNlsInfo::InternalGetSortKey) - QCFuncElement("InternalGetNlsVersionEx", COMNlsInfo::InternalGetNlsVersionEx) FCFuncEnd() FCFuncStart(gEncodingTableFuncs) @@ -880,39 +874,6 @@ FCFuncStart(gEncodingTableFuncs) FCFuncElement("GetEncodingData", COMNlsInfo::nativeGetEncodingTableDataPointer) FCFuncElement("GetCodePageData", COMNlsInfo::nativeGetCodePageTableDataPointer) FCFuncEnd() - -FCFuncStart(gCalendarDataFuncs) - FCFuncElement("nativeGetTwoDigitYearMax", CalendarData::nativeGetTwoDigitYearMax) - FCFuncElement("nativeGetCalendarData", CalendarData::nativeGetCalendarData) - FCFuncElement("nativeGetCalendars", CalendarData::nativeGetCalendars) -FCFuncEnd() - -FCFuncStart(gCultureDataFuncs) - FCFuncElement("nativeGetNumberFormatInfoValues", COMNlsInfo::nativeGetNumberFormatInfoValues) - FCFuncElement("nativeEnumTimeFormats", CalendarData::nativeEnumTimeFormats) - FCFuncElement("LCIDToLocaleName", COMNlsInfo::LCIDToLocaleName) - FCFuncElement("LocaleNameToLCID", COMNlsInfo::LocaleNameToLCID) - - QCFuncElement("nativeEnumCultureNames", COMNlsInfo::nativeEnumCultureNames) - -FCFuncEnd() - -FCFuncStart(gCultureInfoFuncs) - QCFuncElement("InternalGetDefaultLocaleName", COMNlsInfo::InternalGetDefaultLocaleName) - FCFuncElement("nativeGetLocaleInfoEx", COMNlsInfo::nativeGetLocaleInfoEx) - FCFuncElement("nativeGetLocaleInfoExInt", COMNlsInfo::nativeGetLocaleInfoExInt) - - QCFuncElement("InternalGetUserDefaultUILanguage", COMNlsInfo::InternalGetUserDefaultUILanguage) - QCFuncElement("InternalGetSystemDefaultUILanguage", COMNlsInfo::InternalGetSystemDefaultUILanguage) -FCFuncEnd() - -FCFuncStart(gTextInfoFuncs) - FCFuncElement("InternalChangeCaseChar", COMNlsInfo::InternalChangeCaseChar) - FCFuncElement("InternalChangeCaseString", COMNlsInfo::InternalChangeCaseString) - FCFuncElement("InternalGetCaseInsHash", COMNlsInfo::InternalGetCaseInsHash) - QCFuncElement("InternalCompareStringOrdinalIgnoreCase", COMNlsInfo::InternalCompareStringOrdinalIgnoreCase) - QCFuncElement("InternalTryFindStringOrdinalIgnoreCase", COMNlsInfo::InternalTryFindStringOrdinalIgnoreCase) -FCFuncEnd() #endif // !defined(FEATURE_COREFX_GLOBALIZATION) #ifdef FEATURE_COREFX_GLOBALIZATION @@ -1403,16 +1364,9 @@ FCClassElement("AssemblyName", "System.Reflection", gAssemblyNameFuncs) FCClassElement("Assert", "System.Diagnostics", gDiagnosticsAssert) FCClassElement("BCLDebug", "System", gBCLDebugFuncs) FCClassElement("Buffer", "System", gBufferFuncs) -#if !defined(FEATURE_COREFX_GLOBALIZATION) -FCClassElement("CalendarData", "System.Globalization", gCalendarDataFuncs) -#endif // !defined(FEATURE_COREFX_GLOBALIZATION) FCClassElement("CompareInfo", "System.Globalization", gCompareInfoFuncs) FCClassElement("CompatibilitySwitch", "System.Runtime.Versioning", gCompatibilitySwitchFuncs) FCClassElement("CriticalHandle", "System.Runtime.InteropServices", gCriticalHandleFuncs) -#if !defined(FEATURE_COREFX_GLOBALIZATION) -FCClassElement("CultureData", "System.Globalization", gCultureDataFuncs) -FCClassElement("CultureInfo", "System.Globalization", gCultureInfoFuncs) -#endif FCClassElement("Currency", "System", gCurrencyFuncs) FCClassElement("CustomAttribute", "System.Reflection", gCOMCustomAttributeFuncs) FCClassElement("CustomAttributeEncodedArgument", "System.Reflection", gCustomAttributeEncodedArgument) @@ -1521,9 +1475,6 @@ FCClassElement("String", "System", gStringFuncs) FCClassElement("StringBuilder", "System.Text", gStringBufferFuncs) FCClassElement("StubHelpers", "System.StubHelpers", gStubHelperFuncs) FCClassElement("SynchronizationContext", "System.Threading", gContextSynchronizationFuncs) -#if !defined(FEATURE_COREFX_GLOBALIZATION) -FCClassElement("TextInfo", "System.Globalization", gTextInfoFuncs) -#endif // !defined(FEATURE_COREFX_GLOBALIZATION) FCClassElement("Thread", "System.Threading", gThreadFuncs) FCClassElement("ThreadPool", "System.Threading", gThreadPoolFuncs) FCClassElement("TimerQueue", "System.Threading", gTimerFuncs) diff --git a/src/coreclr/src/vm/mscorlib.cpp b/src/coreclr/src/vm/mscorlib.cpp index 4d98eba..963e890 100644 --- a/src/coreclr/src/vm/mscorlib.cpp +++ b/src/coreclr/src/vm/mscorlib.cpp @@ -34,7 +34,6 @@ #include "excep.h" #include "fcall.h" #include "nlsinfo.h" -#include "calendardata.h" #include "commodule.h" #include "marshalnative.h" #include "system.h" diff --git a/src/coreclr/src/vm/mscorlib.h b/src/coreclr/src/vm/mscorlib.h index 178ce2c..f0850b7 100644 --- a/src/coreclr/src/vm/mscorlib.h +++ b/src/coreclr/src/vm/mscorlib.h @@ -304,29 +304,6 @@ DEFINE_FIELD_U(m_encodedEnumType, CustomAttributeType, m_enumType) DEFINE_FIELD_U(m_encodedArrayType, CustomAttributeType, m_arrayType) DEFINE_FIELD_U(m_padding, CustomAttributeType, m_padding) -#ifndef FEATURE_COREFX_GLOBALIZATION -DEFINE_CLASS_U(Globalization, CalendarData, CalendarDataBaseObject) -DEFINE_FIELD_U(sNativeName, CalendarDataBaseObject, sNativeName) -DEFINE_FIELD_U(saShortDates, CalendarDataBaseObject, saShortDates) -DEFINE_FIELD_U(saYearMonths, CalendarDataBaseObject, saYearMonths) -DEFINE_FIELD_U(saLongDates, CalendarDataBaseObject, saLongDates) -DEFINE_FIELD_U(sMonthDay, CalendarDataBaseObject, sMonthDay) -DEFINE_FIELD_U(saEraNames, CalendarDataBaseObject, saEraNames) -DEFINE_FIELD_U(saAbbrevEraNames, CalendarDataBaseObject, saAbbrevEraNames) -DEFINE_FIELD_U(saAbbrevEnglishEraNames,CalendarDataBaseObject, saAbbrevEnglishEraNames) -DEFINE_FIELD_U(saDayNames, CalendarDataBaseObject, saDayNames) -DEFINE_FIELD_U(saAbbrevDayNames, CalendarDataBaseObject, saAbbrevDayNames) -DEFINE_FIELD_U(saSuperShortDayNames, CalendarDataBaseObject, saSuperShortDayNames) -DEFINE_FIELD_U(saMonthNames, CalendarDataBaseObject, saMonthNames) -DEFINE_FIELD_U(saAbbrevMonthNames, CalendarDataBaseObject, saAbbrevMonthNames) -DEFINE_FIELD_U(saMonthGenitiveNames, CalendarDataBaseObject, saMonthGenitiveNames) -DEFINE_FIELD_U(saAbbrevMonthGenitiveNames, CalendarDataBaseObject, saAbbrevMonthGenitiveNames) -DEFINE_FIELD_U(saLeapYearMonthNames, CalendarDataBaseObject, saLeapYearMonthNames) -DEFINE_FIELD_U(iTwoDigitYearMax, CalendarDataBaseObject, iTwoDigitYearMax) -DEFINE_FIELD_U(iCurrentEra, CalendarDataBaseObject, iCurrentEra) -DEFINE_FIELD_U(bUseUserOverrides, CalendarDataBaseObject, bUseUserOverrides) -#endif - DEFINE_CLASS_U(Globalization, CultureInfo, CultureInfoBaseObject) DEFINE_FIELD_U(compareInfo, CultureInfoBaseObject, compareInfo) DEFINE_FIELD_U(textInfo, CultureInfoBaseObject, textInfo) diff --git a/src/coreclr/src/vm/object.h b/src/coreclr/src/vm/object.h index 1a9d8fd..65969bf 100644 --- a/src/coreclr/src/vm/object.h +++ b/src/coreclr/src/vm/object.h @@ -1606,43 +1606,6 @@ public: }; // class CultureInfoBaseObject -#ifndef FEATURE_COREFX_GLOBALIZATION -typedef DPTR(class CalendarDataBaseObject) PTR_CalendarDataBaseObject; -class CalendarDataBaseObject : public Object -{ -public: - /* 0x000 */ STRINGREF sNativeName ; // Calendar Name for the locale - - // Formats - - /* 0x008 */ PTRARRAYREF saShortDates ; // Short Data format, default first - /* 0x010 */ PTRARRAYREF saYearMonths ; // Year/Month Data format, default first - /* 0x018 */ PTRARRAYREF saLongDates ; // Long Data format, default first - /* 0x020 */ STRINGREF sMonthDay ; // Month/Day format - - // Calendar Parts Names - /* 0x028 */ PTRARRAYREF saEraNames ; // Names of Eras - /* 0x030 */ PTRARRAYREF saAbbrevEraNames ; // Abbreviated Era Names - /* 0x038 */ PTRARRAYREF saAbbrevEnglishEraNames ; // Abbreviated Era Names in English - /* 0x040 */ PTRARRAYREF saDayNames ; // Day Names, null to use locale data, starts on Sunday - /* 0x048 */ PTRARRAYREF saAbbrevDayNames ; // Abbrev Day Names, null to use locale data, starts on Sunday - /* 0x050 */ PTRARRAYREF saSuperShortDayNames ; // Super short Day of week names - /* 0x058 */ PTRARRAYREF saMonthNames ; // Month Names (13) - /* 0x060 */ PTRARRAYREF saAbbrevMonthNames ; // Abbrev Month Names (13) - /* 0x068 */ PTRARRAYREF saMonthGenitiveNames ; // Genitive Month Names (13) - /* 0x070 */ PTRARRAYREF saAbbrevMonthGenitiveNames ; // Genitive Abbrev Month Names (13) - /* 0x078 */ PTRARRAYREF saLeapYearMonthNames ; // Multiple strings for the month names in a leap year. - - // Integers at end to make marshaller happier - /* 0x080 */ INT32 iTwoDigitYearMax ; // Max 2 digit year (for Y2K bug data entry) - /* 0x084 */ INT32 iCurrentEra ; // current era # (usually 1) - - // Use overrides? - /* 0x088 */ CLR_BOOL bUseUserOverrides ; // True if we want user overrides. -}; // class CalendarDataBaseObject -#endif - - typedef DPTR(class ThreadBaseObject) PTR_ThreadBaseObject; class ThreadBaseObject : public Object {