diff --git a/src/coreclr/src/System.Private.CoreLib/PinvokeAnalyzerExceptionList.analyzerdata b/src/coreclr/src/System.Private.CoreLib/PinvokeAnalyzerExceptionList.analyzerdata index 141a5c2a73ce0..c53e2b766ad13 100644 --- a/src/coreclr/src/System.Private.CoreLib/PinvokeAnalyzerExceptionList.analyzerdata +++ b/src/coreclr/src/System.Private.CoreLib/PinvokeAnalyzerExceptionList.analyzerdata @@ -6,5 +6,41 @@ normaliz.dll!NormalizeString user32.dll!GetProcessWindowStation user32.dll!GetUserObjectInformationW - -kernel32.dll!GetGeoInfo \ No newline at end of file + +kernel32.dll!GetGeoInfo + + +libSystem.Globalization.Native!GlobalizationNative_ChangeCase +libSystem.Globalization.Native!GlobalizationNative_ChangeCaseInvariant +libSystem.Globalization.Native!GlobalizationNative_ChangeCaseTurkish +libSystem.Globalization.Native!GlobalizationNative_CloseSortHandle +libSystem.Globalization.Native!GlobalizationNative_CompareString +libSystem.Globalization.Native!GlobalizationNative_CompareStringOrdinalIgnoreCase +libSystem.Globalization.Native!GlobalizationNative_EndsWith +libSystem.Globalization.Native!GlobalizationNative_EnumCalendarInfo +libSystem.Globalization.Native!GlobalizationNative_GetCalendarInfo +libSystem.Globalization.Native!GlobalizationNative_GetCalendars +libSystem.Globalization.Native!GlobalizationNative_GetDefaultLocaleName +libSystem.Globalization.Native!GlobalizationNative_GetICUVersion +libSystem.Globalization.Native!GlobalizationNative_GetJapaneseEraStartDate +libSystem.Globalization.Native!GlobalizationNative_GetLatestJapaneseEra +libSystem.Globalization.Native!GlobalizationNative_GetLocaleInfoGroupingSizes +libSystem.Globalization.Native!GlobalizationNative_GetLocaleInfoInt +libSystem.Globalization.Native!GlobalizationNative_GetLocaleInfoString +libSystem.Globalization.Native!GlobalizationNative_GetLocaleName +libSystem.Globalization.Native!GlobalizationNative_GetLocales +libSystem.Globalization.Native!GlobalizationNative_GetLocaleTimeFormat +libSystem.Globalization.Native!GlobalizationNative_GetSortHandle +libSystem.Globalization.Native!GlobalizationNative_GetSortKey +libSystem.Globalization.Native!GlobalizationNative_GetSortVersion +libSystem.Globalization.Native!GlobalizationNative_GetTimeZoneDisplayName +libSystem.Globalization.Native!GlobalizationNative_IndexOf +libSystem.Globalization.Native!GlobalizationNative_IndexOfOrdinalIgnoreCase +libSystem.Globalization.Native!GlobalizationNative_IsNormalized +libSystem.Globalization.Native!GlobalizationNative_IsPredefinedLocale +libSystem.Globalization.Native!GlobalizationNative_LastIndexOf +libSystem.Globalization.Native!GlobalizationNative_LoadICU +libSystem.Globalization.Native!GlobalizationNative_NormalizeString +libSystem.Globalization.Native!GlobalizationNative_StartsWith +libSystem.Globalization.Native!GlobalizationNative_ToAscii +libSystem.Globalization.Native!GlobalizationNative_ToUnicode diff --git a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj index e1d9a09182ef6..b0456f3825d1e 100644 --- a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -357,9 +357,6 @@ - - Common\Interop\Windows\Interop.BOOL.cs - diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Unix.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Unix.cs index dccd60c78ce0d..6cb513abc23d0 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Unix.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Unix.cs @@ -6,6 +6,12 @@ namespace System.Globalization { internal static partial class GlobalizationMode { + // Order of these properties in Windows matter because GetUseIcuMode is dependent on Invariant. + // So we need Invariant to be initialized first. + internal static bool Invariant { get; } = GetGlobalizationInvariantMode(); + + internal static bool UseNls => false; + private static bool GetGlobalizationInvariantMode() { bool invariantEnabled = GetInvariantSwitchValue(); diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Windows.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Windows.cs index f5def09ab96ab..50285f01da202 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Windows.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Windows.cs @@ -6,9 +6,12 @@ namespace System.Globalization { internal static partial class GlobalizationMode { - private static bool GetGlobalizationInvariantMode() - { - return GetInvariantSwitchValue(); - } + // Order of these properties in Windows matter because GetUseIcuMode is dependent on Invariant. + // So we need Invariant to be initialized first. + internal static bool Invariant { get; } = GetInvariantSwitchValue(); + + internal static bool UseNls { get; } = !Invariant && + (GetSwitchValue("System.Globalization.UseNls", "DOTNET_SYSTEM_GLOBALIZATION_USENLS") || + Interop.Globalization.LoadICU() == 0); } } diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.cs index b216677e1a711..1433e5220b976 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.cs @@ -6,18 +6,18 @@ namespace System.Globalization { internal static partial class GlobalizationMode { - internal static bool Invariant { get; } = GetGlobalizationInvariantMode(); + private static bool GetInvariantSwitchValue() => + GetSwitchValue("System.Globalization.Invariant", "DOTNET_SYSTEM_GLOBALIZATION_INVARIANT"); - // GetInvariantSwitchValue calls CLRConfig first to detect if the switch is defined in the config file. + // GetSwitchValue calls CLRConfig first to detect if the switch is defined in the config file. // if the switch is defined we just use the value of this switch. otherwise, we'll try to get the switch // value from the environment variable if it is defined. - internal static bool GetInvariantSwitchValue() + private static bool GetSwitchValue(string switchName, string envVariable) { - bool ret = CLRConfig.GetBoolValue("System.Globalization.Invariant", out bool exist); + bool ret = CLRConfig.GetBoolValue(switchName, out bool exist); if (!exist) { - // Linux doesn't support environment variable names include dots - string? switchValue = Environment.GetEnvironmentVariable("DOTNET_SYSTEM_GLOBALIZATION_INVARIANT"); + string? switchValue = Environment.GetEnvironmentVariable(envVariable); if (switchValue != null) { ret = bool.IsTrueStringIgnoreCase(switchValue) || switchValue.Equals("1"); diff --git a/src/libraries/Common/src/Interop/Unix/System.Globalization.Native/Interop.Calendar.cs b/src/libraries/Common/src/Interop/Interop.Calendar.cs similarity index 100% rename from src/libraries/Common/src/Interop/Unix/System.Globalization.Native/Interop.Calendar.cs rename to src/libraries/Common/src/Interop/Interop.Calendar.cs diff --git a/src/libraries/Common/src/Interop/Unix/System.Globalization.Native/Interop.Casing.cs b/src/libraries/Common/src/Interop/Interop.Casing.cs similarity index 100% rename from src/libraries/Common/src/Interop/Unix/System.Globalization.Native/Interop.Casing.cs rename to src/libraries/Common/src/Interop/Interop.Casing.cs diff --git a/src/libraries/Common/src/Interop/Unix/System.Globalization.Native/Interop.Collation.cs b/src/libraries/Common/src/Interop/Interop.Collation.cs similarity index 100% rename from src/libraries/Common/src/Interop/Unix/System.Globalization.Native/Interop.Collation.cs rename to src/libraries/Common/src/Interop/Interop.Collation.cs diff --git a/src/libraries/Common/src/Interop/Unix/System.Globalization.Native/Interop.ICU.cs b/src/libraries/Common/src/Interop/Interop.ICU.cs similarity index 100% rename from src/libraries/Common/src/Interop/Unix/System.Globalization.Native/Interop.ICU.cs rename to src/libraries/Common/src/Interop/Interop.ICU.cs diff --git a/src/libraries/Common/src/Interop/Unix/System.Globalization.Native/Interop.Idna.cs b/src/libraries/Common/src/Interop/Interop.Idna.cs similarity index 100% rename from src/libraries/Common/src/Interop/Unix/System.Globalization.Native/Interop.Idna.cs rename to src/libraries/Common/src/Interop/Interop.Idna.cs diff --git a/src/libraries/Common/src/Interop/Interop.Libraries.cs b/src/libraries/Common/src/Interop/Interop.Libraries.cs new file mode 100644 index 0000000000000..4fee38ad35bd2 --- /dev/null +++ b/src/libraries/Common/src/Interop/Interop.Libraries.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +internal static partial class Interop +{ + internal static partial class Libraries + { + internal const string GlobalizationNative = "libSystem.Globalization.Native"; + } +} diff --git a/src/libraries/Common/src/Interop/Unix/System.Globalization.Native/Interop.Locale.cs b/src/libraries/Common/src/Interop/Interop.Locale.cs similarity index 100% rename from src/libraries/Common/src/Interop/Unix/System.Globalization.Native/Interop.Locale.cs rename to src/libraries/Common/src/Interop/Interop.Locale.cs diff --git a/src/libraries/Common/src/Interop/Unix/System.Globalization.Native/Interop.Normalization.cs b/src/libraries/Common/src/Interop/Interop.Normalization.cs similarity index 100% rename from src/libraries/Common/src/Interop/Unix/System.Globalization.Native/Interop.Normalization.cs rename to src/libraries/Common/src/Interop/Interop.Normalization.cs diff --git a/src/libraries/Common/src/Interop/Unix/System.Globalization.Native/Interop.ResultCode.cs b/src/libraries/Common/src/Interop/Interop.ResultCode.cs similarity index 100% rename from src/libraries/Common/src/Interop/Unix/System.Globalization.Native/Interop.ResultCode.cs rename to src/libraries/Common/src/Interop/Interop.ResultCode.cs diff --git a/src/libraries/Common/src/Interop/Unix/System.Globalization.Native/Interop.TimeZoneInfo.cs b/src/libraries/Common/src/Interop/Interop.TimeZoneInfo.cs similarity index 100% rename from src/libraries/Common/src/Interop/Unix/System.Globalization.Native/Interop.TimeZoneInfo.cs rename to src/libraries/Common/src/Interop/Interop.TimeZoneInfo.cs diff --git a/src/libraries/Common/src/Interop/Unix/System.Globalization.Native/Interop.Utils.cs b/src/libraries/Common/src/Interop/Interop.Utils.cs similarity index 100% rename from src/libraries/Common/src/Interop/Unix/System.Globalization.Native/Interop.Utils.cs rename to src/libraries/Common/src/Interop/Interop.Utils.cs diff --git a/src/libraries/Common/src/Interop/Unix/Interop.Libraries.cs b/src/libraries/Common/src/Interop/Unix/Interop.Libraries.cs index 2ee0c3e7e841b..f511948294884 100644 --- a/src/libraries/Common/src/Interop/Unix/Interop.Libraries.cs +++ b/src/libraries/Common/src/Interop/Unix/Interop.Libraries.cs @@ -8,7 +8,6 @@ internal static partial class Libraries { // Shims internal const string SystemNative = "libSystem.Native"; - internal const string GlobalizationNative = "libSystem.Globalization.Native"; internal const string NetSecurityNative = "libSystem.Net.Security.Native"; internal const string CryptoNative = "libSystem.Security.Cryptography.Native.OpenSsl"; internal const string CompressionNative = "libSystem.IO.Compression.Native"; diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.Unix.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.Unix.cs index 58aa95010c095..4ecee83ba6693 100644 --- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.Unix.cs +++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.Unix.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Reflection; using System.Runtime.InteropServices; using System.Xml.Linq; @@ -51,9 +50,6 @@ public static partial class PlatformDetection public static bool IsNotFedoraOrRedHatFamily => !IsFedora && !IsRedHatFamily; public static bool IsNotDebian10 => !IsDebian10; - private static Lazy m_icuVersion = new Lazy(GetICUVersion); - public static Version ICUVersion => m_icuVersion.Value; - public static bool IsSuperUser => !IsWindows ? libc.geteuid() == 0 : throw new PlatformNotSupportedException(); @@ -110,25 +106,6 @@ public static string LibcVersion } } - private static Version GetICUVersion() - { - int version = 0; - Type interopGlobalization = Type.GetType("Interop+Globalization"); - if (interopGlobalization != null) - { - MethodInfo methodInfo = interopGlobalization.GetMethod("GetICUVersion", BindingFlags.NonPublic | BindingFlags.Static); - if (methodInfo != null) - { - version = (int)methodInfo.Invoke(null, null); - } - } - - return new Version( version & 0xFF, - (version >> 8) & 0xFF, - (version >> 16) & 0xFF, - version >> 24); - } - private static Version GetOSXProductVersion() { if (IsOSX) diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs index a04e3b1750993..e6b1845a1ea68 100644 --- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs +++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs @@ -4,6 +4,7 @@ using System.IO; using System.Security; +using System.Reflection; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; using Microsoft.Win32; @@ -150,6 +151,35 @@ public static string GetDistroVersionString() } } + private static Lazy m_icuVersion = new Lazy(GetICUVersion); + public static Version ICUVersion => m_icuVersion.Value; + + public static bool IsIcuGlobalization => ICUVersion > new Version(0,0,0,0); + public static bool IsNlsGlobalization => !IsIcuGlobalization; + + private static Version GetICUVersion() + { + int version = 0; + try + { + Type interopGlobalization = Type.GetType("Interop+Globalization"); + if (interopGlobalization != null) + { + MethodInfo methodInfo = interopGlobalization.GetMethod("GetICUVersion", BindingFlags.NonPublic | BindingFlags.Static); + if (methodInfo != null) + { + version = (int)methodInfo.Invoke(null, null); + } + } + } + catch { } + + return new Version(version >> 24, + (version >> 16) & 0xFF, + (version >> 8) & 0xFF, + version & 0xFF); + } + private static bool GetIsInContainer() { if (IsWindows) diff --git a/src/libraries/Common/tests/Tests/System/StringTests.cs b/src/libraries/Common/tests/Tests/System/StringTests.cs index 18f494d9440e4..55ff465804165 100644 --- a/src/libraries/Common/tests/Tests/System/StringTests.cs +++ b/src/libraries/Common/tests/Tests/System/StringTests.cs @@ -12,6 +12,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.DotNet.RemoteExecutor; +using Microsoft.DotNet.XUnitExtensions; using Xunit; #pragma warning disable xUnit2009 // these are the tests for String and so should be using the explicit methods on String @@ -1678,8 +1679,10 @@ public static void EndsWith_StringComparison(string s, string value, StringCompa Assert.Equal(expected, s.AsSpan().EndsWith(value.AsSpan(), comparisonType)); } - [Theory] - [ActiveIssue("https://github.com/dotnet/runtime/issues/4673", TestPlatforms.AnyUnix)] + // NOTE: This is by design. ICU ignores the null characters (i.e. null characters have no weights for the string comparison). + // For desired behavior, use ordinal comparison instead of linguistic comparison. + // This is a known difference between NLS and ICU (https://github.com/dotnet/runtime/issues/4673). + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))] [InlineData(StringComparison.CurrentCulture)] [InlineData(StringComparison.CurrentCultureIgnoreCase)] [InlineData(StringComparison.Ordinal)] @@ -1699,11 +1702,10 @@ public static void EndsWith_NullInStrings(StringComparison comparison) Assert.False("test".AsSpan().EndsWith("\0st".AsSpan(), comparison)); } - // NOTE: This is by design. Unix ignores the null characters (i.e. null characters have no weights for the string comparison). + // NOTE: This is by design. ICU ignores the null characters (i.e. null characters have no weights for the string comparison). // For desired behavior, use ordinal comparison instead of linguistic comparison. - // This is a known difference between Windows and Unix (https://github.com/dotnet/runtime/issues/4673). - [Theory] - [PlatformSpecific(TestPlatforms.Windows)] + // This is a known difference between NLS and ICU (https://github.com/dotnet/runtime/issues/4673). + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))] [InlineData(StringComparison.InvariantCulture)] [InlineData(StringComparison.InvariantCultureIgnoreCase)] public static void EndsWith_NullInStrings_NonOrdinal(StringComparison comparison) @@ -1862,11 +1864,11 @@ public static void EndsWithNoMatch_Char() string s1 = new string(first); string s2 = new string(second); - //On Linux there are some characters in the range of 0~32 which has a sort weight. - //For example null character on Linux will be ignored if it is compared to anything - //while on Windows null will be always compared as ordinal. - //For desired behavior, use ordinal comparison instead of linguistic comparison. - //This is a known difference between Windows and Unix (https://github.com/dotnet/runtime/issues/4673). + // On ICU there are some characters in the range of 0~32 which have a sort weight. + // For example null character on ICU will be ignored if it is compared to anything + // while on NLS null will be always compared as ordinal. + // For desired behavior, use ordinal comparison instead of linguistic comparison. + // This is a known difference between NLS and ICU (https://github.com/dotnet/runtime/issues/4673). bool b = s1.EndsWith(s2, StringComparison.Ordinal); Assert.False(b); @@ -2524,9 +2526,9 @@ public static IEnumerable Equals_EncyclopaediaData() yield return new object[] { StringComparison.Ordinal, false }; yield return new object[] { StringComparison.OrdinalIgnoreCase, false }; - // Windows and ICU disagree about how these strings compare in the default locale. - yield return new object[] { StringComparison.InvariantCulture, PlatformDetection.IsWindows }; - yield return new object[] { StringComparison.InvariantCultureIgnoreCase, PlatformDetection.IsWindows }; + // NLS and ICU disagree about how these strings compare in the default locale. + yield return new object[] { StringComparison.InvariantCulture, PlatformDetection.IsNlsGlobalization }; + yield return new object[] { StringComparison.InvariantCultureIgnoreCase, PlatformDetection.IsNlsGlobalization }; } [Theory] @@ -2616,7 +2618,7 @@ public static void Format_Invalid() #pragma warning restore IDE0043 // Format string contains invalid placeholder } - [Theory] + [ConditionalTheory] [InlineData("Hello", 'l', 0, 5, 2)] [InlineData("Hello", 'x', 0, 5, -1)] [InlineData("Hello", 'l', 1, 4, 2)] @@ -2628,9 +2630,7 @@ public static void Format_Invalid() [InlineData("Hello", 'l', 0, 3, 2)] [InlineData("Hello", 'o', 5, 0, -1)] [InlineData("H" + SoftHyphen + "ello", 'e', 0, 3, 2)] - // For some reason, this is failing on *nix with ordinal comparisons. - // Possibly related issue: https://github.com/dotnet/runtime/issues/4673 - // [InlineData("Hello", '\0', 0, 5, -1)] // .NET strings are terminated with a null character, but they should not be included as part of the string + [InlineData("Hello", '\0', 0, 5, -1)] // .NET strings are terminated with a null character, but they should not be included as part of the string [InlineData("\ud800\udfff", '\ud800', 0, 1, 0)] // Surrogate characters [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 'A', 0, 26, 0)] [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 'B', 1, 25, 1)] @@ -2668,6 +2668,14 @@ public static void Format_Invalid() [InlineData("", 'H', 0, 0, -1)] public static void IndexOf_SingleLetter(string s, char target, int startIndex, int count, int expected) { + // This is by design. ICU ignores the null characters (i.e. null characters have no weights for the string comparison). + // For desired behavior, use ordinal comparison instead of linguistic comparison. + // This is a known difference between NLS and ICU (https://github.com/dotnet/runtime/issues/4673). + if (target == '\0' && PlatformDetection.IsIcuGlobalization) + { + throw new SkipTestException("Target \\0 is not supported in ICU"); + } + bool safeForCurrentCulture = IsSafeForCurrentCultureComparisons(s) && IsSafeForCurrentCultureComparisons(target.ToString()); @@ -2774,8 +2782,10 @@ private static bool IsSafeForCurrentCultureComparisons(string str) return true; } - [Theory] - [ActiveIssue("https://github.com/dotnet/runtime/issues/4673", TestPlatforms.AnyUnix)] + // NOTE: This is by design. ICU ignores the null characters (i.e. null characters have no weights for the string comparison). + // For desired behavior, use ordinal comparison instead of linguistic comparison. + // This is a known difference between NLS and ICU (https://github.com/dotnet/runtime/issues/4673). + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))] [InlineData("He\0lo", "He\0lo", 0)] [InlineData("He\0lo", "He\0", 0)] [InlineData("He\0lo", "\0", 2)] @@ -2934,14 +2944,14 @@ public static void IndexOf_HungarianDoubleCompression_HungarianCulture() string target = "ddzs"; /* - There are differences between Windows and ICU regarding contractions. - Windows has equal contraction collation weights, including case (target="Ddzs" same behavior as "ddzs"). + There are differences between NLS and ICU regarding contractions. + NLS has equal contraction collation weights, including case (target="Ddzs" same behavior as "ddzs"). ICU has different contraction collation weights, depending on locale collation rules. If CurrentCultureIgnoreCase is specified, ICU will use 'secondary' collation rules which ignore the contraction collation weights (defined as 'tertiary' rules) */ - Assert.Equal(PlatformDetection.IsWindows ? 0 : -1, source.IndexOf(target)); - Assert.Equal(PlatformDetection.IsWindows ? 0 : -1, source.IndexOf(target, StringComparison.CurrentCulture)); + Assert.Equal(PlatformDetection.IsNlsGlobalization ? 0 : -1, source.IndexOf(target)); + Assert.Equal(PlatformDetection.IsNlsGlobalization ? 0 : -1, source.IndexOf(target, StringComparison.CurrentCulture)); Assert.Equal(0, source.IndexOf(target, StringComparison.CurrentCultureIgnoreCase)); Assert.Equal(-1, source.IndexOf(target, StringComparison.Ordinal)); @@ -2949,7 +2959,7 @@ which ignore the contraction collation weights (defined as 'tertiary' rules) ReadOnlySpan span = source.AsSpan(); - Assert.Equal(PlatformDetection.IsWindows ? 0 : -1, span.IndexOf(target.AsSpan(), StringComparison.CurrentCulture)); + Assert.Equal(PlatformDetection.IsNlsGlobalization ? 0 : -1, span.IndexOf(target.AsSpan(), StringComparison.CurrentCulture)); Assert.Equal(0, span.IndexOf(target.AsSpan(), StringComparison.CurrentCultureIgnoreCase)); Assert.Equal(-1, span.IndexOf(target.AsSpan(), StringComparison.Ordinal)); @@ -3861,8 +3871,10 @@ public static void LastIndexOf_Match_SingleLetter() } } - [Theory] - [ActiveIssue("https://github.com/dotnet/runtime/issues/4673", TestPlatforms.AnyUnix)] + // NOTE: This is by design. ICU ignores the null characters (i.e. null characters have no weights for the string comparison). + // For desired behavior, use ordinal comparison instead of linguistic comparison. + // This is a known difference between NLS and ICU (https://github.com/dotnet/runtime/issues/4673). + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))] [InlineData("He\0lo", "He\0lo", 0)] [InlineData("He\0lo", "He\0", 0)] [InlineData("He\0lo", "\0", 2)] @@ -4648,8 +4660,10 @@ public static void StartsWith_StringComparison(string s, string value, StringCom Assert.Equal(expected, s.AsSpan().StartsWith(value.AsSpan(), comparisonType)); } - [Theory] - [ActiveIssue("https://github.com/dotnet/runtime/issues/4673", TestPlatforms.AnyUnix)] + // NOTE: This is by design. ICU ignores the null characters (i.e. null characters have no weights for the string comparison). + // For desired behavior, use ordinal comparison instead of linguistic comparison. + // This is a known difference between NLS and ICU (https://github.com/dotnet/runtime/issues/4673). + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))] [InlineData(StringComparison.CurrentCulture)] [InlineData(StringComparison.CurrentCultureIgnoreCase)] [InlineData(StringComparison.Ordinal)] @@ -7074,11 +7088,10 @@ public static void StartsWithNoMatchNonOrdinal_StringComparison() Assert.False(span.StartsWith(value, StringComparison.InvariantCultureIgnoreCase)); } - // NOTE: This is by design. Unix ignores the null characters (i.e. null characters have no weights for the string comparison). + // NOTE: This is by design. ICU ignores the null characters (i.e. null characters have no weights for the string comparison). // For desired behavior, use ordinal comparison instead of linguistic comparison. - // This is a known difference between Windows and Unix (https://github.com/dotnet/runtime/issues/4673). - [Theory] - [PlatformSpecific(TestPlatforms.Windows)] + // This is a known difference between NLS and ICU (https://github.com/dotnet/runtime/issues/4673). + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))] [InlineData(StringComparison.CurrentCulture)] [InlineData(StringComparison.CurrentCultureIgnoreCase)] [InlineData(StringComparison.InvariantCulture)] diff --git a/src/libraries/Native/Unix/Common/pal_atomic.h b/src/libraries/Native/Unix/Common/pal_atomic.h index a2ebcf62612dc..908f33043981b 100644 --- a/src/libraries/Native/Unix/Common/pal_atomic.h +++ b/src/libraries/Native/Unix/Common/pal_atomic.h @@ -9,10 +9,11 @@ #include "windows.h" #endif +// The args passed in should match InterlockedCompareExchangePointer Windows API static int pal_atomic_cas_ptr(void* volatile* dest, void* exchange, void* comparand) { #if defined(TARGET_UNIX) - return __atomic_compare_exchange_n(dest, exchange, comparand, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + return __atomic_compare_exchange_n(dest, &comparand, exchange, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); #elif defined(TARGET_WINDOWS) return InterlockedCompareExchangePointer(dest, exchange, comparand) == comparand; #endif diff --git a/src/libraries/Native/Unix/System.Globalization.Native/pal_collation.c b/src/libraries/Native/Unix/System.Globalization.Native/pal_collation.c index 081a01693ecc8..4410c3e34fb5b 100644 --- a/src/libraries/Native/Unix/System.Globalization.Native/pal_collation.c +++ b/src/libraries/Native/Unix/System.Globalization.Native/pal_collation.c @@ -404,7 +404,7 @@ static const UCollator* GetCollatorFromSortHandle(SortHandle* pSortHandle, int32 pCollator = CloneCollatorWithOptions(pSortHandle->collatorsPerOption[0], options, pErr); UCollator* pNull = NULL; - if (!pal_atomic_cas_ptr((void* volatile*)&pSortHandle->collatorsPerOption[options], &pNull, pCollator)) + if (!pal_atomic_cas_ptr((void* volatile*)&pSortHandle->collatorsPerOption[options], pCollator, pNull)) { ucol_close(pCollator); pCollator = pSortHandle->collatorsPerOption[options]; diff --git a/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim.c b/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim.c index 5438b44b37255..fbea9cf6ca87f 100644 --- a/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim.c +++ b/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim.c @@ -374,7 +374,11 @@ int32_t GlobalizationNative_LoadICU() // return the current loaded ICU version int32_t GlobalizationNative_GetICUVersion() { - int32_t version; - u_getVersion((uint8_t *) &version); - return version; + if (u_getVersion_ptr == NULL) + return 0; + + UVersionInfo versionInfo; + u_getVersion(versionInfo); + + return (versionInfo[0] << 24) + (versionInfo[1] << 16) + (versionInfo[2] << 8) + versionInfo[3]; } diff --git a/src/libraries/System.Data.Common/tests/System/Data/SqlTypes/SqlStringSortingTest.cs b/src/libraries/System.Data.Common/tests/System/Data/SqlTypes/SqlStringSortingTest.cs index a4403b41c6feb..529116388e9a0 100644 --- a/src/libraries/System.Data.Common/tests/System/Data/SqlTypes/SqlStringSortingTest.cs +++ b/src/libraries/System.Data.Common/tests/System/Data/SqlTypes/SqlStringSortingTest.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Globalization; using System.Text; +using Microsoft.DotNet.XUnitExtensions; using Xunit; namespace System.Data.SqlTypes.Tests @@ -37,12 +38,8 @@ public static class SqlStringSortingTest private static readonly UnicodeEncoding s_unicodeEncoding = new UnicodeEncoding(bigEndian: false, byteOrderMark: false, throwOnInvalidBytes: true); - [ActiveIssue("https://github.com/dotnet/runtime/issues/18912", TestPlatforms.AnyUnix)] // TODO: Add this to the theory below when the issue is addressed on Unix - [Theory] + [ConditionalTheory] [InlineData("ja-JP", 0x0411)] // Japanese - Japan - public static void SqlStringValidComparisonTest_Windows(string cultureName, int localeId) => SqlStringValidComparisonTest(cultureName, localeId); - - [Theory] [InlineData("ar-SA", 0x0401)] // Arabic - Saudi Arabia [InlineData("de-DE", 0x0407)] // German - Germany [InlineData("hi-IN", 0x0439)] // Hindi - India @@ -56,6 +53,12 @@ public static class SqlStringSortingTest [InlineData("en-US", 0x0409)] // English - United States public static void SqlStringValidComparisonTest(string cultureName, int localeId) { + if (PlatformDetection.IsIcuGlobalization && cultureName == "ja-JP" && localeId == 0x0411) + { + // TODO: Remove this once: https://github.com/dotnet/runtime/issues/18912 is fixed on ICU. + throw new SkipTestException($"PlatformDetection.IsIcuGlobalization and cultureName == ja-JP"); + } + var culture = new CultureInfo(cultureName); const SqlCompareOptions DefaultCompareOption = SqlCompareOptions.IgnoreCase | SqlCompareOptions.IgnoreKanaType | SqlCompareOptions.IgnoreWidth; diff --git a/src/libraries/System.Globalization.Calendars/tests/System/Globalization/CalendarTestBase.cs b/src/libraries/System.Globalization.Calendars/tests/System/Globalization/CalendarTestBase.cs index 62c6166a8973c..6e9b83982f2ab 100644 --- a/src/libraries/System.Globalization.Calendars/tests/System/Globalization/CalendarTestBase.cs +++ b/src/libraries/System.Globalization.Calendars/tests/System/Globalization/CalendarTestBase.cs @@ -430,8 +430,8 @@ public void GetEra_Invalid_ThrowsArgumentOutOfRangeException() Calendar calendar = Calendar; Assert.All(DateTime_TestData(calendar), dt => { - // JapaneseCalendar throws on Unix (ICU), but not on Windows - if ((calendar is JapaneseCalendar && PlatformDetection.IsWindows) || calendar is HebrewCalendar || calendar is TaiwanLunisolarCalendar || calendar is JapaneseLunisolarCalendar) + // JapaneseCalendar throws on ICU, but not on NLS + if ((calendar is JapaneseCalendar && PlatformDetection.IsNlsGlobalization) || calendar is HebrewCalendar || calendar is TaiwanLunisolarCalendar || calendar is JapaneseLunisolarCalendar) { calendar.GetEra(dt); } diff --git a/src/libraries/System.Globalization.Calendars/tests/TaiwanCalendar/TaiwanCalendarDaysAndMonths.cs b/src/libraries/System.Globalization.Calendars/tests/TaiwanCalendar/TaiwanCalendarDaysAndMonths.cs index a159467be3593..6da5b82fc4b74 100644 --- a/src/libraries/System.Globalization.Calendars/tests/TaiwanCalendar/TaiwanCalendarDaysAndMonths.cs +++ b/src/libraries/System.Globalization.Calendars/tests/TaiwanCalendar/TaiwanCalendarDaysAndMonths.cs @@ -32,7 +32,7 @@ public void DayNames_MonthNames() private static string[] GetExpectedMonthNames() { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + if (PlatformDetection.IsNlsGlobalization) { return new string[] { diff --git a/src/libraries/System.Globalization.Extensions/System.Globalization.Extensions.sln b/src/libraries/System.Globalization.Extensions/System.Globalization.Extensions.sln index 5e66148e84464..1290450544bc9 100644 --- a/src/libraries/System.Globalization.Extensions/System.Globalization.Extensions.sln +++ b/src/libraries/System.Globalization.Extensions/System.Globalization.Extensions.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27213.1 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29923.206 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Globalization.Extensions.Tests", "tests\System.Globalization.Extensions.Tests.csproj", "{BC439554-4AB4-4C94-8E28-C00EDE4FD1C7}" ProjectSection(ProjectDependencies) = postProject @@ -20,7 +20,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E107E9C1-E89 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{2E666815-2EDB-464B-9DF6-380BF4789AD4}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestUtilities", "..\Common\tests\TestUtilities\TestUtilities.csproj", "{A36A6300-3BE9-42CA-B7BC-62E926E07CC5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Globalization.Extensions.Nls.Tests", "tests\NlsTests\System.Globalization.Extensions.Nls.Tests.csproj", "{CB60359A-D0ED-474E-B395-9589F1A5656A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -40,10 +40,10 @@ Global {3634FAA3-A33E-406A-94EE-5611C6CC2810}.Debug|Any CPU.Build.0 = Debug|Any CPU {3634FAA3-A33E-406A-94EE-5611C6CC2810}.Release|Any CPU.ActiveCfg = Release|Any CPU {3634FAA3-A33E-406A-94EE-5611C6CC2810}.Release|Any CPU.Build.0 = Release|Any CPU - {A36A6300-3BE9-42CA-B7BC-62E926E07CC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A36A6300-3BE9-42CA-B7BC-62E926E07CC5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A36A6300-3BE9-42CA-B7BC-62E926E07CC5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A36A6300-3BE9-42CA-B7BC-62E926E07CC5}.Release|Any CPU.Build.0 = Release|Any CPU + {CB60359A-D0ED-474E-B395-9589F1A5656A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CB60359A-D0ED-474E-B395-9589F1A5656A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CB60359A-D0ED-474E-B395-9589F1A5656A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CB60359A-D0ED-474E-B395-9589F1A5656A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -52,7 +52,7 @@ Global {BC439554-4AB4-4C94-8E28-C00EDE4FD1C7} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} {2B96AA10-84C0-4927-8611-8D2474B990E8} = {E107E9C1-E893-4E87-987E-04EF0DCEAEFD} {3634FAA3-A33E-406A-94EE-5611C6CC2810} = {2E666815-2EDB-464B-9DF6-380BF4789AD4} - {A36A6300-3BE9-42CA-B7BC-62E926E07CC5} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} + {CB60359A-D0ED-474E-B395-9589F1A5656A} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3E550ED4-6053-4B16-97D1-BAADB02924FE} diff --git a/src/libraries/System.Globalization.Extensions/tests/GetStringComparerTests.cs b/src/libraries/System.Globalization.Extensions/tests/GetStringComparerTests.cs index da82bbb23730c..ff53746996900 100644 --- a/src/libraries/System.Globalization.Extensions/tests/GetStringComparerTests.cs +++ b/src/libraries/System.Globalization.Extensions/tests/GetStringComparerTests.cs @@ -10,8 +10,6 @@ namespace System.Globalization.Tests { public class GetStringComparerTests { - private static bool s_isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); - [Fact] public void GetStringComparer_Invalid() { @@ -35,9 +33,9 @@ public void GetStringComparer_Invalid() [InlineData("abc", "ABC", "en-US", CompareOptions.OrdinalIgnoreCase, 0, 0)] [InlineData("Cot\u00E9", "cot\u00E9", "fr-FR", CompareOptions.IgnoreCase, 0, 0)] [InlineData("cot\u00E9", "c\u00F4te", "fr-FR", CompareOptions.None, 1, -1)] - public static void Compare(string x, string y, string cultureName, CompareOptions options, int expectedWindows, int expectedICU) + public static void Compare(string x, string y, string cultureName, CompareOptions options, int expectedNls, int expectedICU) { - int expected = s_isWindows ? expectedWindows : expectedICU; + int expected = PlatformDetection.IsNlsGlobalization ? expectedNls : expectedICU; StringComparer comparer = new CultureInfo(cultureName).CompareInfo.GetStringComparer(options); Assert.Equal(expected, Math.Sign(comparer.Compare(x, y))); diff --git a/src/libraries/System.Globalization.Extensions/tests/IdnMapping/IdnMappingGetAsciiTests.cs b/src/libraries/System.Globalization.Extensions/tests/IdnMapping/IdnMappingGetAsciiTests.cs index f1a65e0fb8c4d..93f093d061c7b 100644 --- a/src/libraries/System.Globalization.Extensions/tests/IdnMapping/IdnMappingGetAsciiTests.cs +++ b/src/libraries/System.Globalization.Extensions/tests/IdnMapping/IdnMappingGetAsciiTests.cs @@ -22,9 +22,9 @@ public static IEnumerable GetAscii_TestData() continue; } string ascii = c.ToString(); - if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) // expected platform differences, see https://github.com/dotnet/runtime/issues/17190 + if (PlatformDetection.IsIcuGlobalization) // expected platform differences, see https://github.com/dotnet/runtime/issues/17190 { - if ((c >= 'A' && c <= 'Z')) + if (c >= 'A' && c <= 'Z') { yield return new object[] { ascii, 0, 1, ascii.ToLower() }; } diff --git a/src/libraries/System.Globalization.Extensions/tests/IdnMapping/IdnMappingGetUnicodeTests.cs b/src/libraries/System.Globalization.Extensions/tests/IdnMapping/IdnMappingGetUnicodeTests.cs index 6cafb29e901d1..6c583830d08c1 100644 --- a/src/libraries/System.Globalization.Extensions/tests/IdnMapping/IdnMappingGetUnicodeTests.cs +++ b/src/libraries/System.Globalization.Extensions/tests/IdnMapping/IdnMappingGetUnicodeTests.cs @@ -96,7 +96,7 @@ public static IEnumerable GetUnicode_Invalid_TestData() yield return new object[] { "abc" + (char)0x7F + "def", 0, 7, typeof(ArgumentException) }; - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) // expected platform differences, see https://github.com/dotnet/runtime/issues/17190 + if (PlatformDetection.IsNlsGlobalization) // expected platform differences, see https://github.com/dotnet/runtime/issues/17190 { yield return new object[] { "xn--\u1234", 0, 5, typeof(ArgumentException) }; yield return new object[] { "xn--\u1234pck", 0, 8, typeof(ArgumentException) }; diff --git a/src/libraries/System.Globalization.Extensions/tests/IdnMapping/IdnMappingUseStd3AsciiRulesTests.cs b/src/libraries/System.Globalization.Extensions/tests/IdnMapping/IdnMappingUseStd3AsciiRulesTests.cs index 98949eac574d1..10d468ae33a53 100644 --- a/src/libraries/System.Globalization.Extensions/tests/IdnMapping/IdnMappingUseStd3AsciiRulesTests.cs +++ b/src/libraries/System.Globalization.Extensions/tests/IdnMapping/IdnMappingUseStd3AsciiRulesTests.cs @@ -22,8 +22,6 @@ namespace System.Globalization.Tests /// public class UseStd3AsciiRules { - private static bool s_isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); - [Fact] public void UseStd3AsciiRules_IsFalseByDefault() { @@ -52,7 +50,7 @@ public void UseStd3AsciiRules_ChangesGetAsciiBehavior(string unicode, bool conta var idnStd3False = new IdnMapping { UseStd3AsciiRules = false }; var idnStd3True = new IdnMapping { UseStd3AsciiRules = true }; - if (containsInvalidHyphen && !s_isWindows) + if (containsInvalidHyphen && PlatformDetection.IsIcuGlobalization) { // ICU always fails on leading/trailing hyphens regardless of the Std3 rules option. AssertExtensions.Throws("unicode", () => idnStd3False.GetAscii(unicode)); diff --git a/src/libraries/System.Globalization.Extensions/tests/NlsTests/System.Globalization.Extensions.Nls.Tests.csproj b/src/libraries/System.Globalization.Extensions/tests/NlsTests/System.Globalization.Extensions.Nls.Tests.csproj new file mode 100644 index 0000000000000..87bbcd42ac7c5 --- /dev/null +++ b/src/libraries/System.Globalization.Extensions/tests/NlsTests/System.Globalization.Extensions.Nls.Tests.csproj @@ -0,0 +1,66 @@ + + + $(NetCoreAppCurrent)-Windows_NT + true + + + + GetStringComparerTests.cs + + + IdnMapping\Data\ConformanceIdnaUnicodeTestResult.cs + + + IdnMapping\Data\Unicode_9_0\Unicode_9_0_IdnaTest.cs + + + IdnMapping\Data\Unicode_11_0\Unicode_11_0_IdnaTest.cs + + + IdnMapping\IdnMappingIdnaConformanceTests.cs + + + IdnMapping\Data\Factory.cs + + + IdnMapping\Data\ConformanceIdnaTestResult.cs + + + IdnMapping\Data\Unicode_6_0\Unicode_6_0_IdnaTest.cs + + + IdnMapping\Data\Unicode_Win7\Unicode_Win7_IdnaTest.cs + + + IdnMapping\Data\IConformanceIdnaTest.cs + + + IdnMapping\IdnMappingGetAsciiTests.cs + + + IdnMapping\IdnMappingGetUnicodeTests.cs + + + IdnMapping\IdnMappingUseStd3AsciiRulesTests.cs + + + Normalization\StringNormalizationTests.cs + + + Normalization\NormalizationAll.cs + + + + + + + + + NormalizationDataWin8 + + + NormalizationDataWin7 + + + + \ No newline at end of file diff --git a/src/libraries/System.Globalization.Extensions/tests/NlsTests/runtimeconfig.template.json b/src/libraries/System.Globalization.Extensions/tests/NlsTests/runtimeconfig.template.json new file mode 100644 index 0000000000000..ec1e96166f3b3 --- /dev/null +++ b/src/libraries/System.Globalization.Extensions/tests/NlsTests/runtimeconfig.template.json @@ -0,0 +1,5 @@ +{ + "configProperties": { + "System.Globalization.UseNls": true + } +} diff --git a/src/libraries/System.Globalization/System.Globalization.sln b/src/libraries/System.Globalization/System.Globalization.sln index 4607fdfb14973..36e21495107ba 100644 --- a/src/libraries/System.Globalization/System.Globalization.sln +++ b/src/libraries/System.Globalization/System.Globalization.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27213.1 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29923.206 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Globalization.Tests", "tests\System.Globalization.Tests.csproj", "{484C92C6-6D2C-45BC-A5E2-4A12BA228E1E}" ProjectSection(ProjectDependencies) = postProject @@ -25,6 +25,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E107E9C1-E89 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{2E666815-2EDB-464B-9DF6-380BF4789AD4}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Globalization.Nls.Tests", "tests\NlsTests\System.Globalization.Nls.Tests.csproj", "{6B71E284-DA57-4657-9E08-A02BAFB877CC}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestUtilities", "..\Common\tests\TestUtilities\TestUtilities.csproj", "{443745F1-A200-432B-A666-3D0E9F938DED}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestUtilities.Unicode", "..\Common\tests\TestUtilities.Unicode\TestUtilities.Unicode.csproj", "{E5ECA266-C7E9-4597-8FFA-095BD6890407}" @@ -51,6 +53,10 @@ Global {E1E58C98-808F-4065-9C1D-E6411166AF6F}.Debug|Any CPU.Build.0 = Debug|Any CPU {E1E58C98-808F-4065-9C1D-E6411166AF6F}.Release|Any CPU.ActiveCfg = Release|Any CPU {E1E58C98-808F-4065-9C1D-E6411166AF6F}.Release|Any CPU.Build.0 = Release|Any CPU + {6B71E284-DA57-4657-9E08-A02BAFB877CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6B71E284-DA57-4657-9E08-A02BAFB877CC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6B71E284-DA57-4657-9E08-A02BAFB877CC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6B71E284-DA57-4657-9E08-A02BAFB877CC}.Release|Any CPU.Build.0 = Release|Any CPU {443745F1-A200-432B-A666-3D0E9F938DED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {443745F1-A200-432B-A666-3D0E9F938DED}.Debug|Any CPU.Build.0 = Debug|Any CPU {443745F1-A200-432B-A666-3D0E9F938DED}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -68,6 +74,7 @@ Global {9A8926D9-1D4C-4069-8965-A626F6CA8C29} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} {2395E8CA-73CB-40DF-BE40-A60BC189B737} = {E107E9C1-E893-4E87-987E-04EF0DCEAEFD} {E1E58C98-808F-4065-9C1D-E6411166AF6F} = {2E666815-2EDB-464B-9DF6-380BF4789AD4} + {6B71E284-DA57-4657-9E08-A02BAFB877CC} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} {443745F1-A200-432B-A666-3D0E9F938DED} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} {E5ECA266-C7E9-4597-8FFA-095BD6890407} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} EndGlobalSection diff --git a/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.Compare.cs b/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.Compare.cs index 3b2f121f586b7..963d7ad33a0f2 100644 --- a/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.Compare.cs +++ b/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.Compare.cs @@ -16,12 +16,12 @@ public class CompareInfoCompareTests // On Windows, hiragana characters sort after katakana. // On ICU, it is the opposite - private static int s_expectedHiraganaToKatakanaCompare = PlatformDetection.IsWindows ? 1 : -1; + private static int s_expectedHiraganaToKatakanaCompare = PlatformDetection.IsNlsGlobalization ? 1 : -1; // On Windows, all halfwidth characters sort before fullwidth characters. // On ICU, half and fullwidth characters that aren't in the "Halfwidth and fullwidth forms" block U+FF00-U+FFEF // sort before the corresponding characters that are in the block U+FF00-U+FFEF - private static int s_expectedHalfToFullFormsComparison = PlatformDetection.IsWindows ? -1 : 1; + private static int s_expectedHalfToFullFormsComparison = PlatformDetection.IsNlsGlobalization ? -1 : 1; private const string SoftHyphen = "\u00AD"; @@ -39,13 +39,13 @@ public static IEnumerable Compare_Kana_TestData() yield return new object[] { s_invariantCompare, "\u3070\u3073\u3076\u3079\u307C", "\u30D0\u30D3\u3076\u30D9\uFF8E\uFF9E", CompareOptions.None, s_expectedHiraganaToKatakanaCompare }; yield return new object[] { s_invariantCompare, "\u3060", "\uFF80\uFF9E", CompareOptions.None, s_expectedHiraganaToKatakanaCompare }; - bool isWindows = PlatformDetection.IsWindows; + bool useNls = PlatformDetection.IsNlsGlobalization; - yield return new object[] { s_invariantCompare, "\u30C7\u30BF\u30D9\u30B9", "\uFF83\uFF9E\uFF80\uFF8D\uFF9E\uFF7D", CompareOptions.None, isWindows ? 1 : -1 }; - yield return new object[] { s_invariantCompare, "\u30C7", "\uFF83\uFF9E", CompareOptions.None, isWindows ? 1 : -1 }; - yield return new object[] { s_invariantCompare, "\u30C7\u30BF", "\uFF83\uFF9E\uFF80", CompareOptions.None, isWindows ? 1 : -1 }; - yield return new object[] { s_invariantCompare, "\u30C7\u30BF\u30D9", "\uFF83\uFF9E\uFF80\uFF8D\uFF9E", CompareOptions.None, isWindows ? 1 : -1 }; - yield return new object[] { s_invariantCompare, "\uFF83\uFF9E\uFF70\uFF80\uFF8D\uFF9E\uFF70\uFF7D", "\u3067\u30FC\u305F\u3079\u30FC\u3059", CompareOptions.None, isWindows ? -1 : 1 }; + yield return new object[] { s_invariantCompare, "\u30C7\u30BF\u30D9\u30B9", "\uFF83\uFF9E\uFF80\uFF8D\uFF9E\uFF7D", CompareOptions.None, useNls ? 1 : -1 }; + yield return new object[] { s_invariantCompare, "\u30C7", "\uFF83\uFF9E", CompareOptions.None, useNls ? 1 : -1 }; + yield return new object[] { s_invariantCompare, "\u30C7\u30BF", "\uFF83\uFF9E\uFF80", CompareOptions.None, useNls ? 1 : -1 }; + yield return new object[] { s_invariantCompare, "\u30C7\u30BF\u30D9", "\uFF83\uFF9E\uFF80\uFF8D\uFF9E", CompareOptions.None, useNls ? 1 : -1 }; + yield return new object[] { s_invariantCompare, "\uFF83\uFF9E\uFF70\uFF80\uFF8D\uFF9E\uFF70\uFF7D", "\u3067\u30FC\u305F\u3079\u30FC\u3059", CompareOptions.None, useNls ? -1 : 1 }; } public static IEnumerable Compare_TestData() @@ -237,28 +237,29 @@ public static IEnumerable Compare_TestData() yield return new object[] { new CultureInfo("es-ES").CompareInfo, "llegar", "lugar", CompareOptions.None, -1 }; // Misc differences between platforms - bool isWindows = PlatformDetection.IsWindows; - - yield return new object[] { s_invariantCompare, "\u3042", "\u30A1", CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase, isWindows ? 1: 0 }; - yield return new object[] { s_invariantCompare, "'\u3000'", "''", CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase, isWindows ? 1 : -1 }; - yield return new object[] { s_invariantCompare, "\u30BF", "\uFF80", CompareOptions.None, isWindows ? 1 : -1 }; - yield return new object[] { s_invariantCompare, "'\u3000'", "''", CompareOptions.None, isWindows ? 1 : -1 }; - yield return new object[] { s_invariantCompare, "\u30FC", "\uFF70", CompareOptions.None, isWindows ? 0 : -1 }; - yield return new object[] { s_hungarianCompare, "dzsdzs", "ddzs", CompareOptions.None, isWindows ? 0 : -1 }; - yield return new object[] { s_invariantCompare, "Test's", "Tests", CompareOptions.None, isWindows ? 1 : -1 }; + bool useNls = PlatformDetection.IsNlsGlobalization; + + yield return new object[] { s_invariantCompare, "\u3042", "\u30A1", CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase, useNls ? 1: 0 }; + yield return new object[] { s_invariantCompare, "'\u3000'", "''", CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase, useNls ? 1 : -1 }; + yield return new object[] { s_invariantCompare, "\u30BF", "\uFF80", CompareOptions.None, useNls ? 1 : -1 }; + yield return new object[] { s_invariantCompare, "'\u3000'", "''", CompareOptions.None, useNls ? 1 : -1 }; + yield return new object[] { s_invariantCompare, "\u30FC", "\uFF70", CompareOptions.None, useNls ? 0 : -1 }; + yield return new object[] { s_hungarianCompare, "dzsdzs", "ddzs", CompareOptions.None, useNls ? 0 : -1 }; + yield return new object[] { s_invariantCompare, "Test's", "Tests", CompareOptions.None, useNls ? 1 : -1 }; yield return new object[] { new CultureInfo("de-DE").CompareInfo, "\u00DC", "UE", CompareOptions.None, -1 }; - yield return new object[] { new CultureInfo("de-DE_phoneb").CompareInfo, "\u00DC", "UE", CompareOptions.None, isWindows ? 0 : -1 }; - yield return new object[] { new CultureInfo("es-ES_tradnl").CompareInfo, "llegar", "lugar", CompareOptions.None, isWindows ? 1 : -1 }; + yield return new object[] { new CultureInfo("de-DE_phoneb").CompareInfo, "\u00DC", "UE", CompareOptions.None, useNls ? 0 : -1 }; + yield return new object[] { new CultureInfo("es-ES_tradnl").CompareInfo, "llegar", "lugar", CompareOptions.None, useNls ? 1 : -1 }; } // There is a regression in Windows 190xx version with the Kana comparison. Avoid running this test there. public static bool IsNotWindowsKanaRegressedVersion() => !PlatformDetection.IsWindows10Version1903OrGreater || + PlatformDetection.IsIcuGlobalization || s_invariantCompare.Compare("\u3060", "\uFF80\uFF9E", CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase) == 0; [Fact] public void CompareWithUnassignedChars() { - int result = PlatformDetection.IsWindows ? 0 : -1; + int result = PlatformDetection.IsNlsGlobalization ? 0 : -1; Compare(s_invariantCompare, "FooBar", "Foo\uFFFFBar", CompareOptions.None, result); Compare(s_invariantCompare, "FooBar", "Foo\uFFFFBar", CompareOptions.IgnoreNonSpace, result); } diff --git a/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.IndexOf.cs b/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.IndexOf.cs index 2036dc4d80ce1..6c41d6ca5088a 100644 --- a/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.IndexOf.cs +++ b/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.IndexOf.cs @@ -109,26 +109,26 @@ public static IEnumerable IndexOf_TestData() yield return new object[] { s_currentCompare, "\u0131", "\u0130", 0, 1, CompareOptions.Ordinal, -1 }; // Platform differences - yield return new object[] { s_hungarianCompare, "foobardzsdzs", "rddzs", 0, 12, CompareOptions.None, PlatformDetection.IsWindows ? 5 : -1}; + yield return new object[] { s_hungarianCompare, "foobardzsdzs", "rddzs", 0, 12, CompareOptions.None, PlatformDetection.IsNlsGlobalization ? 5 : -1}; } public static IEnumerable IndexOf_Aesc_Ligature_TestData() { - bool isWindows = PlatformDetection.IsWindows; + bool useNls = PlatformDetection.IsNlsGlobalization; // Searches for the ligature \u00C6 string source1 = "Is AE or ae the same as \u00C6 or \u00E6?"; - yield return new object[] { s_invariantCompare, source1, "AE", 8, 18, CompareOptions.None, isWindows ? 24 : -1}; + yield return new object[] { s_invariantCompare, source1, "AE", 8, 18, CompareOptions.None, useNls ? 24 : -1}; yield return new object[] { s_invariantCompare, source1, "ae", 8, 18, CompareOptions.None, 9 }; yield return new object[] { s_invariantCompare, source1, "\u00C6", 8, 18, CompareOptions.None, 24 }; - yield return new object[] { s_invariantCompare, source1, "\u00E6", 8, 18, CompareOptions.None, isWindows ? 9 : -1}; + yield return new object[] { s_invariantCompare, source1, "\u00E6", 8, 18, CompareOptions.None, useNls ? 9 : -1}; yield return new object[] { s_invariantCompare, source1, "AE", 8, 18, CompareOptions.Ordinal, -1 }; yield return new object[] { s_invariantCompare, source1, "ae", 8, 18, CompareOptions.Ordinal, 9 }; yield return new object[] { s_invariantCompare, source1, "\u00C6", 8, 18, CompareOptions.Ordinal, 24 }; yield return new object[] { s_invariantCompare, source1, "\u00E6", 8, 18, CompareOptions.Ordinal, -1 }; yield return new object[] { s_invariantCompare, source1, "AE", 8, 18, CompareOptions.IgnoreCase, 9 }; yield return new object[] { s_invariantCompare, source1, "ae", 8, 18, CompareOptions.IgnoreCase, 9 }; - yield return new object[] { s_invariantCompare, source1, "\u00C6", 8, 18, CompareOptions.IgnoreCase, isWindows? 9 : 24 }; - yield return new object[] { s_invariantCompare, source1, "\u00E6", 8, 18, CompareOptions.IgnoreCase, isWindows? 9 : 24 }; + yield return new object[] { s_invariantCompare, source1, "\u00C6", 8, 18, CompareOptions.IgnoreCase, useNls ? 9 : 24 }; + yield return new object[] { s_invariantCompare, source1, "\u00E6", 8, 18, CompareOptions.IgnoreCase, useNls ? 9 : 24 }; } public static IEnumerable IndexOf_U_WithDiaeresis_TestData() @@ -225,9 +225,9 @@ private static void IndexOf_Char(CompareInfo compareInfo, string source, char va [Fact] public void IndexOf_UnassignedUnicode() { - bool isWindows = PlatformDetection.IsWindows; - IndexOf_String(s_invariantCompare, "FooBar", "Foo\uFFFFBar", 0, 6, CompareOptions.None, isWindows ? 0 : -1); - IndexOf_String(s_invariantCompare, "~FooBar", "Foo\uFFFFBar", 0, 7, CompareOptions.IgnoreNonSpace, isWindows ? 1 : -1); + bool useNls = PlatformDetection.IsNlsGlobalization; + IndexOf_String(s_invariantCompare, "FooBar", "Foo\uFFFFBar", 0, 6, CompareOptions.None, useNls ? 0 : -1); + IndexOf_String(s_invariantCompare, "~FooBar", "Foo\uFFFFBar", 0, 7, CompareOptions.IgnoreNonSpace, useNls ? 1 : -1); } [Fact] diff --git a/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.IsPrefix.cs b/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.IsPrefix.cs index a9a93c5093468..32f45d42adaad 100644 --- a/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.IsPrefix.cs +++ b/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.IsPrefix.cs @@ -71,15 +71,16 @@ public static IEnumerable IsPrefix_TestData() yield return new object[] { s_invariantCompare, "Test's can be interesting", "Tests", CompareOptions.None, false }; // Platform differences - yield return new object[] { s_hungarianCompare, "dzsdzsfoobar", "ddzsf", CompareOptions.None, PlatformDetection.IsWindows ? true : false }; - yield return new object[] { s_invariantCompare, "''Tests", "Tests", CompareOptions.IgnoreSymbols, PlatformDetection.IsWindows ? true : false }; - yield return new object[] { s_frenchCompare, "\u0153", "oe", CompareOptions.None, PlatformDetection.IsWindows ? true : false }; - yield return new object[] { s_invariantCompare, "\uD800\uDC00", "\uD800", CompareOptions.None, PlatformDetection.IsWindows ? true : false }; - yield return new object[] { s_invariantCompare, "\uD800\uDC00", "\uD800", CompareOptions.IgnoreCase, PlatformDetection.IsWindows ? true : false }; + bool useNls = PlatformDetection.IsNlsGlobalization; + yield return new object[] { s_hungarianCompare, "dzsdzsfoobar", "ddzsf", CompareOptions.None, useNls ? true : false }; + yield return new object[] { s_invariantCompare, "''Tests", "Tests", CompareOptions.IgnoreSymbols, useNls ? true : false }; + yield return new object[] { s_frenchCompare, "\u0153", "oe", CompareOptions.None, useNls ? true : false }; + yield return new object[] { s_invariantCompare, "\uD800\uDC00", "\uD800", CompareOptions.None, useNls ? true : false }; + yield return new object[] { s_invariantCompare, "\uD800\uDC00", "\uD800", CompareOptions.IgnoreCase, useNls ? true : false }; // ICU bugs // UInt16 overflow: https://unicode-org.atlassian.net/browse/ICU-20832 fixed in https://github.com/unicode-org/icu/pull/840 (ICU 65) - if (PlatformDetection.IsWindows || PlatformDetection.ICUVersion.Major >= 65) + if (useNls || PlatformDetection.ICUVersion.Major >= 65) { yield return new object[] { s_frenchCompare, "b", new string('a', UInt16.MaxValue + 1), CompareOptions.None, false }; } @@ -106,7 +107,7 @@ public void IsPrefix(CompareInfo compareInfo, string source, string value, Compa [Fact] public void IsPrefix_UnassignedUnicode() { - bool result = PlatformDetection.IsWindows ? true : false; + bool result = PlatformDetection.IsNlsGlobalization ? true : false; IsPrefix(s_invariantCompare, "FooBar", "Foo\uFFFFBar", CompareOptions.None, result); IsPrefix(s_invariantCompare, "FooBar", "Foo\uFFFFBar", CompareOptions.IgnoreNonSpace, result); } diff --git a/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.IsSuffix.cs b/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.IsSuffix.cs index 3d920dd917855..909ceb92cd272 100644 --- a/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.IsSuffix.cs +++ b/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.IsSuffix.cs @@ -82,10 +82,10 @@ public static IEnumerable IsSuffix_TestData() yield return new object[] { s_invariantCompare, "a\u0000b", "b\u0000b", CompareOptions.None, false }; // Platform differences - yield return new object[] { s_hungarianCompare, "foobardzsdzs", "rddzs", CompareOptions.None, PlatformDetection.IsWindows ? true : false }; - yield return new object[] { s_frenchCompare, "\u0153", "oe", CompareOptions.None, PlatformDetection.IsWindows ? true : false }; - yield return new object[] { s_invariantCompare, "\uD800\uDC00", "\uDC00", CompareOptions.None, PlatformDetection.IsWindows ? true : false }; - yield return new object[] { s_invariantCompare, "\uD800\uDC00", "\uDC00", CompareOptions.IgnoreCase, PlatformDetection.IsWindows ? true : false }; + yield return new object[] { s_hungarianCompare, "foobardzsdzs", "rddzs", CompareOptions.None, PlatformDetection.IsIcuGlobalization ? false : true }; + yield return new object[] { s_frenchCompare, "\u0153", "oe", CompareOptions.None, PlatformDetection.IsIcuGlobalization ? false : true }; + yield return new object[] { s_invariantCompare, "\uD800\uDC00", "\uDC00", CompareOptions.None, PlatformDetection.IsIcuGlobalization ? false : true }; + yield return new object[] { s_invariantCompare, "\uD800\uDC00", "\uDC00", CompareOptions.IgnoreCase, PlatformDetection.IsIcuGlobalization ? false : true }; } [Theory] @@ -109,7 +109,7 @@ public void IsSuffix(CompareInfo compareInfo, string source, string value, Compa [Fact] public void IsSuffix_UnassignedUnicode() { - bool result = PlatformDetection.IsWindows ? true : false; + bool result = PlatformDetection.IsIcuGlobalization ? false : true; IsSuffix(s_invariantCompare, "FooBar", "Foo\uFFFFBar", CompareOptions.None, result); IsSuffix(s_invariantCompare, "FooBar", "Foo\uFFFFBar", CompareOptions.IgnoreNonSpace, result); diff --git a/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.LastIndexOf.cs b/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.LastIndexOf.cs index 4d0474e7d4a7b..2702d72494c0a 100644 --- a/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.LastIndexOf.cs +++ b/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.LastIndexOf.cs @@ -81,26 +81,26 @@ public static IEnumerable LastIndexOf_TestData() yield return new object[] { s_invariantCompare, "cbabababdbaba", "ab", 12, 13, CompareOptions.None, 10 }; // Platform differences - yield return new object[] { s_hungarianCompare, "foobardzsdzs", "rddzs", 11, 12, CompareOptions.None, PlatformDetection.IsWindows ? 5 : -1 }; + yield return new object[] { s_hungarianCompare, "foobardzsdzs", "rddzs", 11, 12, CompareOptions.None, PlatformDetection.IsNlsGlobalization ? 5 : -1 }; } public static IEnumerable LastIndexOf_Aesc_Ligature_TestData() { - bool isWindows = PlatformDetection.IsWindows; + bool useNls = PlatformDetection.IsNlsGlobalization; // Searches for the ligature \u00C6 string source = "Is AE or ae the same as \u00C6 or \u00E6?"; - yield return new object[] { s_invariantCompare, source, "AE", 25, 18, CompareOptions.None, isWindows ? 24 : -1 }; + yield return new object[] { s_invariantCompare, source, "AE", 25, 18, CompareOptions.None, useNls ? 24 : -1 }; yield return new object[] { s_invariantCompare, source, "ae", 25, 18, CompareOptions.None, 9 }; yield return new object[] { s_invariantCompare, source, '\u00C6', 25, 18, CompareOptions.None, 24 }; - yield return new object[] { s_invariantCompare, source, '\u00E6', 25, 18, CompareOptions.None, isWindows ? 9 : -1 }; + yield return new object[] { s_invariantCompare, source, '\u00E6', 25, 18, CompareOptions.None, useNls ? 9 : -1 }; yield return new object[] { s_invariantCompare, source, "AE", 25, 18, CompareOptions.Ordinal, -1 }; yield return new object[] { s_invariantCompare, source, "ae", 25, 18, CompareOptions.Ordinal, 9 }; yield return new object[] { s_invariantCompare, source, '\u00C6', 25, 18, CompareOptions.Ordinal, 24 }; yield return new object[] { s_invariantCompare, source, '\u00E6', 25, 18, CompareOptions.Ordinal, -1 }; - yield return new object[] { s_invariantCompare, source, "AE", 25, 18, CompareOptions.IgnoreCase, isWindows ? 24 : 9 }; - yield return new object[] { s_invariantCompare, source, "ae", 25, 18, CompareOptions.IgnoreCase, isWindows ? 24 : 9 }; + yield return new object[] { s_invariantCompare, source, "AE", 25, 18, CompareOptions.IgnoreCase, useNls ? 24 : 9 }; + yield return new object[] { s_invariantCompare, source, "ae", 25, 18, CompareOptions.IgnoreCase, useNls ? 24 : 9 }; yield return new object[] { s_invariantCompare, source, '\u00C6', 25, 18, CompareOptions.IgnoreCase, 24 }; yield return new object[] { s_invariantCompare, source, '\u00E6', 25, 18, CompareOptions.IgnoreCase, 24 }; } @@ -233,9 +233,9 @@ public void LastIndexOf_Aesc_Ligature(CompareInfo compareInfo, string source, st [Fact] public void LastIndexOf_UnassignedUnicode() { - bool isWindows = PlatformDetection.IsWindows; - LastIndexOf_String(s_invariantCompare, "FooBar", "Foo\uFFFFBar", 5, 6, CompareOptions.None, isWindows ? 0 : -1); - LastIndexOf_String(s_invariantCompare, "~FooBar", "Foo\uFFFFBar", 6, 7, CompareOptions.IgnoreNonSpace, isWindows ? 1 : -1); + bool useNls = PlatformDetection.IsNlsGlobalization; + LastIndexOf_String(s_invariantCompare, "FooBar", "Foo\uFFFFBar", 5, 6, CompareOptions.None, useNls ? 0 : -1); + LastIndexOf_String(s_invariantCompare, "~FooBar", "Foo\uFFFFBar", 6, 7, CompareOptions.IgnoreNonSpace, useNls ? 1 : -1); } [Fact] diff --git a/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.cs b/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.cs index b83073a76f98a..d4a76202094d2 100644 --- a/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.cs +++ b/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.cs @@ -95,14 +95,14 @@ public static IEnumerable CompareInfo_TestData() yield return new object[] { "tr-TR" , 0x041f }; } - // On Windows, hiragana characters sort after katakana. + // On NLS, hiragana characters sort after katakana. // On ICU, it is the opposite - private static int s_expectedHiraganaToKatakanaCompare = PlatformDetection.IsWindows ? 1 : -1; + private static int s_expectedHiraganaToKatakanaCompare = PlatformDetection.IsNlsGlobalization ? 1 : -1; - // On Windows, all halfwidth characters sort before fullwidth characters. + // On NLS, all halfwidth characters sort before fullwidth characters. // On ICU, half and fullwidth characters that aren't in the "Halfwidth and fullwidth forms" block U+FF00-U+FFEF // sort before the corresponding characters that are in the block U+FF00-U+FFEF - private static int s_expectedHalfToFullFormsComparison = PlatformDetection.IsWindows ? -1 : 1; + private static int s_expectedHalfToFullFormsComparison = PlatformDetection.IsNlsGlobalization ? -1 : 1; private static CompareInfo s_invariantCompare = CultureInfo.InvariantCulture.CompareInfo; private static CompareInfo s_turkishCompare = new CultureInfo("tr-TR").CompareInfo; diff --git a/src/libraries/System.Globalization/tests/CultureInfo/CultureInfoAll.cs b/src/libraries/System.Globalization/tests/CultureInfo/CultureInfoAll.cs index 028cf71e49780..cf40173884bd6 100644 --- a/src/libraries/System.Globalization/tests/CultureInfo/CultureInfoAll.cs +++ b/src/libraries/System.Globalization/tests/CultureInfo/CultureInfoAll.cs @@ -14,9 +14,9 @@ namespace System.Globalization.Tests { public class CultureInfoAll { - [Fact] [PlatformSpecific(TestPlatforms.Windows)] // P/Invoke to Win32 function - public void TestAllCultures() + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))] + public void TestAllCultures_Nls() { Assert.True(EnumSystemLocalesEx(EnumLocales, LOCALE_WINDOWS, IntPtr.Zero, IntPtr.Zero), "EnumSystemLocalesEx has failed"); diff --git a/src/libraries/System.Globalization/tests/DateTimeFormatInfo/DateTimeFormatInfoData.cs b/src/libraries/System.Globalization/tests/DateTimeFormatInfo/DateTimeFormatInfoData.cs index c8fcdb9de007d..ed232bbf21593 100644 --- a/src/libraries/System.Globalization/tests/DateTimeFormatInfo/DateTimeFormatInfoData.cs +++ b/src/libraries/System.Globalization/tests/DateTimeFormatInfo/DateTimeFormatInfoData.cs @@ -11,12 +11,12 @@ internal static class DateTimeFormatInfoData { public static string EnUSEraName() { - return PlatformDetection.IsWindows ? "A.D." : "AD"; + return PlatformDetection.IsNlsGlobalization ? "A.D." : "AD"; } public static string EnUSAbbreviatedEraName() { - return PlatformDetection.IsWindows ? "AD" : "A"; + return PlatformDetection.IsNlsGlobalization ? "AD" : "A"; } public static string JaJPAbbreviatedEraName() @@ -24,7 +24,7 @@ public static string JaJPAbbreviatedEraName() // For Windows(() => DateTimeFormatInfo.InvariantInfo.LongTimePattern = "HH:mm:ss"); } - [Fact] - [PlatformSpecific(TestPlatforms.AnyUnix)] - public void LongTimePattern_CheckReadingTimeFormatWithSingleQuotes() + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsIcuGlobalization))] + public void LongTimePattern_CheckReadingTimeFormatWithSingleQuotes_ICU() { // Usually fr-CA long time format has a single quotes e.g. "HH 'h' mm 'min' ss 's'". // Ensuring when reading such formats from ICU we'll not eat the spaces after the single quotes. diff --git a/src/libraries/System.Globalization/tests/DateTimeFormatInfo/DateTimeFormatInfoTests.cs b/src/libraries/System.Globalization/tests/DateTimeFormatInfo/DateTimeFormatInfoTests.cs index e8ccf90be063f..14c5afab10006 100644 --- a/src/libraries/System.Globalization/tests/DateTimeFormatInfo/DateTimeFormatInfoTests.cs +++ b/src/libraries/System.Globalization/tests/DateTimeFormatInfo/DateTimeFormatInfoTests.cs @@ -71,12 +71,12 @@ public void NativeCalendarName_Get_ReturnsExpected(DateTimeFormatInfo dtfi, Cale } catch { - if (PlatformDetection.IsWindows) + if (PlatformDetection.IsNlsGlobalization) { // Persian calendar is recently supported as one of the optional calendars for fa-IR Assert.True(calendar is PersianCalendar, "Exception can occur only with PersianCalendar"); } - else // !PlatformDetection.IsWindows + else // !PlatformDetection.IsNlsGlobalization { Assert.True(calendar is HijriCalendar || calendar is UmAlQuraCalendar || calendar is ThaiBuddhistCalendar || calendar is HebrewCalendar || calendar is KoreanCalendar, "failed to set the calendar on DTFI"); diff --git a/src/libraries/System.Globalization/tests/IcuTests.cs b/src/libraries/System.Globalization/tests/IcuTests.cs new file mode 100644 index 0000000000000..8e862c910195f --- /dev/null +++ b/src/libraries/System.Globalization/tests/IcuTests.cs @@ -0,0 +1,40 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Reflection; +using Xunit; +using Xunit.Sdk; + +namespace System.Globalization.Tests +{ + public class IcuTests + { + private static bool IsIcuCompatiblePlatform => PlatformDetection.IsNotWindows || + (!PlatformDetection.IsMonoRuntime && + PlatformDetection.IsWindows10Version1903OrGreater); + + [ConditionalFact(nameof(IsIcuCompatiblePlatform))] + public static void IcuShouldBeUsedByDefault() + { + Type globalizationMode = Type.GetType("System.Globalization.GlobalizationMode"); + if (globalizationMode != null) + { + MethodInfo methodInfo = globalizationMode.GetProperty("UseNls", BindingFlags.NonPublic | BindingFlags.Static)?.GetMethod; + if (methodInfo != null) + { + Assert.False((bool)methodInfo.Invoke(null, null)); + return; + } + } + + throw new XunitException("Couldn't get System.Globalization.GlobalizationMode.UseIcu property."); + } + + [ConditionalFact(nameof(IsIcuCompatiblePlatform))] + public static void IcuShouldBeLoaded() + { + Assert.True(PlatformDetection.IsIcuGlobalization); + } + } +} diff --git a/src/libraries/System.Globalization/tests/Invariant/InvariantMode.cs b/src/libraries/System.Globalization/tests/Invariant/InvariantMode.cs index d6c7676eb1f80..67c0b09e2b272 100644 --- a/src/libraries/System.Globalization/tests/Invariant/InvariantMode.cs +++ b/src/libraries/System.Globalization/tests/Invariant/InvariantMode.cs @@ -476,6 +476,12 @@ public static IEnumerable GetUnicode_TestData() yield return new object[] { "xn--de-jg4avhby1noc0d", 0, 21, "\u30D1\u30D5\u30A3\u30FC\u0064\u0065\u30EB\u30F3\u30D0" }; } + [Fact] + public static void IcuShouldNotBeLoaded() + { + Assert.False(PlatformDetection.IsIcuGlobalization); + } + [Theory] [MemberData(nameof(Cultures_TestData))] public void TestCultureData(string cultureName) diff --git a/src/libraries/System.Globalization/tests/NlsTests/NlsSwitchTests.cs b/src/libraries/System.Globalization/tests/NlsTests/NlsSwitchTests.cs new file mode 100644 index 0000000000000..c7885ac41774e --- /dev/null +++ b/src/libraries/System.Globalization/tests/NlsTests/NlsSwitchTests.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Reflection; +using Xunit; +using Xunit.Sdk; + +namespace System.Globalization.Tests +{ + public class NlsSwitchTests + { + [Fact] + public static void NlsRuntimeSwitchIsHonored() + { + Type globalizationMode = Type.GetType("System.Globalization.GlobalizationMode"); + if (globalizationMode != null) + { + MethodInfo methodInfo = globalizationMode.GetProperty("UseNls", BindingFlags.NonPublic | BindingFlags.Static)?.GetMethod; + if (methodInfo != null) + { + Assert.True((bool)methodInfo.Invoke(null, null)); + return; + } + } + + throw new XunitException("Couldn't get System.Globalization.GlobalizationMode.UseIcu property."); + } + + [Fact] + public static void IcuShouldNotBeLoaded() + { + Assert.False(PlatformDetection.IsIcuGlobalization); + } + } +} diff --git a/src/libraries/System.Globalization/tests/NlsTests/System.Globalization.Nls.Tests.csproj b/src/libraries/System.Globalization/tests/NlsTests/System.Globalization.Nls.Tests.csproj new file mode 100644 index 0000000000000..7ab807472ea6d --- /dev/null +++ b/src/libraries/System.Globalization/tests/NlsTests/System.Globalization.Nls.Tests.csproj @@ -0,0 +1,345 @@ + + + true + true + true + + $(NetCoreAppCurrent)-Windows_NT + 13.0 + + + + + + CompareInfo\CompareInfoTests.cs + + + CompareInfo\CompareInfoTests.IndexOf.cs + + + CompareInfo\CompareInfoTests.IsPrefix.cs + + + CompareInfo\CompareInfoTests.Compare.cs + + + CompareInfo\CompareInfoTests.IsSuffix.cs + + + CompareInfo\CompareInfoTests.LastIndexOf.cs + + + NumberFormatInfo\NumberFormatInfoCurrentInfo.cs + + + NumberFormatInfo\NumberFormatInfoValidateParseStyle.cs + + + NumberFormatInfo\NumberFormatInfoData.cs + + + NumberFormatInfo\NumberFormatInfoCurrencySymbol.cs + + + NumberFormatInfo\NumberFormatInfoNaNSymbol.cs + + + NumberFormatInfo\NumberFormatInfoPercentGroupSeparator.cs + + + NumberFormatInfo\NumberFormatInfoPercentDecimalSeparator.cs + + + NumberFormatInfo\NumberFormatInfoPercentDecimalDigits.cs + + + NumberFormatInfo\NumberFormatInfoPerMilleSymbol.cs + + + NumberFormatInfo\NumberFormatInfoPercentSymbol.cs + + + NumberFormatInfo\NumberFormatInfoPositiveSign.cs + + + NumberFormatInfo\NumberFormatInfoPositiveInfinitySymbol.cs + + + NumberFormatInfo\NumberFormatInfoNegativeSign.cs + + + NumberFormatInfo\NumberFormatInfoNegativeInfinitySymbol.cs + + + NumberFormatInfo\NumberFormatInfoNumberGroupSeparator.cs + + + NumberFormatInfo\NumberFormatInfoNumberDecimalSeparator.cs + + + NumberFormatInfo\NumberFormatInfoNumberDecimalDigits.cs + + + NumberFormatInfo\NumberFormatInfoNumberGroupSizes.cs + + + NumberFormatInfo\NumberFormatInfoNumberNegativePattern.cs + + + NumberFormatInfo\NumberFormatInfoPercentNegativePattern.cs + + + NumberFormatInfo\NumberFormatInfoPercentGroupSizes.cs + + + NumberFormatInfo\NumberFormatInfoPercentPositivePattern.cs + + + CultureInfo\CultureInfoAll.cs + + + CultureInfo\CultureInfoAsync.cs + + + CultureInfo\CultureInfoCalendar.cs + + + CultureInfo\CultureInfoClone.cs + + + CultureInfo\CultureInfoCompareInfo.cs + + + CultureInfo\CultureInfoCtor.cs + + + CultureInfo\CultureInfoDateTimeFormat.cs + + + CultureInfo\CultureInfoEnglishName.cs + + + CultureInfo\CultureInfoEquals.cs + + + CultureInfo\CultureInfoGetFormat.cs + + + CultureInfo\CultureInfoGetHashCode.cs + + + CultureInfo\CultureInfoIsNeutralCulture.cs + + + CultureInfo\CultureInfoNativeName.cs + + + CultureInfo\CultureInfoNumberFormat.cs + + + CultureInfo\CultureInfoParent.cs + + + CultureInfo\CultureInfoReadOnly.cs + + + CultureInfo\CultureInfoTwoLetterISOLanguageName.cs + + + CultureInfo\CultureInfoCurrentCulture.cs + + + CultureInfo\GetCultureInfo.cs + + + DateTimeFormatInfo\DateTimeFormatInfoAbbreviatedDayNames.cs + + + DateTimeFormatInfo\DateTimeFormatInfoAbbreviatedMonthGenitiveNames.cs + + + DateTimeFormatInfo\DateTimeFormatInfoAbbreviatedMonthNames.cs + + + DateTimeFormatInfo\DateTimeFormatInfoAMDesignator.cs + + + DateTimeFormatInfo\DateTimeFormatInfoCalendar.cs + + + DateTimeFormatInfo\DateTimeFormatInfoCalendarWeekRule.cs + + + DateTimeFormatInfo\DateTimeFormatInfoClone.cs + + + DateTimeFormatInfo\DateTimeFormatInfoData.cs + + + DateTimeFormatInfo\DateTimeFormatInfoDayNames.cs + + + DateTimeFormatInfo\DateTimeFormatInfoFirstDayOfWeek.cs + + + DateTimeFormatInfo\DateTimeFormatInfoFullDateTimePattern.cs + + + DateTimeFormatInfo\DateTimeFormatInfoGetAbbreviatedDayName.cs + + + DateTimeFormatInfo\DateTimeFormatInfoGetAbbreviatedEraName.cs + + + DateTimeFormatInfo\DateTimeFormatInfoGetAbbreviatedMonthName.cs + + + DateTimeFormatInfo\DateTimeFormatInfoGetDayName.cs + + + DateTimeFormatInfo\DateTimeFormatInfoGetEra.cs + + + DateTimeFormatInfo\DateTimeFormatInfoGetEraName.cs + + + DateTimeFormatInfo\DateTimeFormatInfoGetFormat.cs + + + DateTimeFormatInfo\DateTimeFormatInfoGetInstance.cs + + + DateTimeFormatInfo\DateTimeFormatInfoGetMonthName.cs + + + DateTimeFormatInfo\DateTimeFormatInfoLongDatePattern.cs + + + DateTimeFormatInfo\DateTimeFormatInfoLongTimePattern.cs + + + DateTimeFormatInfo\DateTimeFormatInfoMonthDayPattern.cs + + + DateTimeFormatInfo\DateTimeFormatInfoMonthGenitiveNames.cs + + + DateTimeFormatInfo\DateTimeFormatInfoMonthNames.cs + + + DateTimeFormatInfo\DateTimeFormatInfoPMDesignator.cs + + + DateTimeFormatInfo\DateTimeFormatInfoReadOnly.cs + + + DateTimeFormatInfo\DateTimeFormatInfoRFC1123Pattern.cs + + + DateTimeFormatInfo\DateTimeFormatInfoShortDatePattern.cs + + + DateTimeFormatInfo\DateTimeFormatInfoShortestDayNames.cs + + + DateTimeFormatInfo\DateTimeFormatInfoShortTimePattern.cs + + + DateTimeFormatInfo\DateTimeFormatInfoSortableDateTimePattern.cs + + + DateTimeFormatInfo\DateTimeFormatInfoTests.cs + + + DateTimeFormatInfo\DateTimeFormatInfoUniversalSortableDateTimePattern.cs + + + DateTimeFormatInfo\DateTimeFormatInfoYearMonthPattern.cs + + + NumberFormatInfo\NumberFormatInfoClone.cs + + + NumberFormatInfo\NumberFormatInfoCurrencyDecimalDigits.cs + + + NumberFormatInfo\NumberFormatInfoCurrencyDecimalSeparator.cs + + + NumberFormatInfo\NumberFormatInfoCurrencyGroupSeparator.cs + + + NumberFormatInfo\NumberFormatInfoCurrencyGroupSizes.cs + + + NumberFormatInfo\NumberFormatInfoCurrencyNegativePattern.cs + + + NumberFormatInfo\NumberFormatInfoCurrencyPositivePattern.cs + + + NumberFormatInfo\NumberFormatInfoGetFormat.cs + + + NumberFormatInfo\NumberFormatInfoGetInstance.cs + + + NumberFormatInfo\NumberFormatInfoReadOnly.cs + + + NumberFormatInfo\NumberFormatInfoTests.cs + + + System\Globalization\CharUnicodeInfoTestData.cs + + + System\Globalization\CharUnicodeInfoTests.cs + + + System\Globalization\CharUnicodeInfoTests.Generated.cs + + + System\Globalization\CultureNotFoundExceptionTests.cs + + + System\Globalization\GraphemeBreakTest.cs + + + System\Globalization\RegionInfoTests.cs + + + System\Globalization\StringInfoTests.cs + + + System\Globalization\SortVersionTests.cs + + + System\Globalization\TextInfoTests.cs + + + System\Globalization\TextElementEnumeratorTests.cs + + + System\Globalization\UnicodeCategoryTests.cs + + + + Common\System\RandomDataGenerator.cs + + + + + + CharUnicodeInfo\UnicodeData.$(UnicodeUcdVersion).txt + UnicodeData.txt + + + CharUnicodeInfo\GraphemeBreakTest-$(UnicodeUcdVersion).0.txt + GraphemeBreakTest.txt + + + + + + + diff --git a/src/libraries/System.Globalization/tests/NlsTests/runtimeconfig.template.json b/src/libraries/System.Globalization/tests/NlsTests/runtimeconfig.template.json new file mode 100644 index 0000000000000..f93c6039127bd --- /dev/null +++ b/src/libraries/System.Globalization/tests/NlsTests/runtimeconfig.template.json @@ -0,0 +1,5 @@ +{ + "configProperties": { + "System.Globalization.UseNls": true + } +} diff --git a/src/libraries/System.Globalization/tests/NumberFormatInfo/NumberFormatInfoCurrencyDecimalDigits.cs b/src/libraries/System.Globalization/tests/NumberFormatInfo/NumberFormatInfoCurrencyDecimalDigits.cs index 475c74f19f77e..e67bd53a742f3 100644 --- a/src/libraries/System.Globalization/tests/NumberFormatInfo/NumberFormatInfoCurrencyDecimalDigits.cs +++ b/src/libraries/System.Globalization/tests/NumberFormatInfo/NumberFormatInfoCurrencyDecimalDigits.cs @@ -18,9 +18,9 @@ public static IEnumerable CurrencyDecimalDigits_TestData() [Theory] [MemberData(nameof(CurrencyDecimalDigits_TestData))] - public void CurrencyDecimalDigits_Get_ReturnsExpected(NumberFormatInfo format, int expectedWindows, int expectedIcu) + public void CurrencyDecimalDigits_Get_ReturnsExpected(NumberFormatInfo format, int expectedNls, int expectedIcu) { - int expected = PlatformDetection.IsWindows ? expectedWindows : expectedIcu; + int expected = PlatformDetection.IsNlsGlobalization ? expectedNls : expectedIcu; Assert.Equal(expected, format.CurrencyDecimalDigits); } diff --git a/src/libraries/System.Globalization/tests/NumberFormatInfo/NumberFormatInfoData.cs b/src/libraries/System.Globalization/tests/NumberFormatInfo/NumberFormatInfoData.cs index e6500c9741ff0..dfffa504f7f96 100644 --- a/src/libraries/System.Globalization/tests/NumberFormatInfo/NumberFormatInfoData.cs +++ b/src/libraries/System.Globalization/tests/NumberFormatInfo/NumberFormatInfoData.cs @@ -29,13 +29,13 @@ internal static int[] GetCurrencyNegativePatterns(string localeName) switch (localeName) { case "en-US": - return PlatformDetection.IsWindows ? new int[] { 0 } : new int[] { 1, 0 }; + return PlatformDetection.IsNlsGlobalization ? new int[] { 0 } : new int[] { 1, 0 }; case "en-CA": - return PlatformDetection.IsWindows ? new int[] { 1 } : new int[] { 1, 0 }; + return PlatformDetection.IsNlsGlobalization ? new int[] { 1 } : new int[] { 1, 0 }; case "fa-IR": - if (PlatformDetection.IsWindows) + if (PlatformDetection.IsNlsGlobalization) { return (PlatformDetection.WindowsVersion < 10) ? new int[] { 3 } : new int[] { 6, 3 }; } @@ -53,7 +53,7 @@ internal static int[] GetCurrencyNegativePatterns(string localeName) } case "fr-CD": - if (PlatformDetection.IsWindows) + if (PlatformDetection.IsNlsGlobalization) { return (PlatformDetection.WindowsVersion < 10) ? new int[] { 4 } : new int[] { 8 }; } @@ -63,13 +63,13 @@ internal static int[] GetCurrencyNegativePatterns(string localeName) } case "as": - return PlatformDetection.IsWindows ? new int[] { 12 } : new int[] { 9 }; + return PlatformDetection.IsNlsGlobalization ? new int[] { 12 } : new int[] { 9 }; case "es-BO": - return (PlatformDetection.IsWindows && PlatformDetection.WindowsVersion < 10) ? new int[] { 14 } : new int[] { 1 }; + return (PlatformDetection.IsNlsGlobalization && PlatformDetection.WindowsVersion < 10) ? new int[] { 14 } : new int[] { 1 }; case "fr-CA": - return PlatformDetection.IsWindows ? new int[] { 15 } : new int[] { 8, 15 }; + return PlatformDetection.IsNlsGlobalization ? new int[] { 15 } : new int[] { 8, 15 }; } throw DateTimeFormatInfoData.GetCultureNotSupportedException(CultureInfo.GetCultureInfo(localeName)); diff --git a/src/libraries/System.Globalization/tests/NumberFormatInfo/NumberFormatInfoNumberDecimalDigits.cs b/src/libraries/System.Globalization/tests/NumberFormatInfo/NumberFormatInfoNumberDecimalDigits.cs index 581c0e8c19753..9902cc6867d38 100644 --- a/src/libraries/System.Globalization/tests/NumberFormatInfo/NumberFormatInfoNumberDecimalDigits.cs +++ b/src/libraries/System.Globalization/tests/NumberFormatInfo/NumberFormatInfoNumberDecimalDigits.cs @@ -17,9 +17,9 @@ public static IEnumerable NumberDecimalDigits_TestData() [Theory] [MemberData(nameof(NumberDecimalDigits_TestData))] - public void NumberDecimalDigits_Get_ReturnsExpected(NumberFormatInfo format, int expectedWindows, int expectedIcu) + public void NumberDecimalDigits_Get_ReturnsExpected(NumberFormatInfo format, int expectedNls, int expectedIcu) { - int expected = PlatformDetection.IsWindows ? expectedWindows : expectedIcu; + int expected = PlatformDetection.IsNlsGlobalization ? expectedNls : expectedIcu; Assert.Equal(expected, format.NumberDecimalDigits); } diff --git a/src/libraries/System.Globalization/tests/NumberFormatInfo/NumberFormatInfoPercentNegativePattern.cs b/src/libraries/System.Globalization/tests/NumberFormatInfo/NumberFormatInfoPercentNegativePattern.cs index 5bf6adc65e31d..81fa3d986d0e7 100644 --- a/src/libraries/System.Globalization/tests/NumberFormatInfo/NumberFormatInfoPercentNegativePattern.cs +++ b/src/libraries/System.Globalization/tests/NumberFormatInfo/NumberFormatInfoPercentNegativePattern.cs @@ -18,15 +18,14 @@ public static IEnumerable PercentNegativePattern_TestData() } /// - /// Not testing for Windows as the culture data can change + /// Not testing for NLS as the culture data can change /// https://blogs.msdn.microsoft.com/shawnste/2005/04/05/culture-data-shouldnt-be-considered-stable-except-for-invariant/ /// In the CultureInfoAll test class we are testing the expected behavior - /// for Windows by enumerating all locales on the system and then test them. + /// for NLS by enumerating all locales on the system and then test them. /// - [Theory] - [PlatformSpecific(TestPlatforms.AnyUnix)] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsIcuGlobalization))] [MemberData(nameof(PercentNegativePattern_TestData))] - public void PercentNegativePattern_Get_ReturnsExpected(NumberFormatInfo format, int expected) + public void PercentNegativePattern_Get_ReturnsExpected_ICU(NumberFormatInfo format, int expected) { Assert.Equal(expected, format.PercentNegativePattern); } diff --git a/src/libraries/System.Globalization/tests/NumberFormatInfo/NumberFormatInfoPercentPositivePattern.cs b/src/libraries/System.Globalization/tests/NumberFormatInfo/NumberFormatInfoPercentPositivePattern.cs index da028a2521dc3..1dd54259761c9 100644 --- a/src/libraries/System.Globalization/tests/NumberFormatInfo/NumberFormatInfoPercentPositivePattern.cs +++ b/src/libraries/System.Globalization/tests/NumberFormatInfo/NumberFormatInfoPercentPositivePattern.cs @@ -18,15 +18,14 @@ public static IEnumerable PercentPositivePattern_TestData() } /// - /// Not testing for Windows as the culture data can change + /// Not testing for NLS as the culture data can change /// https://blogs.msdn.microsoft.com/shawnste/2005/04/05/culture-data-shouldnt-be-considered-stable-except-for-invariant/ /// In the CultureInfoAll test class we are testing the expected behavior - /// for Windows by enumerating all locales on the system and then test them. + /// for NLS by enumerating all locales on the system and then test them. /// - [Theory] - [PlatformSpecific(TestPlatforms.AnyUnix)] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsIcuGlobalization))] [MemberData(nameof(PercentPositivePattern_TestData))] - public void PercentPositivePattern_Get_ReturnsExpected(NumberFormatInfo format, int expected) + public void PercentPositivePattern_Get_ReturnsExpected_ICU(NumberFormatInfo format, int expected) { Assert.Equal(expected, format.PercentPositivePattern); } diff --git a/src/libraries/System.Globalization/tests/System.Globalization.Tests.csproj b/src/libraries/System.Globalization/tests/System.Globalization.Tests.csproj index be9b72574bcdc..a0a3271278c24 100644 --- a/src/libraries/System.Globalization/tests/System.Globalization.Tests.csproj +++ b/src/libraries/System.Globalization/tests/System.Globalization.Tests.csproj @@ -1,4 +1,4 @@ - + true true @@ -7,6 +7,7 @@ 13.0 + diff --git a/src/libraries/System.Globalization/tests/System/Globalization/RegionInfoTests.cs b/src/libraries/System.Globalization/tests/System/Globalization/RegionInfoTests.cs index 6ca55371c4822..68f43247120d9 100644 --- a/src/libraries/System.Globalization/tests/System/Globalization/RegionInfoTests.cs +++ b/src/libraries/System.Globalization/tests/System/Globalization/RegionInfoTests.cs @@ -52,8 +52,8 @@ public void Ctor_InvalidName_ThrowsArgumentException(string name) AssertExtensions.Throws("name", () => new RegionInfo(name)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindows))] - public void CurrentRegion() + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsIcuGlobalization))] + public void CurrentRegion_Icu() { using (new ThreadCultureChange("en-US")) { @@ -63,8 +63,8 @@ public void CurrentRegion() } } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsWindows))] - public void TestCurrentRegion() + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))] + public void TestCurrentRegion_Nls() { RemoteExecutor.Invoke(() => { @@ -149,15 +149,15 @@ public void TwoLetterISORegionName(string name, string expected) public static IEnumerable RegionInfo_TestData() { yield return new object[] { 0x409, 244, "US Dollar", "US Dollar", "\u0055\u0053\u0020\u0044\u006f\u006c\u006c\u0061\u0072", "USA", "USA" }; - yield return new object[] { 0x411, 122, "Japanese Yen", "Japanese Yen", PlatformDetection.IsWindows ? "\u5186" : "\u65e5\u672c\u5186", "JPN", "JPN" }; + yield return new object[] { 0x411, 122, "Japanese Yen", "Japanese Yen", PlatformDetection.IsNlsGlobalization ? "\u5186" : "\u65e5\u672c\u5186", "JPN", "JPN" }; yield return new object[] { 0x804, 45, "Chinese Yuan", "PRC Yuan Renminbi", "\u4eba\u6c11\u5e01", "CHN", "CHN" }; - yield return new object[] { 0x401, 205, "Saudi Riyal", "Saudi Riyal", PlatformDetection.IsWindows ? + yield return new object[] { 0x401, 205, "Saudi Riyal", "Saudi Riyal", PlatformDetection.IsNlsGlobalization ? "\u0631\u064a\u0627\u0644\u00a0\u0633\u0639\u0648\u062f\u064a" : "\u0631\u064a\u0627\u0644\u0020\u0633\u0639\u0648\u062f\u064a", "SAU", "SAU" }; - yield return new object[] { 0x412, 134, "South Korean Won", "Korean Won", PlatformDetection.IsWindows ? "\uc6d0" : "\ub300\ud55c\ubbfc\uad6d\u0020\uc6d0", "KOR", "KOR" }; + yield return new object[] { 0x412, 134, "South Korean Won", "Korean Won", PlatformDetection.IsNlsGlobalization ? "\uc6d0" : "\ub300\ud55c\ubbfc\uad6d\u0020\uc6d0", "KOR", "KOR" }; yield return new object[] { 0x40d, 117, "Israeli New Shekel", "Israeli New Sheqel", - PlatformDetection.IsWindows || PlatformDetection.ICUVersion.Major >= 58 ? "\u05e9\u05e7\u05dc\u0020\u05d7\u05d3\u05e9" : "\u05e9\u05f4\u05d7", "ISR", "ISR" }; + PlatformDetection.IsNlsGlobalization || PlatformDetection.ICUVersion.Major >= 58 ? "\u05e9\u05e7\u05dc\u0020\u05d7\u05d3\u05e9" : "\u05e9\u05f4\u05d7", "ISR", "ISR" }; } [Theory] diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 9d531ff4ff1dc..8fe6a6dca457c 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -240,16 +240,24 @@ + + + + + + + + @@ -267,14 +275,22 @@ + + + + + + + + @@ -287,6 +303,8 @@ + + @@ -976,6 +994,58 @@ + + Common\Interop\Interop.Libraries.cs + + + Common\Interop\Interop.Calendar.cs + + + Common\Interop\Interop.Casing.cs + + + Common\Interop\Interop.Collation.cs + + + Common\Interop\Interop.ICU.cs + + + Common\Interop\Interop.Idna.cs + + + Common\Interop\Interop.Locale.cs + + + Common\Interop\Interop.Normalization.cs + + + Common\Interop\Interop.ResultCode.cs + + + Common\Interop\Interop.TimeZoneInfo.cs + + + Common\Interop\Interop.Utils.cs + + + Common\Interop\Interop.Errors.cs + + + + Common\Interop\Windows\Interop.BOOL.cs + + + Common\Interop\Windows\Kernel32\Interop.Globalization.cs + + + Common\Interop\Windows\Kernel32\Interop.ResolveLocaleName.cs + + + Common\Interop\Windows\Normaliz\Interop.Idna.cs + + + Common\Interop\Windows\Normaliz\Interop.Normalization.cs + Common\System\HResults.cs @@ -1180,9 +1250,6 @@ Common\Interop\Windows\Crypt32\Interop.CryptProtectMemory.cs - - Common\Interop\Windows\Interop.BOOL.cs - Common\Interop\Windows\Interop.BOOLEAN.cs @@ -1297,9 +1364,6 @@ Common\Interop\Windows\Kernel32\Interop.GetTempPathW.cs - - Common\Interop\Windows\Kernel32\Interop.Globalization.cs - Common\Interop\Windows\Kernel32\Interop.GlobalMemoryStatusEx.cs @@ -1348,9 +1412,6 @@ Common\Interop\Windows\Kernel32\Interop.ReadFile_SafeHandle_NativeOverlapped.cs - - Common\Interop\Windows\Kernel32\Interop.ResolveLocaleName.cs - Common\Interop\Windows\Kernel32\Interop.SECURITY_ATTRIBUTES.cs @@ -1411,12 +1472,6 @@ Common\Interop\Windows\Kernel32\Interop.WriteFile_SafeHandle_NativeOverlapped.cs - - Common\Interop\Windows\Normaliz\Interop.Idna.cs - - - Common\Interop\Windows\Normaliz\Interop.Normalization.cs - Common\Interop\Windows\NtDll\Interop.NtQueryInformationFile.cs @@ -1471,15 +1526,7 @@ - - - - - - - - @@ -1519,7 +1566,6 @@ - Common\Interop\Windows\OleAut32\Interop.SysAllocStringLen.cs @@ -1585,36 +1631,6 @@ Common\Interop\Unix\Interop.Libraries.cs - - Common\Interop\Unix\System.Globalization.Native\Interop.Calendar.cs - - - Common\Interop\Unix\System.Globalization.Native\Interop.Casing.cs - - - Common\Interop\Unix\System.Globalization.Native\Interop.Collation.cs - - - Common\Interop\Unix\System.Globalization.Native\Interop.ICU.cs - - - Common\Interop\Unix\System.Globalization.Native\Interop.Idna.cs - - - Common\Interop\Unix\System.Globalization.Native\Interop.Locale.cs - - - Common\Interop\Unix\System.Globalization.Native\Interop.Normalization.cs - - - Common\Interop\Unix\System.Globalization.Native\Interop.ResultCode.cs - - - Common\Interop\Unix\System.Globalization.Native\Interop.TimeZoneInfo.cs - - - Common\Interop\Unix\System.Globalization.Native\Interop.Utils.cs - Common\Interop\Unix\System.Native\Interop.Access.cs @@ -1728,16 +1744,7 @@ - - - - - - - - - diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/Calendar.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/Calendar.cs index 7d86971a87919..48e34ffaad35c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/Calendar.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/Calendar.cs @@ -725,7 +725,7 @@ internal static long TimeToTicks(int hour, int minute, int second, int milliseco internal static int GetSystemTwoDigitYearSetting(CalendarId CalID, int defaultYearValue) { - int twoDigitYearMax = CalendarData.GetTwoDigitYearMax(CalID); + int twoDigitYearMax = GlobalizationMode.UseNls ? CalendarData.NlsGetTwoDigitYearMax(CalID) : CalendarData.IcuGetTwoDigitYearMax(CalID); return twoDigitYearMax >= 0 ? twoDigitYearMax : defaultYearValue; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.Icu.cs similarity index 94% rename from src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.Unix.cs rename to src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.Icu.cs index e62a707684cc0..b58bc4a311ec1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.Icu.cs @@ -31,8 +31,10 @@ internal enum CalendarDataType internal partial class CalendarData { - private bool LoadCalendarDataFromSystem(string localeName, CalendarId calendarId) + private bool IcuLoadCalendarDataFromSystem(string localeName, CalendarId calendarId) { + Debug.Assert(!GlobalizationMode.UseNls); + bool result = true; // these can return null but are later replaced with String.Empty or other non-nullable value @@ -79,17 +81,20 @@ private bool LoadCalendarDataFromSystem(string localeName, CalendarId calendarId return result; } - internal static int GetTwoDigitYearMax(CalendarId calendarId) + internal static int IcuGetTwoDigitYearMax(CalendarId calendarId) { + Debug.Assert(!GlobalizationMode.UseNls); + // There is no user override for this value on Linux or in ICU. // So just return -1 to use the hard-coded defaults. return -1; } // Call native side to figure out which calendars are allowed - internal static int GetCalendars(string localeName, bool useUserOverride, CalendarId[] calendars) + internal static int IcuGetCalendars(string localeName, bool useUserOverride, CalendarId[] calendars) { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(!GlobalizationMode.UseNls); // NOTE: there are no 'user overrides' on Linux int count = Interop.Globalization.GetCalendars(localeName, calendars, calendars.Length); @@ -104,8 +109,9 @@ internal static int GetCalendars(string localeName, bool useUserOverride, Calend return count; } - private static bool SystemSupportsTaiwaneseCalendar() + private static bool IcuSystemSupportsTaiwaneseCalendar() { + Debug.Assert(!GlobalizationMode.UseNls); return true; } @@ -133,7 +139,7 @@ private static bool EnumDatePatterns(string localeName, CalendarId calendarId, C { datePatterns = null; - EnumCalendarsData callbackContext = default; + IcuEnumCalendarsData callbackContext = default; callbackContext.Results = new List(); callbackContext.DisallowDuplicates = true; bool result = EnumCalendarInfo(localeName, calendarId, dataType, ref callbackContext); @@ -358,7 +364,7 @@ private static bool EnumMonthNames(string localeName, CalendarId calendarId, Cal { monthNames = null; - EnumCalendarsData callbackContext = default; + IcuEnumCalendarsData callbackContext = default; callbackContext.Results = new List(); bool result = EnumCalendarInfo(localeName, calendarId, dataType, ref callbackContext); if (result) @@ -406,7 +412,7 @@ internal static bool EnumCalendarInfo(string localeName, CalendarId calendarId, { calendarData = null; - EnumCalendarsData callbackContext = default; + IcuEnumCalendarsData callbackContext = default; callbackContext.Results = new List(); bool result = EnumCalendarInfo(localeName, calendarId, dataType, ref callbackContext); if (result) @@ -417,7 +423,7 @@ internal static bool EnumCalendarInfo(string localeName, CalendarId calendarId, return result; } - private static unsafe bool EnumCalendarInfo(string localeName, CalendarId calendarId, CalendarDataType dataType, ref EnumCalendarsData callbackContext) + private static unsafe bool EnumCalendarInfo(string localeName, CalendarId calendarId, CalendarDataType dataType, ref IcuEnumCalendarsData callbackContext) { return Interop.Globalization.EnumCalendarInfo(EnumCalendarInfoCallback, localeName, calendarId, dataType, (IntPtr)Unsafe.AsPointer(ref callbackContext)); } @@ -426,7 +432,7 @@ private static unsafe void EnumCalendarInfoCallback(string calendarString, IntPt { try { - ref EnumCalendarsData callbackContext = ref Unsafe.As(ref *(byte*)context); + ref IcuEnumCalendarsData callbackContext = ref Unsafe.As(ref *(byte*)context); if (callbackContext.DisallowDuplicates) { @@ -450,7 +456,7 @@ private static unsafe void EnumCalendarInfoCallback(string calendarString, IntPt } } - private struct EnumCalendarsData + private struct IcuEnumCalendarsData { public List Results; public bool DisallowDuplicates; diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.Nls.cs similarity index 94% rename from src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.Windows.cs rename to src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.Nls.cs index 8031df90e0e28..79ff55073850d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.Nls.cs @@ -10,9 +10,10 @@ namespace System.Globalization { internal partial class CalendarData { - private bool LoadCalendarDataFromSystem(string localeName, CalendarId calendarId) + private bool NlsLoadCalendarDataFromSystem(string localeName, CalendarId calendarId) { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(GlobalizationMode.UseNls); bool ret = true; @@ -106,17 +107,23 @@ private bool LoadCalendarDataFromSystem(string localeName, CalendarId calendarId } // Get native two digit year max - internal static int GetTwoDigitYearMax(CalendarId calendarId) => - GlobalizationMode.Invariant ? Invariant.iTwoDigitYearMax : - CallGetCalendarInfoEx(null, calendarId, CAL_ITWODIGITYEARMAX, out int twoDigitYearMax) ? twoDigitYearMax : - -1; + internal static int NlsGetTwoDigitYearMax(CalendarId calendarId) + { + Debug.Assert(GlobalizationMode.UseNls); + + return GlobalizationMode.Invariant ? Invariant.iTwoDigitYearMax : + CallGetCalendarInfoEx(null, calendarId, CAL_ITWODIGITYEARMAX, out int twoDigitYearMax) ? + twoDigitYearMax : + -1; + } // Call native side to figure out which calendars are allowed - internal static int GetCalendars(string localeName, bool useUserOverride, CalendarId[] calendars) + internal static int NlsGetCalendars(string localeName, bool useUserOverride, CalendarId[] calendars) { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(GlobalizationMode.UseNls); - EnumCalendarsData data = default; + NlsEnumCalendarsData data = default; data.userOverride = 0; data.calendars = new List(); @@ -147,9 +154,10 @@ internal static int GetCalendars(string localeName, bool useUserOverride, Calend return data.calendars.Count; } - private static bool SystemSupportsTaiwaneseCalendar() + private static bool NlsSystemSupportsTaiwaneseCalendar() { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(GlobalizationMode.UseNls); // Taiwanese calendar get listed as one of the optional zh-TW calendars only when having zh-TW UI return CallGetCalendarInfoEx("zh-TW", CalendarId.TAIWAN, CAL_SCALNAME, out string _); @@ -221,7 +229,7 @@ private static void CheckSpecialCalendar(ref CalendarId calendar, ref string loc // Taiwan calendar data is not always in all language version of OS due to Geopolical reasons. // It is only available in zh-TW localized versions of Windows. // Let's check if OS supports it. If not, fallback to Greogrian localized for Taiwan calendar. - if (!SystemSupportsTaiwaneseCalendar()) + if (!NlsSystemSupportsTaiwaneseCalendar()) { calendar = CalendarId.GREGORIAN; } @@ -410,7 +418,7 @@ private static bool GetCalendarMonthInfo(string localeName, CalendarId calendar, // // struct to help our calendar data enumaration callback // - private struct EnumCalendarsData + public struct NlsEnumCalendarsData { public int userOverride; // user override value (if found) public List calendars; // list of calendars found so far @@ -419,7 +427,7 @@ private struct EnumCalendarsData // [NativeCallable(CallingConvention = CallingConvention.StdCall)] private static unsafe Interop.BOOL EnumCalendarsCallback(char* lpCalendarInfoString, uint calendar, IntPtr reserved, void* lParam) { - ref EnumCalendarsData context = ref Unsafe.As(ref *(byte*)lParam); + ref NlsEnumCalendarsData context = ref Unsafe.As(ref *(byte*)lParam); try { // If we had a user override, check to make sure this differs diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.cs index 997cfb406e430..72d14b38d0cd2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.cs @@ -105,7 +105,11 @@ internal CalendarData(string localeName, CalendarId calendarId, bool bUseUserOve Debug.Assert(!GlobalizationMode.Invariant); - if (!LoadCalendarDataFromSystem(localeName, calendarId)) + bool loadedCalendarData = GlobalizationMode.UseNls ? + NlsLoadCalendarDataFromSystem(localeName, calendarId) : + IcuLoadCalendarDataFromSystem(localeName, calendarId); + + if (!loadedCalendarData) { // LoadCalendarDataFromSystem sometimes can fail on Linux if the installed ICU package is missing some resources. // The ICU package can miss some resources in some cases like if someone compile and build the ICU package manually or ICU has a regression. @@ -376,5 +380,9 @@ private static string CalendarIdToCultureName(CalendarId calendarId) return "en-US"; } + + private bool SystemSupportsTaiwaneseCalendar() => GlobalizationMode.UseNls ? + NlsSystemSupportsTaiwaneseCalendar() : + IcuSystemSupportsTaiwaneseCalendar(); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.Icu.cs similarity index 94% rename from src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.Unix.cs rename to src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.Icu.cs index d0db147a74607..0c2762550f80a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.Icu.cs @@ -11,22 +11,19 @@ namespace System.Globalization { public partial class CompareInfo { - [NonSerialized] - private IntPtr _sortHandle; - [NonSerialized] private bool _isAsciiEqualityOrdinal; - private void InitSort(CultureInfo culture) + private void IcuInitSortHandle() { - _sortName = culture.SortName; - if (GlobalizationMode.Invariant) { _isAsciiEqualityOrdinal = true; } else { + Debug.Assert(!GlobalizationMode.UseNls); + // Inline the following condition to avoid potential implementation cycles within globalization // // _isAsciiEqualityOrdinal = _sortName == "" || _sortName == "en" || _sortName.StartsWith("en-", StringComparison.Ordinal); @@ -38,9 +35,10 @@ private void InitSort(CultureInfo culture) } } - internal static unsafe int IndexOfOrdinalCore(ReadOnlySpan source, ReadOnlySpan value, bool ignoreCase, bool fromBeginning) + private static unsafe int IcuIndexOfOrdinalCore(ReadOnlySpan source, ReadOnlySpan value, bool ignoreCase, bool fromBeginning) { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(!GlobalizationMode.UseNls); Debug.Assert(!value.IsEmpty); // Ordinal (non-linguistic) comparisons require the length of the target string to be no greater @@ -99,9 +97,10 @@ internal static unsafe int IndexOfOrdinalCore(ReadOnlySpan source, ReadOnl return -1; } - internal static unsafe int LastIndexOfOrdinalCore(string source, string value, int startIndex, int count, bool ignoreCase) + private static unsafe int IcuLastIndexOfOrdinalCore(string source, string value, int startIndex, int count, bool ignoreCase) { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(!GlobalizationMode.UseNls); Debug.Assert(source != null); Debug.Assert(value != null); @@ -148,9 +147,10 @@ internal static unsafe int LastIndexOfOrdinalCore(string source, string value, i return -1; } - private static unsafe int CompareStringOrdinalIgnoreCase(ref char string1, int count1, ref char string2, int count2) + private static unsafe int IcuCompareStringOrdinalIgnoreCase(ref char string1, int count1, ref char string2, int count2) { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(!GlobalizationMode.UseNls); Debug.Assert(count1 > 0); Debug.Assert(count2 > 0); @@ -167,9 +167,10 @@ private static unsafe int CompareStringOrdinalIgnoreCase(ref char string1, int c // TODO https://github.com/dotnet/runtime/issues/8890: // This method shouldn't be necessary, as we should be able to just use the overload // that takes two spans. But due to this issue, that's adding significant overhead. - private unsafe int CompareString(ReadOnlySpan string1, string string2, CompareOptions options) + private unsafe int IcuCompareString(ReadOnlySpan string1, string string2, CompareOptions options) { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(!GlobalizationMode.UseNls); Debug.Assert(string2 != null); Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0); @@ -183,9 +184,10 @@ private unsafe int CompareString(ReadOnlySpan string1, string string2, Com } } - private unsafe int CompareString(ReadOnlySpan string1, ReadOnlySpan string2, CompareOptions options) + private unsafe int IcuCompareString(ReadOnlySpan string1, ReadOnlySpan string2, CompareOptions options) { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(!GlobalizationMode.UseNls); Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0); // Unlike NLS, ICU (ucol_getSortKey) allows passing nullptr for either of the source arguments @@ -198,9 +200,10 @@ private unsafe int CompareString(ReadOnlySpan string1, ReadOnlySpan } } - internal unsafe int IndexOfCore(string source, string target, int startIndex, int count, CompareOptions options, int* matchLengthPtr) + private unsafe int IcuIndexOfCore(string source, string target, int startIndex, int count, CompareOptions options, int* matchLengthPtr) { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(!GlobalizationMode.UseNls); Debug.Assert(!string.IsNullOrEmpty(source)); Debug.Assert(target != null); @@ -228,10 +231,10 @@ internal unsafe int IndexOfCore(string source, string target, int startIndex, in return index != -1 ? index + startIndex : -1; } - // For now, this method is only called from Span APIs with either options == CompareOptions.None or CompareOptions.IgnoreCase - internal unsafe int IndexOfCore(ReadOnlySpan source, ReadOnlySpan target, CompareOptions options, int* matchLengthPtr, bool fromBeginning) + private unsafe int IcuIndexOfCore(ReadOnlySpan source, ReadOnlySpan target, CompareOptions options, int* matchLengthPtr, bool fromBeginning) { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(!GlobalizationMode.UseNls); Debug.Assert(source.Length != 0); Debug.Assert(target.Length != 0); @@ -457,9 +460,10 @@ private unsafe int IndexOfOrdinalHelper(ReadOnlySpan source, ReadOnlySpan< } } - private unsafe int LastIndexOfCore(string source, string target, int startIndex, int count, CompareOptions options) + private unsafe int IcuLastIndexOfCore(string source, string target, int startIndex, int count, CompareOptions options) { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(!GlobalizationMode.UseNls); Debug.Assert(!string.IsNullOrEmpty(source)); Debug.Assert(target != null); @@ -475,7 +479,7 @@ private unsafe int LastIndexOfCore(string source, string target, int startIndex, if (options == CompareOptions.Ordinal) { - return LastIndexOfOrdinalCore(source, target, startIndex, count, ignoreCase: false); + return IcuLastIndexOfOrdinalCore(source, target, startIndex, count, ignoreCase: false); } // startIndex is the index into source where we start search backwards from. leftStartIndex is the index into source @@ -503,7 +507,7 @@ private unsafe int LastIndexOfCore(string source, string target, int startIndex, return lastIndex != -1 ? lastIndex + leftStartIndex : -1; } - private unsafe bool StartsWith(ReadOnlySpan source, ReadOnlySpan prefix, CompareOptions options) + private unsafe bool IcuStartsWith(ReadOnlySpan source, ReadOnlySpan prefix, CompareOptions options) { Debug.Assert(!GlobalizationMode.Invariant); @@ -656,9 +660,10 @@ private unsafe bool StartsWithOrdinalHelper(ReadOnlySpan source, ReadOnlyS } } - private unsafe bool EndsWith(ReadOnlySpan source, ReadOnlySpan suffix, CompareOptions options) + private unsafe bool IcuEndsWith(ReadOnlySpan source, ReadOnlySpan suffix, CompareOptions options) { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(!GlobalizationMode.UseNls); Debug.Assert(!suffix.IsEmpty); Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0); @@ -771,9 +776,10 @@ private unsafe bool EndsWithOrdinalHelper(ReadOnlySpan source, ReadOnlySpa } } - private unsafe SortKey CreateSortKey(string source, CompareOptions options) + private unsafe SortKey IcuCreateSortKey(string source, CompareOptions options) { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(!GlobalizationMode.UseNls); if (source==null) { throw new ArgumentNullException(nameof(source)); } @@ -800,9 +806,10 @@ private unsafe SortKey CreateSortKey(string source, CompareOptions options) return new SortKey(this, source, options, keyData); } - private static unsafe bool IsSortable(char *text, int length) + private static unsafe bool IcuIsSortable(char *text, int length) { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(!GlobalizationMode.UseNls); int index = 0; UnicodeCategory uc; @@ -843,9 +850,10 @@ private static unsafe bool IsSortable(char *text, int length) // ---- PAL layer ends here ---- // ----------------------------- - internal unsafe int GetHashCodeOfStringCore(ReadOnlySpan source, CompareOptions options) + private unsafe int IcuGetHashCodeOfString(ReadOnlySpan source, CompareOptions options) { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(!GlobalizationMode.UseNls); Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0); // according to ICU User Guide the performance of ucol_getSortKey is worse when it is called with null output buffer @@ -914,9 +922,10 @@ private static bool CanUseAsciiOrdinalForOptions(CompareOptions options) return (options & CompareOptions.IgnoreSymbols) == 0; } - private SortVersion GetSortVersion() + private SortVersion IcuGetSortVersion() { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(!GlobalizationMode.UseNls); int sortVersion = Interop.Globalization.GetSortVersion(_sortHandle); return new SortVersion(sortVersion, LCID, new Guid(sortVersion, 0, 0, 0, 0, 0, 0, diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.Nls.cs similarity index 90% rename from src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.Windows.cs rename to src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.Nls.cs index cbc7ac00209d2..cf0b5fd6c8a15 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.Nls.cs @@ -10,7 +10,13 @@ namespace System.Globalization { public partial class CompareInfo { - internal static unsafe IntPtr GetSortHandle(string cultureName) + private void NlsInitSortHandle() + { + Debug.Assert(GlobalizationMode.UseNls); + _sortHandle = NlsGetSortHandle(_sortName); + } + + internal static unsafe IntPtr NlsGetSortHandle(string cultureName) { if (GlobalizationMode.Invariant) { @@ -36,12 +42,6 @@ internal static unsafe IntPtr GetSortHandle(string cultureName) return IntPtr.Zero; } - private void InitSort(CultureInfo culture) - { - _sortName = culture.SortName; - _sortHandle = GetSortHandle(_sortName); - } - private static unsafe int FindStringOrdinal( uint dwFindStringOrdinalFlags, ReadOnlySpan source, @@ -75,9 +75,10 @@ private static unsafe int FindStringOrdinal( } } - internal static int IndexOfOrdinalCore(ReadOnlySpan source, ReadOnlySpan value, bool ignoreCase, bool fromBeginning) + private static int NlsIndexOfOrdinalCore(ReadOnlySpan source, ReadOnlySpan value, bool ignoreCase, bool fromBeginning) { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(GlobalizationMode.UseNls); Debug.Assert(source.Length != 0); Debug.Assert(value.Length != 0); @@ -86,9 +87,10 @@ internal static int IndexOfOrdinalCore(ReadOnlySpan source, ReadOnlySpan source, CompareOptions options) + private unsafe int NlsGetHashCodeOfString(ReadOnlySpan source, CompareOptions options) { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(GlobalizationMode.UseNls); Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0); // LCMapStringEx doesn't support passing cchSrc = 0, so if given a null or empty input @@ -166,9 +169,10 @@ private unsafe int GetHashCodeOfStringCore(ReadOnlySpan source, CompareOpt } } - private static unsafe int CompareStringOrdinalIgnoreCase(ref char string1, int count1, ref char string2, int count2) + private static unsafe int NlsCompareStringOrdinalIgnoreCase(ref char string1, int count1, ref char string2, int count2) { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(GlobalizationMode.UseNls); Debug.Assert(count1 > 0); Debug.Assert(count2 > 0); @@ -192,10 +196,11 @@ private static unsafe int CompareStringOrdinalIgnoreCase(ref char string1, int c // TODO https://github.com/dotnet/runtime/issues/8890: // This method shouldn't be necessary, as we should be able to just use the overload // that takes two spans. But due to this issue, that's adding significant overhead. - private unsafe int CompareString(ReadOnlySpan string1, string string2, CompareOptions options) + private unsafe int NlsCompareString(ReadOnlySpan string1, string string2, CompareOptions options) { Debug.Assert(string2 != null); Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(GlobalizationMode.UseNls); Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0); string? localeName = _sortHandle != IntPtr.Zero ? null : _sortName; @@ -237,9 +242,10 @@ private unsafe int CompareString(ReadOnlySpan string1, string string2, Com } } - private unsafe int CompareString(ReadOnlySpan string1, ReadOnlySpan string2, CompareOptions options) + private unsafe int NlsCompareString(ReadOnlySpan string1, ReadOnlySpan string2, CompareOptions options) { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(GlobalizationMode.UseNls); Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0); string? localeName = _sortHandle != IntPtr.Zero ? null : _sortName; @@ -337,7 +343,7 @@ private unsafe int FindString( } } - internal unsafe int IndexOfCore(string source, string target, int startIndex, int count, CompareOptions options, int* matchLengthPtr) + private unsafe int NlsIndexOfCore(string source, string target, int startIndex, int count, CompareOptions options, int* matchLengthPtr) { Debug.Assert(!GlobalizationMode.Invariant); @@ -354,9 +360,10 @@ internal unsafe int IndexOfCore(string source, string target, int startIndex, in return -1; } - internal unsafe int IndexOfCore(ReadOnlySpan source, ReadOnlySpan target, CompareOptions options, int* matchLengthPtr, bool fromBeginning) + private unsafe int NlsIndexOfCore(ReadOnlySpan source, ReadOnlySpan target, CompareOptions options, int* matchLengthPtr, bool fromBeginning) { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(GlobalizationMode.UseNls); Debug.Assert(target.Length != 0); Debug.Assert(options == CompareOptions.None || options == CompareOptions.IgnoreCase); @@ -365,9 +372,10 @@ internal unsafe int IndexOfCore(ReadOnlySpan source, ReadOnlySpan ta return FindString(positionFlag | (uint)GetNativeCompareFlags(options), source, target, matchLengthPtr); } - private unsafe int LastIndexOfCore(string source, string target, int startIndex, int count, CompareOptions options) + private unsafe int NlsLastIndexOfCore(string source, string target, int startIndex, int count, CompareOptions options) { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(GlobalizationMode.UseNls); Debug.Assert(!string.IsNullOrEmpty(source)); Debug.Assert(target != null); @@ -396,7 +404,7 @@ private unsafe int LastIndexOfCore(string source, string target, int startIndex, return -1; } - private unsafe bool StartsWith(ReadOnlySpan source, ReadOnlySpan prefix, CompareOptions options) + private unsafe bool NlsStartsWith(ReadOnlySpan source, ReadOnlySpan prefix, CompareOptions options) { Debug.Assert(!GlobalizationMode.Invariant); @@ -407,9 +415,10 @@ private unsafe bool StartsWith(ReadOnlySpan source, ReadOnlySpan pre return FindString(FIND_STARTSWITH | (uint)GetNativeCompareFlags(options), source, prefix, null) >= 0; } - private unsafe bool EndsWith(ReadOnlySpan source, ReadOnlySpan suffix, CompareOptions options) + private unsafe bool NlsEndsWith(ReadOnlySpan source, ReadOnlySpan suffix, CompareOptions options) { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(GlobalizationMode.UseNls); Debug.Assert(!suffix.IsEmpty); Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0); @@ -417,10 +426,6 @@ private unsafe bool EndsWith(ReadOnlySpan source, ReadOnlySpan suffi return FindString(FIND_ENDSWITH | (uint)GetNativeCompareFlags(options), source, suffix, null) >= 0; } - // PAL ends here - [NonSerialized] - private IntPtr _sortHandle; - private const uint LCMAP_SORTKEY = 0x00000400; private const int FIND_STARTSWITH = 0x00100000; @@ -472,9 +477,10 @@ private static unsafe int FastLastIndexOfString(string source, string target, in return retValue; } - private unsafe SortKey CreateSortKey(string source, CompareOptions options) + private unsafe SortKey NlsCreateSortKey(string source, CompareOptions options) { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(GlobalizationMode.UseNls); if (source == null) { throw new ArgumentNullException(nameof(source)); } @@ -525,9 +531,10 @@ private unsafe SortKey CreateSortKey(string source, CompareOptions options) return new SortKey(this, source, options, keyData); } - private static unsafe bool IsSortable(char* text, int length) + private static unsafe bool NlsIsSortable(char* text, int length) { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(GlobalizationMode.UseNls); Debug.Assert(text != null); return Interop.Kernel32.IsNLSDefinedString(Interop.Kernel32.COMPARE_STRING, 0, IntPtr.Zero, text, length); @@ -571,9 +578,10 @@ private static int GetNativeCompareFlags(CompareOptions options) return nativeCompareFlags; } - private unsafe SortVersion GetSortVersion() + private unsafe SortVersion NlsGetSortVersion() { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(GlobalizationMode.UseNls); Interop.Kernel32.NlsVersionInfoEx nlsVersion = default; nlsVersion.dwNLSVersionInfoSize = sizeof(Interop.Kernel32.NlsVersionInfoEx); diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.cs index 57c6f81afeb2c..83b475c8039cf 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.cs @@ -39,6 +39,9 @@ public sealed partial class CompareInfo : IDeserializationCallback [OptionalField(VersionAdded = 2)] private string m_name; // The name used to construct this CompareInfo. Do not rename (binary serialization) + [NonSerialized] + private IntPtr _sortHandle; + [NonSerialized] private string _sortName = null!; // The name that defines our behavior @@ -131,7 +134,7 @@ public static unsafe bool IsSortable(char ch) } char* pChar = &ch; - return IsSortable(pChar, 1); + return IsSortableCore(pChar, 1); } public static unsafe bool IsSortable(string text) @@ -153,7 +156,26 @@ public static unsafe bool IsSortable(string text) fixed (char* pChar = text) { - return IsSortable(pChar, text.Length); + return IsSortableCore(pChar, text.Length); + } + } + + private static unsafe bool IsSortableCore(char* pChar, int length) => + GlobalizationMode.UseNls ? + NlsIsSortable(pChar, length) : + IcuIsSortable(pChar, length); + + private void InitSort(CultureInfo culture) + { + _sortName = culture.SortName; + + if (GlobalizationMode.UseNls) + { + NlsInitSortHandle(); + } + else + { + IcuInitSortHandle(); } } @@ -281,7 +303,7 @@ public int Compare(string? string1, string? string2, CompareOptions options) return string.CompareOrdinal(string1, string2); } - return CompareString(string1.AsSpan(), string2.AsSpan(), options); + return CompareStringCore(string1.AsSpan(), string2.AsSpan(), options); } // TODO https://github.com/dotnet/runtime/issues/8890: @@ -323,7 +345,7 @@ internal int Compare(ReadOnlySpan string1, string? string2, CompareOptions string.CompareOrdinal(string1, string2.AsSpan()); } - return CompareString(string1, string2, options); + return CompareStringCore(string1, string2, options); } internal int CompareOptionNone(ReadOnlySpan string1, ReadOnlySpan string2) @@ -336,7 +358,7 @@ internal int CompareOptionNone(ReadOnlySpan string1, ReadOnlySpan st return GlobalizationMode.Invariant ? string.CompareOrdinal(string1, string2) : - CompareString(string1, string2, CompareOptions.None); + CompareStringCore(string1, string2, CompareOptions.None); } internal int CompareOptionIgnoreCase(ReadOnlySpan string1, ReadOnlySpan string2) @@ -349,7 +371,7 @@ internal int CompareOptionIgnoreCase(ReadOnlySpan string1, ReadOnlySpan @@ -448,9 +470,22 @@ public int Compare(string? string1, int offset1, int length1, string? string2, i return string.CompareOrdinal(span1, span2); } - return CompareString(span1, span2, options); + return CompareStringCore(span1, span2, options); } + // TODO https://github.com/dotnet/runtime/issues/8890: + // This method shouldn't be necessary, as we should be able to just use the overload + // that takes two spans. But due to this issue, that's adding significant overhead. + private unsafe int CompareStringCore(ReadOnlySpan string1, string string2, CompareOptions options) => + GlobalizationMode.UseNls ? + NlsCompareString(string1, string2, options) : + IcuCompareString(string1, string2, options); + + private unsafe int CompareStringCore(ReadOnlySpan string1, ReadOnlySpan string2, CompareOptions options) => + GlobalizationMode.UseNls ? + NlsCompareString(string1, string2, options) : + IcuCompareString(string1, string2, options); + /// /// CompareOrdinalIgnoreCase compare two string ordinally with ignoring the case. /// it assumes the strings are Ascii string till we hit non Ascii character in strA or strB and then we continue the comparison by @@ -528,7 +563,7 @@ internal static int CompareOrdinalIgnoreCase(ref char strA, int lengthA, ref cha range -= length; - return CompareStringOrdinalIgnoreCase(ref charA, lengthA - range, ref charB, lengthB - range); + return CompareStringOrdinalIgnoreCaseCore(ref charA, lengthA - range, ref charB, lengthB - range); } internal static bool EqualsOrdinalIgnoreCase(ref char charA, ref char charB, int length) @@ -638,7 +673,7 @@ private static bool EqualsOrdinalIgnoreCaseNonAscii(ref char charA, ref char cha { if (!GlobalizationMode.Invariant) { - return CompareStringOrdinalIgnoreCase(ref charA, length, ref charB, length) == 0; + return CompareStringOrdinalIgnoreCaseCore(ref charA, length, ref charB, length) == 0; } else { @@ -670,6 +705,11 @@ private static bool EqualsOrdinalIgnoreCaseNonAscii(ref char charA, ref char cha } } + private static unsafe int CompareStringOrdinalIgnoreCaseCore(ref char string1, int count1, ref char string2, int count2) => + GlobalizationMode.UseNls ? + NlsCompareStringOrdinalIgnoreCase(ref string1, count1, ref string2, count2) : + IcuCompareStringOrdinalIgnoreCase(ref string1, count1, ref string2, count2); + /// /// Determines whether prefix is a prefix of string. If prefix equals /// string.Empty, true is returned. @@ -714,7 +754,7 @@ public bool IsPrefix(string source, string prefix, CompareOptions options) return source.StartsWith(prefix, (options & CompareOptions.IgnoreCase) != 0 ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal); } - return StartsWith(source, prefix, options); + return StartsWithCore(source, prefix, options); } internal bool IsPrefix(ReadOnlySpan source, ReadOnlySpan prefix, CompareOptions options) @@ -725,9 +765,14 @@ internal bool IsPrefix(ReadOnlySpan source, ReadOnlySpan prefix, Com Debug.Assert(!GlobalizationMode.Invariant); Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0); - return StartsWith(source, prefix, options); + return StartsWithCore(source, prefix, options); } + private unsafe bool StartsWithCore(ReadOnlySpan source, ReadOnlySpan prefix, CompareOptions options) => + GlobalizationMode.UseNls ? + NlsStartsWith(source, prefix, options) : + IcuStartsWith(source, prefix, options); + public bool IsPrefix(string source, string prefix) { return IsPrefix(source, prefix, 0); @@ -777,7 +822,7 @@ public bool IsSuffix(string source, string suffix, CompareOptions options) return source.EndsWith(suffix, (options & CompareOptions.IgnoreCase) != 0 ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal); } - return EndsWith(source, suffix, options); + return EndsWithCore(source, suffix, options); } internal bool IsSuffix(ReadOnlySpan source, ReadOnlySpan suffix, CompareOptions options) @@ -788,7 +833,7 @@ internal bool IsSuffix(ReadOnlySpan source, ReadOnlySpan suffix, Com Debug.Assert(!GlobalizationMode.Invariant); Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0); - return EndsWith(source, suffix, options); + return EndsWithCore(source, suffix, options); } public bool IsSuffix(string source, string suffix) @@ -796,6 +841,11 @@ public bool IsSuffix(string source, string suffix) return IsSuffix(source, suffix, 0); } + private unsafe bool EndsWithCore(ReadOnlySpan source, ReadOnlySpan suffix, CompareOptions options) => + GlobalizationMode.UseNls ? + NlsEndsWith(source, suffix, options) : + IcuEndsWith(source, suffix, options); + /// /// Returns the first index where value is found in string. The /// search starts from startIndex and ends at endIndex. Returns -1 if @@ -983,6 +1033,11 @@ internal int LastIndexOfOrdinal(ReadOnlySpan source, ReadOnlySpan va return IndexOfOrdinalCore(source, value, ignoreCase, fromBeginning: false); } + private static int IndexOfOrdinalCore(ReadOnlySpan source, ReadOnlySpan value, bool ignoreCase, bool fromBeginning) => + GlobalizationMode.UseNls ? + NlsIndexOfOrdinalCore(source, value, ignoreCase, fromBeginning) : + IcuIndexOfOrdinalCore(source, value, ignoreCase, fromBeginning); + internal unsafe int IndexOf(ReadOnlySpan source, ReadOnlySpan value, CompareOptions options) { Debug.Assert(!GlobalizationMode.Invariant); @@ -1110,6 +1165,16 @@ ref value.GetRawStringData(), } } + private unsafe int IndexOfCore(string source, string target, int startIndex, int count, CompareOptions options, int* matchLengthPtr) => + GlobalizationMode.UseNls ? + NlsIndexOfCore(source, target, startIndex, count, options, matchLengthPtr) : + IcuIndexOfCore(source, target, startIndex, count, options, matchLengthPtr); + + private unsafe int IndexOfCore(ReadOnlySpan source, ReadOnlySpan target, CompareOptions options, int* matchLengthPtr, bool fromBeginning) => + GlobalizationMode.UseNls ? + NlsIndexOfCore(source, target, options, matchLengthPtr, fromBeginning) : + IcuIndexOfCore(source, target, options, matchLengthPtr, fromBeginning); + internal static int IndexOfOrdinal(string source, string value, int startIndex, int count, bool ignoreCase) { Debug.Assert(source != null); @@ -1360,7 +1425,12 @@ public int LastIndexOf(string source, string value, int startIndex, int count, C return LastIndexOfCore(source, value, startIndex, count, options); } - private static int LastIndexOfOrdinal(string source, string value, int startIndex, int count, bool ignoreCase) + private int LastIndexOfCore(string source, string target, int startIndex, int count, CompareOptions options) => + GlobalizationMode.UseNls ? + NlsLastIndexOfCore(source, target, startIndex, count, options) : + IcuLastIndexOfCore(source, target, startIndex, count, options); + + internal static int LastIndexOfOrdinal(string source, string value, int startIndex, int count, bool ignoreCase) { Debug.Assert(!string.IsNullOrEmpty(source)); Debug.Assert(value != null); @@ -1385,7 +1455,9 @@ private static int LastIndexOfOrdinal(string source, string value, int startInde return -1; } - return LastIndexOfOrdinalCore(source, value, startIndex, count, ignoreCase); + return GlobalizationMode.UseNls ? + NlsLastIndexOfOrdinalCore(source, value, startIndex, count, ignoreCase) : + IcuLastIndexOfOrdinalCore(source, value, startIndex, count, ignoreCase); } /// @@ -1398,7 +1470,7 @@ public SortKey GetSortKey(string source, CompareOptions options) return InvariantCreateSortKey(source, options); } - return CreateSortKey(source, options); + return CreateSortKeyCore(source, options); } public SortKey GetSortKey(string source) @@ -1408,9 +1480,14 @@ public SortKey GetSortKey(string source) return InvariantCreateSortKey(source, CompareOptions.None); } - return CreateSortKey(source, CompareOptions.None); + return CreateSortKeyCore(source, CompareOptions.None); } + private SortKey CreateSortKeyCore(string source, CompareOptions options) => + GlobalizationMode.UseNls ? + NlsCreateSortKey(source, options) : + IcuCreateSortKey(source, options); + public override bool Equals(object? value) { return value is CompareInfo otherCompareInfo @@ -1490,6 +1567,11 @@ public int GetHashCode(ReadOnlySpan source, CompareOptions options) } } + private unsafe int GetHashCodeOfStringCore(ReadOnlySpan source, CompareOptions options) => + GlobalizationMode.UseNls ? + NlsGetHashCodeOfString(source, options) : + IcuGetHashCodeOfString(source, options); + public override string ToString() => "CompareInfo - " + Name; public SortVersion Version @@ -1508,7 +1590,7 @@ public SortVersion Version } else { - m_SortVersion = GetSortVersion(); + m_SortVersion = GlobalizationMode.UseNls ? NlsGetSortVersion() : IcuGetSortVersion(); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs similarity index 70% rename from src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Unix.cs rename to src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs index 67ad489e47adf..452a75fb16e25 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs @@ -18,11 +18,12 @@ internal partial class CultureData /// 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 unsafe bool InitCultureData() + private unsafe bool IcuInitCultureData() { Debug.Assert(_sRealName != null); Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(!GlobalizationMode.UseNls); string realNameBuffer = _sRealName; @@ -73,7 +74,7 @@ private unsafe bool InitCultureData() _bNeutral = TwoLetterISOCountryName.Length == 0; - _sSpecificCulture = _bNeutral ? LocaleData.GetSpecificCultureName(_sRealName) : _sRealName; + _sSpecificCulture = _bNeutral ? IcuLocaleData.GetSpecificCultureName(_sRealName) : _sRealName; // Remove the sort from sName unless custom culture if (index > 0 && !_bNeutral && !IsCustomCultureId(_iLanguage)) @@ -113,26 +114,28 @@ internal static unsafe bool GetDefaultLocaleName(out string? windowsName) return true; } - private string GetLocaleInfo(LocaleStringData type) + private string IcuGetLocaleInfo(LocaleStringData type) { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(!GlobalizationMode.UseNls); - Debug.Assert(_sWindowsName != null, "[CultureData.GetLocaleInfo] Expected _sWindowsName to be populated already"); - return GetLocaleInfo(_sWindowsName, type); + Debug.Assert(_sWindowsName != null, "[CultureData.IcuGetLocaleInfo] Expected _sWindowsName to be populated already"); + return IcuGetLocaleInfo(_sWindowsName, 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 unsafe string GetLocaleInfo(string localeName, LocaleStringData type) + private unsafe string IcuGetLocaleInfo(string localeName, LocaleStringData type) { - Debug.Assert(localeName != null, "[CultureData.GetLocaleInfo] Expected localeName to be not be null"); + Debug.Assert(!GlobalizationMode.UseNls); + Debug.Assert(localeName != null, "[CultureData.IcuGetLocaleInfo] Expected localeName to be not be null"); switch (type) { case LocaleStringData.NegativeInfinitySymbol: // not an equivalent in ICU; prefix the PositiveInfinitySymbol with NegativeSign - return GetLocaleInfo(localeName, LocaleStringData.NegativeSign) + - GetLocaleInfo(localeName, LocaleStringData.PositiveInfinitySymbol); + return IcuGetLocaleInfo(localeName, LocaleStringData.NegativeSign) + + IcuGetLocaleInfo(localeName, LocaleStringData.PositiveInfinitySymbol); } char* buffer = stackalloc char[ICU_ULOC_KEYWORD_AND_VALUES_CAPACITY]; @@ -140,18 +143,18 @@ private unsafe string GetLocaleInfo(string localeName, LocaleStringData type) if (!result) { // Failed, just use empty string - Debug.Fail("[CultureData.GetLocaleInfo(LocaleStringData)] Failed"); + Debug.Fail("[CultureData.IcuGetLocaleInfo(LocaleStringData)] Failed"); return string.Empty; } return new string(buffer); } - private int GetLocaleInfo(LocaleNumberData type) + private int IcuGetLocaleInfo(LocaleNumberData type) { - Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(!GlobalizationMode.UseNls); - Debug.Assert(_sWindowsName != null, "[CultureData.GetLocaleInfo(LocaleNumberData)] Expected _sWindowsName to be populated already"); + Debug.Assert(_sWindowsName != null, "[CultureData.IcuGetLocaleInfo(LocaleNumberData)] Expected _sWindowsName to be populated already"); switch (type) { @@ -166,22 +169,23 @@ private int GetLocaleInfo(LocaleNumberData type) if (!result) { // Failed, just use 0 - Debug.Fail("[CultureData.GetLocaleInfo(LocaleNumberData)] failed"); + Debug.Fail("[CultureData.IcuGetLocaleInfo(LocaleNumberData)] failed"); } return value; } - private int[] GetLocaleInfo(LocaleGroupingData type) + private int[] IcuGetLocaleInfo(LocaleGroupingData type) { - Debug.Assert(_sWindowsName != null, "[CultureData.GetLocaleInfo(LocaleGroupingData)] Expected _sWindowsName to be populated already"); + Debug.Assert(!GlobalizationMode.UseNls); + Debug.Assert(_sWindowsName != null, "[CultureData.IcuGetLocaleInfo(LocaleGroupingData)] Expected _sWindowsName to be populated already"); int primaryGroupingSize = 0; int secondaryGroupingSize = 0; bool result = Interop.Globalization.GetLocaleInfoGroupingSizes(_sWindowsName, (uint)type, ref primaryGroupingSize, ref secondaryGroupingSize); if (!result) { - Debug.Fail("[CultureData.GetLocaleInfo(LocaleGroupingData type)] failed"); + Debug.Fail("[CultureData.IcuGetLocaleInfo(LocaleGroupingData type)] failed"); } if (secondaryGroupingSize == 0) @@ -192,10 +196,11 @@ private int[] GetLocaleInfo(LocaleGroupingData type) return new int[] { primaryGroupingSize, secondaryGroupingSize }; } - private string GetTimeFormatString() => GetTimeFormatString(shortFormat: false); + private string IcuGetTimeFormatString() => IcuGetTimeFormatString(shortFormat: false); - private unsafe string GetTimeFormatString(bool shortFormat) + private unsafe string IcuGetTimeFormatString(bool shortFormat) { + Debug.Assert(!GlobalizationMode.UseNls); Debug.Assert(_sWindowsName != null, "[CultureData.GetTimeFormatString(bool shortFormat)] Expected _sWindowsName to be populated already"); char* buffer = stackalloc char[ICU_ULOC_KEYWORD_AND_VALUES_CAPACITY]; @@ -212,32 +217,32 @@ private unsafe string GetTimeFormatString(bool shortFormat) return ConvertIcuTimeFormatString(span.Slice(0, span.IndexOf('\0'))); } - private int GetFirstDayOfWeek() => GetLocaleInfo(LocaleNumberData.FirstDayOfWeek); + private int IcuGetFirstDayOfWeek() => IcuGetLocaleInfo(LocaleNumberData.FirstDayOfWeek); - private string[] GetTimeFormats() + private string[] IcuGetTimeFormats() { - string format = GetTimeFormatString(false); + string format = IcuGetTimeFormatString(false); return new string[] { format }; } - private string[] GetShortTimeFormats() + private string[] IcuGetShortTimeFormats() { - string format = GetTimeFormatString(true); + string format = IcuGetTimeFormatString(true); return new string[] { format }; } - private static CultureData? GetCultureDataFromRegionName(string? regionName) + private static CultureData? IcuGetCultureDataFromRegionName(string? regionName) { // no support to lookup by region name, other than the hard-coded list in CultureData return null; } - private static string GetLanguageDisplayName(string cultureName) + private static string IcuGetLanguageDisplayName(string cultureName) { - return new CultureInfo(cultureName)._cultureData.GetLocaleInfo(cultureName, LocaleStringData.LocalizedDisplayName); + return new CultureInfo(cultureName)._cultureData.IcuGetLocaleInfo(cultureName, LocaleStringData.LocalizedDisplayName); } - private static string? GetRegionDisplayName() + private static string? IcuGetRegionDisplayName() { // use the fallback which is to return NativeName return null; @@ -299,65 +304,75 @@ private static string ConvertIcuTimeFormatString(ReadOnlySpan icuFormatStr return result.Slice(0, resultPos).ToString(); } - private static string? LCIDToLocaleName(int culture) + private static string? IcuLCIDToLocaleName(int culture) { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(!GlobalizationMode.UseNls); - return LocaleData.LCIDToLocaleName(culture); + return IcuLocaleData.LCIDToLocaleName(culture); } - private static int LocaleNameToLCID(string cultureName) + private static int IcuLocaleNameToLCID(string cultureName) { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(!GlobalizationMode.UseNls); - int lcid = LocaleData.GetLocaleDataNumericPart(cultureName, LocaleDataParts.Lcid); + int lcid = IcuLocaleData.GetLocaleDataNumericPart(cultureName, IcuLocaleDataParts.Lcid); return lcid == -1 ? CultureInfo.LOCALE_CUSTOM_UNSPECIFIED : lcid; } - private static int GetAnsiCodePage(string cultureName) + private static int IcuGetAnsiCodePage(string cultureName) { - int ansiCodePage = LocaleData.GetLocaleDataNumericPart(cultureName, LocaleDataParts.AnsiCodePage); + Debug.Assert(!GlobalizationMode.UseNls); + int ansiCodePage = IcuLocaleData.GetLocaleDataNumericPart(cultureName, IcuLocaleDataParts.AnsiCodePage); return ansiCodePage == -1 ? CultureData.Invariant.ANSICodePage : ansiCodePage; } - private static int GetOemCodePage(string cultureName) + private static int IcuGetOemCodePage(string cultureName) { - int oemCodePage = LocaleData.GetLocaleDataNumericPart(cultureName, LocaleDataParts.OemCodePage); + Debug.Assert(!GlobalizationMode.UseNls); + int oemCodePage = IcuLocaleData.GetLocaleDataNumericPart(cultureName, IcuLocaleDataParts.OemCodePage); return oemCodePage == -1 ? CultureData.Invariant.OEMCodePage : oemCodePage; } - private static int GetMacCodePage(string cultureName) + private static int IcuGetMacCodePage(string cultureName) { - int macCodePage = LocaleData.GetLocaleDataNumericPart(cultureName, LocaleDataParts.MacCodePage); + Debug.Assert(!GlobalizationMode.UseNls); + int macCodePage = IcuLocaleData.GetLocaleDataNumericPart(cultureName, IcuLocaleDataParts.MacCodePage); return macCodePage == -1 ? CultureData.Invariant.MacCodePage : macCodePage; } - private static int GetEbcdicCodePage(string cultureName) + private static int IcuGetEbcdicCodePage(string cultureName) { - int ebcdicCodePage = LocaleData.GetLocaleDataNumericPart(cultureName, LocaleDataParts.EbcdicCodePage); + Debug.Assert(!GlobalizationMode.UseNls); + int ebcdicCodePage = IcuLocaleData.GetLocaleDataNumericPart(cultureName, IcuLocaleDataParts.EbcdicCodePage); return ebcdicCodePage == -1 ? CultureData.Invariant.EBCDICCodePage : ebcdicCodePage; } - private static int GetGeoId(string cultureName) + private static int IcuGetGeoId(string cultureName) { - int geoId = LocaleData.GetLocaleDataNumericPart(cultureName, LocaleDataParts.GeoId); + Debug.Assert(!GlobalizationMode.UseNls); + int geoId = IcuLocaleData.GetLocaleDataNumericPart(cultureName, IcuLocaleDataParts.GeoId); return geoId == -1 ? CultureData.Invariant.GeoId : geoId; } - private static int GetDigitSubstitution(string cultureName) + private static int IcuGetDigitSubstitution(string cultureName) { - int digitSubstitution = LocaleData.GetLocaleDataNumericPart(cultureName, LocaleDataParts.DigitSubstitution); + Debug.Assert(!GlobalizationMode.UseNls); + int digitSubstitution = IcuLocaleData.GetLocaleDataNumericPart(cultureName, IcuLocaleDataParts.DigitSubstitution); return digitSubstitution == -1 ? (int) DigitShapes.None : digitSubstitution; } - private static string GetThreeLetterWindowsLanguageName(string cultureName) + private static string IcuGetThreeLetterWindowsLanguageName(string cultureName) { - return LocaleData.GetThreeLetterWindowsLanguageName(cultureName) ?? "ZZZ" /* default lang name */; + Debug.Assert(!GlobalizationMode.UseNls); + return IcuLocaleData.GetThreeLetterWindowsLanguageName(cultureName) ?? "ZZZ" /* default lang name */; } - private static CultureInfo[] EnumCultures(CultureTypes types) + private static CultureInfo[] IcuEnumCultures(CultureTypes types) { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(!GlobalizationMode.UseNls); if ((types & (CultureTypes.NeutralCultures | CultureTypes.SpecificCultures)) == 0) { @@ -406,15 +421,10 @@ private static CultureInfo[] EnumCultures(CultureTypes types) return list.ToArray(); } - private static string GetConsoleFallbackName(string cultureName) + private static string IcuGetConsoleFallbackName(string cultureName) { - return LocaleData.GetConsoleUICulture(cultureName); + Debug.Assert(!GlobalizationMode.UseNls); + return IcuLocaleData.GetConsoleUICulture(cultureName); } - - internal bool IsWin32Installed => false; - - internal bool IsReplacementCulture => false; - - internal static CultureData GetCurrentRegionData() => CultureInfo.CurrentCulture._cultureData; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Nls.cs similarity index 88% rename from src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Windows.cs rename to src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Nls.cs index 48e4552c520ef..49a2b1d0615e0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Nls.cs @@ -46,9 +46,10 @@ internal partial class CultureData /// For a neutral we just populate the neutral name, but we leave the windows name pointing to the /// windows locale that's going to provide data for us. /// - private unsafe bool InitCultureData() + private unsafe bool NlsInitCultureData() { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(GlobalizationMode.UseNls); int result; string realNameBuffer = _sRealName; @@ -179,23 +180,26 @@ internal static unsafe int GetLocaleInfoEx(string lpLocaleName, uint lcType, cha return Interop.Kernel32.GetLocaleInfoEx(lpLocaleName, lcType, lpLCData, cchData); } - private string GetLocaleInfo(LocaleStringData type) + private string NlsGetLocaleInfo(LocaleStringData type) { + Debug.Assert(GlobalizationMode.UseNls); Debug.Assert(_sWindowsName != null, "[CultureData.DoGetLocaleInfo] Expected _sWindowsName to be populated by already"); - return GetLocaleInfo(_sWindowsName, type); + return NlsGetLocaleInfo(_sWindowsName, 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 string GetLocaleInfo(string localeName, LocaleStringData type) + private string NlsGetLocaleInfo(string localeName, LocaleStringData type) { + Debug.Assert(GlobalizationMode.UseNls); uint lctype = (uint)type; return GetLocaleInfoFromLCType(localeName, lctype, UseUserOverride); } - private int GetLocaleInfo(LocaleNumberData type) + private int NlsGetLocaleInfo(LocaleNumberData type) { + Debug.Assert(GlobalizationMode.UseNls); uint lctype = (uint)type; // Fix lctype if we don't want overrides @@ -210,20 +214,23 @@ private int GetLocaleInfo(LocaleNumberData type) return GetLocaleInfoExInt(_sWindowsName, lctype); } - private int[] GetLocaleInfo(LocaleGroupingData type) + private int[] NlsGetLocaleInfo(LocaleGroupingData type) { + Debug.Assert(GlobalizationMode.UseNls); Debug.Assert(_sWindowsName != null, "[CultureData.DoGetLocaleInfoInt] Expected _sWindowsName to be populated by already"); return ConvertWin32GroupString(GetLocaleInfoFromLCType(_sWindowsName, (uint)type, UseUserOverride)); } - private string? GetTimeFormatString() + private string? NlsGetTimeFormatString() { + Debug.Assert(GlobalizationMode.UseNls); Debug.Assert(_sWindowsName != null, "[CultureData.DoGetLocaleInfoInt] Expected _sWindowsName to be populated by already"); return ReescapeWin32String(GetLocaleInfoFromLCType(_sWindowsName, Interop.Kernel32.LOCALE_STIMEFORMAT, UseUserOverride)); } - private int GetFirstDayOfWeek() + private int NlsGetFirstDayOfWeek() { + Debug.Assert(GlobalizationMode.UseNls); Debug.Assert(_sWindowsName != null, "[CultureData.DoGetLocaleInfoInt] Expected _sWindowsName to be populated by already"); int result = GetLocaleInfoExInt(_sWindowsName, Interop.Kernel32.LOCALE_IFIRSTDAYOFWEEK | (!UseUserOverride ? Interop.Kernel32.LOCALE_NOUSEROVERRIDE : 0)); @@ -232,18 +239,20 @@ private int GetFirstDayOfWeek() return ConvertFirstDayOfWeekMonToSun(result); } - private string[]? GetTimeFormats() + private string[]? NlsGetTimeFormats() { // Note that this gets overrides for us all the time + Debug.Assert(GlobalizationMode.UseNls); Debug.Assert(_sWindowsName != null, "[CultureData.DoEnumTimeFormats] Expected _sWindowsName to be populated by already"); string[]? result = ReescapeWin32Strings(nativeEnumTimeFormats(_sWindowsName, 0, UseUserOverride)); return result; } - private string[]? GetShortTimeFormats() + private string[]? NlsGetShortTimeFormats() { // Note that this gets overrides for us all the time + Debug.Assert(GlobalizationMode.UseNls); Debug.Assert(_sWindowsName != null, "[CultureData.DoEnumShortTimeFormats] Expected _sWindowsName to be populated by already"); string[]? result = ReescapeWin32Strings(nativeEnumTimeFormats(_sWindowsName, Interop.Kernel32.TIME_NOSECONDS, UseUserOverride)); @@ -252,9 +261,10 @@ private int GetFirstDayOfWeek() // Enumerate all system cultures and then try to find out which culture has // region name match the requested region name - private static CultureData? GetCultureDataFromRegionName(string regionName) + private static CultureData? NlsGetCultureDataFromRegionName(string regionName) { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(GlobalizationMode.UseNls); Debug.Assert(regionName != null); EnumLocaleData context; @@ -275,31 +285,35 @@ private int GetFirstDayOfWeek() return null; } - private string GetLanguageDisplayName(string cultureName) + private string NlsGetLanguageDisplayName(string cultureName) { + Debug.Assert(GlobalizationMode.UseNls); + // Usually the UI culture shouldn't be different than what we got from WinRT except // if DefaultThreadCurrentUICulture was set CultureInfo? ci; if (CultureInfo.DefaultThreadCurrentUICulture != null && - ((ci = CultureInfo.GetUserDefaultCulture()) != null) && + ((ci = CultureInfo.NlsGetUserDefaultCulture()) != null) && !CultureInfo.DefaultThreadCurrentUICulture.Name.Equals(ci.Name)) { return NativeName; } else { - return GetLocaleInfo(cultureName, LocaleStringData.LocalizedDisplayName); + return NlsGetLocaleInfo(cultureName, LocaleStringData.LocalizedDisplayName); } } - private string GetRegionDisplayName() + private string NlsGetRegionDisplayName() { + Debug.Assert(GlobalizationMode.UseNls); + // If the current UI culture matching the OS UI language, we'll get the display name from the OS. // otherwise, we use the native name as we don't carry resources for the region display names anyway. if (CultureInfo.CurrentUICulture.Name.Equals(CultureInfo.UserDefaultUICulture.Name)) { - return GetLocaleInfo(LocaleStringData.LocalizedCountryName); + return NlsGetLocaleInfo(LocaleStringData.LocalizedCountryName); } return NativeCountryName; @@ -580,16 +594,17 @@ private static unsafe Interop.BOOL EnumTimeCallback(char* lpTimeFormatString, vo return null; } - private static int LocaleNameToLCID(string cultureName) + private static int NlsLocaleNameToLCID(string cultureName) { Debug.Assert(!GlobalizationMode.Invariant); return Interop.Kernel32.LocaleNameToLCID(cultureName, Interop.Kernel32.LOCALE_ALLOW_NEUTRAL_NAMES); } - private static unsafe string? LCIDToLocaleName(int culture) + private static unsafe string? NlsLCIDToLocaleName(int culture) { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(GlobalizationMode.UseNls); char* pBuffer = stackalloc char[Interop.Kernel32.LOCALE_NAME_MAX_LENGTH + 1]; // +1 for the null termination int length = Interop.Kernel32.LCIDToLocaleName(culture, pBuffer, Interop.Kernel32.LOCALE_NAME_MAX_LENGTH + 1, Interop.Kernel32.LOCALE_ALLOW_NEUTRAL_NAMES); @@ -602,44 +617,52 @@ private static int LocaleNameToLCID(string cultureName) return null; } - private int GetAnsiCodePage(string cultureName) + private int NlsGetAnsiCodePage(string cultureName) { - return GetLocaleInfo(LocaleNumberData.AnsiCodePage); + Debug.Assert(GlobalizationMode.UseNls); + return NlsGetLocaleInfo(LocaleNumberData.AnsiCodePage); } - private int GetOemCodePage(string cultureName) + private int NlsGetOemCodePage(string cultureName) { - return GetLocaleInfo(LocaleNumberData.OemCodePage); + Debug.Assert(GlobalizationMode.UseNls); + return NlsGetLocaleInfo(LocaleNumberData.OemCodePage); } - private int GetMacCodePage(string cultureName) + private int NlsGetMacCodePage(string cultureName) { - return GetLocaleInfo(LocaleNumberData.MacCodePage); + Debug.Assert(GlobalizationMode.UseNls); + return NlsGetLocaleInfo(LocaleNumberData.MacCodePage); } - private int GetEbcdicCodePage(string cultureName) + private int NlsGetEbcdicCodePage(string cultureName) { - return GetLocaleInfo(LocaleNumberData.EbcdicCodePage); + Debug.Assert(GlobalizationMode.UseNls); + return NlsGetLocaleInfo(LocaleNumberData.EbcdicCodePage); } - private int GetGeoId(string cultureName) + private int NlsGetGeoId(string cultureName) { - return GetLocaleInfo(LocaleNumberData.GeoId); + Debug.Assert(GlobalizationMode.UseNls); + return NlsGetLocaleInfo(LocaleNumberData.GeoId); } - private int GetDigitSubstitution(string cultureName) + private int NlsGetDigitSubstitution(string cultureName) { - return GetLocaleInfo(LocaleNumberData.DigitSubstitution); + Debug.Assert(GlobalizationMode.UseNls); + return NlsGetLocaleInfo(LocaleNumberData.DigitSubstitution); } - private string GetThreeLetterWindowsLanguageName(string cultureName) + private string NlsGetThreeLetterWindowsLanguageName(string cultureName) { - return GetLocaleInfo(cultureName, LocaleStringData.AbbreviatedWindowsLanguageName); + Debug.Assert(GlobalizationMode.UseNls); + return NlsGetLocaleInfo(cultureName, LocaleStringData.AbbreviatedWindowsLanguageName); } - private static CultureInfo[] EnumCultures(CultureTypes types) + private static CultureInfo[] NlsEnumCultures(CultureTypes types) { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(GlobalizationMode.UseNls); uint flags = 0; @@ -687,17 +710,17 @@ private static CultureInfo[] EnumCultures(CultureTypes types) return cultures; } - private string GetConsoleFallbackName(string cultureName) + private string NlsGetConsoleFallbackName(string cultureName) { - return GetLocaleInfo(cultureName, LocaleStringData.ConsoleFallbackName); + Debug.Assert(GlobalizationMode.UseNls); + return NlsGetLocaleInfo(cultureName, LocaleStringData.ConsoleFallbackName); } - internal bool IsWin32Installed => true; - - internal bool IsReplacementCulture + internal bool NlsIsReplacementCulture { get { + Debug.Assert(GlobalizationMode.UseNls); EnumData context = default; context.strings = new List(); @@ -716,8 +739,9 @@ internal bool IsReplacementCulture } } - internal static unsafe CultureData GetCurrentRegionData() + internal static unsafe CultureData NlsGetCurrentRegionData() { + Debug.Assert(GlobalizationMode.UseNls); Span geoIso2Letters = stackalloc char[10]; int geoId = Interop.Kernel32.GetUserGeoID(Interop.Kernel32.GEOCLASS_NATION); 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 70fc78879bbd8..011befa56aa1f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.cs @@ -474,7 +474,7 @@ internal partial class CultureData // If not found in the hard coded table we'll have to find a culture that works for us if (!GlobalizationMode.Invariant && (retVal == null || retVal.IsNeutralCulture)) { - retVal = GetCultureDataFromRegionName(cultureName); + retVal = GlobalizationMode.UseNls ? NlsGetCultureDataFromRegionName(cultureName) : IcuGetCultureDataFromRegionName(cultureName); } // If we found one we can use, then cache it for next time @@ -540,7 +540,7 @@ internal static CultureInfo[] GetCultures(CultureTypes types) } #pragma warning restore 618 - return EnumCultures(types); + return GlobalizationMode.UseNls ? NlsEnumCultures(types) : IcuEnumCultures(types); } private static CultureData CreateCultureWithInvariantData() @@ -802,7 +802,7 @@ private static string NormalizeCultureName(string name, out bool isNeutralName) culture._sRealName = cultureName; // Ask native code if that one's real - if (!culture.InitCultureData() && !culture.InitCompatibilityCultureData()) + if (!culture.InitCultureDataCore() && !culture.InitCompatibilityCultureData()) { return null; } @@ -832,7 +832,7 @@ private bool InitCompatibilityCultureData() } _sRealName = fallbackCultureName; - if (!InitCultureData()) + if (!InitCultureDataCore()) { return false; } @@ -844,6 +844,10 @@ private bool InitCompatibilityCultureData() return true; } + private bool InitCultureDataCore() => GlobalizationMode.UseNls ? + NlsInitCultureData() : + IcuInitCultureData(); + /// We'd rather people use the named version since this doesn't allow custom locales internal static CultureData GetCultureData(int culture, bool bUseUserOverride) { @@ -863,7 +867,7 @@ internal static CultureData GetCultureData(int culture, bool bUseUserOverride) // Convert the lcid to a name, then use that // Note that this will return neutral names (unlike Vista native API) - localeName = LCIDToLocaleName(culture); + localeName = GlobalizationMode.UseNls ? NlsLCIDToLocaleName(culture) : IcuLCIDToLocaleName(culture); if (!string.IsNullOrEmpty(localeName)) { @@ -914,7 +918,7 @@ internal string CultureName // Parent name (which may be a custom locale/culture) // Ask using the real name, so that we get parents of neutrals - internal string ParentName => _sParent ??= GetLocaleInfo(_sRealName!, LocaleStringData.ParentName); + internal string ParentName => _sParent ??= GetLocaleInfoCore(_sRealName!, LocaleStringData.ParentName); // Localized pretty name for this locale (ie: Inglis (estados Unitos)) internal string DisplayName @@ -944,15 +948,15 @@ internal string DisplayName if (Name.Equals(ZH_CHT, StringComparison.OrdinalIgnoreCase)) { - localizedDisplayName = GetLanguageDisplayName("zh-Hant"); + localizedDisplayName = GetLanguageDisplayNameCore("zh-Hant"); } else if (Name.Equals(ZH_CHS, StringComparison.OrdinalIgnoreCase)) { - localizedDisplayName = GetLanguageDisplayName("zh-Hans"); + localizedDisplayName = GetLanguageDisplayNameCore("zh-Hans"); } else { - localizedDisplayName = GetLanguageDisplayName(Name); + localizedDisplayName = GetLanguageDisplayNameCore(Name); } } catch @@ -983,7 +987,7 @@ internal string DisplayName } else { - localizedDisplayName = GetLocaleInfo(LocaleStringData.LocalizedDisplayName); + localizedDisplayName = GetLocaleInfoCore(LocaleStringData.LocalizedDisplayName); } } } @@ -995,6 +999,10 @@ internal string DisplayName } } + private string GetLanguageDisplayNameCore(string cultureName) => GlobalizationMode.UseNls ? + NlsGetLanguageDisplayName(cultureName) : + IcuGetLanguageDisplayName(cultureName); + /// /// English pretty name for this locale (ie: English (United States)) /// @@ -1020,7 +1028,7 @@ internal string EnglishName } else { - englishDisplayName = GetLocaleInfo(LocaleStringData.EnglishDisplayName); + englishDisplayName = GetLocaleInfoCore(LocaleStringData.EnglishDisplayName); // if it isn't found build one: if (string.IsNullOrEmpty(englishDisplayName)) @@ -1079,7 +1087,7 @@ internal string NativeName } else { - nativeDisplayName = GetLocaleInfo(LocaleStringData.NativeDisplayName); + nativeDisplayName = GetLocaleInfoCore(LocaleStringData.NativeDisplayName); // if it isn't found build one: if (string.IsNullOrEmpty(nativeDisplayName)) @@ -1112,17 +1120,19 @@ internal string SpecificCultureName /// /// iso 639 language name, ie: en /// - internal string TwoLetterISOLanguageName => _sISO639Language ??= GetLocaleInfo(LocaleStringData.Iso639LanguageTwoLetterName); + internal string TwoLetterISOLanguageName => _sISO639Language ??= GetLocaleInfoCore(LocaleStringData.Iso639LanguageTwoLetterName); /// /// iso 639 language name, ie: eng /// - internal string ThreeLetterISOLanguageName => _sISO639Language2 ??= GetLocaleInfo(LocaleStringData.Iso639LanguageThreeLetterName); + internal string ThreeLetterISOLanguageName => _sISO639Language2 ??= GetLocaleInfoCore(LocaleStringData.Iso639LanguageThreeLetterName); /// /// abbreviated windows language name (ie: enu) (non-standard, avoid this) /// - internal string ThreeLetterWindowsLanguageName => _sAbbrevLang ??= GetThreeLetterWindowsLanguageName(_sRealName!); + internal string ThreeLetterWindowsLanguageName => _sAbbrevLang ??= GlobalizationMode.UseNls ? + NlsGetThreeLetterWindowsLanguageName(_sRealName!) : + IcuGetThreeLetterWindowsLanguageName(_sRealName!); /// /// Localized name for this language (Windows Only) ie: Inglis @@ -1146,7 +1156,7 @@ private string LocalizedLanguageName } else { - _sLocalizedLanguage = GetLocaleInfo(LocaleStringData.LocalizedLanguageName); + _sLocalizedLanguage = GetLocaleInfoCore(LocaleStringData.LocalizedLanguageName); } } @@ -1157,17 +1167,17 @@ private string LocalizedLanguageName /// /// English name for this language (Windows Only) ie: German /// - private string EnglishLanguageName => _sEnglishLanguage ??= GetLocaleInfo(LocaleStringData.EnglishLanguageName); + private string EnglishLanguageName => _sEnglishLanguage ??= GetLocaleInfoCore(LocaleStringData.EnglishLanguageName); /// /// Native name of this language (Windows Only) ie: Deutsch /// - private string NativeLanguageName => _sNativeLanguage ??= GetLocaleInfo(LocaleStringData.NativeLanguageName); + private string NativeLanguageName => _sNativeLanguage ??= GetLocaleInfoCore(LocaleStringData.NativeLanguageName); /// /// region name (eg US) /// - internal string RegionName => _sRegionName ??= GetLocaleInfo(LocaleStringData.Iso3166CountryName); + internal string RegionName => _sRegionName ??= GetLocaleInfoCore(LocaleStringData.Iso3166CountryName); internal int GeoId { @@ -1175,7 +1185,7 @@ internal int GeoId { if (_iGeoId == undef) { - _iGeoId = GetGeoId(_sRealName!); + _iGeoId = GlobalizationMode.UseNls ? NlsGetGeoId(_sRealName!) : IcuGetGeoId(_sRealName!); } return _iGeoId; } @@ -1193,7 +1203,7 @@ internal string LocalizedCountryName { try { - localizedCountry = GetRegionDisplayName(); + localizedCountry = GlobalizationMode.UseNls ? NlsGetRegionDisplayName() : IcuGetRegionDisplayName(); } catch { @@ -1211,22 +1221,22 @@ internal string LocalizedCountryName /// /// english country name (RegionInfo) ie: Germany /// - internal string EnglishCountryName => _sEnglishCountry ??= GetLocaleInfo(LocaleStringData.EnglishCountryName); + internal string EnglishCountryName => _sEnglishCountry ??= GetLocaleInfoCore(LocaleStringData.EnglishCountryName); /// /// native country name (RegionInfo) ie: Deutschland /// - internal string NativeCountryName => _sNativeCountry ??= GetLocaleInfo(LocaleStringData.NativeCountryName); + internal string NativeCountryName => _sNativeCountry ??= GetLocaleInfoCore(LocaleStringData.NativeCountryName); /// /// ISO 3166 Country Name /// - internal string TwoLetterISOCountryName => _sISO3166CountryName ??= GetLocaleInfo(LocaleStringData.Iso3166CountryName); + internal string TwoLetterISOCountryName => _sISO3166CountryName ??= GetLocaleInfoCore(LocaleStringData.Iso3166CountryName); /// /// 3 letter ISO 3166 country code /// - internal string ThreeLetterISOCountryName => _sISO3166CountryName2 ??= GetLocaleInfo(LocaleStringData.Iso3166CountryName2); + internal string ThreeLetterISOCountryName => _sISO3166CountryName2 ??= GetLocaleInfoCore(LocaleStringData.Iso3166CountryName2); internal int KeyboardLayoutId { @@ -1251,27 +1261,29 @@ internal int KeyboardLayoutId /// /// Console fallback name (ie: locale to use for console apps for unicode-only locales) /// - internal string SCONSOLEFALLBACKNAME => _sConsoleFallbackName ??= GetConsoleFallbackName(_sRealName!); + internal string SCONSOLEFALLBACKNAME => _sConsoleFallbackName ??= GlobalizationMode.UseNls ? + NlsGetConsoleFallbackName(_sRealName!) : + IcuGetConsoleFallbackName(_sRealName!); /// /// (user can override) grouping of digits /// - internal int[] NumberGroupSizes => _waGrouping ??= GetLocaleInfo(LocaleGroupingData.Digit); + internal int[] NumberGroupSizes => _waGrouping ??= GetLocaleInfoCore(LocaleGroupingData.Digit); /// /// Not a Number /// - private string NaNSymbol => _sNaN ??= GetLocaleInfo(LocaleStringData.NaNSymbol); + private string NaNSymbol => _sNaN ??= GetLocaleInfoCore(LocaleStringData.NaNSymbol); /// /// + Infinity /// - private string PositiveInfinitySymbol => _sPositiveInfinity ??= GetLocaleInfo(LocaleStringData.PositiveInfinitySymbol); + private string PositiveInfinitySymbol => _sPositiveInfinity ??= GetLocaleInfoCore(LocaleStringData.PositiveInfinitySymbol); /// /// - Infinity /// - private string NegativeInfinitySymbol => _sNegativeInfinity ??= GetLocaleInfo(LocaleStringData.NegativeInfinitySymbol); + private string NegativeInfinitySymbol => _sNegativeInfinity ??= GetLocaleInfoCore(LocaleStringData.NegativeInfinitySymbol); /// /// Negative Percent (0-3) @@ -1283,7 +1295,7 @@ private int PercentNegativePattern if (_iNegativePercent == undef) { // Note that <= Windows Vista this is synthesized by native code - _iNegativePercent = GetLocaleInfo(LocaleNumberData.NegativePercentFormat); + _iNegativePercent = GetLocaleInfoCore(LocaleNumberData.NegativePercentFormat); } return _iNegativePercent; } @@ -1299,7 +1311,7 @@ private int PercentPositivePattern if (_iPositivePercent == undef) { // Note that <= Windows Vista this is synthesized by native code - _iPositivePercent = GetLocaleInfo(LocaleNumberData.PositivePercentFormat); + _iPositivePercent = GetLocaleInfoCore(LocaleNumberData.PositivePercentFormat); } return _iPositivePercent; } @@ -1308,37 +1320,37 @@ private int PercentPositivePattern /// /// Percent (%) symbol /// - private string PercentSymbol => _sPercent ??= GetLocaleInfo(LocaleStringData.PercentSymbol); + private string PercentSymbol => _sPercent ??= GetLocaleInfoCore(LocaleStringData.PercentSymbol); /// /// PerMille symbol /// - private string PerMilleSymbol => _sPerMille ??= GetLocaleInfo(LocaleStringData.PerMilleSymbol); + private string PerMilleSymbol => _sPerMille ??= GetLocaleInfoCore(LocaleStringData.PerMilleSymbol); /// /// (user can override) local monetary symbol, eg: $ /// - internal string CurrencySymbol => _sCurrency ??= GetLocaleInfo(LocaleStringData.MonetarySymbol); + internal string CurrencySymbol => _sCurrency ??= GetLocaleInfoCore(LocaleStringData.MonetarySymbol); /// /// international monetary symbol (RegionInfo), eg: USD /// - internal string ISOCurrencySymbol => _sIntlMonetarySymbol ??= GetLocaleInfo(LocaleStringData.Iso4217MonetarySymbol); + internal string ISOCurrencySymbol => _sIntlMonetarySymbol ??= GetLocaleInfoCore(LocaleStringData.Iso4217MonetarySymbol); /// /// English name for this currency (RegionInfo), eg: US Dollar /// - internal string CurrencyEnglishName => _sEnglishCurrency ??= GetLocaleInfo(LocaleStringData.CurrencyEnglishName); + internal string CurrencyEnglishName => _sEnglishCurrency ??= GetLocaleInfoCore(LocaleStringData.CurrencyEnglishName); /// /// Native name for this currency (RegionInfo), eg: Schweiz Frank /// - internal string CurrencyNativeName => _sNativeCurrency ??= GetLocaleInfo(LocaleStringData.CurrencyNativeName); + internal string CurrencyNativeName => _sNativeCurrency ??= GetLocaleInfoCore(LocaleStringData.CurrencyNativeName); /// /// (user can override) monetary grouping of digits /// - internal int[] CurrencyGroupSizes => _waMonetaryGrouping ??= GetLocaleInfo(LocaleGroupingData.Monetary); + internal int[] CurrencyGroupSizes => _waMonetaryGrouping ??= GetLocaleInfoCore(LocaleGroupingData.Monetary); /// /// (user can override) system of measurement 0=metric, 1=US (RegionInfo) @@ -1349,7 +1361,7 @@ internal int MeasurementSystem { if (_iMeasure == undef) { - _iMeasure = GetLocaleInfo(LocaleNumberData.MeasurementSystem); + _iMeasure = GetLocaleInfoCore(LocaleNumberData.MeasurementSystem); } return _iMeasure; } @@ -1358,17 +1370,17 @@ internal int MeasurementSystem /// /// (user can override) list Separator /// - internal string ListSeparator => _sListSeparator ??= GetLocaleInfo(LocaleStringData.ListSeparator); + internal string ListSeparator => _sListSeparator ??= GetLocaleInfoCore(LocaleStringData.ListSeparator); /// /// (user can override) AM designator /// - internal string AMDesignator => _sAM1159 ??= GetLocaleInfo(LocaleStringData.AMDesignator); + internal string AMDesignator => _sAM1159 ??= GetLocaleInfoCore(LocaleStringData.AMDesignator); /// /// (user can override) PM designator /// - internal string PMDesignator => _sPM2359 ??= GetLocaleInfo(LocaleStringData.PMDesignator); + internal string PMDesignator => _sPM2359 ??= GetLocaleInfoCore(LocaleStringData.PMDesignator); /// /// (user can override) time format @@ -1381,7 +1393,7 @@ internal string[] LongTimes { Debug.Assert(!GlobalizationMode.Invariant); - string[]? longTimes = GetTimeFormats(); + string[]? longTimes = GlobalizationMode.UseNls ? NlsGetTimeFormats() : IcuGetTimeFormats(); if (longTimes == null || longTimes.Length == 0) { _saLongTimes = Invariant._saLongTimes!; @@ -1408,7 +1420,7 @@ internal string[] ShortTimes Debug.Assert(!GlobalizationMode.Invariant); // Try to get the short times from the OS/culture.dll - string[]? shortTimes = GetShortTimeFormats(); + string[]? shortTimes = GlobalizationMode.UseNls ? NlsGetShortTimeFormats() : IcuGetShortTimeFormats(); if (shortTimes == null || shortTimes.Length == 0) { @@ -1559,7 +1571,7 @@ internal int FirstDayOfWeek { if (_iFirstDayOfWeek == undef) { - _iFirstDayOfWeek = GetFirstDayOfWeek(); + _iFirstDayOfWeek = GlobalizationMode.UseNls ? NlsGetFirstDayOfWeek() : IcuGetFirstDayOfWeek(); } return _iFirstDayOfWeek; } @@ -1572,7 +1584,7 @@ internal int CalendarWeekRule { if (_iFirstWeekOfYear == undef) { - _iFirstWeekOfYear = GetLocaleInfo(LocaleNumberData.FirstWeekOfYear); + _iFirstWeekOfYear = GetLocaleInfoCore(LocaleNumberData.FirstWeekOfYear); } return _iFirstWeekOfYear; } @@ -1665,7 +1677,10 @@ internal CalendarId[] CalendarIds // Default calendar should be first CalendarId[] calendars = new CalendarId[23]; Debug.Assert(_sWindowsName != null, "[CultureData.CalendarIds] Expected _sWindowsName to be populated by already"); - int count = CalendarData.GetCalendars(_sWindowsName, _bUseOverrides, calendars); + + int count = GlobalizationMode.UseNls + ? CalendarData.NlsGetCalendars(_sWindowsName, _bUseOverrides, calendars) + : CalendarData.IcuGetCalendars(_sWindowsName, _bUseOverrides, calendars); // See if we had a calendar to add. if (count == 0) @@ -1711,7 +1726,7 @@ internal CalendarId[] CalendarIds // Prior to Vista the enumeration didn't have default calendar first if (temp.Length > 1) { - CalendarId i = (CalendarId)GetLocaleInfo(LocaleNumberData.CalendarType); + CalendarId i = (CalendarId)GetLocaleInfoCore(LocaleNumberData.CalendarType); if (temp[1] == i) { temp[1] = temp[0]; @@ -1784,7 +1799,7 @@ private int ReadingLayout if (_iReadingLayout == undef) { Debug.Assert(_sRealName != null, "[CultureData.IsRightToLeft] Expected _sRealName to be populated by already"); - _iReadingLayout = GetLocaleInfo(LocaleNumberData.ReadingLayout); + _iReadingLayout = GetLocaleInfoCore(LocaleNumberData.ReadingLayout); } return _iReadingLayout; @@ -1835,7 +1850,7 @@ internal int ANSICodePage { if (_iDefaultAnsiCodePage == undef) { - _iDefaultAnsiCodePage = GetAnsiCodePage(_sRealName!); + _iDefaultAnsiCodePage = GlobalizationMode.UseNls ? NlsGetAnsiCodePage(_sRealName!) : IcuGetAnsiCodePage(_sRealName!); } return _iDefaultAnsiCodePage; } @@ -1850,7 +1865,7 @@ internal int OEMCodePage { if (_iDefaultOemCodePage == undef) { - _iDefaultOemCodePage = GetOemCodePage(_sRealName!); + _iDefaultOemCodePage = GlobalizationMode.UseNls ? NlsGetOemCodePage(_sRealName!) : IcuGetOemCodePage(_sRealName!); } return _iDefaultOemCodePage; } @@ -1865,7 +1880,7 @@ internal int MacCodePage { if (_iDefaultMacCodePage == undef) { - _iDefaultMacCodePage = GetMacCodePage(_sRealName!); + _iDefaultMacCodePage = GlobalizationMode.UseNls ? NlsGetMacCodePage(_sRealName!) : IcuGetMacCodePage(_sRealName!); } return _iDefaultMacCodePage; } @@ -1880,7 +1895,7 @@ internal int EBCDICCodePage { if (_iDefaultEbcdicCodePage == undef) { - _iDefaultEbcdicCodePage = GetEbcdicCodePage(_sRealName!); + _iDefaultEbcdicCodePage = GlobalizationMode.UseNls ? NlsGetEbcdicCodePage(_sRealName!) : IcuGetEbcdicCodePage(_sRealName!); } return _iDefaultEbcdicCodePage; } @@ -1893,7 +1908,7 @@ internal int LCID if (_iLanguage == 0) { Debug.Assert(_sRealName != null, "[CultureData.LCID] Expected this.sRealName to be populated already"); - _iLanguage = LocaleNameToLCID(_sRealName); + _iLanguage = GlobalizationMode.UseNls ? NlsLocaleNameToLCID(_sRealName) : IcuLocaleNameToLCID(_sRealName); } return _iLanguage; } @@ -1905,6 +1920,14 @@ internal int LCID internal bool IsInvariantCulture => string.IsNullOrEmpty(Name); + internal bool IsWin32Installed => GlobalizationMode.UseNls; + + internal bool IsReplacementCulture => GlobalizationMode.UseNls ? NlsIsReplacementCulture : false; + + internal static unsafe CultureData GetCurrentRegionData() => GlobalizationMode.UseNls ? + NlsGetCurrentRegionData() : + CultureInfo.CurrentCulture._cultureData; + /// /// Get an instance of our default calendar /// @@ -1917,7 +1940,7 @@ internal Calendar DefaultCalendar return CultureInfo.GetCalendarInstance(CalendarIds[0]); } - CalendarId defaultCalId = (CalendarId)GetLocaleInfo(LocaleNumberData.CalendarType); + CalendarId defaultCalId = (CalendarId)GetLocaleInfoCore(LocaleNumberData.CalendarType); if (defaultCalId == 0) { @@ -1958,7 +1981,7 @@ internal string TimeSeparator { if (_sTimeSeparator == null) { - string? longTimeFormat = GetTimeFormatString(); + string? longTimeFormat = GlobalizationMode.UseNls ? NlsGetTimeFormatString() : IcuGetTimeFormatString(); if (string.IsNullOrEmpty(longTimeFormat)) { longTimeFormat = LongTimes[0]; @@ -2160,24 +2183,24 @@ internal void GetNFIValues(NumberFormatInfo nfi) { Debug.Assert(_sWindowsName != null, "[CultureData.GetNFIValues] Expected _sWindowsName to be populated by already"); // String values - nfi._positiveSign = GetLocaleInfo(LocaleStringData.PositiveSign); - nfi._negativeSign = GetLocaleInfo(LocaleStringData.NegativeSign); + nfi._positiveSign = GetLocaleInfoCore(LocaleStringData.PositiveSign); + nfi._negativeSign = GetLocaleInfoCore(LocaleStringData.NegativeSign); - nfi._numberDecimalSeparator = GetLocaleInfo(LocaleStringData.DecimalSeparator); - nfi._numberGroupSeparator = GetLocaleInfo(LocaleStringData.ThousandSeparator); - nfi._currencyGroupSeparator = GetLocaleInfo(LocaleStringData.MonetaryThousandSeparator); - nfi._currencyDecimalSeparator = GetLocaleInfo(LocaleStringData.MonetaryDecimalSeparator); - nfi._currencySymbol = GetLocaleInfo(LocaleStringData.MonetarySymbol); + nfi._numberDecimalSeparator = GetLocaleInfoCore(LocaleStringData.DecimalSeparator); + nfi._numberGroupSeparator = GetLocaleInfoCore(LocaleStringData.ThousandSeparator); + nfi._currencyGroupSeparator = GetLocaleInfoCore(LocaleStringData.MonetaryThousandSeparator); + nfi._currencyDecimalSeparator = GetLocaleInfoCore(LocaleStringData.MonetaryDecimalSeparator); + nfi._currencySymbol = GetLocaleInfoCore(LocaleStringData.MonetarySymbol); // Numeric values - nfi._numberDecimalDigits = GetLocaleInfo(LocaleNumberData.FractionalDigitsCount); - nfi._currencyDecimalDigits = GetLocaleInfo(LocaleNumberData.MonetaryFractionalDigitsCount); - nfi._currencyPositivePattern = GetLocaleInfo(LocaleNumberData.PositiveMonetaryNumberFormat); - nfi._currencyNegativePattern = GetLocaleInfo(LocaleNumberData.NegativeMonetaryNumberFormat); - nfi._numberNegativePattern = GetLocaleInfo(LocaleNumberData.NegativeNumberFormat); + nfi._numberDecimalDigits = GetLocaleInfoCore(LocaleNumberData.FractionalDigitsCount); + nfi._currencyDecimalDigits = GetLocaleInfoCore(LocaleNumberData.MonetaryFractionalDigitsCount); + nfi._currencyPositivePattern = GetLocaleInfoCore(LocaleNumberData.PositiveMonetaryNumberFormat); + nfi._currencyNegativePattern = GetLocaleInfoCore(LocaleNumberData.NegativeMonetaryNumberFormat); + nfi._numberNegativePattern = GetLocaleInfoCore(LocaleNumberData.NegativeNumberFormat); // LOCALE_SNATIVEDIGITS (array of 10 single character strings). - string digits = GetLocaleInfo(LocaleStringData.Digits); + string digits = GetLocaleInfoCore(LocaleStringData.Digits); nfi._nativeDigits = new string[10]; for (int i = 0; i < nfi._nativeDigits.Length; i++) { @@ -2185,7 +2208,7 @@ internal void GetNFIValues(NumberFormatInfo nfi) } Debug.Assert(_sRealName != null); - nfi._digitSubstitution = GetDigitSubstitution(_sRealName); + nfi._digitSubstitution = GlobalizationMode.UseNls ? NlsGetDigitSubstitution(_sRealName) : IcuGetDigitSubstitution(_sRealName); } // Gather additional data @@ -2230,6 +2253,22 @@ internal void GetNFIValues(NumberFormatInfo nfi) /// internal static string AnsiToLower(string testString) => TextInfo.ToLowerAsciiInvariant(testString); + private int GetLocaleInfoCore(LocaleNumberData type) => GlobalizationMode.UseNls ? + NlsGetLocaleInfo(type) : + IcuGetLocaleInfo(type); + + private string GetLocaleInfoCore(LocaleStringData type) => GlobalizationMode.UseNls ? + NlsGetLocaleInfo(type) : + IcuGetLocaleInfo(type); + + private string GetLocaleInfoCore(string localeName, LocaleStringData type) => GlobalizationMode.UseNls ? + NlsGetLocaleInfo(localeName, type) : + IcuGetLocaleInfo(localeName, type); + + private int[] GetLocaleInfoCore(LocaleGroupingData type) => GlobalizationMode.UseNls ? + NlsGetLocaleInfo(type) : + IcuGetLocaleInfo(type); + /// /// The numeric values of the enum members match their Win32 counterparts. The CultureData Win32 PAL implementation /// takes a dependency on this fact, in order to prevent having to construct a mapping from internal values to LCTypes. diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureInfo.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureInfo.Icu.cs similarity index 76% rename from src/libraries/System.Private.CoreLib/src/System/Globalization/CultureInfo.Unix.cs rename to src/libraries/System.Private.CoreLib/src/System/Globalization/CultureInfo.Icu.cs index da4c00af2f487..6fb99d3e69750 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureInfo.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureInfo.Icu.cs @@ -8,8 +8,10 @@ namespace System.Globalization { public partial class CultureInfo : IFormatProvider { - internal static CultureInfo GetUserDefaultCulture() + internal static CultureInfo IcuGetUserDefaultCulture() { + Debug.Assert(!GlobalizationMode.UseNls); + if (GlobalizationMode.Invariant) return CultureInfo.InvariantCulture; @@ -28,8 +30,10 @@ internal static CultureInfo GetUserDefaultCulture() return cultureInfo; } - private static CultureInfo GetPredefinedCultureInfo(string name) + private static CultureInfo IcuGetPredefinedCultureInfo(string name) { + Debug.Assert(!GlobalizationMode.UseNls); + if (!Interop.Globalization.IsPredefinedLocale(name)) { throw new CultureNotFoundException(nameof(name), SR.Format(SR.Argument_InvalidPredefinedCultureName, name)); @@ -38,8 +42,10 @@ private static CultureInfo GetPredefinedCultureInfo(string name) return GetCultureInfo(name); } - private static CultureInfo GetUserDefaultUICulture() + private static CultureInfo IcuGetUserDefaultUICulture() { + Debug.Assert(!GlobalizationMode.UseNls); + return InitializeUserDefaultCulture(); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureInfo.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureInfo.Nls.cs similarity index 88% rename from src/libraries/System.Private.CoreLib/src/System/Globalization/CultureInfo.Windows.cs rename to src/libraries/System.Private.CoreLib/src/System/Globalization/CultureInfo.Nls.cs index dd4f34f58c2c2..6265fc8afe556 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureInfo.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureInfo.Nls.cs @@ -2,12 +2,16 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Diagnostics; + namespace System.Globalization { public partial class CultureInfo : IFormatProvider { - internal static CultureInfo GetUserDefaultCulture() + internal static CultureInfo NlsGetUserDefaultCulture() { + Debug.Assert(GlobalizationMode.UseNls); + if (GlobalizationMode.Invariant) return CultureInfo.InvariantCulture; @@ -26,8 +30,10 @@ internal static CultureInfo GetUserDefaultCulture() return GetCultureByName(strDefault); } - private static CultureInfo GetPredefinedCultureInfo(string name) + private static CultureInfo NlsGetPredefinedCultureInfo(string name) { + Debug.Assert(GlobalizationMode.UseNls); + CultureInfo culture = GetCultureInfo(name); string englishName = culture.EnglishName; @@ -44,8 +50,10 @@ private static CultureInfo GetPredefinedCultureInfo(string name) return culture; } - private static unsafe CultureInfo GetUserDefaultUICulture() + private static unsafe CultureInfo NlsGetUserDefaultUICulture() { + Debug.Assert(GlobalizationMode.UseNls); + if (GlobalizationMode.Invariant) return CultureInfo.InvariantCulture; diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureInfo.cs index e853316861459..f4daf5c08555e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureInfo.cs @@ -819,6 +819,14 @@ internal static Calendar GetCalendarInstanceRare(CalendarId calType) return new GregorianCalendar(); } + internal static CultureInfo GetUserDefaultCulture() => GlobalizationMode.UseNls ? + NlsGetUserDefaultCulture() : + IcuGetUserDefaultCulture(); + + private static CultureInfo GetUserDefaultUICulture() => GlobalizationMode.UseNls ? + NlsGetUserDefaultUICulture() : + IcuGetUserDefaultUICulture(); + /// /// Return/set the default calendar used by this culture. /// This value can be overridden by regional option if this is a current culture. @@ -1123,7 +1131,9 @@ public static CultureInfo GetCultureInfo(string name, bool predefinedOnly) if (predefinedOnly) { - return GetPredefinedCultureInfo(name); + return GlobalizationMode.UseNls ? + NlsGetPredefinedCultureInfo(name) : + IcuGetPredefinedCultureInfo(name); } return GetCultureInfo(name); diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/LocaleData.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/IcuLocaleData.cs similarity index 99% rename from src/libraries/System.Private.CoreLib/src/System/Globalization/LocaleData.Unix.cs rename to src/libraries/System.Private.CoreLib/src/System/Globalization/IcuLocaleData.cs index 2af5eb9c250a1..3750e75519372 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/LocaleData.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/IcuLocaleData.cs @@ -10,7 +10,7 @@ namespace System.Globalization { - internal enum LocaleDataParts + internal enum IcuLocaleDataParts { Lcid = 0, AnsiCodePage = 1, @@ -23,7 +23,7 @@ internal enum LocaleDataParts ConsoleLocaleIndex = 8 } - internal static class LocaleData + internal static class IcuLocaleData { // this is done rather than using a large readonly array of strings to avoid // generating a large amount of code in the static constructor. @@ -4454,7 +4454,7 @@ internal static class LocaleData return null; } - internal static int GetLocaleDataNumericPart(string cultureName, LocaleDataParts part) + internal static int GetLocaleDataNumericPart(string cultureName, IcuLocaleDataParts part) { int index = SearchCultureName(cultureName); if (index < 0) @@ -4480,7 +4480,7 @@ internal static int GetLocaleDataNumericPart(string cultureName, LocaleDataParts return c_threeLetterWindowsLanguageName.Substring(index * 3, 3); } - internal static string GetLocaleDataMappedCulture(string cultureName, LocaleDataParts part) + internal static string GetLocaleDataMappedCulture(string cultureName, IcuLocaleDataParts part) { int indexToIndicesTable = GetLocaleDataNumericPart(cultureName, part); if (indexToIndicesTable < 0) @@ -4496,12 +4496,12 @@ internal static string GetLocaleDataMappedCulture(string cultureName, LocaleData internal static string GetSpecificCultureName(string cultureName) { - return GetLocaleDataMappedCulture(cultureName, LocaleDataParts.SpecificLocaleIndex); + return GetLocaleDataMappedCulture(cultureName, IcuLocaleDataParts.SpecificLocaleIndex); } internal static string GetConsoleUICulture(string cultureName) { - return GetLocaleDataMappedCulture(cultureName, LocaleDataParts.ConsoleLocaleIndex); + return GetLocaleDataMappedCulture(cultureName, IcuLocaleDataParts.ConsoleLocaleIndex); } // SearchCultureName will binary search c_localeNames using s_localeNamesIndices. diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/IdnMapping.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/IdnMapping.Icu.cs similarity index 84% rename from src/libraries/System.Private.CoreLib/src/System/Globalization/IdnMapping.Unix.cs rename to src/libraries/System.Private.CoreLib/src/System/Globalization/IdnMapping.Icu.cs index 4c6ee2c404156..82b97b27d3c86 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/IdnMapping.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/IdnMapping.Icu.cs @@ -8,12 +8,13 @@ namespace System.Globalization { public sealed partial class IdnMapping { - private unsafe string GetAsciiCore(string unicodeString, char* unicode, int count) + private unsafe string IcuGetAsciiCore(string unicodeString, char* unicode, int count) { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(!GlobalizationMode.UseNls); Debug.Assert(unicodeString != null && unicodeString.Length >= count); - uint flags = Flags; + uint flags = IcuFlags; CheckInvalidIdnCharacters(unicode, count, flags, nameof(unicode)); const int StackallocThreshold = 512; @@ -51,33 +52,35 @@ private unsafe string GetAsciiCore(string unicodeString, char* unicode, int coun } } - private unsafe string GetUnicodeCore(string asciiString, char* ascii, int count) + private unsafe string IcuGetUnicodeCore(string asciiString, char* ascii, int count) { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(!GlobalizationMode.UseNls); Debug.Assert(asciiString != null && asciiString.Length >= count); - uint flags = Flags; + uint flags = IcuFlags; CheckInvalidIdnCharacters(ascii, count, flags, nameof(ascii)); const int StackAllocThreshold = 512; if (count < StackAllocThreshold) { char* output = stackalloc char[count]; - return GetUnicodeCore(asciiString, ascii, count, flags, output, count, reattempt: true); + return IcuGetUnicodeCore(asciiString, ascii, count, flags, output, count, reattempt: true); } else { char[] output = new char[count]; fixed (char* pOutput = &output[0]) { - return GetUnicodeCore(asciiString, ascii, count, flags, pOutput, count, reattempt: true); + return IcuGetUnicodeCore(asciiString, ascii, count, flags, pOutput, count, reattempt: true); } } } - private unsafe string GetUnicodeCore(string asciiString, char* ascii, int count, uint flags, char* output, int outputLength, bool reattempt) + private unsafe string IcuGetUnicodeCore(string asciiString, char* ascii, int count, uint flags, char* output, int outputLength, bool reattempt) { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(!GlobalizationMode.UseNls); Debug.Assert(asciiString != null && asciiString.Length >= count); int realLen = Interop.Globalization.ToUnicode(flags, ascii, count, output, outputLength); @@ -95,7 +98,7 @@ private unsafe string GetUnicodeCore(string asciiString, char* ascii, int count, char[] newOutput = new char[realLen]; fixed (char* pNewOutput = newOutput) { - return GetUnicodeCore(asciiString, ascii, count, flags, pNewOutput, realLen, reattempt: false); + return IcuGetUnicodeCore(asciiString, ascii, count, flags, pNewOutput, realLen, reattempt: false); } } @@ -106,7 +109,7 @@ private unsafe string GetUnicodeCore(string asciiString, char* ascii, int count, // ---- PAL layer ends here ---- // ----------------------------- - private uint Flags + private uint IcuFlags { get { diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/IdnMapping.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/IdnMapping.Nls.cs similarity index 77% rename from src/libraries/System.Private.CoreLib/src/System/Globalization/IdnMapping.Windows.cs rename to src/libraries/System.Private.CoreLib/src/System/Globalization/IdnMapping.Nls.cs index 6a97c04ebf661..54ebee2742a18 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/IdnMapping.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/IdnMapping.Nls.cs @@ -10,12 +10,13 @@ namespace System.Globalization { public sealed partial class IdnMapping { - private unsafe string GetAsciiCore(string unicodeString, char* unicode, int count) + private unsafe string NlsGetAsciiCore(string unicodeString, char* unicode, int count) { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(GlobalizationMode.UseNls); Debug.Assert(unicodeString != null && unicodeString.Length >= count); - uint flags = Flags; + uint flags = NlsFlags; // Determine the required length int length = Interop.Normaliz.IdnToAscii(flags, unicode, count, null, 0); @@ -29,21 +30,22 @@ private unsafe string GetAsciiCore(string unicodeString, char* unicode, int coun if (length < StackAllocThreshold) { char* output = stackalloc char[length]; - return GetAsciiCore(unicodeString, unicode, count, flags, output, length); + return NlsGetAsciiCore(unicodeString, unicode, count, flags, output, length); } else { char[] output = new char[length]; fixed (char* pOutput = &output[0]) { - return GetAsciiCore(unicodeString, unicode, count, flags, pOutput, length); + return NlsGetAsciiCore(unicodeString, unicode, count, flags, pOutput, length); } } } - private unsafe string GetAsciiCore(string unicodeString, char* unicode, int count, uint flags, char* output, int outputLength) + private unsafe string NlsGetAsciiCore(string unicodeString, char* unicode, int count, uint flags, char* output, int outputLength) { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(GlobalizationMode.UseNls); Debug.Assert(unicodeString != null && unicodeString.Length >= count); int length = Interop.Normaliz.IdnToAscii(flags, unicode, count, output, outputLength); @@ -55,12 +57,13 @@ private unsafe string GetAsciiCore(string unicodeString, char* unicode, int coun return GetStringForOutput(unicodeString, unicode, count, output, length); } - private unsafe string GetUnicodeCore(string asciiString, char* ascii, int count) + private unsafe string NlsGetUnicodeCore(string asciiString, char* ascii, int count) { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(GlobalizationMode.UseNls); Debug.Assert(asciiString != null && asciiString.Length >= count); - uint flags = Flags; + uint flags = NlsFlags; // Determine the required length int length = Interop.Normaliz.IdnToUnicode(flags, ascii, count, null, 0); @@ -74,21 +77,22 @@ private unsafe string GetUnicodeCore(string asciiString, char* ascii, int count) if (length < StackAllocThreshold) { char* output = stackalloc char[length]; - return GetUnicodeCore(asciiString, ascii, count, flags, output, length); + return NlsGetUnicodeCore(asciiString, ascii, count, flags, output, length); } else { char[] output = new char[length]; fixed (char* pOutput = &output[0]) { - return GetUnicodeCore(asciiString, ascii, count, flags, pOutput, length); + return NlsGetUnicodeCore(asciiString, ascii, count, flags, pOutput, length); } } } - private unsafe string GetUnicodeCore(string asciiString, char* ascii, int count, uint flags, char* output, int outputLength) + private unsafe string NlsGetUnicodeCore(string asciiString, char* ascii, int count, uint flags, char* output, int outputLength) { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(GlobalizationMode.UseNls); Debug.Assert(asciiString != null && asciiString.Length >= count); int length = Interop.Normaliz.IdnToUnicode(flags, ascii, count, output, outputLength); @@ -104,7 +108,7 @@ private unsafe string GetUnicodeCore(string asciiString, char* ascii, int count, // ---- PAL layer ends here ---- // ----------------------------- - private uint Flags + private uint NlsFlags { get { diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/IdnMapping.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/IdnMapping.cs index 3ec32b27a6666..f309a42270fef 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/IdnMapping.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/IdnMapping.cs @@ -92,7 +92,9 @@ public string GetAscii(string unicode, int index, int count) { fixed (char* pUnicode = unicode) { - return GetAsciiCore(unicode, pUnicode + index, count); + return GlobalizationMode.UseNls ? + NlsGetAsciiCore(unicode, pUnicode + index, count) : + IcuGetAsciiCore(unicode, pUnicode + index, count); } } } @@ -134,7 +136,9 @@ public string GetUnicode(string ascii, int index, int count) { fixed (char* pAscii = ascii) { - return GetUnicodeCore(ascii, pAscii + index, count); + return GlobalizationMode.UseNls ? + NlsGetUnicodeCore(ascii, pAscii + index, count) : + IcuGetUnicodeCore(ascii, pAscii + index, count); } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/JapaneseCalendar.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/JapaneseCalendar.Icu.cs similarity index 96% rename from src/libraries/System.Private.CoreLib/src/System/Globalization/JapaneseCalendar.Unix.cs rename to src/libraries/System.Private.CoreLib/src/System/Globalization/JapaneseCalendar.Icu.cs index ed4e20de10447..3f9aac684a201 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/JapaneseCalendar.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/JapaneseCalendar.Icu.cs @@ -9,13 +9,15 @@ namespace System.Globalization { public partial class JapaneseCalendar : Calendar { - private static EraInfo[]? GetJapaneseEras() + private static EraInfo[]? IcuGetJapaneseEras() { if (GlobalizationMode.Invariant) { return null; } + Debug.Assert(!GlobalizationMode.UseNls); + string[]? eraNames; if (!CalendarData.EnumCalendarInfo("ja-JP", CalendarId.JAPAN, CalendarDataType.EraNames, out eraNames)) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/JapaneseCalendar.Win32.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/JapaneseCalendar.Nls.cs similarity index 92% rename from src/libraries/System.Private.CoreLib/src/System/Globalization/JapaneseCalendar.Win32.cs rename to src/libraries/System.Private.CoreLib/src/System/Globalization/JapaneseCalendar.Nls.cs index 23ac1eebed26e..09a59adb02ae6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/JapaneseCalendar.Win32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/JapaneseCalendar.Nls.cs @@ -2,12 +2,17 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Diagnostics; + +#if TARGET_WINDOWS using Internal.Win32; +#endif namespace System.Globalization { public partial class JapaneseCalendar : Calendar { +#if TARGET_WINDOWS private const string JapaneseErasHive = @"System\CurrentControlSet\Control\Nls\Calendars\Japanese\Eras"; // We know about 4 built-in eras, however users may add additional era(s) from the @@ -25,8 +30,10 @@ public partial class JapaneseCalendar : Calendar // . is a delimiter, but the value of . doesn't matter. // '_' marks the space between the japanese era name, japanese abbreviated era name // english name, and abbreviated english names. - private static EraInfo[]? GetJapaneseEras() + private static EraInfo[]? NlsGetJapaneseEras() { + Debug.Assert(GlobalizationMode.UseNls); + // Look in the registry key and see if we can find any ranges int iFoundEras = 0; EraInfo[]? registryEraRanges = null; @@ -114,6 +121,17 @@ public partial class JapaneseCalendar : Calendar // Return our ranges return registryEraRanges; } +#else + // no-op, in Unix we never call this function. + // the reason to have it is to simplify the build + // this way we avoid having to include RegistryKey + // and all it's windows PInvokes. + private static EraInfo[]? NlsGetJapaneseEras() + { + Debug.Fail("Should never be called non-Windows platforms."); + throw new PlatformNotSupportedException(); + } +#endif // // Compare two era ranges, eg just the ticks diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/JapaneseCalendar.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/JapaneseCalendar.cs index 02b15af270d98..2ed5af5b8e72d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/JapaneseCalendar.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/JapaneseCalendar.cs @@ -69,7 +69,7 @@ internal static EraInfo[] GetEraInfo() { // See if we need to build it return s_japaneseEraInfo ?? - (s_japaneseEraInfo = GetJapaneseEras()) ?? + (s_japaneseEraInfo = GlobalizationMode.UseNls ? NlsGetJapaneseEras() : IcuGetJapaneseEras()) ?? // See if we have to use the built-in eras (s_japaneseEraInfo = new EraInfo[] { diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/Normalization.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/Normalization.Icu.cs similarity index 88% rename from src/libraries/System.Private.CoreLib/src/System/Globalization/Normalization.Unix.cs rename to src/libraries/System.Private.CoreLib/src/System/Globalization/Normalization.Icu.cs index 0a95d018f0ab0..a897fcf6f1f02 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/Normalization.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/Normalization.Icu.cs @@ -11,14 +11,10 @@ namespace System.Globalization { internal static partial class Normalization { - internal static unsafe bool IsNormalized(string strInput, NormalizationForm normalizationForm) + private static unsafe bool IcuIsNormalized(string strInput, NormalizationForm normalizationForm) { - if (GlobalizationMode.Invariant) - { - // In Invariant mode we assume all characters are normalized. - // This is because we don't support any linguistic operation on the strings - return true; - } + Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(!GlobalizationMode.UseNls); ValidateArguments(strInput, normalizationForm); @@ -36,14 +32,10 @@ internal static unsafe bool IsNormalized(string strInput, NormalizationForm norm return ret == 1; } - internal static unsafe string Normalize(string strInput, NormalizationForm normalizationForm) + private static unsafe string IcuNormalize(string strInput, NormalizationForm normalizationForm) { - if (GlobalizationMode.Invariant) - { - // In Invariant mode we assume all characters are normalized. - // This is because we don't support any linguistic operation on the strings - return strInput; - } + Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(!GlobalizationMode.UseNls); ValidateArguments(strInput, normalizationForm); diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/Normalization.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/Normalization.Nls.cs similarity index 88% rename from src/libraries/System.Private.CoreLib/src/System/Globalization/Normalization.Windows.cs rename to src/libraries/System.Private.CoreLib/src/System/Globalization/Normalization.Nls.cs index 65e459a13a3e9..e6c413580f8f8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/Normalization.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/Normalization.Nls.cs @@ -11,15 +11,10 @@ namespace System.Globalization { internal static partial class Normalization { - internal static unsafe bool IsNormalized(string strInput, NormalizationForm normalizationForm) + private static unsafe bool NlsIsNormalized(string strInput, NormalizationForm normalizationForm) { - if (GlobalizationMode.Invariant) - { - // In Invariant mode we assume all characters are normalized. - // This is because we don't support any linguistic operation on the strings - return true; - } - + Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(GlobalizationMode.UseNls); Debug.Assert(strInput != null); // The only way to know if IsNormalizedString failed is through checking the Win32 last error @@ -59,15 +54,10 @@ internal static unsafe bool IsNormalized(string strInput, NormalizationForm norm return result != Interop.BOOL.FALSE; } - internal static unsafe string Normalize(string strInput, NormalizationForm normalizationForm) + private static unsafe string NlsNormalize(string strInput, NormalizationForm normalizationForm) { - if (GlobalizationMode.Invariant) - { - // In Invariant mode we assume all characters are normalized. - // This is because we don't support any linguistic operation on the strings - return strInput; - } - + Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(GlobalizationMode.UseNls); Debug.Assert(strInput != null); if (strInput.Length == 0) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/Normalization.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/Normalization.cs new file mode 100644 index 0000000000000..d71b3380a5463 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/Normalization.cs @@ -0,0 +1,40 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Text; + +namespace System.Globalization +{ + internal static partial class Normalization + { + internal static bool IsNormalized(string strInput, NormalizationForm normalizationForm) + { + if (GlobalizationMode.Invariant) + { + // In Invariant mode we assume all characters are normalized. + // This is because we don't support any linguistic operation on the strings + return true; + } + + return GlobalizationMode.UseNls ? + NlsIsNormalized(strInput, normalizationForm) : + IcuIsNormalized(strInput, normalizationForm); + } + + internal static string Normalize(string strInput, NormalizationForm normalizationForm) + { + if (GlobalizationMode.Invariant) + { + // In Invariant mode we assume all characters are normalized. + // This is because we don't support any linguistic operation on the strings + return strInput; + } + + return GlobalizationMode.UseNls ? + NlsNormalize(strInput, normalizationForm) : + IcuNormalize(strInput, normalizationForm); + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.Icu.cs similarity index 90% rename from src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.Unix.cs rename to src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.Icu.cs index ef6cff420b9d2..d499c408530dd 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.Icu.cs @@ -10,8 +10,6 @@ public partial class TextInfo { private Tristate _needsTurkishCasing = Tristate.NotInitialized; - private void FinishInitialization() { } - // ----------------------------- // ---- PAL layer ends here ---- // ----------------------------- @@ -25,9 +23,10 @@ private static bool NeedsTurkishCasing(string localeName) private bool IsInvariant { get { return _cultureName.Length == 0; } } - internal unsafe void ChangeCase(char* src, int srcLen, char* dstBuffer, int dstBufferCapacity, bool bToUpper) + internal unsafe void IcuChangeCase(char* src, int srcLen, char* dstBuffer, int dstBufferCapacity, bool bToUpper) { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(!GlobalizationMode.UseNls); if (IsInvariant) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.Nls.cs similarity index 88% rename from src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.Windows.cs rename to src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.Nls.cs index 948644769da09..b49552f2062ae 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.Nls.cs @@ -8,14 +8,10 @@ namespace System.Globalization { public partial class TextInfo { - private unsafe void FinishInitialization() - { - _sortHandle = CompareInfo.GetSortHandle(_textInfoName); - } - - private unsafe void ChangeCase(char* pSource, int pSourceLen, char* pResult, int pResultLen, bool toUpper) + private unsafe void NlsChangeCase(char* pSource, int pSourceLen, char* pResult, int pResultLen, bool toUpper) { Debug.Assert(!GlobalizationMode.Invariant); + Debug.Assert(GlobalizationMode.UseNls); Debug.Assert(pSource != null); Debug.Assert(pResult != null); Debug.Assert(pSourceLen >= 0); diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.cs index 7e8d1ff673e39..14d564258043a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.cs @@ -54,7 +54,10 @@ internal TextInfo(CultureData cultureData) _cultureName = _cultureData.CultureName; _textInfoName = _cultureData.TextInfoName; - FinishInitialization(); + if (GlobalizationMode.UseNls) + { + _sortHandle = CompareInfo.NlsGetSortHandle(_textInfoName); + } } private TextInfo(CultureData cultureData, bool readOnly) @@ -176,7 +179,7 @@ private unsafe char ChangeCase(char c, bool toUpper) Debug.Assert(!GlobalizationMode.Invariant); char dst = default; - ChangeCase(&c, 1, &dst, 1, toUpper); + ChangeCaseCore(&c, 1, &dst, 1, toUpper); return dst; } @@ -301,7 +304,7 @@ private unsafe void ChangeCaseCommon(ref char source, ref char dest // has a case conversion that's different from the invariant culture, even for ASCII data (e.g., tr-TR converts // 'i' (U+0069) to Latin Capital Letter I With Dot Above (U+0130)). - ChangeCase(pSource + currIdx, charCount, pDestination + currIdx, charCount, toUpper); + ChangeCaseCore(pSource + currIdx, charCount, pDestination + currIdx, charCount, toUpper); } Return: @@ -406,7 +409,7 @@ private unsafe string ChangeCaseCommon(string source) where TConver // and run the culture-aware logic over the remainder of the data fixed (char* pResult = result) { - ChangeCase(pSource + currIdx, source.Length - (int)currIdx, pResult + currIdx, result.Length - (int)currIdx, toUpper); + ChangeCaseCore(pSource + currIdx, source.Length - (int)currIdx, pResult + currIdx, result.Length - (int)currIdx, toUpper); } return result; } @@ -815,6 +818,18 @@ private int AddTitlecaseLetter(ref StringBuilder result, ref string input, int i return inputIndex; } + private unsafe void ChangeCaseCore(char* src, int srcLen, char* dstBuffer, int dstBufferCapacity, bool bToUpper) + { + if (GlobalizationMode.UseNls) + { + NlsChangeCase(src, srcLen, dstBuffer, dstBufferCapacity, bToUpper); + } + else + { + IcuChangeCase(src, srcLen, dstBuffer, dstBufferCapacity, bToUpper); + } + } + // Used in ToTitleCase(): // When we find a starting letter, the following array decides if a category should be // considered as word seprator or not. diff --git a/src/libraries/System.Runtime/tests/NlsTests/System.Runtime.Nls.Tests.csproj b/src/libraries/System.Runtime/tests/NlsTests/System.Runtime.Nls.Tests.csproj new file mode 100644 index 0000000000000..fa46b81ababd8 --- /dev/null +++ b/src/libraries/System.Runtime/tests/NlsTests/System.Runtime.Nls.Tests.csproj @@ -0,0 +1,61 @@ + + + true + true + true + $(NetCoreAppCurrent)-Windows_NT + + + + Helpers.cs + + + System\ArrayTests.cs + + + System\String.SplitTests.cs + + + System\StringComparerTests.cs + + + System\StringGetHashCodeTests.cs + + + System\StringSplitExtensions.cs + + + System\StringTests.cs + + + System\Text\RuneTests.cs + + + System\Text\RuneTests.TestData.cs + + + System\Text\StringBuilderTests.cs + + + System\Uri.CreateStringTests.cs + + + System\Uri.CreateUriTests.cs + + + System\Uri.MethodsTests.cs + + + Common\System\EnumTypes.cs + + + Common\System\MockType.cs + + + Common\System\StringTests.cs + + + + + + diff --git a/src/libraries/System.Runtime/tests/NlsTests/runtimeconfig.template.json b/src/libraries/System.Runtime/tests/NlsTests/runtimeconfig.template.json new file mode 100644 index 0000000000000..f93c6039127bd --- /dev/null +++ b/src/libraries/System.Runtime/tests/NlsTests/runtimeconfig.template.json @@ -0,0 +1,5 @@ +{ + "configProperties": { + "System.Globalization.UseNls": true + } +} diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj index bff03ab27acf5..f517e557666bd 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj @@ -32,7 +32,7 @@ Common\System\Collections\TestBase.NonGeneric.cs - System\StringTests.cs + Common\System\StringTests.cs Common\System\Collections\IDictionary.NonGeneric.Tests.cs diff --git a/src/libraries/System.Runtime/tests/System/Text/RuneTests.cs b/src/libraries/System.Runtime/tests/System/Text/RuneTests.cs index 6772d7438c34d..e22f802fc07ef 100644 --- a/src/libraries/System.Runtime/tests/System/Text/RuneTests.cs +++ b/src/libraries/System.Runtime/tests/System/Text/RuneTests.cs @@ -12,7 +12,7 @@ namespace System.Text.Tests { public static partial class RuneTests { - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsWindows8xOrLater))] // the localization tables used by our test data only exist on Win8+ + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsWindows8xOrLater), nameof(PlatformDetection.IsNlsGlobalization))] // the localization tables used by our test data only exist on Win8+ [PlatformSpecific(TestPlatforms.Windows)] [InlineData('0', '0', '0', "en-US")] [InlineData('a', 'A', 'a', "en-US")] @@ -38,7 +38,7 @@ public static void Casing_CultureAware(int original, int upper, int lower, strin } // Invariant ToUpper / ToLower doesn't modify Turkish I or majuscule Eszett - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsWindows8xOrLater))] // the localization tables used by our test data only exist on Win8+ + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsWindows8xOrLater), nameof(PlatformDetection.IsNlsGlobalization))] // the localization tables used by our test data only exist on Win8+ [PlatformSpecific(TestPlatforms.Windows)] [InlineData('0', '0', '0')] [InlineData('a', 'A', 'a')] diff --git a/src/libraries/System.Runtime/tests/System/Uri.MethodsTests.cs b/src/libraries/System.Runtime/tests/System/Uri.MethodsTests.cs index 444aca48cfa63..a6fce5f4bcb2f 100644 --- a/src/libraries/System.Runtime/tests/System/Uri.MethodsTests.cs +++ b/src/libraries/System.Runtime/tests/System/Uri.MethodsTests.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Runtime.InteropServices; using Xunit; @@ -536,7 +537,7 @@ public static IEnumerable GetComponents_Basic_TestData() Uri invalidPunicodeUri = new Uri("http://xn--\u1234pck.com"); yield return new object[] { invalidPunicodeUri, UriComponents.Host, "xn--\u1234pck.com" }; - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) // expected platform differences, see https://github.com/dotnet/runtime/issues/17190 + if (PlatformDetection.IsNlsGlobalization) // expected platform differences, see https://github.com/dotnet/runtime/issues/17190 { yield return new object[] { invalidPunicodeUri, UriComponents.NormalizedHost, "xn--\u1234pck.com" }; } diff --git a/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Comparison.cs b/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Comparison.cs index 8e139fb63312f..7d0c5bad6141e 100644 --- a/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Comparison.cs +++ b/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Comparison.cs @@ -47,8 +47,8 @@ public static void Equals_Ordinal() AssertEqualOrdinal(Utf8Span.Empty, u8("")); } - [Theory] [PlatformSpecific(TestPlatforms.Windows)] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))] [InlineData(null, null, StringComparison.OrdinalIgnoreCase, null, true)] [InlineData("encyclopaedia", "encyclopædia", StringComparison.OrdinalIgnoreCase, null, false)] [InlineData("encyclopaedia", "encyclopædia", StringComparison.InvariantCulture, null, true)] diff --git a/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Searching.cs b/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Searching.cs index 36b1842f48a7e..a9c4102b11dd4 100644 --- a/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Searching.cs +++ b/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.Searching.cs @@ -80,8 +80,8 @@ public static void TryFind_Char_Ordinal(ustring source, char searchTerm, Range? } } - [Theory] [PlatformSpecific(TestPlatforms.Windows)] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))] [MemberData(nameof(TryFindData_Char_WithComparison))] public static void TryFind_Char_WithComparison(ustring source, char searchTerm, StringComparison comparison, CultureInfo currentCulture, Range? expectedForwardMatch, Range? expectedBackwardMatch) { @@ -227,8 +227,8 @@ public static void TryFind_Rune_Ordinal(ustring source, Rune searchTerm, Range? } } - [Theory] [PlatformSpecific(TestPlatforms.Windows)] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))] [MemberData(nameof(TryFindData_Rune_WithComparison))] public static void TryFind_Rune_WithComparison(ustring source, Rune searchTerm, StringComparison comparison, CultureInfo currentCulture, Range? expectedForwardMatch, Range? expectedBackwardMatch) { @@ -374,8 +374,8 @@ public static void TryFind_Utf8Span_Ordinal(ustring source, ustring searchTerm, } } - [Theory] [PlatformSpecific(TestPlatforms.Windows)] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))] [MemberData(nameof(TryFindData_Utf8Span_WithComparison))] public static void TryFind_Utf8Span_WithComparison(ustring source, ustring searchTerm, StringComparison comparison, CultureInfo currentCulture, Range? expectedForwardMatch, Range? expectedBackwardMatch) { diff --git a/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.cs b/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.cs index ab1b528d49674..cb169fd2423b0 100644 --- a/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.cs +++ b/src/libraries/System.Utf8String.Experimental/tests/System/Utf8SpanTests.cs @@ -76,8 +76,8 @@ public static void GetHashCode_Ordinal() } } - [Fact] [PlatformSpecific(TestPlatforms.Windows)] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))] public static void GetHashCode_WithComparison() { // Since hash code generation is randomized, it's possible (though unlikely) that diff --git a/src/libraries/System.Utf8String.Experimental/tests/System/Utf8StringTests.Searching.cs b/src/libraries/System.Utf8String.Experimental/tests/System/Utf8StringTests.Searching.cs index 245877de18b0d..f25d0e90699df 100644 --- a/src/libraries/System.Utf8String.Experimental/tests/System/Utf8StringTests.Searching.cs +++ b/src/libraries/System.Utf8String.Experimental/tests/System/Utf8StringTests.Searching.cs @@ -87,8 +87,8 @@ public static void TryFind_Char_Ordinal(ustring source, char searchTerm, Range? public static IEnumerable TryFindData_Char_WithComparison() => Utf8SpanTests.TryFindData_Char_WithComparison(); - [Theory] [PlatformSpecific(TestPlatforms.Windows)] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))] [MemberData(nameof(TryFindData_Char_WithComparison))] public static void TryFind_Char_WithComparison(ustring source, char searchTerm, StringComparison comparison, CultureInfo currentCulture, Range? expectedForwardMatch, Range? expectedBackwardMatch) { @@ -240,8 +240,8 @@ public static void TryFind_Rune_Ordinal(ustring source, Rune searchTerm, Range? public static IEnumerable TryFindData_Rune_WithComparison() => Utf8SpanTests.TryFindData_Rune_WithComparison(); - [Theory] [PlatformSpecific(TestPlatforms.Windows)] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))] [MemberData(nameof(TryFindData_Rune_WithComparison))] public static void TryFind_Rune_WithComparison(ustring source, Rune searchTerm, StringComparison comparison, CultureInfo currentCulture, Range? expectedForwardMatch, Range? expectedBackwardMatch) { @@ -393,8 +393,8 @@ public static void TryFind_Utf8String_Ordinal(ustring source, ustring searchTerm public static IEnumerable TryFindData_Utf8String_WithComparison() => Utf8SpanTests.TryFindData_Utf8Span_WithComparison(); - [Theory] [PlatformSpecific(TestPlatforms.Windows)] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))] [MemberData(nameof(TryFindData_Utf8String_WithComparison))] public static void TryFind_Utf8String_WithComparison(ustring source, ustring searchTerm, StringComparison comparison, CultureInfo currentCulture, Range? expectedForwardMatch, Range? expectedBackwardMatch) { diff --git a/src/libraries/System.Utf8String.Experimental/tests/System/Utf8StringTests.cs b/src/libraries/System.Utf8String.Experimental/tests/System/Utf8StringTests.cs index 260382383a68a..773dea57bf9ce 100644 --- a/src/libraries/System.Utf8String.Experimental/tests/System/Utf8StringTests.cs +++ b/src/libraries/System.Utf8String.Experimental/tests/System/Utf8StringTests.cs @@ -78,8 +78,8 @@ public static void GetHashCode_Ordinal() } } - [Fact] [PlatformSpecific(TestPlatforms.Windows)] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNlsGlobalization))] public static void GetHashCode_WithComparison() { // Since hash code generation is randomized, it's possible (though unlikely) that diff --git a/src/mono/netcore/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Unix.Mono.cs b/src/mono/netcore/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Unix.Mono.cs index c31e58411a852..e3ca7ce617366 100644 --- a/src/mono/netcore/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Unix.Mono.cs +++ b/src/mono/netcore/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Unix.Mono.cs @@ -8,6 +8,8 @@ namespace System.Globalization { internal partial class GlobalizationMode { + internal static bool UseNls => false; + private static bool GetGlobalizationInvariantMode() { bool invariantEnabled = GetInvariantSwitchValue(); @@ -19,7 +21,7 @@ private static bool GetGlobalizationInvariantMode() } // Keep this in a separate method to avoid loading the native lib in invariant mode - [MethodImplAttribute(MethodImplOptions.NoInlining)] + [MethodImpl(MethodImplOptions.NoInlining)] private static void LoadICU() { int res = Interop.Globalization.LoadICU(); diff --git a/src/mono/netcore/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Windows.Mono.cs b/src/mono/netcore/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Windows.Mono.cs index 326723995fed6..5aaa143170376 100644 --- a/src/mono/netcore/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Windows.Mono.cs +++ b/src/mono/netcore/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Windows.Mono.cs @@ -6,9 +6,11 @@ namespace System.Globalization { internal partial class GlobalizationMode { + internal static bool UseNls => true; + private static bool GetGlobalizationInvariantMode() { return GetInvariantSwitchValue(); } } -} \ No newline at end of file +}