From ee9a5359f046b079ff7daa1e93302587704a34ec Mon Sep 17 00:00:00 2001 From: Meri Khamoyan Date: Thu, 6 Apr 2023 09:07:05 +0200 Subject: [PATCH 01/16] Implementations for local int, gouping size and timeformat --- .../Common/src/Interop/Interop.Locale.OSX.cs | 12 + .../tests/Hybrid/HybridMode.cs | 83 ++++ .../System/Globalization/CultureData.OSX.cs | 100 +++- .../System/Globalization/CultureData.Unix.cs | 4 + .../src/System/Globalization/CultureData.cs | 24 +- .../System.Globalization.Native/entrypoints.c | 4 + .../System.Globalization.Native/pal_locale.m | 457 ++++++++++++++++++ .../pal_localeNumberData.h | 11 + .../pal_localeStringData.h | 10 +- .../pal_locale_internal.h | 28 ++ 10 files changed, 724 insertions(+), 9 deletions(-) diff --git a/src/libraries/Common/src/Interop/Interop.Locale.OSX.cs b/src/libraries/Common/src/Interop/Interop.Locale.OSX.cs index 52882346b63222..c07a54dcf09d33 100644 --- a/src/libraries/Common/src/Interop/Interop.Locale.OSX.cs +++ b/src/libraries/Common/src/Interop/Interop.Locale.OSX.cs @@ -12,5 +12,17 @@ internal static partial class Globalization [LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetLocaleInfoStringNative", StringMarshalling = StringMarshalling.Utf8)] internal static unsafe partial string GetLocaleInfoStringNative(string localeName, uint localeStringData); + + [LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetLocaleInfoIntNative", StringMarshalling = StringMarshalling.Utf16)] + internal static partial int GetLocaleInfoIntNative(string localeName, uint localeNumberData); + + [LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetLocaleInfoPrimaryGroupingSizeNative", StringMarshalling = StringMarshalling.Utf16)] + internal static partial int GetLocaleInfoPrimaryGroupingSizeNative(string localeName, uint localeGroupingData); + + [LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetLocaleInfoSecondaryGroupingSizeNative", StringMarshalling = StringMarshalling.Utf16)] + internal static partial int GetLocaleInfoSecondaryGroupingSizeNative(string localeName, uint localeGroupingData); + + [LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetLocaleTimeFormatNative", StringMarshalling = StringMarshalling.Utf16)] + internal static unsafe partial string GetLocaleTimeFormatNative(string localeName, [MarshalAs(UnmanagedType.Bool)] bool shortFormat); } } diff --git a/src/libraries/System.Globalization/tests/Hybrid/HybridMode.cs b/src/libraries/System.Globalization/tests/Hybrid/HybridMode.cs index f5443f8c2c3c01..68545402090306 100644 --- a/src/libraries/System.Globalization/tests/Hybrid/HybridMode.cs +++ b/src/libraries/System.Globalization/tests/Hybrid/HybridMode.cs @@ -44,5 +44,88 @@ public void TwoLetterISOLanguageName(string name, string expected) { Assert.Equal(expected, new CultureInfo(name).TwoLetterISOLanguageName); } + + /*public static IEnumerable FirstDayOfWeek_Get_TestData() + { + yield return new object[] { DateTimeFormatInfo.InvariantInfo, DayOfWeek.Sunday }; + yield return new object[] { new CultureInfo("en-US", false).DateTimeFormat, DayOfWeek.Sunday }; + yield return new object[] { new CultureInfo("fr-FR", false).DateTimeFormat, DayOfWeek.Monday }; + } + + [Theory] + [MemberData(nameof(FirstDayOfWeek_Get_TestData))] + public void FirstDayOfWeek(DateTimeFormatInfo format, DayOfWeek expected) + { + Assert.Equal(expected, format.FirstDayOfWeek); + }*/ + + [Theory] + [InlineData(DayOfWeek.Sunday)] + [InlineData(DayOfWeek.Monday)] + [InlineData(DayOfWeek.Tuesday)] + [InlineData(DayOfWeek.Wednesday)] + [InlineData(DayOfWeek.Thursday)] + [InlineData(DayOfWeek.Friday)] + [InlineData(DayOfWeek.Saturday)] + public void FirstDayOfWeek_Set_GetReturnsExpected(DayOfWeek value) + { + var format = new DateTimeFormatInfo(); + format.FirstDayOfWeek = value; + Assert.Equal(value, format.FirstDayOfWeek); + } + + public static IEnumerable CurrencyNegativePatternTestLocales() + { + yield return new object[] { "en-US" }; + yield return new object[] { "en-CA" }; + yield return new object[] { "fa-IR" }; + yield return new object[] { "fr-CD" }; + yield return new object[] { "fr-CA" }; + } + + [Theory] + [MemberData(nameof(CurrencyNegativePatternTestLocales))] + public void CurrencyNegativePattern_Get_ReturnsExpected_ByLocale(string locale) + { + CultureInfo culture; + try + { + culture = CultureInfo.GetCultureInfo(locale); + } + catch (CultureNotFoundException) + { + return; // ignore unsupported culture + } + + NumberFormatInfo format = culture.NumberFormat; + Assert.Contains(format.CurrencyNegativePattern, GetCurrencyNegativePatterns(locale)); + } + + internal static int[] GetCurrencyNegativePatterns(string localeName) + { + // CentOS uses an older ICU than Ubuntu, which means the "Linux" values need to allow for + // multiple values, since we can't tell which version of ICU we are using, or whether we are + // on CentOS or Ubuntu. + // When multiple values are returned, the "older" ICU value is returned last. + + switch (localeName) + { + case "en-US": + return new int[] { 1, 0 }; + case "en-CA": + return new int[] { 1, 0 }; + case "fa-IR": + return new int[] { 1, 0 }; + case "fr-CD": + return new int[] { 8, 15 }; + case "as": + return new int[] { 9 }; + case "fr-CA": + return new int[] { 8, 15 }; + } + + return new int[] { 0 }; + //throw DateTimeFormatInfoData.GetCultureNotSupportedException(CultureInfo.GetCultureInfo(localeName)); + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.OSX.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.OSX.cs index 870d8504fb9678..6b8780d5bdf2be 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.OSX.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.OSX.cs @@ -21,7 +21,7 @@ private bool InitNativeCultureDataCore() return true; } - internal static unsafe string GetLocaleNameNative(string localeName) + internal static string GetLocaleNameNative(string localeName) { return Interop.Globalization.GetLocaleNameNative(localeName); } @@ -36,11 +36,107 @@ private string GetLocaleInfoNative(LocaleStringData type) // For LOCALE_SPARENT we need the option of using the "real" name (forcing neutral names) instead of the // "windows" name, which can be specific for downlevel (< windows 7) os's. - private static unsafe string GetLocaleInfoNative(string localeName, LocaleStringData type) + private static string GetLocaleInfoNative(string localeName, LocaleStringData type) { Debug.Assert(localeName != null, "[CultureData.GetLocaleInfoNative] Expected localeName to be not be null"); return Interop.Globalization.GetLocaleInfoStringNative(localeName, (uint)type); } + + private int GetLocaleInfoNative(LocaleNumberData type) + { + Debug.Assert(_sWindowsName != null, "[CultureData.GetLocaleInfoNative(LocaleNumberData)] Expected _sWindowsName to be populated already"); + + switch (type) + { + case LocaleNumberData.CalendarType: + // returning 0 will cause the first supported calendar to be returned, which is the preferred calendar + return 0; + } + + + return Interop.Globalization.GetLocaleInfoIntNative(_sWindowsName, (uint)type); + } + + private int[] GetLocaleInfoNative(LocaleGroupingData type) + { + Debug.Assert(_sWindowsName != null, "[CultureData.GetLocaleInfoNative(LocaleGroupingData)] Expected _sWindowsName to be populated already"); + + int primaryGroupingSize = Interop.Globalization.GetLocaleInfoPrimaryGroupingSizeNative(_sWindowsName, (uint)type); + int secondaryGroupingSize = Interop.Globalization.GetLocaleInfoSecondaryGroupingSizeNative(_sWindowsName, (uint)type); + + if (secondaryGroupingSize == 0) + { + return new int[] { primaryGroupingSize }; + } + + return new int[] { primaryGroupingSize, secondaryGroupingSize }; + } + + private string GetTimeFormatStringNative() => GetTimeFormatStringNative(shortFormat: false); + + private unsafe string GetTimeFormatStringNative(bool shortFormat) + { + Debug.Assert(_sWindowsName != null, "[CultureData.GetTimeFormatStringNative(bool shortFormat)] Expected _sWindowsName to be populated already"); + + string result = Interop.Globalization.GetLocaleTimeFormatNative(_sWindowsName, shortFormat); + + return ConvertIcuTimeFormatString(result); + } + + private static string ConvertNativeTimeFormatString(string nativeFormatString) + { + Span result = stackalloc char[ICU_ULOC_FULLNAME_CAPACITY]; + + bool amPmAdded = false; + int resultPos = 0; + + for (int i = 0; i < nativeFormatString.Length; i++) + { + switch (nativeFormatString[i]) + { + case '\'': + result[resultPos++] = nativeFormatString[i++]; + while (i < nativeFormatString.Length) + { + char current = nativeFormatString[i]; + result[resultPos++] = current; + if (current == '\'') + { + break; + } + i++; + } + break; + + case ':': + case '.': + case 'H': + case 'h': + case 'm': + case 's': + result[resultPos++] = nativeFormatString[i]; + break; + + case ' ': + case '\u00A0': + // Convert nonbreaking spaces into regular spaces + result[resultPos++] = ' '; + break; + + case 'a': // AM/PM + if (!amPmAdded) + { + amPmAdded = true; + result[resultPos++] = 't'; + result[resultPos++] = 't'; + } + break; + + } + } + + return result.Slice(0, resultPos).ToString(); + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Unix.cs index 2a253d5367be30..42a675017e2ebf 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Unix.cs @@ -20,7 +20,11 @@ internal sealed partial class CultureData private string[]? GetTimeFormatsCore(bool shortFormat) { +#if TARGET_OSX || TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS + string format = GlobalizationMode.Hybrid ? GetTimeFormatStringNative(shortFormat) : IcuGetTimeFormatString(shortFormat); +#else string format = IcuGetTimeFormatString(shortFormat); +#endif return new string[] { format }; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.cs index 92037366b63891..c5138b97ecb1cd 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.cs @@ -1541,7 +1541,11 @@ internal int FirstDayOfWeek { if (_iFirstDayOfWeek == undef && !GlobalizationMode.Invariant) { +#if TARGET_OSX || TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS + _iFirstDayOfWeek = GlobalizationMode.Hybrid ? GetLocaleInfoNative(LocaleNumberData.FirstDayOfWeek) : IcuGetLocaleInfo(LocaleNumberData.FirstDayOfWeek); +#else _iFirstDayOfWeek = ShouldUseUserOverrideNlsData ? NlsGetFirstDayOfWeek() : IcuGetLocaleInfo(LocaleNumberData.FirstDayOfWeek); +#endif } return _iFirstDayOfWeek; } @@ -1949,7 +1953,11 @@ internal string TimeSeparator } else { +#if TARGET_OSX || TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS + string? longTimeFormat = GlobalizationMode.Hybrid ? GetTimeFormatStringNative() : IcuGetTimeFormatString(); +#else string? longTimeFormat = ShouldUseUserOverrideNlsData ? NlsGetTimeFormatString() : IcuGetTimeFormatString(); +#endif if (string.IsNullOrEmpty(longTimeFormat)) { longTimeFormat = LongTimes[0]; @@ -2285,8 +2293,11 @@ private int GetLocaleInfoCore(LocaleNumberData type) // This is never reached but helps illinker statically remove dependencies if (GlobalizationMode.Invariant) return 0; - +#if TARGET_OSX || TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS + return GlobalizationMode.Hybrid ? GetLocaleInfoNative(type) : IcuGetLocaleInfo(type); +#else return GlobalizationMode.UseNls ? NlsGetLocaleInfo(type) : IcuGetLocaleInfo(type); +#endif } private int GetLocaleInfoCoreUserOverride(LocaleNumberData type) @@ -2294,8 +2305,11 @@ private int GetLocaleInfoCoreUserOverride(LocaleNumberData type) // This is never reached but helps illinker statically remove dependencies if (GlobalizationMode.Invariant) return 0; - +#if TARGET_OSX || TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS + return GlobalizationMode.Hybrid ? GetLocaleInfoNative(type) : IcuGetLocaleInfo(type); +#else return ShouldUseUserOverrideNlsData ? NlsGetLocaleInfo(type) : IcuGetLocaleInfo(type); +#endif } private string GetLocaleInfoCoreUserOverride(LocaleStringData type) @@ -2342,8 +2356,12 @@ private int[] GetLocaleInfoCoreUserOverride(LocaleGroupingData type) // This is never reached but helps illinker statically remove dependencies if (GlobalizationMode.Invariant) return null!; - + // TODO hybrid mode implement groupingdata +#if TARGET_OSX || TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS + return GlobalizationMode.Hybrid ? GetLocaleInfoNative(type) : IcuGetLocaleInfo(type); +#else return ShouldUseUserOverrideNlsData ? NlsGetLocaleInfo(type) : IcuGetLocaleInfo(type); +#endif } /// diff --git a/src/native/libs/System.Globalization.Native/entrypoints.c b/src/native/libs/System.Globalization.Native/entrypoints.c index 10254d6256db4a..c3c65b2196cde9 100644 --- a/src/native/libs/System.Globalization.Native/entrypoints.c +++ b/src/native/libs/System.Globalization.Native/entrypoints.c @@ -61,6 +61,10 @@ static const Entry s_globalizationNative[] = #ifdef __APPLE__ DllImportEntry(GlobalizationNative_GetLocaleNameNative) DllImportEntry(GlobalizationNative_GetLocaleInfoStringNative) + DllImportEntry(GlobalizationNative_GetLocaleInfoIntNative) + DllImportEntry(GlobalizationNative_GetLocaleInfoPrimaryGroupingSizeNative) + DllImportEntry(GlobalizationNative_GetLocaleInfoSecondaryGroupingSizeNative) + DllImportEntry(GlobalizationNative_GetLocaleTimeFormatNative) #endif }; diff --git a/src/native/libs/System.Globalization.Native/pal_locale.m b/src/native/libs/System.Globalization.Native/pal_locale.m index 7400c48b1c9ba6..4314b4bce3508d 100644 --- a/src/native/libs/System.Globalization.Native/pal_locale.m +++ b/src/native/libs/System.Globalization.Native/pal_locale.m @@ -4,6 +4,7 @@ #include #include "pal_locale_internal.h" #include "pal_localeStringData.h" +#include "pal_localeNumberData.h" #import #import @@ -151,6 +152,462 @@ return strdup(value); } + +// invariant character definitions +#define CHAR_CURRENCY ((char)0x00A4) // international currency +#define CHAR_SPACE ((char)0x0020) // space +#define CHAR_NBSPACE ((char)0x00A0) // space +#define CHAR_DIGIT ((char)0x0023) // '#' +#define CHAR_MINUS ((char)0x002D) // '-' +#define CHAR_PERCENT ((char)0x0025) // '%' +#define CHAR_OPENPAREN ((char)0x0028) // '(' +#define CHAR_CLOSEPAREN ((char)0x0029) // ')' +#define CHAR_ZERO ((char)0x0030) // '0' + +/* +Function: +NormalizeNumericPattern + +Returns a numeric string pattern in a format that we can match against the +appropriate managed pattern. +*/ +static char* NormalizeNumericPattern(const char* srcPattern, int isNegative) +{ + int iStart = 0; + int iEnd = strlen(srcPattern); + int32_t iNegativePatternStart = -1; + + for (int i = iStart; i < iEnd; i++) + { + if (srcPattern[i] == ';') + { + iNegativePatternStart = i; + } + } + + if (iNegativePatternStart >= 0) + { + if (isNegative) + { + iStart = iNegativePatternStart + 1; + } + else + { + iEnd = iNegativePatternStart - 1; + } + } + + int index = 0; + int minusAdded = false; + int digitAdded = false; + int currencyAdded = false; + int spaceAdded = false; + + for (int i = iStart; i <= iEnd; i++) + { + char ch = srcPattern[i]; + switch (ch) + { + case CHAR_MINUS: + case CHAR_OPENPAREN: + case CHAR_CLOSEPAREN: + minusAdded = true; + break; + } + } + + // international currency symbol (CHAR_CURRENCY) + // The positive pattern comes first, then an optional negative pattern + // separated by a semicolon + // A destPattern example: "(C n)" where C represents the currency symbol, and + // n is the number + char* destPattern; + + // if there is no negative subpattern, prefix the minus sign + if (isNegative && !minusAdded) + { + int length = (iEnd - iStart) + 2; + destPattern = (char*)calloc((size_t)length, sizeof(char)); + if (!destPattern) + { + return NULL; + } + destPattern[index++] = '-'; + } + else + { + int length = (iEnd - iStart) + 1; + destPattern = (char*)calloc((size_t)length, sizeof(char)); + if (!destPattern) + { + return NULL; + } + } + + for (int i = iStart; i <= iEnd; i++) + { + char ch = srcPattern[i]; + switch (ch) + { + case CHAR_DIGIT: + case CHAR_ZERO: + if (!digitAdded) + { + digitAdded = true; + destPattern[index++] = 'n'; + } + break; + + case CHAR_CURRENCY: + if (!currencyAdded) + { + currencyAdded = true; + destPattern[index++] = 'C'; + } + break; + + case CHAR_SPACE: + case CHAR_NBSPACE: + if (!spaceAdded) + { + spaceAdded = true; + destPattern[index++] = ' '; + } + break; + + case CHAR_MINUS: + case CHAR_OPENPAREN: + case CHAR_CLOSEPAREN: + minusAdded = true; + destPattern[index++] = (char)ch; + break; + + case CHAR_PERCENT: + destPattern[index++] = '%'; + break; + } + } + + return destPattern; +} + +/* +Function: +GetNumericPattern + +Determines the pattern from the decimalFormat and returns the matching pattern's +index from patterns[]. +Returns index -1 if no pattern is found. +*/ +static int GetNumericPatternNative(const char* pNumberFormat, + const char* patterns[], + int patternsCount, + int isNegative) +{ + const int INVALID_FORMAT = -1; + const int MAX_DOTNET_NUMERIC_PATTERN_LENGTH = 6; // example: "(C n)" plus terminator + char* normalizedPattern = NormalizeNumericPattern(pNumberFormat, isNegative); + + if (!normalizedPattern) + { + return U_MEMORY_ALLOCATION_ERROR; + } + + size_t normalizedPatternLength = strlen(normalizedPattern); + + assert(normalizedPatternLength > 0); + assert(normalizedPatternLength < MAX_DOTNET_NUMERIC_PATTERN_LENGTH); + + if (normalizedPatternLength == 0 || normalizedPatternLength >= MAX_DOTNET_NUMERIC_PATTERN_LENGTH) + { + free(normalizedPattern); + return INVALID_FORMAT; + } + + for (int i = 0; i < patternsCount; i++) + { + if (strcmp(normalizedPattern, patterns[i]) == 0) + { + free(normalizedPattern); + return i; + } + } + + assert(false); // should have found a valid pattern + + free(normalizedPattern); + return INVALID_FORMAT; +} + +int32_t GlobalizationNative_GetLocaleInfoIntNative(const char* localeName, LocaleNumberData localeNumberData) +{ + bool isSuccess = true; + int32_t value; + NSString *locName = [NSString stringWithFormat:@"%s", localeName]; + NSLocale *currentLocale = [[NSLocale alloc] initWithLocaleIdentifier:locName]; + + switch (localeNumberData) + { + case LocaleNumber_MeasurementSystem: + { + const char *measurementSystem = [[currentLocale objectForKey:NSLocaleMeasurementSystem] UTF8String]; + NSLocale *usLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]; + const char *us_measurementSystem = [[usLocale objectForKey:NSLocaleMeasurementSystem] UTF8String]; + value = (measurementSystem == us_measurementSystem) ? 1 : 0; + break; + } + case LocaleNumber_FractionalDigitsCount: + { + NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init]; + numberFormatter.locale = currentLocale; + numberFormatter.numberStyle = NSNumberFormatterDecimalStyle; + value = (int32_t)numberFormatter.maximumFractionDigits; + break; + } + case LocaleNumber_NegativeNumberFormat: + { + NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init]; + numberFormatter.locale = currentLocale; + numberFormatter.numberStyle = NSNumberFormatterDecimalStyle; + static const char* Patterns[] = {"(n)", "-n", "- n", "n-", "n -"}; + const char *pFormat = [[numberFormatter negativeFormat] UTF8String]; + value = GetNumericPatternNative(pFormat, Patterns, sizeof(Patterns)/sizeof(Patterns[0]), true); + if (value >= 0) + { + return value; + } + else + { + isSuccess = false; + } + break; + } + case LocaleNumber_MonetaryFractionalDigitsCount: + { + NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init]; + numberFormatter.locale = currentLocale; + numberFormatter.numberStyle = NSNumberFormatterCurrencyStyle; + value = (int32_t)numberFormatter.maximumFractionDigits; + break; + } + case LocaleNumber_PositiveMonetaryNumberFormat: + { + NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init]; + numberFormatter.locale = currentLocale; + numberFormatter.numberStyle = NSNumberFormatterCurrencyStyle; + static const char* Patterns[] = {"Cn", "nC", "C n", "n C"}; + const char *pFormat = [[numberFormatter positiveFormat] UTF8String]; + value = GetNumericPatternNative(pFormat, Patterns, sizeof(Patterns)/sizeof(Patterns[0]), false); + if (value >= 0) + { + return value; + } + else + { + isSuccess = false; + } + break; + } + case LocaleNumber_NegativeMonetaryNumberFormat: + { + NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init]; + numberFormatter.locale = currentLocale; + numberFormatter.numberStyle = NSNumberFormatterCurrencyStyle; + static const char* Patterns[] = {"(Cn)", + "-Cn", + "C-n", + "Cn-", + "(nC)", + "-nC", + "n-C", + "nC-", + "-n C", + "-C n", + "n C-", + "C n-", + "C -n", + "n- C", + "(C n)", + "(n C)", + "C- n" }; + const char *pFormat = [[numberFormatter negativeFormat] UTF8String]; + value = GetNumericPatternNative(pFormat, Patterns, sizeof(Patterns)/sizeof(Patterns[0]), true); + if (value >= 0) + { + return value; + } + else + { + isSuccess = false; + } + break; + } + case LocaleNumber_FirstWeekOfYear: + { + NSCalendar *calendar = [currentLocale objectForKey:NSLocaleCalendar]; + int minDaysInWeek = (int32_t)[calendar minimumDaysInFirstWeek]; + if (minDaysInWeek == 1) + { + value = WeekRule_FirstDay; + } + else if (minDaysInWeek == 7) + { + value = WeekRule_FirstFullWeek; + } + else if (minDaysInWeek >= 4) + { + value = WeekRule_FirstFourDayWeek; + } + else + { + isSuccess = false; + } + break; + } + case LocaleNumber_ReadingLayout: + { + NSLocaleLanguageDirection langDir = [NSLocale characterDirectionForLanguage:[[NSLocale currentLocale] objectForKey:NSLocaleLanguageCode]]; + // 0 - Left to right (such as en-US) + // 1 - Right to left (such as arabic locales) + value = NSLocaleLanguageDirectionRightToLeft == langDir ? 1 : 0; + break; + } + case LocaleNumber_FirstDayofWeek: + { + NSCalendar *calendar = [currentLocale objectForKey:NSLocaleCalendar]; + value = (int32_t)[calendar firstWeekday]; + break; + } + case LocaleNumber_NegativePercentFormat: + { + NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init]; + numberFormatter.locale = currentLocale; + numberFormatter.numberStyle = NSNumberFormatterPercentStyle; + static const char* Patterns[] = {"-n %", "-n%", "-%n", "%-n", "%n-", "n-%", "n%-", "-% n", "n %-", "% n-", "% -n", "n- %"}; + const char *pFormat = [[numberFormatter negativeFormat] UTF8String]; + value = GetNumericPatternNative(pFormat, Patterns, sizeof(Patterns)/sizeof(Patterns[0]), true); + if (value >= 0) + { + return value; + } + else + { + isSuccess = false; + } + break; + } + case LocaleNumber_PositivePercentFormat: + { + NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init]; + numberFormatter.locale = currentLocale; + numberFormatter.numberStyle = NSNumberFormatterPercentStyle; + static const char* Patterns[] = {"n %", "n%", "%n", "% n"}; + const char *pFormat = [[numberFormatter positiveFormat] UTF8String]; + value = GetNumericPatternNative(pFormat, Patterns, sizeof(Patterns)/sizeof(Patterns[0]), false); + if (value >= 0) + { + return value; + } + else + { + isSuccess = false; + } + break; + } + default: + assert(isSuccess,"GlobalizationNative_GetLocaleInfoIntNative Failed."); + break; + } + + return value; +} + +/* +PAL Function: +GlobalizationNative_GetLocaleInfoPrimaryGroupingSizeNative + +Returns primary grouping size for decimal and currency +*/ +int32_t GlobalizationNative_GetLocaleInfoPrimaryGroupingSizeNative(const char* localeName, LocaleNumberData localeGroupingData) +{ + bool isSuccess = true; + NSString *locName = [NSString stringWithFormat:@"%s", localeName]; + NSLocale *currentLocale = [[NSLocale alloc] initWithLocaleIdentifier:locName]; + NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init]; + numberFormatter.locale = currentLocale; + + switch (localeGroupingData) + { + case LocaleNumber_Digit: + numberFormatter.numberStyle = NSNumberFormatterDecimalStyle; + break; + case LocaleNumber_Monetary: + numberFormatter.numberStyle = NSNumberFormatterCurrencyStyle; + break; + default: + isSuccess = false; + assert(isSuccess,"GlobalizationNative_GetLocaleInfoPrimaryGroupingSizeNative Failed."); + break; + } + + return [numberFormatter groupingSize]; +} + +/* +PAL Function: +GlobalizationNative_GetLocaleInfoSecondaryGroupingSizeNative + +Returns secondary grouping size for decimal and currency +*/ +int32_t GlobalizationNative_GetLocaleInfoSecondaryGroupingSizeNative(const char* localeName, LocaleNumberData localeGroupingData) +{ + NSString *locName = [NSString stringWithFormat:@"%s", localeName]; + NSLocale *currentLocale = [[NSLocale alloc] initWithLocaleIdentifier:locName]; + NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init]; + numberFormatter.locale = currentLocale; + + switch (localeGroupingData) + { + case LocaleNumber_Digit: + numberFormatter.numberStyle = NSNumberFormatterDecimalStyle; + break; + case LocaleNumber_Monetary: + numberFormatter.numberStyle = NSNumberFormatterCurrencyStyle; + break; + default: + isSuccess = false; + assert(isSuccess,"GlobalizationNative_GetLocaleInfoSecondaryGroupingSizeNative Failed"); + break; + } + + return [numberFormatter secondaryGroupingSize]; +} + +/* +PAL Function: +GlobalizationNative_GetLocaleTimeFormatNative + +Returns time format information (in native format, it needs to be converted to .NET's format). +*/ +const char* GlobalizationNative_GetLocaleTimeFormatNative(const char* localeName, int shortFormat) +{ + NSString *locName = [NSString stringWithFormat:@"%s", localeName]; + NSLocale *currentLocale = [[NSLocale alloc] initWithLocaleIdentifier:locName]; + NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init]; + [dateFormatter setLocale:currentLocale]; + + if (shortFormat != 0) + { + [dateFormatter setTimeStyle:NSDateFormatterShortStyle]; + } + else + { + [dateFormatter setTimeStyle:NSDateFormatterMediumStyle]; + } + + return strdup([[dateFormatter dateFormat] UTF8String]); +} + #endif #if defined(TARGET_MACCATALYST) || defined(TARGET_IOS) || defined(TARGET_TVOS) diff --git a/src/native/libs/System.Globalization.Native/pal_localeNumberData.h b/src/native/libs/System.Globalization.Native/pal_localeNumberData.h index db5c401b8b7b60..995a1abd0217de 100644 --- a/src/native/libs/System.Globalization.Native/pal_localeNumberData.h +++ b/src/native/libs/System.Globalization.Native/pal_localeNumberData.h @@ -43,3 +43,14 @@ PALEXPORT int32_t GlobalizationNative_GetLocaleInfoGroupingSizes(const UChar* lo LocaleNumberData localeGroupingData, int32_t* primaryGroupSize, int32_t* secondaryGroupSize); + +#ifdef __APPLE__ +PALEXPORT int32_t GlobalizationNative_GetLocaleInfoIntNative(const char* localeName, + LocaleNumberData localeNumberData); + +PALEXPORT int32_t GlobalizationNative_GetLocaleInfoPrimaryGroupingSizeNative(const char* localeName, + LocaleNumberData localeGroupingData); + +PALEXPORT int32_t GlobalizationNative_GetLocaleInfoSecondaryGroupingSizeNative(const char* localeName, + LocaleNumberData localeGroupingData); +#endif \ No newline at end of file diff --git a/src/native/libs/System.Globalization.Native/pal_localeStringData.h b/src/native/libs/System.Globalization.Native/pal_localeStringData.h index e6030d8a4cf86e..175a7ced3f1dc3 100644 --- a/src/native/libs/System.Globalization.Native/pal_localeStringData.h +++ b/src/native/libs/System.Globalization.Native/pal_localeStringData.h @@ -50,11 +50,13 @@ PALEXPORT int32_t GlobalizationNative_GetLocaleInfoString(const UChar* localeNam int32_t valueLength, const UChar* uiLocaleName); -#ifdef __APPLE__ -PALEXPORT const char* GlobalizationNative_GetLocaleInfoStringNative(const char* localeName, LocaleStringData localeStringData); -#endif - PALEXPORT int32_t GlobalizationNative_GetLocaleTimeFormat(const UChar* localeName, int shortFormat, UChar* value, int32_t valueLength); +#ifdef __APPLE__ +PALEXPORT const char* GlobalizationNative_GetLocaleInfoStringNative(const char* localeName, LocaleStringData localeStringData); + +PALEXPORT const char* GlobalizationNative_GetLocaleTimeFormatNative(const char* localeName, int shortFormat); +#endif + diff --git a/src/native/libs/System.Globalization.Native/pal_locale_internal.h b/src/native/libs/System.Globalization.Native/pal_locale_internal.h index 1f9edb7640cbfb..bd52d53501ca8d 100644 --- a/src/native/libs/System.Globalization.Native/pal_locale_internal.h +++ b/src/native/libs/System.Globalization.Native/pal_locale_internal.h @@ -5,6 +5,7 @@ #include "pal_icushim_internal.h" #include "pal_localeStringData.h" +#include "pal_localeNumberData.h" /* Function: @@ -78,4 +79,31 @@ Returns string locale information if found for the specified locale name for App Returns empty string if not found. */ const char* GlobalizationNative_GetLocaleInfoStringNative(const char* localeName, LocaleStringData localeStringData); + +/* +Function: +GlobalizationNative_GetLocaleInfoIntNative + +Returns int locale information if found for the specified locale name for Apple platforms. +Returns empty string if not found. +*/ +int32_t GlobalizationNative_GetLocaleInfoIntNative(const char* localeName, LocaleNumberData localeStringData); + +/* +Function: +GlobalizationNative_GetLocaleInfoPrimaryGroupingSizeNative + +Returns int locale information if found for the specified locale name for Apple platforms. +Returns empty string if not found. +*/ +int32_t GlobalizationNative_GetLocaleInfoPrimaryGroupingSizeNative(const char* localeName, LocaleNumberData localeStringData); + +/* +Function: +GlobalizationNative_GetLocaleInfoSecondaryGroupingSizeNative + +Returns int locale information if found for the specified locale name for Apple platforms. +Returns empty string if not found. +*/ +int32_t GlobalizationNative_GetLocaleInfoSecondaryGroupingSizeNative(const char* localeName, LocaleNumberData localeStringData); #endif From c7fa90ccbb2170384b1ea95f6d891451b4a948b0 Mon Sep 17 00:00:00 2001 From: Meri Khamoyan Date: Thu, 6 Apr 2023 15:39:51 +0200 Subject: [PATCH 02/16] Added test cases, minor changes --- .../Common/src/Interop/Interop.Locale.OSX.cs | 8 +++--- .../tests/Hybrid/HybridMode.cs | 28 ++----------------- .../System/Globalization/CultureData.OSX.cs | 3 +- .../System/Globalization/CultureData.Unix.cs | 7 ++++- .../src/System/Globalization/CultureData.cs | 2 +- .../System.Globalization.Native/pal_locale.m | 24 ++++++++-------- .../pal_localeNumberData.h | 2 +- 7 files changed, 27 insertions(+), 47 deletions(-) diff --git a/src/libraries/Common/src/Interop/Interop.Locale.OSX.cs b/src/libraries/Common/src/Interop/Interop.Locale.OSX.cs index c07a54dcf09d33..6f8fc9e00f522b 100644 --- a/src/libraries/Common/src/Interop/Interop.Locale.OSX.cs +++ b/src/libraries/Common/src/Interop/Interop.Locale.OSX.cs @@ -13,16 +13,16 @@ internal static partial class Globalization [LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetLocaleInfoStringNative", StringMarshalling = StringMarshalling.Utf8)] internal static unsafe partial string GetLocaleInfoStringNative(string localeName, uint localeStringData); - [LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetLocaleInfoIntNative", StringMarshalling = StringMarshalling.Utf16)] + [LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetLocaleInfoIntNative", StringMarshalling = StringMarshalling.Utf8)] internal static partial int GetLocaleInfoIntNative(string localeName, uint localeNumberData); - [LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetLocaleInfoPrimaryGroupingSizeNative", StringMarshalling = StringMarshalling.Utf16)] + [LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetLocaleInfoPrimaryGroupingSizeNative", StringMarshalling = StringMarshalling.Utf8)] internal static partial int GetLocaleInfoPrimaryGroupingSizeNative(string localeName, uint localeGroupingData); - [LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetLocaleInfoSecondaryGroupingSizeNative", StringMarshalling = StringMarshalling.Utf16)] + [LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetLocaleInfoSecondaryGroupingSizeNative", StringMarshalling = StringMarshalling.Utf8)] internal static partial int GetLocaleInfoSecondaryGroupingSizeNative(string localeName, uint localeGroupingData); - [LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetLocaleTimeFormatNative", StringMarshalling = StringMarshalling.Utf16)] + [LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetLocaleTimeFormatNative", StringMarshalling = StringMarshalling.Utf8)] internal static unsafe partial string GetLocaleTimeFormatNative(string localeName, [MarshalAs(UnmanagedType.Bool)] bool shortFormat); } } diff --git a/src/libraries/System.Globalization/tests/Hybrid/HybridMode.cs b/src/libraries/System.Globalization/tests/Hybrid/HybridMode.cs index 68545402090306..7fd06b863d65ca 100644 --- a/src/libraries/System.Globalization/tests/Hybrid/HybridMode.cs +++ b/src/libraries/System.Globalization/tests/Hybrid/HybridMode.cs @@ -45,7 +45,7 @@ public void TwoLetterISOLanguageName(string name, string expected) Assert.Equal(expected, new CultureInfo(name).TwoLetterISOLanguageName); } - /*public static IEnumerable FirstDayOfWeek_Get_TestData() + public static IEnumerable FirstDayOfWeek_Get_TestData() { yield return new object[] { DateTimeFormatInfo.InvariantInfo, DayOfWeek.Sunday }; yield return new object[] { new CultureInfo("en-US", false).DateTimeFormat, DayOfWeek.Sunday }; @@ -57,21 +57,6 @@ public void TwoLetterISOLanguageName(string name, string expected) public void FirstDayOfWeek(DateTimeFormatInfo format, DayOfWeek expected) { Assert.Equal(expected, format.FirstDayOfWeek); - }*/ - - [Theory] - [InlineData(DayOfWeek.Sunday)] - [InlineData(DayOfWeek.Monday)] - [InlineData(DayOfWeek.Tuesday)] - [InlineData(DayOfWeek.Wednesday)] - [InlineData(DayOfWeek.Thursday)] - [InlineData(DayOfWeek.Friday)] - [InlineData(DayOfWeek.Saturday)] - public void FirstDayOfWeek_Set_GetReturnsExpected(DayOfWeek value) - { - var format = new DateTimeFormatInfo(); - format.FirstDayOfWeek = value; - Assert.Equal(value, format.FirstDayOfWeek); } public static IEnumerable CurrencyNegativePatternTestLocales() @@ -87,15 +72,7 @@ public static IEnumerable CurrencyNegativePatternTestLocales() [MemberData(nameof(CurrencyNegativePatternTestLocales))] public void CurrencyNegativePattern_Get_ReturnsExpected_ByLocale(string locale) { - CultureInfo culture; - try - { - culture = CultureInfo.GetCultureInfo(locale); - } - catch (CultureNotFoundException) - { - return; // ignore unsupported culture - } + CultureInfo culture = new CultureInfo(locale); NumberFormatInfo format = culture.NumberFormat; Assert.Contains(format.CurrencyNegativePattern, GetCurrencyNegativePatterns(locale)); @@ -125,7 +102,6 @@ internal static int[] GetCurrencyNegativePatterns(string localeName) } return new int[] { 0 }; - //throw DateTimeFormatInfoData.GetCultureNotSupportedException(CultureInfo.GetCultureInfo(localeName)); } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.OSX.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.OSX.cs index 6b8780d5bdf2be..3be4f1593b6d54 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.OSX.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.OSX.cs @@ -18,6 +18,8 @@ private bool InitNativeCultureDataCore() string realNameBuffer = _sRealName; _sWindowsName = GetLocaleNameNative(realNameBuffer); + _sName = _sWindowsName; + _sRealName = _sName; return true; } @@ -54,7 +56,6 @@ private int GetLocaleInfoNative(LocaleNumberData type) return 0; } - return Interop.Globalization.GetLocaleInfoIntNative(_sWindowsName, (uint)type); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Unix.cs index 42a675017e2ebf..a84b88f54b6304 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Unix.cs @@ -7,7 +7,12 @@ namespace System.Globalization { internal sealed partial class CultureData { - private bool InitCultureDataCore() => InitIcuCultureDataCore(); + private bool InitCultureDataCore() => +#if TARGET_OSX || TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS + InitNativeCultureDataCore(); +#else + InitIcuCultureDataCore(); +#endif // Unix doesn't support user overrides partial void InitUserOverride(bool useUserOverride); diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.cs index c5138b97ecb1cd..12ea4ded8beedd 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.cs @@ -2356,7 +2356,7 @@ private int[] GetLocaleInfoCoreUserOverride(LocaleGroupingData type) // This is never reached but helps illinker statically remove dependencies if (GlobalizationMode.Invariant) return null!; - // TODO hybrid mode implement groupingdata + #if TARGET_OSX || TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS return GlobalizationMode.Hybrid ? GetLocaleInfoNative(type) : IcuGetLocaleInfo(type); #else diff --git a/src/native/libs/System.Globalization.Native/pal_locale.m b/src/native/libs/System.Globalization.Native/pal_locale.m index 4314b4bce3508d..6c74ad8555efda 100644 --- a/src/native/libs/System.Globalization.Native/pal_locale.m +++ b/src/native/libs/System.Globalization.Native/pal_locale.m @@ -35,10 +35,10 @@ const char* GlobalizationNative_GetLocaleNameNative(const char* localeName) { - NSString *locName = [NSString stringWithFormat:@"%s", localeName]; - NSLocale *currentLocale = [[NSLocale alloc] initWithLocaleIdentifier:locName]; - const char* value = [currentLocale.localeIdentifier UTF8String]; - return strdup(value); + NSString *locName = [NSString stringWithFormat:@"%s", localeName]; + NSLocale *currentLocale = [[NSLocale alloc] initWithLocaleIdentifier:locName]; + const char* value = [currentLocale.localeIdentifier UTF8String]; + return strdup(value); } const char* GlobalizationNative_GetLocaleInfoStringNative(const char* localeName, LocaleStringData localeStringData) @@ -52,7 +52,6 @@ [dateFormatter setLocale:currentLocale]; NSLocale *gbLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_GB"]; - switch (localeStringData) { ///// localized name of locale, eg "German (Germany)" in UI language (corresponds to LOCALE_SLOCALIZEDDISPLAYNAME) @@ -149,7 +148,6 @@ value = ""; break; } - return strdup(value); } @@ -475,7 +473,7 @@ int32_t GlobalizationNative_GetLocaleInfoIntNative(const char* localeName, Local case LocaleNumber_FirstDayofWeek: { NSCalendar *calendar = [currentLocale objectForKey:NSLocaleCalendar]; - value = (int32_t)[calendar firstWeekday]; + value = [calendar firstWeekday] - 1; // .NET is 0-based and in Apple is 1-based; break; } case LocaleNumber_NegativePercentFormat: @@ -515,7 +513,7 @@ int32_t GlobalizationNative_GetLocaleInfoIntNative(const char* localeName, Local break; } default: - assert(isSuccess,"GlobalizationNative_GetLocaleInfoIntNative Failed."); + assert(isSuccess); break; } @@ -546,11 +544,10 @@ int32_t GlobalizationNative_GetLocaleInfoPrimaryGroupingSizeNative(const char* l break; default: isSuccess = false; - assert(isSuccess,"GlobalizationNative_GetLocaleInfoPrimaryGroupingSizeNative Failed."); + assert(isSuccess); break; } - - return [numberFormatter groupingSize]; + return [numberFormatter groupingSize]; } /* @@ -560,7 +557,8 @@ int32_t GlobalizationNative_GetLocaleInfoPrimaryGroupingSizeNative(const char* l Returns secondary grouping size for decimal and currency */ int32_t GlobalizationNative_GetLocaleInfoSecondaryGroupingSizeNative(const char* localeName, LocaleNumberData localeGroupingData) -{ +{ + bool isSuccess = true; NSString *locName = [NSString stringWithFormat:@"%s", localeName]; NSLocale *currentLocale = [[NSLocale alloc] initWithLocaleIdentifier:locName]; NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init]; @@ -576,7 +574,7 @@ int32_t GlobalizationNative_GetLocaleInfoSecondaryGroupingSizeNative(const char* break; default: isSuccess = false; - assert(isSuccess,"GlobalizationNative_GetLocaleInfoSecondaryGroupingSizeNative Failed"); + assert(isSuccess); break; } diff --git a/src/native/libs/System.Globalization.Native/pal_localeNumberData.h b/src/native/libs/System.Globalization.Native/pal_localeNumberData.h index 995a1abd0217de..933946949200e0 100644 --- a/src/native/libs/System.Globalization.Native/pal_localeNumberData.h +++ b/src/native/libs/System.Globalization.Native/pal_localeNumberData.h @@ -53,4 +53,4 @@ PALEXPORT int32_t GlobalizationNative_GetLocaleInfoPrimaryGroupingSizeNative(con PALEXPORT int32_t GlobalizationNative_GetLocaleInfoSecondaryGroupingSizeNative(const char* localeName, LocaleNumberData localeGroupingData); -#endif \ No newline at end of file +#endif From a8b4fa51c69910f11a4e8601ac31eeabaa7f0d5b Mon Sep 17 00:00:00 2001 From: Meri Khamoyan Date: Thu, 6 Apr 2023 15:47:43 +0200 Subject: [PATCH 03/16] Remove unrelated comment --- .../System.Globalization/tests/Hybrid/HybridMode.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/libraries/System.Globalization/tests/Hybrid/HybridMode.cs b/src/libraries/System.Globalization/tests/Hybrid/HybridMode.cs index 7fd06b863d65ca..b0343d652de94a 100644 --- a/src/libraries/System.Globalization/tests/Hybrid/HybridMode.cs +++ b/src/libraries/System.Globalization/tests/Hybrid/HybridMode.cs @@ -80,11 +80,6 @@ public void CurrencyNegativePattern_Get_ReturnsExpected_ByLocale(string locale) internal static int[] GetCurrencyNegativePatterns(string localeName) { - // CentOS uses an older ICU than Ubuntu, which means the "Linux" values need to allow for - // multiple values, since we can't tell which version of ICU we are using, or whether we are - // on CentOS or Ubuntu. - // When multiple values are returned, the "older" ICU value is returned last. - switch (localeName) { case "en-US": From 32623c948174addbcf51af3b109929e75847bf8e Mon Sep 17 00:00:00 2001 From: Meri Khamoyan Date: Thu, 6 Apr 2023 16:40:26 +0200 Subject: [PATCH 04/16] fix build --- src/native/libs/System.Globalization.Native/pal_locale.m | 4 +++- .../libs/System.Globalization.Native/pal_locale_internal.h | 7 ++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/native/libs/System.Globalization.Native/pal_locale.m b/src/native/libs/System.Globalization.Native/pal_locale.m index 6c74ad8555efda..0c2ce97f3fcfe6 100644 --- a/src/native/libs/System.Globalization.Native/pal_locale.m +++ b/src/native/libs/System.Globalization.Native/pal_locale.m @@ -457,7 +457,8 @@ int32_t GlobalizationNative_GetLocaleInfoIntNative(const char* localeName, Local value = WeekRule_FirstFourDayWeek; } else - { + { + value = -1; isSuccess = false; } break; @@ -513,6 +514,7 @@ int32_t GlobalizationNative_GetLocaleInfoIntNative(const char* localeName, Local break; } default: + value = -1; assert(isSuccess); break; } diff --git a/src/native/libs/System.Globalization.Native/pal_locale_internal.h b/src/native/libs/System.Globalization.Native/pal_locale_internal.h index bd52d53501ca8d..f28f5a68883286 100644 --- a/src/native/libs/System.Globalization.Native/pal_locale_internal.h +++ b/src/native/libs/System.Globalization.Native/pal_locale_internal.h @@ -85,7 +85,6 @@ const char* GlobalizationNative_GetLocaleInfoStringNative(const char* localeName GlobalizationNative_GetLocaleInfoIntNative Returns int locale information if found for the specified locale name for Apple platforms. -Returns empty string if not found. */ int32_t GlobalizationNative_GetLocaleInfoIntNative(const char* localeName, LocaleNumberData localeStringData); @@ -93,8 +92,7 @@ int32_t GlobalizationNative_GetLocaleInfoIntNative(const char* localeName, Local Function: GlobalizationNative_GetLocaleInfoPrimaryGroupingSizeNative -Returns int locale information if found for the specified locale name for Apple platforms. -Returns empty string if not found. +Returns primary grouping size for decimal and currency for the specified locale name for Apple platforms. */ int32_t GlobalizationNative_GetLocaleInfoPrimaryGroupingSizeNative(const char* localeName, LocaleNumberData localeStringData); @@ -102,8 +100,7 @@ int32_t GlobalizationNative_GetLocaleInfoPrimaryGroupingSizeNative(const char* l Function: GlobalizationNative_GetLocaleInfoSecondaryGroupingSizeNative -Returns int locale information if found for the specified locale name for Apple platforms. -Returns empty string if not found. +Returns secondary grouping size for decimal and currency for the specified locale name for Apple platforms. */ int32_t GlobalizationNative_GetLocaleInfoSecondaryGroupingSizeNative(const char* localeName, LocaleNumberData localeStringData); #endif From a60500f52eed2dc3fdc55c28ddc8306051f6d998 Mon Sep 17 00:00:00 2001 From: Meri Khamoyan Date: Thu, 6 Apr 2023 18:11:16 +0200 Subject: [PATCH 05/16] HybridTests don't run on osx --- .../System.Globalization/tests/Hybrid/Hybrid.IOS.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Globalization/tests/Hybrid/Hybrid.IOS.Tests.csproj b/src/libraries/System.Globalization/tests/Hybrid/Hybrid.IOS.Tests.csproj index b2622d1a6afb95..9cee5bb3c1de8d 100644 --- a/src/libraries/System.Globalization/tests/Hybrid/Hybrid.IOS.Tests.csproj +++ b/src/libraries/System.Globalization/tests/Hybrid/Hybrid.IOS.Tests.csproj @@ -1,6 +1,6 @@ - $(NetCoreAppCurrent)-ios;$(NetCoreAppCurrent)-tvos;$(NetCoreAppCurrent)-maccatalyst;$(NetCoreAppCurrent)-osx + $(NetCoreAppCurrent)-ios;$(NetCoreAppCurrent)-tvos;$(NetCoreAppCurrent)-maccatalyst true true From 7cbe7d73e71f27bbe894395217fec55834260526 Mon Sep 17 00:00:00 2001 From: Meri Khamoyan Date: Wed, 12 Apr 2023 08:45:21 +0200 Subject: [PATCH 06/16] Done changes requested by review --- .../Common/src/Interop/Interop.Locale.OSX.cs | 6 +-- .../tests/Hybrid/HybridMode.cs | 35 ++++------------ .../System/Globalization/CultureData.OSX.cs | 15 +++---- .../System.Globalization.Native/pal_locale.c | 22 ++++++++++ .../System.Globalization.Native/pal_locale.h | 14 +++++-- .../System.Globalization.Native/pal_locale.m | 36 +++++------------ .../pal_localeNumberData.h | 6 ++- .../pal_localeStringData.c | 21 ---------- .../pal_localeStringData.h | 6 +-- .../pal_locale_internal.h | 40 ------------------- 10 files changed, 63 insertions(+), 138 deletions(-) diff --git a/src/libraries/Common/src/Interop/Interop.Locale.OSX.cs b/src/libraries/Common/src/Interop/Interop.Locale.OSX.cs index 6f8fc9e00f522b..0f7b1763d58509 100644 --- a/src/libraries/Common/src/Interop/Interop.Locale.OSX.cs +++ b/src/libraries/Common/src/Interop/Interop.Locale.OSX.cs @@ -8,10 +8,10 @@ internal static partial class Interop internal static partial class Globalization { [LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetLocaleNameNative", StringMarshalling = StringMarshalling.Utf8)] - internal static unsafe partial string GetLocaleNameNative(string localeName); + internal static partial string GetLocaleNameNative(string localeName); [LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetLocaleInfoStringNative", StringMarshalling = StringMarshalling.Utf8)] - internal static unsafe partial string GetLocaleInfoStringNative(string localeName, uint localeStringData); + internal static partial string GetLocaleInfoStringNative(string localeName, uint localeStringData); [LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetLocaleInfoIntNative", StringMarshalling = StringMarshalling.Utf8)] internal static partial int GetLocaleInfoIntNative(string localeName, uint localeNumberData); @@ -23,6 +23,6 @@ internal static partial class Globalization internal static partial int GetLocaleInfoSecondaryGroupingSizeNative(string localeName, uint localeGroupingData); [LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetLocaleTimeFormatNative", StringMarshalling = StringMarshalling.Utf8)] - internal static unsafe partial string GetLocaleTimeFormatNative(string localeName, [MarshalAs(UnmanagedType.Bool)] bool shortFormat); + internal static partial string GetLocaleTimeFormatNative(string localeName, [MarshalAs(UnmanagedType.Bool)] bool shortFormat); } } diff --git a/src/libraries/System.Globalization/tests/Hybrid/HybridMode.cs b/src/libraries/System.Globalization/tests/Hybrid/HybridMode.cs index b0343d652de94a..446aed947db0da 100644 --- a/src/libraries/System.Globalization/tests/Hybrid/HybridMode.cs +++ b/src/libraries/System.Globalization/tests/Hybrid/HybridMode.cs @@ -61,42 +61,23 @@ public void FirstDayOfWeek(DateTimeFormatInfo format, DayOfWeek expected) public static IEnumerable CurrencyNegativePatternTestLocales() { - yield return new object[] { "en-US" }; - yield return new object[] { "en-CA" }; - yield return new object[] { "fa-IR" }; - yield return new object[] { "fr-CD" }; - yield return new object[] { "fr-CA" }; + yield return new object[] { "en-US", new int[] { 1, 0 } }; + yield return new object[] { "en-CA", new int[] { 1, 0 } }; + yield return new object[] { "fa-IR" , new int[] { 1, 0 } }; + yield return new object[] { "fr-CD", new int[] { 8, 15 } }; + yield return new object[] { "fr-CA", new int[] { 8, 15 } }; + yield return new object[] { "as", new int[] { 9 } }; } [Theory] [MemberData(nameof(CurrencyNegativePatternTestLocales))] - public void CurrencyNegativePattern_Get_ReturnsExpected_ByLocale(string locale) + public void CurrencyNegativePattern_Get_ReturnsExpected_ByLocale(string locale, int[] expectedNegativePattern) { CultureInfo culture = new CultureInfo(locale); NumberFormatInfo format = culture.NumberFormat; - Assert.Contains(format.CurrencyNegativePattern, GetCurrencyNegativePatterns(locale)); + Assert.Contains(format.CurrencyNegativePattern, expectedNegativePattern); } - internal static int[] GetCurrencyNegativePatterns(string localeName) - { - switch (localeName) - { - case "en-US": - return new int[] { 1, 0 }; - case "en-CA": - return new int[] { 1, 0 }; - case "fa-IR": - return new int[] { 1, 0 }; - case "fr-CD": - return new int[] { 8, 15 }; - case "as": - return new int[] { 9 }; - case "fr-CA": - return new int[] { 8, 15 }; - } - - return new int[] { 0 }; - } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.OSX.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.OSX.cs index 3be4f1593b6d54..55db6334f188f1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.OSX.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.OSX.cs @@ -17,9 +17,7 @@ private bool InitNativeCultureDataCore() Debug.Assert(!GlobalizationMode.Invariant); string realNameBuffer = _sRealName; - _sWindowsName = GetLocaleNameNative(realNameBuffer); - _sName = _sWindowsName; - _sRealName = _sName; + sWindowsName = _sName = _sRealName = GetLocaleNameNative(realNameBuffer); return true; } @@ -49,12 +47,9 @@ private int GetLocaleInfoNative(LocaleNumberData type) { Debug.Assert(_sWindowsName != null, "[CultureData.GetLocaleInfoNative(LocaleNumberData)] Expected _sWindowsName to be populated already"); - switch (type) - { - case LocaleNumberData.CalendarType: - // returning 0 will cause the first supported calendar to be returned, which is the preferred calendar - return 0; - } + // returning 0 will cause the first supported calendar to be returned, which is the preferred calendar + if (type == LocaleNumberData.CalendarType) + return 0; return Interop.Globalization.GetLocaleInfoIntNative(_sWindowsName, (uint)type); } @@ -76,7 +71,7 @@ private int[] GetLocaleInfoNative(LocaleGroupingData type) private string GetTimeFormatStringNative() => GetTimeFormatStringNative(shortFormat: false); - private unsafe string GetTimeFormatStringNative(bool shortFormat) + private string GetTimeFormatStringNative(bool shortFormat) { Debug.Assert(_sWindowsName != null, "[CultureData.GetTimeFormatStringNative(bool shortFormat)] Expected _sWindowsName to be populated already"); diff --git a/src/native/libs/System.Globalization.Native/pal_locale.c b/src/native/libs/System.Globalization.Native/pal_locale.c index eada9f12ddd39f..ccae3e0c6d989f 100644 --- a/src/native/libs/System.Globalization.Native/pal_locale.c +++ b/src/native/libs/System.Globalization.Native/pal_locale.c @@ -277,3 +277,25 @@ int32_t GlobalizationNative_IsPredefinedLocale(const UChar* localeName) return err == U_ZERO_ERROR; } + +/* +PAL Function: +GetLocaleTimeFormat + +Obtains time format information (in ICU format, it needs to be converted to .NET's format). +Returns 1 for success, 0 otherwise +*/ +int32_t GlobalizationNative_GetLocaleTimeFormat(const UChar* localeName, + int shortFormat, + UChar* value, + int32_t valueLength) +{ + UErrorCode err = U_ZERO_ERROR; + char locale[ULOC_FULLNAME_CAPACITY]; + GetLocale(localeName, locale, ULOC_FULLNAME_CAPACITY, false, &err); + UDateFormatStyle style = (shortFormat != 0) ? UDAT_SHORT : UDAT_MEDIUM; + UDateFormat* pFormat = udat_open(style, UDAT_NONE, locale, NULL, 0, NULL, 0, &err); + udat_toPattern(pFormat, false, value, valueLength, &err); + udat_close(pFormat); + return UErrorCodeToBool(err); +} diff --git a/src/native/libs/System.Globalization.Native/pal_locale.h b/src/native/libs/System.Globalization.Native/pal_locale.h index 7ee6d1e7ee56e1..634053388c8e25 100644 --- a/src/native/libs/System.Globalization.Native/pal_locale.h +++ b/src/native/libs/System.Globalization.Native/pal_locale.h @@ -9,10 +9,18 @@ PALEXPORT int32_t GlobalizationNative_GetLocales(UChar *value, int32_t valueLeng PALEXPORT int32_t GlobalizationNative_GetLocaleName(const UChar* localeName, UChar* value, int32_t valueLength); +PALEXPORT int32_t GlobalizationNative_GetDefaultLocaleName(UChar* value, int32_t valueLength); + +PALEXPORT int32_t GlobalizationNative_IsPredefinedLocale(const UChar* localeName); + +PALEXPORT int32_t GlobalizationNative_GetLocaleTimeFormat(const UChar* localeName, + int shortFormat, UChar* value, + int32_t valueLength); + #ifdef __APPLE__ + PALEXPORT const char* GlobalizationNative_GetLocaleNameNative(const char* localeName); -#endif -PALEXPORT int32_t GlobalizationNative_GetDefaultLocaleName(UChar* value, int32_t valueLength); +PALEXPORT const char* GlobalizationNative_GetLocaleTimeFormatNative(const char* localeName, int shortFormat); -PALEXPORT int32_t GlobalizationNative_IsPredefinedLocale(const UChar* localeName); +#endif diff --git a/src/native/libs/System.Globalization.Native/pal_locale.m b/src/native/libs/System.Globalization.Native/pal_locale.m index 0c2ce97f3fcfe6..cbda906ff3a15d 100644 --- a/src/native/libs/System.Globalization.Native/pal_locale.m +++ b/src/native/libs/System.Globalization.Native/pal_locale.m @@ -154,7 +154,7 @@ // invariant character definitions #define CHAR_CURRENCY ((char)0x00A4) // international currency #define CHAR_SPACE ((char)0x0020) // space -#define CHAR_NBSPACE ((char)0x00A0) // space +#define CHAR_NBSPACE ((char)0x00A0) // no-break space #define CHAR_DIGIT ((char)0x0023) // '#' #define CHAR_MINUS ((char)0x002D) // '-' #define CHAR_PERCENT ((char)0x0025) // '%' @@ -370,11 +370,7 @@ int32_t GlobalizationNative_GetLocaleInfoIntNative(const char* localeName, Local static const char* Patterns[] = {"(n)", "-n", "- n", "n-", "n -"}; const char *pFormat = [[numberFormatter negativeFormat] UTF8String]; value = GetNumericPatternNative(pFormat, Patterns, sizeof(Patterns)/sizeof(Patterns[0]), true); - if (value >= 0) - { - return value; - } - else + if (value < 0) { isSuccess = false; } @@ -396,11 +392,7 @@ int32_t GlobalizationNative_GetLocaleInfoIntNative(const char* localeName, Local static const char* Patterns[] = {"Cn", "nC", "C n", "n C"}; const char *pFormat = [[numberFormatter positiveFormat] UTF8String]; value = GetNumericPatternNative(pFormat, Patterns, sizeof(Patterns)/sizeof(Patterns[0]), false); - if (value >= 0) - { - return value; - } - else + if (value < 0) { isSuccess = false; } @@ -430,11 +422,7 @@ int32_t GlobalizationNative_GetLocaleInfoIntNative(const char* localeName, Local "C- n" }; const char *pFormat = [[numberFormatter negativeFormat] UTF8String]; value = GetNumericPatternNative(pFormat, Patterns, sizeof(Patterns)/sizeof(Patterns[0]), true); - if (value >= 0) - { - return value; - } - else + if (value < 0) { isSuccess = false; } @@ -485,11 +473,7 @@ int32_t GlobalizationNative_GetLocaleInfoIntNative(const char* localeName, Local static const char* Patterns[] = {"-n %", "-n%", "-%n", "%-n", "%n-", "n-%", "n%-", "-% n", "n %-", "% n-", "% -n", "n- %"}; const char *pFormat = [[numberFormatter negativeFormat] UTF8String]; value = GetNumericPatternNative(pFormat, Patterns, sizeof(Patterns)/sizeof(Patterns[0]), true); - if (value >= 0) - { - return value; - } - else + if (value < 0) { isSuccess = false; } @@ -503,11 +487,7 @@ int32_t GlobalizationNative_GetLocaleInfoIntNative(const char* localeName, Local static const char* Patterns[] = {"n %", "n%", "%n", "% n"}; const char *pFormat = [[numberFormatter positiveFormat] UTF8String]; value = GetNumericPatternNative(pFormat, Patterns, sizeof(Patterns)/sizeof(Patterns[0]), false); - if (value >= 0) - { - return value; - } - else + if (value < 0) { isSuccess = false; } @@ -515,10 +495,12 @@ int32_t GlobalizationNative_GetLocaleInfoIntNative(const char* localeName, Local } default: value = -1; - assert(isSuccess); + isSuccess = false; break; } + assert(isSuccess); + return value; } diff --git a/src/native/libs/System.Globalization.Native/pal_localeNumberData.h b/src/native/libs/System.Globalization.Native/pal_localeNumberData.h index 933946949200e0..c1c876f9b2ec48 100644 --- a/src/native/libs/System.Globalization.Native/pal_localeNumberData.h +++ b/src/native/libs/System.Globalization.Native/pal_localeNumberData.h @@ -45,12 +45,14 @@ PALEXPORT int32_t GlobalizationNative_GetLocaleInfoGroupingSizes(const UChar* lo int32_t* secondaryGroupSize); #ifdef __APPLE__ + PALEXPORT int32_t GlobalizationNative_GetLocaleInfoIntNative(const char* localeName, LocaleNumberData localeNumberData); PALEXPORT int32_t GlobalizationNative_GetLocaleInfoPrimaryGroupingSizeNative(const char* localeName, - LocaleNumberData localeGroupingData); + LocaleNumberData localeGroupingData); PALEXPORT int32_t GlobalizationNative_GetLocaleInfoSecondaryGroupingSizeNative(const char* localeName, - LocaleNumberData localeGroupingData); + LocaleNumberData localeGroupingData); + #endif diff --git a/src/native/libs/System.Globalization.Native/pal_localeStringData.c b/src/native/libs/System.Globalization.Native/pal_localeStringData.c index 9120b89bd329bd..488cbc8cb35f2a 100644 --- a/src/native/libs/System.Globalization.Native/pal_localeStringData.c +++ b/src/native/libs/System.Globalization.Native/pal_localeStringData.c @@ -401,24 +401,3 @@ int32_t GlobalizationNative_GetLocaleInfoString(const UChar* localeName, return UErrorCodeToBool(status); } -/* -PAL Function: -GetLocaleTimeFormat - -Obtains time format information (in ICU format, it needs to be converted to .NET's format). -Returns 1 for success, 0 otherwise -*/ -int32_t GlobalizationNative_GetLocaleTimeFormat(const UChar* localeName, - int shortFormat, - UChar* value, - int32_t valueLength) -{ - UErrorCode err = U_ZERO_ERROR; - char locale[ULOC_FULLNAME_CAPACITY]; - GetLocale(localeName, locale, ULOC_FULLNAME_CAPACITY, false, &err); - UDateFormatStyle style = (shortFormat != 0) ? UDAT_SHORT : UDAT_MEDIUM; - UDateFormat* pFormat = udat_open(style, UDAT_NONE, locale, NULL, 0, NULL, 0, &err); - udat_toPattern(pFormat, false, value, valueLength, &err); - udat_close(pFormat); - return UErrorCodeToBool(err); -} diff --git a/src/native/libs/System.Globalization.Native/pal_localeStringData.h b/src/native/libs/System.Globalization.Native/pal_localeStringData.h index 175a7ced3f1dc3..be563bf7c1690e 100644 --- a/src/native/libs/System.Globalization.Native/pal_localeStringData.h +++ b/src/native/libs/System.Globalization.Native/pal_localeStringData.h @@ -50,13 +50,9 @@ PALEXPORT int32_t GlobalizationNative_GetLocaleInfoString(const UChar* localeNam int32_t valueLength, const UChar* uiLocaleName); -PALEXPORT int32_t GlobalizationNative_GetLocaleTimeFormat(const UChar* localeName, - int shortFormat, UChar* value, - int32_t valueLength); - #ifdef __APPLE__ + PALEXPORT const char* GlobalizationNative_GetLocaleInfoStringNative(const char* localeName, LocaleStringData localeStringData); -PALEXPORT const char* GlobalizationNative_GetLocaleTimeFormatNative(const char* localeName, int shortFormat); #endif diff --git a/src/native/libs/System.Globalization.Native/pal_locale_internal.h b/src/native/libs/System.Globalization.Native/pal_locale_internal.h index f28f5a68883286..cad99d06a890a3 100644 --- a/src/native/libs/System.Globalization.Native/pal_locale_internal.h +++ b/src/native/libs/System.Globalization.Native/pal_locale_internal.h @@ -63,44 +63,4 @@ Detects the default locale string for Apple platforms */ char* DetectDefaultAppleLocaleName(void); -/* -Function: -GlobalizationNative_GetLocaleNameNative - -Returns native locale name for Apple platforms -*/ -const char* GlobalizationNative_GetLocaleNameNative(const char* localeName); - -/* -Function: -GlobalizationNative_GetLocaleInfoStringNative - -Returns string locale information if found for the specified locale name for Apple platforms. -Returns empty string if not found. -*/ -const char* GlobalizationNative_GetLocaleInfoStringNative(const char* localeName, LocaleStringData localeStringData); - -/* -Function: -GlobalizationNative_GetLocaleInfoIntNative - -Returns int locale information if found for the specified locale name for Apple platforms. -*/ -int32_t GlobalizationNative_GetLocaleInfoIntNative(const char* localeName, LocaleNumberData localeStringData); - -/* -Function: -GlobalizationNative_GetLocaleInfoPrimaryGroupingSizeNative - -Returns primary grouping size for decimal and currency for the specified locale name for Apple platforms. -*/ -int32_t GlobalizationNative_GetLocaleInfoPrimaryGroupingSizeNative(const char* localeName, LocaleNumberData localeStringData); - -/* -Function: -GlobalizationNative_GetLocaleInfoSecondaryGroupingSizeNative - -Returns secondary grouping size for decimal and currency for the specified locale name for Apple platforms. -*/ -int32_t GlobalizationNative_GetLocaleInfoSecondaryGroupingSizeNative(const char* localeName, LocaleNumberData localeStringData); #endif From b3261e65435d3179c1b4685e26927e4d1f7fb83c Mon Sep 17 00:00:00 2001 From: Meri Khamoyan Date: Wed, 12 Apr 2023 09:17:32 +0200 Subject: [PATCH 07/16] Fix the name --- .../src/System/Globalization/CultureData.OSX.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.OSX.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.OSX.cs index 55db6334f188f1..ce9c13a9f9b85c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.OSX.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.OSX.cs @@ -17,7 +17,7 @@ private bool InitNativeCultureDataCore() Debug.Assert(!GlobalizationMode.Invariant); string realNameBuffer = _sRealName; - sWindowsName = _sName = _sRealName = GetLocaleNameNative(realNameBuffer); + _sWindowsName = _sName = _sRealName = GetLocaleNameNative(realNameBuffer); return true; } From 446a40da357029396fae810e96d1b3cd39adcaee Mon Sep 17 00:00:00 2001 From: Meri Khamoyan Date: Wed, 12 Apr 2023 11:43:00 +0200 Subject: [PATCH 08/16] Fix failure on OSX --- .../src/System/Globalization/CultureData.OSX.cs | 8 +++++--- .../src/System/Globalization/CultureData.Unix.cs | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.OSX.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.OSX.cs index ce9c13a9f9b85c..642032252d5607 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.OSX.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.OSX.cs @@ -7,11 +7,13 @@ namespace System.Globalization { internal sealed partial class CultureData { + private const int LOC_FULLNAME_CAPACITY = 157; // max size of locale name + /// /// This method uses the sRealName field (which is initialized by the constructor before this is called) to /// initialize the rest of the state of CultureData based on the underlying OS globalization library. /// - private bool InitNativeCultureDataCore() + private bool InitAppleCultureDataCore() { Debug.Assert(_sRealName != null); Debug.Assert(!GlobalizationMode.Invariant); @@ -77,12 +79,12 @@ private string GetTimeFormatStringNative(bool shortFormat) string result = Interop.Globalization.GetLocaleTimeFormatNative(_sWindowsName, shortFormat); - return ConvertIcuTimeFormatString(result); + return ConvertNativeTimeFormatString(result); } private static string ConvertNativeTimeFormatString(string nativeFormatString) { - Span result = stackalloc char[ICU_ULOC_FULLNAME_CAPACITY]; + Span result = stackalloc char[LOC_FULLNAME_CAPACITY]; bool amPmAdded = false; int resultPos = 0; diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Unix.cs index a84b88f54b6304..76c009beb6466f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Unix.cs @@ -9,7 +9,7 @@ internal sealed partial class CultureData { private bool InitCultureDataCore() => #if TARGET_OSX || TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS - InitNativeCultureDataCore(); + GlobalizationMode.Hybrid ? InitAppleCultureDataCore() : InitIcuCultureDataCore(); #else InitIcuCultureDataCore(); #endif From 11f6729f8414193658aa25ec7c39cbb8a21313b5 Mon Sep 17 00:00:00 2001 From: Meri Khamoyan Date: Wed, 12 Apr 2023 19:04:56 +0200 Subject: [PATCH 09/16] refactored GlobalizationNative_GetLocaleInfoInt... --- .../System.Globalization.Native/pal_locale.m | 141 ++++++++---------- 1 file changed, 62 insertions(+), 79 deletions(-) diff --git a/src/native/libs/System.Globalization.Native/pal_locale.m b/src/native/libs/System.Globalization.Native/pal_locale.m index cbda906ff3a15d..a6bd4a4c5f435c 100644 --- a/src/native/libs/System.Globalization.Native/pal_locale.m +++ b/src/native/libs/System.Globalization.Native/pal_locale.m @@ -203,8 +203,7 @@ for (int i = iStart; i <= iEnd; i++) { - char ch = srcPattern[i]; - switch (ch) + switch (srcPattern[i]) { case CHAR_MINUS: case CHAR_OPENPAREN: @@ -337,6 +336,63 @@ static int GetNumericPatternNative(const char* pNumberFormat, return INVALID_FORMAT; } +static int32_t GetValueForNumberFormat(NSLocale *currentLocale, LocaleNumberData localeNumberData) +{ + NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init]; + numberFormatter.locale = currentLocale; + const char *pFormat; + int32_t value; + + switch(localeNumberData) + { + case LocaleNumber_PositiveMonetaryNumberFormat: + { + numberFormatter.numberStyle = NSNumberFormatterCurrencyStyle; + static const char* Patterns[] = {"Cn", "nC", "C n", "n C"}; + pFormat = [[numberFormatter positiveFormat] UTF8String]; + value = GetNumericPatternNative(pFormat, Patterns, sizeof(Patterns)/sizeof(Patterns[0]), false); + break; + } + case LocaleNumber_NegativeMonetaryNumberFormat: + { + numberFormatter.numberStyle = NSNumberFormatterCurrencyStyle; + static const char* Patterns[] = {"(Cn)", "-Cn", "C-n", "Cn-", "(nC)", "-nC", "n-C", "nC-", "-n C", + "-C n", "n C-", "C n-", "C -n", "n- C", "(C n)", "(n C)", "C- n" }; + pFormat = [[numberFormatter negativeFormat] UTF8String]; + value = GetNumericPatternNative(pFormat, Patterns, sizeof(Patterns)/sizeof(Patterns[0]), true); + break; + } + case LocaleNumber_NegativeNumberFormat: + { + numberFormatter.numberStyle = NSNumberFormatterDecimalStyle; + static const char* Patterns[] = {"(n)", "-n", "- n", "n-", "n -"}; + pFormat = [[numberFormatter negativeFormat] UTF8String]; + value = GetNumericPatternNative(pFormat, Patterns, sizeof(Patterns)/sizeof(Patterns[0]), true); + break; + } + case LocaleNumber_NegativePercentFormat: + { + numberFormatter.numberStyle = NSNumberFormatterPercentStyle; + static const char* Patterns[] = {"-n %", "-n%", "-%n", "%-n", "%n-", "n-%", "n%-", "-% n", "n %-", "% n-", "% -n", "n- %"}; + pFormat = [[numberFormatter negativeFormat] UTF8String]; + value = GetNumericPatternNative(pFormat, Patterns, sizeof(Patterns)/sizeof(Patterns[0]), true); + break; + } + case LocaleNumber_PositivePercentFormat: + { + numberFormatter.numberStyle = NSNumberFormatterPercentStyle; + static const char* Patterns[] = {"n %", "n%", "%n", "% n"}; + pFormat = [[numberFormatter positiveFormat] UTF8String]; + value = GetNumericPatternNative(pFormat, Patterns, sizeof(Patterns)/sizeof(Patterns[0]), false); + break; + } + default: + return -1; + } + + return value; +} + int32_t GlobalizationNative_GetLocaleInfoIntNative(const char* localeName, LocaleNumberData localeNumberData) { bool isSuccess = true; @@ -362,20 +418,6 @@ int32_t GlobalizationNative_GetLocaleInfoIntNative(const char* localeName, Local value = (int32_t)numberFormatter.maximumFractionDigits; break; } - case LocaleNumber_NegativeNumberFormat: - { - NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init]; - numberFormatter.locale = currentLocale; - numberFormatter.numberStyle = NSNumberFormatterDecimalStyle; - static const char* Patterns[] = {"(n)", "-n", "- n", "n-", "n -"}; - const char *pFormat = [[numberFormatter negativeFormat] UTF8String]; - value = GetNumericPatternNative(pFormat, Patterns, sizeof(Patterns)/sizeof(Patterns[0]), true); - if (value < 0) - { - isSuccess = false; - } - break; - } case LocaleNumber_MonetaryFractionalDigitsCount: { NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init]; @@ -385,43 +427,12 @@ int32_t GlobalizationNative_GetLocaleInfoIntNative(const char* localeName, Local break; } case LocaleNumber_PositiveMonetaryNumberFormat: - { - NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init]; - numberFormatter.locale = currentLocale; - numberFormatter.numberStyle = NSNumberFormatterCurrencyStyle; - static const char* Patterns[] = {"Cn", "nC", "C n", "n C"}; - const char *pFormat = [[numberFormatter positiveFormat] UTF8String]; - value = GetNumericPatternNative(pFormat, Patterns, sizeof(Patterns)/sizeof(Patterns[0]), false); - if (value < 0) - { - isSuccess = false; - } - break; - } case LocaleNumber_NegativeMonetaryNumberFormat: + case LocaleNumber_NegativeNumberFormat: + case LocaleNumber_NegativePercentFormat: + case LocaleNumber_PositivePercentFormat: { - NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init]; - numberFormatter.locale = currentLocale; - numberFormatter.numberStyle = NSNumberFormatterCurrencyStyle; - static const char* Patterns[] = {"(Cn)", - "-Cn", - "C-n", - "Cn-", - "(nC)", - "-nC", - "n-C", - "nC-", - "-n C", - "-C n", - "n C-", - "C n-", - "C -n", - "n- C", - "(C n)", - "(n C)", - "C- n" }; - const char *pFormat = [[numberFormatter negativeFormat] UTF8String]; - value = GetNumericPatternNative(pFormat, Patterns, sizeof(Patterns)/sizeof(Patterns[0]), true); + value = GetValueForNumberFormat(currentLocale, localeNumberData); if (value < 0) { isSuccess = false; @@ -464,34 +475,6 @@ int32_t GlobalizationNative_GetLocaleInfoIntNative(const char* localeName, Local NSCalendar *calendar = [currentLocale objectForKey:NSLocaleCalendar]; value = [calendar firstWeekday] - 1; // .NET is 0-based and in Apple is 1-based; break; - } - case LocaleNumber_NegativePercentFormat: - { - NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init]; - numberFormatter.locale = currentLocale; - numberFormatter.numberStyle = NSNumberFormatterPercentStyle; - static const char* Patterns[] = {"-n %", "-n%", "-%n", "%-n", "%n-", "n-%", "n%-", "-% n", "n %-", "% n-", "% -n", "n- %"}; - const char *pFormat = [[numberFormatter negativeFormat] UTF8String]; - value = GetNumericPatternNative(pFormat, Patterns, sizeof(Patterns)/sizeof(Patterns[0]), true); - if (value < 0) - { - isSuccess = false; - } - break; - } - case LocaleNumber_PositivePercentFormat: - { - NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init]; - numberFormatter.locale = currentLocale; - numberFormatter.numberStyle = NSNumberFormatterPercentStyle; - static const char* Patterns[] = {"n %", "n%", "%n", "% n"}; - const char *pFormat = [[numberFormatter positiveFormat] UTF8String]; - value = GetNumericPatternNative(pFormat, Patterns, sizeof(Patterns)/sizeof(Patterns[0]), false); - if (value < 0) - { - isSuccess = false; - } - break; } default: value = -1; From fda5c99a67bb165ff4b32735ff9ba5ea9b9ce84f Mon Sep 17 00:00:00 2001 From: Meri Khamoyan Date: Thu, 13 Apr 2023 09:55:09 +0200 Subject: [PATCH 10/16] Include related test files --- .../tests/Hybrid/Hybrid.IOS.Tests.csproj | 26 ++++++++++++++ .../tests/Hybrid/HybridMode.cs | 34 ------------------- 2 files changed, 26 insertions(+), 34 deletions(-) diff --git a/src/libraries/System.Globalization/tests/Hybrid/Hybrid.IOS.Tests.csproj b/src/libraries/System.Globalization/tests/Hybrid/Hybrid.IOS.Tests.csproj index 9cee5bb3c1de8d..b9887c1021aaf4 100644 --- a/src/libraries/System.Globalization/tests/Hybrid/Hybrid.IOS.Tests.csproj +++ b/src/libraries/System.Globalization/tests/Hybrid/Hybrid.IOS.Tests.csproj @@ -6,5 +6,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/libraries/System.Globalization/tests/Hybrid/HybridMode.cs b/src/libraries/System.Globalization/tests/Hybrid/HybridMode.cs index 446aed947db0da..4c960b887ab21f 100644 --- a/src/libraries/System.Globalization/tests/Hybrid/HybridMode.cs +++ b/src/libraries/System.Globalization/tests/Hybrid/HybridMode.cs @@ -45,39 +45,5 @@ public void TwoLetterISOLanguageName(string name, string expected) Assert.Equal(expected, new CultureInfo(name).TwoLetterISOLanguageName); } - public static IEnumerable FirstDayOfWeek_Get_TestData() - { - yield return new object[] { DateTimeFormatInfo.InvariantInfo, DayOfWeek.Sunday }; - yield return new object[] { new CultureInfo("en-US", false).DateTimeFormat, DayOfWeek.Sunday }; - yield return new object[] { new CultureInfo("fr-FR", false).DateTimeFormat, DayOfWeek.Monday }; - } - - [Theory] - [MemberData(nameof(FirstDayOfWeek_Get_TestData))] - public void FirstDayOfWeek(DateTimeFormatInfo format, DayOfWeek expected) - { - Assert.Equal(expected, format.FirstDayOfWeek); - } - - public static IEnumerable CurrencyNegativePatternTestLocales() - { - yield return new object[] { "en-US", new int[] { 1, 0 } }; - yield return new object[] { "en-CA", new int[] { 1, 0 } }; - yield return new object[] { "fa-IR" , new int[] { 1, 0 } }; - yield return new object[] { "fr-CD", new int[] { 8, 15 } }; - yield return new object[] { "fr-CA", new int[] { 8, 15 } }; - yield return new object[] { "as", new int[] { 9 } }; - } - - [Theory] - [MemberData(nameof(CurrencyNegativePatternTestLocales))] - public void CurrencyNegativePattern_Get_ReturnsExpected_ByLocale(string locale, int[] expectedNegativePattern) - { - CultureInfo culture = new CultureInfo(locale); - - NumberFormatInfo format = culture.NumberFormat; - Assert.Contains(format.CurrencyNegativePattern, expectedNegativePattern); - } - } } From 2ee76d9fe0fed67c5dfbf1960ca04e97c072638f Mon Sep 17 00:00:00 2001 From: Meri Khamoyan Date: Thu, 13 Apr 2023 09:56:24 +0200 Subject: [PATCH 11/16] Remove extra line --- src/libraries/System.Globalization/tests/Hybrid/HybridMode.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libraries/System.Globalization/tests/Hybrid/HybridMode.cs b/src/libraries/System.Globalization/tests/Hybrid/HybridMode.cs index 4c960b887ab21f..f5443f8c2c3c01 100644 --- a/src/libraries/System.Globalization/tests/Hybrid/HybridMode.cs +++ b/src/libraries/System.Globalization/tests/Hybrid/HybridMode.cs @@ -44,6 +44,5 @@ public void TwoLetterISOLanguageName(string name, string expected) { Assert.Equal(expected, new CultureInfo(name).TwoLetterISOLanguageName); } - } } From fbab33c5de822093292c38ae9782255d92ef575a Mon Sep 17 00:00:00 2001 From: Meri Khamoyan Date: Thu, 13 Apr 2023 14:58:49 +0200 Subject: [PATCH 12/16] Refactored NormalizeNumericPattern --- .../System.Globalization.Native/pal_locale.m | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/native/libs/System.Globalization.Native/pal_locale.m b/src/native/libs/System.Globalization.Native/pal_locale.m index a6bd4a4c5f435c..2adc1b9254a6f2 100644 --- a/src/native/libs/System.Globalization.Native/pal_locale.m +++ b/src/native/libs/System.Globalization.Native/pal_locale.m @@ -173,18 +173,14 @@ { int iStart = 0; int iEnd = strlen(srcPattern); - int32_t iNegativePatternStart = -1; - for (int i = iStart; i < iEnd; i++) - { - if (srcPattern[i] == ';') - { - iNegativePatternStart = i; - } - } - - if (iNegativePatternStart >= 0) + // ';' separates positive and negative subpatterns. + // When there is no explicit negative subpattern, + // an implicit negative subpattern is formed from the positive pattern with a prefixed. + char * ptrNegativePattern = strrchr(srcPattern,';'); + if (ptrNegativePattern) { + int32_t iNegativePatternStart = ptrNegativePattern - srcPattern; if (isNegative) { iStart = iNegativePatternStart + 1; @@ -211,6 +207,9 @@ minusAdded = true; break; } + + if (minusAdded) + break; } // international currency symbol (CHAR_CURRENCY) @@ -302,12 +301,13 @@ static int GetNumericPatternNative(const char* pNumberFormat, int isNegative) { const int INVALID_FORMAT = -1; + const int MEMORY_ALLOCATION_ERROR = 7; const int MAX_DOTNET_NUMERIC_PATTERN_LENGTH = 6; // example: "(C n)" plus terminator char* normalizedPattern = NormalizeNumericPattern(pNumberFormat, isNegative); if (!normalizedPattern) { - return U_MEMORY_ALLOCATION_ERROR; + return MEMORY_ALLOCATION_ERROR; } size_t normalizedPatternLength = strlen(normalizedPattern); From f214256dc86c50a18dd343e03f2fe7cc75cce4dd Mon Sep 17 00:00:00 2001 From: Meri Khamoyan Date: Thu, 13 Apr 2023 17:48:46 +0200 Subject: [PATCH 13/16] Add assert when value is invalid --- .../src/System/Globalization/CultureData.OSX.cs | 8 +++++++- src/native/libs/System.Globalization.Native/pal_locale.m | 3 +-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.OSX.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.OSX.cs index 642032252d5607..f4cd7b12e3e373 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.OSX.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.OSX.cs @@ -53,7 +53,13 @@ private int GetLocaleInfoNative(LocaleNumberData type) if (type == LocaleNumberData.CalendarType) return 0; - return Interop.Globalization.GetLocaleInfoIntNative(_sWindowsName, (uint)type); + int value = Interop.Globalization.GetLocaleInfoIntNative(_sWindowsName, (uint)type); + if (value < 0) + { + Debug.Fail("[CultureData.GetLocaleInfoNative(LocaleNumberData)] failed"); + } + + return value; } private int[] GetLocaleInfoNative(LocaleGroupingData type) diff --git a/src/native/libs/System.Globalization.Native/pal_locale.m b/src/native/libs/System.Globalization.Native/pal_locale.m index 2adc1b9254a6f2..9c9f5589f1a582 100644 --- a/src/native/libs/System.Globalization.Native/pal_locale.m +++ b/src/native/libs/System.Globalization.Native/pal_locale.m @@ -301,13 +301,12 @@ static int GetNumericPatternNative(const char* pNumberFormat, int isNegative) { const int INVALID_FORMAT = -1; - const int MEMORY_ALLOCATION_ERROR = 7; const int MAX_DOTNET_NUMERIC_PATTERN_LENGTH = 6; // example: "(C n)" plus terminator char* normalizedPattern = NormalizeNumericPattern(pNumberFormat, isNegative); if (!normalizedPattern) { - return MEMORY_ALLOCATION_ERROR; + return INVALID_FORMAT; } size_t normalizedPatternLength = strlen(normalizedPattern); From 64a7f05ef99d019377f1c0e9604c676c242b7c6b Mon Sep 17 00:00:00 2001 From: Meri Khamoyan Date: Fri, 14 Apr 2023 13:04:36 +0200 Subject: [PATCH 14/16] Fix spacing --- src/native/libs/System.Globalization.Native/pal_locale.h | 2 -- .../libs/System.Globalization.Native/pal_localeNumberData.h | 2 -- .../libs/System.Globalization.Native/pal_localeStringData.h | 2 -- .../libs/System.Globalization.Native/pal_locale_internal.h | 3 --- 4 files changed, 9 deletions(-) diff --git a/src/native/libs/System.Globalization.Native/pal_locale.h b/src/native/libs/System.Globalization.Native/pal_locale.h index 634053388c8e25..7fe89f667f2132 100644 --- a/src/native/libs/System.Globalization.Native/pal_locale.h +++ b/src/native/libs/System.Globalization.Native/pal_locale.h @@ -18,9 +18,7 @@ PALEXPORT int32_t GlobalizationNative_GetLocaleTimeFormat(const UChar* localeNam int32_t valueLength); #ifdef __APPLE__ - PALEXPORT const char* GlobalizationNative_GetLocaleNameNative(const char* localeName); PALEXPORT const char* GlobalizationNative_GetLocaleTimeFormatNative(const char* localeName, int shortFormat); - #endif diff --git a/src/native/libs/System.Globalization.Native/pal_localeNumberData.h b/src/native/libs/System.Globalization.Native/pal_localeNumberData.h index c1c876f9b2ec48..a68f1e32e334ab 100644 --- a/src/native/libs/System.Globalization.Native/pal_localeNumberData.h +++ b/src/native/libs/System.Globalization.Native/pal_localeNumberData.h @@ -45,7 +45,6 @@ PALEXPORT int32_t GlobalizationNative_GetLocaleInfoGroupingSizes(const UChar* lo int32_t* secondaryGroupSize); #ifdef __APPLE__ - PALEXPORT int32_t GlobalizationNative_GetLocaleInfoIntNative(const char* localeName, LocaleNumberData localeNumberData); @@ -54,5 +53,4 @@ PALEXPORT int32_t GlobalizationNative_GetLocaleInfoPrimaryGroupingSizeNative(con PALEXPORT int32_t GlobalizationNative_GetLocaleInfoSecondaryGroupingSizeNative(const char* localeName, LocaleNumberData localeGroupingData); - #endif diff --git a/src/native/libs/System.Globalization.Native/pal_localeStringData.h b/src/native/libs/System.Globalization.Native/pal_localeStringData.h index be563bf7c1690e..a6961c39761bfd 100644 --- a/src/native/libs/System.Globalization.Native/pal_localeStringData.h +++ b/src/native/libs/System.Globalization.Native/pal_localeStringData.h @@ -51,8 +51,6 @@ PALEXPORT int32_t GlobalizationNative_GetLocaleInfoString(const UChar* localeNam const UChar* uiLocaleName); #ifdef __APPLE__ - PALEXPORT const char* GlobalizationNative_GetLocaleInfoStringNative(const char* localeName, LocaleStringData localeStringData); - #endif diff --git a/src/native/libs/System.Globalization.Native/pal_locale_internal.h b/src/native/libs/System.Globalization.Native/pal_locale_internal.h index cad99d06a890a3..c754554bbfdd55 100644 --- a/src/native/libs/System.Globalization.Native/pal_locale_internal.h +++ b/src/native/libs/System.Globalization.Native/pal_locale_internal.h @@ -4,8 +4,6 @@ #pragma once #include "pal_icushim_internal.h" -#include "pal_localeStringData.h" -#include "pal_localeNumberData.h" /* Function: @@ -62,5 +60,4 @@ DetectDefaultSystemLocaleName Detects the default locale string for Apple platforms */ char* DetectDefaultAppleLocaleName(void); - #endif From eeb996251f7a854c363ed98d5c99995456133a0a Mon Sep 17 00:00:00 2001 From: Meri Khamoyan Date: Fri, 14 Apr 2023 13:23:24 +0200 Subject: [PATCH 15/16] Refactor as requested by review --- .../System/Globalization/CultureData.OSX.cs | 2 + .../System.Globalization.Native/pal_locale.m | 60 +++++++++---------- 2 files changed, 29 insertions(+), 33 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.OSX.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.OSX.cs index f4cd7b12e3e373..cdbdd65a81426f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.OSX.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.OSX.cs @@ -54,9 +54,11 @@ private int GetLocaleInfoNative(LocaleNumberData type) return 0; int value = Interop.Globalization.GetLocaleInfoIntNative(_sWindowsName, (uint)type); + const int DEFAULT_VALUE = 0; if (value < 0) { Debug.Fail("[CultureData.GetLocaleInfoNative(LocaleNumberData)] failed"); + return DEFAULT_VALUE; } return value; diff --git a/src/native/libs/System.Globalization.Native/pal_locale.m b/src/native/libs/System.Globalization.Native/pal_locale.m index 9c9f5589f1a582..4479e6f4cbe0be 100644 --- a/src/native/libs/System.Globalization.Native/pal_locale.m +++ b/src/native/libs/System.Globalization.Native/pal_locale.m @@ -176,7 +176,7 @@ // ';' separates positive and negative subpatterns. // When there is no explicit negative subpattern, - // an implicit negative subpattern is formed from the positive pattern with a prefixed. + // an implicit negative subpattern is formed from the positive pattern with a prefixed '-'. char * ptrNegativePattern = strrchr(srcPattern,';'); if (ptrNegativePattern) { @@ -191,11 +191,7 @@ } } - int index = 0; int minusAdded = false; - int digitAdded = false; - int currencyAdded = false; - int spaceAdded = false; for (int i = iStart; i <= iEnd; i++) { @@ -218,6 +214,7 @@ // A destPattern example: "(C n)" where C represents the currency symbol, and // n is the number char* destPattern; + int index = 0; // if there is no negative subpattern, prefix the minus sign if (isNegative && !minusAdded) @@ -240,6 +237,10 @@ } } + int digitAdded = false; + int currencyAdded = false; + int spaceAdded = false; + for (int i = iStart; i <= iEnd; i++) { char ch = srcPattern[i]; @@ -274,16 +275,20 @@ case CHAR_MINUS: case CHAR_OPENPAREN: case CHAR_CLOSEPAREN: - minusAdded = true; - destPattern[index++] = (char)ch; - break; - case CHAR_PERCENT: - destPattern[index++] = '%'; + destPattern[index++] = ch; break; } } + const int MAX_DOTNET_NUMERIC_PATTERN_LENGTH = 6; // example: "(C n)" plus terminator + + if (destPattern[0] == '\0' || strlen (destPattern) >= MAX_DOTNET_NUMERIC_PATTERN_LENGTH) + { + free (destPattern); + return NULL; + } + return destPattern; } @@ -295,31 +300,15 @@ index from patterns[]. Returns index -1 if no pattern is found. */ -static int GetNumericPatternNative(const char* pNumberFormat, - const char* patterns[], - int patternsCount, - int isNegative) +static int GetNumericPatternNative(char* normalizedPattern,const char* patterns[], int patternsCount) { const int INVALID_FORMAT = -1; - const int MAX_DOTNET_NUMERIC_PATTERN_LENGTH = 6; // example: "(C n)" plus terminator - char* normalizedPattern = NormalizeNumericPattern(pNumberFormat, isNegative); if (!normalizedPattern) { return INVALID_FORMAT; } - size_t normalizedPatternLength = strlen(normalizedPattern); - - assert(normalizedPatternLength > 0); - assert(normalizedPatternLength < MAX_DOTNET_NUMERIC_PATTERN_LENGTH); - - if (normalizedPatternLength == 0 || normalizedPatternLength >= MAX_DOTNET_NUMERIC_PATTERN_LENGTH) - { - free(normalizedPattern); - return INVALID_FORMAT; - } - for (int i = 0; i < patternsCount; i++) { if (strcmp(normalizedPattern, patterns[i]) == 0) @@ -349,7 +338,8 @@ static int32_t GetValueForNumberFormat(NSLocale *currentLocale, LocaleNumberData numberFormatter.numberStyle = NSNumberFormatterCurrencyStyle; static const char* Patterns[] = {"Cn", "nC", "C n", "n C"}; pFormat = [[numberFormatter positiveFormat] UTF8String]; - value = GetNumericPatternNative(pFormat, Patterns, sizeof(Patterns)/sizeof(Patterns[0]), false); + char* normalizedPattern = NormalizeNumericPattern(pFormat, false); + value = GetNumericPatternNative(normalizedPattern, Patterns, sizeof(Patterns)/sizeof(Patterns[0])); break; } case LocaleNumber_NegativeMonetaryNumberFormat: @@ -358,7 +348,8 @@ static int32_t GetValueForNumberFormat(NSLocale *currentLocale, LocaleNumberData static const char* Patterns[] = {"(Cn)", "-Cn", "C-n", "Cn-", "(nC)", "-nC", "n-C", "nC-", "-n C", "-C n", "n C-", "C n-", "C -n", "n- C", "(C n)", "(n C)", "C- n" }; pFormat = [[numberFormatter negativeFormat] UTF8String]; - value = GetNumericPatternNative(pFormat, Patterns, sizeof(Patterns)/sizeof(Patterns[0]), true); + char* normalizedPattern = NormalizeNumericPattern(pFormat, true); + value = GetNumericPatternNative(normalizedPattern, Patterns, sizeof(Patterns)/sizeof(Patterns[0])); break; } case LocaleNumber_NegativeNumberFormat: @@ -366,7 +357,8 @@ static int32_t GetValueForNumberFormat(NSLocale *currentLocale, LocaleNumberData numberFormatter.numberStyle = NSNumberFormatterDecimalStyle; static const char* Patterns[] = {"(n)", "-n", "- n", "n-", "n -"}; pFormat = [[numberFormatter negativeFormat] UTF8String]; - value = GetNumericPatternNative(pFormat, Patterns, sizeof(Patterns)/sizeof(Patterns[0]), true); + char* normalizedPattern = NormalizeNumericPattern(pFormat, true); + value = GetNumericPatternNative(normalizedPattern, Patterns, sizeof(Patterns)/sizeof(Patterns[0])); break; } case LocaleNumber_NegativePercentFormat: @@ -374,7 +366,8 @@ static int32_t GetValueForNumberFormat(NSLocale *currentLocale, LocaleNumberData numberFormatter.numberStyle = NSNumberFormatterPercentStyle; static const char* Patterns[] = {"-n %", "-n%", "-%n", "%-n", "%n-", "n-%", "n%-", "-% n", "n %-", "% n-", "% -n", "n- %"}; pFormat = [[numberFormatter negativeFormat] UTF8String]; - value = GetNumericPatternNative(pFormat, Patterns, sizeof(Patterns)/sizeof(Patterns[0]), true); + char* normalizedPattern = NormalizeNumericPattern(pFormat, true); + value = GetNumericPatternNative(normalizedPattern, Patterns, sizeof(Patterns)/sizeof(Patterns[0])); break; } case LocaleNumber_PositivePercentFormat: @@ -382,13 +375,14 @@ static int32_t GetValueForNumberFormat(NSLocale *currentLocale, LocaleNumberData numberFormatter.numberStyle = NSNumberFormatterPercentStyle; static const char* Patterns[] = {"n %", "n%", "%n", "% n"}; pFormat = [[numberFormatter positiveFormat] UTF8String]; - value = GetNumericPatternNative(pFormat, Patterns, sizeof(Patterns)/sizeof(Patterns[0]), false); + char* normalizedPattern = NormalizeNumericPattern(pFormat, false); + value = GetNumericPatternNative(normalizedPattern, Patterns, sizeof(Patterns)/sizeof(Patterns[0])); break; } default: return -1; } - + return value; } From 36bbfd38ef9adae5de23da47649250b42faaf94f Mon Sep 17 00:00:00 2001 From: Meri Khamoyan Date: Fri, 14 Apr 2023 14:23:18 +0200 Subject: [PATCH 16/16] Renamed the function and added comment --- .../System.Globalization.Native/pal_locale.m | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/native/libs/System.Globalization.Native/pal_locale.m b/src/native/libs/System.Globalization.Native/pal_locale.m index 4479e6f4cbe0be..66b951db76125c 100644 --- a/src/native/libs/System.Globalization.Native/pal_locale.m +++ b/src/native/libs/System.Globalization.Native/pal_locale.m @@ -167,7 +167,9 @@ NormalizeNumericPattern Returns a numeric string pattern in a format that we can match against the -appropriate managed pattern. +appropriate managed pattern. Examples: +For PositiveMonetaryNumberFormat "ยค#,##0.00" becomes "Cn" +For NegativeNumberFormat "#,##0.00;(#,##0.00)" becomes "(n)" */ static char* NormalizeNumericPattern(const char* srcPattern, int isNegative) { @@ -300,7 +302,7 @@ index from patterns[]. Returns index -1 if no pattern is found. */ -static int GetNumericPatternNative(char* normalizedPattern,const char* patterns[], int patternsCount) +static int GetPatternIndex(char* normalizedPattern,const char* patterns[], int patternsCount) { const int INVALID_FORMAT = -1; @@ -339,17 +341,17 @@ static int32_t GetValueForNumberFormat(NSLocale *currentLocale, LocaleNumberData static const char* Patterns[] = {"Cn", "nC", "C n", "n C"}; pFormat = [[numberFormatter positiveFormat] UTF8String]; char* normalizedPattern = NormalizeNumericPattern(pFormat, false); - value = GetNumericPatternNative(normalizedPattern, Patterns, sizeof(Patterns)/sizeof(Patterns[0])); + value = GetPatternIndex(normalizedPattern, Patterns, sizeof(Patterns)/sizeof(Patterns[0])); break; } case LocaleNumber_NegativeMonetaryNumberFormat: { numberFormatter.numberStyle = NSNumberFormatterCurrencyStyle; static const char* Patterns[] = {"(Cn)", "-Cn", "C-n", "Cn-", "(nC)", "-nC", "n-C", "nC-", "-n C", - "-C n", "n C-", "C n-", "C -n", "n- C", "(C n)", "(n C)", "C- n" }; + "-C n", "n C-", "C n-", "C -n", "n- C", "(C n)", "(n C)", "C- n" }; pFormat = [[numberFormatter negativeFormat] UTF8String]; char* normalizedPattern = NormalizeNumericPattern(pFormat, true); - value = GetNumericPatternNative(normalizedPattern, Patterns, sizeof(Patterns)/sizeof(Patterns[0])); + value = GetPatternIndex(normalizedPattern, Patterns, sizeof(Patterns)/sizeof(Patterns[0])); break; } case LocaleNumber_NegativeNumberFormat: @@ -358,7 +360,7 @@ static int32_t GetValueForNumberFormat(NSLocale *currentLocale, LocaleNumberData static const char* Patterns[] = {"(n)", "-n", "- n", "n-", "n -"}; pFormat = [[numberFormatter negativeFormat] UTF8String]; char* normalizedPattern = NormalizeNumericPattern(pFormat, true); - value = GetNumericPatternNative(normalizedPattern, Patterns, sizeof(Patterns)/sizeof(Patterns[0])); + value = GetPatternIndex(normalizedPattern, Patterns, sizeof(Patterns)/sizeof(Patterns[0])); break; } case LocaleNumber_NegativePercentFormat: @@ -367,7 +369,7 @@ static int32_t GetValueForNumberFormat(NSLocale *currentLocale, LocaleNumberData static const char* Patterns[] = {"-n %", "-n%", "-%n", "%-n", "%n-", "n-%", "n%-", "-% n", "n %-", "% n-", "% -n", "n- %"}; pFormat = [[numberFormatter negativeFormat] UTF8String]; char* normalizedPattern = NormalizeNumericPattern(pFormat, true); - value = GetNumericPatternNative(normalizedPattern, Patterns, sizeof(Patterns)/sizeof(Patterns[0])); + value = GetPatternIndex(normalizedPattern, Patterns, sizeof(Patterns)/sizeof(Patterns[0])); break; } case LocaleNumber_PositivePercentFormat: @@ -376,7 +378,7 @@ static int32_t GetValueForNumberFormat(NSLocale *currentLocale, LocaleNumberData static const char* Patterns[] = {"n %", "n%", "%n", "% n"}; pFormat = [[numberFormatter positiveFormat] UTF8String]; char* normalizedPattern = NormalizeNumericPattern(pFormat, false); - value = GetNumericPatternNative(normalizedPattern, Patterns, sizeof(Patterns)/sizeof(Patterns[0])); + value = GetPatternIndex(normalizedPattern, Patterns, sizeof(Patterns)/sizeof(Patterns[0])); break; } default: