Skip to content

Commit

Permalink
[iOS][non-icu] HybridGlobalization implement GetSortKey on hybrid mode (
Browse files Browse the repository at this point in the history
#95260)

Implement GetSortKey on hybrid mode
  • Loading branch information
mkhamoyan committed Dec 6, 2023
1 parent ec31705 commit b345e2d
Show file tree
Hide file tree
Showing 12 changed files with 277 additions and 148 deletions.
4 changes: 3 additions & 1 deletion docs/design/features/globalization-hybrid-mode.md
Expand Up @@ -463,7 +463,9 @@ Affected public APIs:
- CompareInfo.GetSortKeyLength
- CompareInfo.GetHashCode

Apple Native API does not have an equivalent, so they throw `PlatformNotSupportedException`.
Implemeneted using [stringByFoldingWithOptions:locale:](https://developer.apple.com/documentation/foundation/nsstring/1413779-stringbyfoldingwithoptions)

Note: This implementation does not construct SortKeys like ICU ucol_getSortKey does, and might not adhere to the specifications specifications of SortKey such as SortKeys from different collators not being comparable and merging sortkeys.


## Case change
Expand Down
3 changes: 3 additions & 0 deletions src/libraries/Common/src/Interop/Interop.Collation.iOS.cs
Expand Up @@ -29,5 +29,8 @@ internal static partial class Globalization
[LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_StartsWithNative", StringMarshalling = StringMarshalling.Utf16)]
[MethodImpl(MethodImplOptions.NoInlining)]
internal static unsafe partial int StartsWithNative(string localeName, int lNameLen, char* target, int cwTargetLength, char* source, int cwSourceLength, CompareOptions options);

[LibraryImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetSortKeyNative", StringMarshalling = StringMarshalling.Utf16)]
internal static unsafe partial int GetSortKeyNative(string localeName, int lNameLen, char* str, int strLength, byte* sortKey, int sortKeyLength, CompareOptions options);
}
}
26 changes: 16 additions & 10 deletions src/libraries/Common/tests/Tests/System/StringTests.cs
Expand Up @@ -1009,6 +1009,7 @@ public static void MakeSureNoCompareToChecksGoOutOfRange_StringComparison()
}

[Fact]
[ActiveIssue("https://github.com/dotnet/runtime/issues/95338", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnOSX))]
public static void CompareToNoMatch_StringComparison()
{
for (int length = 1; length < 150; length++)
Expand Down Expand Up @@ -1283,6 +1284,7 @@ public static void ContainsMatchDifferentSpans_StringComparison()
}

[Fact]
[ActiveIssue("https://github.com/dotnet/runtime/issues/95338", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnOSX))]
public static void ContainsNoMatch_StringComparison()
{
for (int length = 1; length < 150; length++)
Expand Down Expand Up @@ -1660,7 +1662,7 @@ public static IEnumerable<object[]> EndsWith_StringComparison_TestData()
yield return new object[] { "", "", StringComparison.CurrentCulture, true };
yield return new object[] { "", "a", StringComparison.CurrentCulture, false };

if (PlatformDetection.IsNotInvariantGlobalization)
if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnOSX)
yield return new object[] { "Hello", "llo" + SoftHyphen, StringComparison.CurrentCulture, true };

// CurrentCultureIgnoreCase
Expand All @@ -1672,7 +1674,7 @@ public static IEnumerable<object[]> EndsWith_StringComparison_TestData()
yield return new object[] { "", "", StringComparison.CurrentCultureIgnoreCase, true };
yield return new object[] { "", "a", StringComparison.CurrentCultureIgnoreCase, false };

if (PlatformDetection.IsNotInvariantGlobalization)
if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnOSX)
yield return new object[] { "Hello", "llo" + SoftHyphen, StringComparison.CurrentCultureIgnoreCase, true };

// InvariantCulture
Expand All @@ -1685,7 +1687,7 @@ public static IEnumerable<object[]> EndsWith_StringComparison_TestData()
yield return new object[] { "", "", StringComparison.InvariantCulture, true };
yield return new object[] { "", "a", StringComparison.InvariantCulture, false };

if (PlatformDetection.IsNotInvariantGlobalization)
if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnOSX)
yield return new object[] { "Hello", "llo" + SoftHyphen, StringComparison.InvariantCulture, true };

// InvariantCultureIgnoreCase
Expand All @@ -1697,7 +1699,7 @@ public static IEnumerable<object[]> EndsWith_StringComparison_TestData()
yield return new object[] { "", "", StringComparison.InvariantCultureIgnoreCase, true };
yield return new object[] { "", "a", StringComparison.InvariantCultureIgnoreCase, false };

if (PlatformDetection.IsNotInvariantGlobalization)
if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnOSX)
yield return new object[] { "Hello", "llo" + SoftHyphen, StringComparison.InvariantCultureIgnoreCase, true };

// Ordinal
Expand Down Expand Up @@ -2109,6 +2111,7 @@ public static void EndsWithMatchDifferentSpans_StringComparison()
}

[Fact]
[ActiveIssue("https://github.com/dotnet/runtime/issues/95338", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnOSX))]
public static void EndsWithNoMatch_StringComparison()
{
for (int length = 1; length < 150; length++)
Expand Down Expand Up @@ -3194,7 +3197,7 @@ public static void IndexOf_TurkishI_EnglishUSCulture()
}
}

[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotInvariantGlobalization))]
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotInvariantGlobalization), nameof(PlatformDetection.IsNotHybridGlobalizationOnOSX))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/60568", TestPlatforms.Android | TestPlatforms.LinuxBionic)]
public static void IndexOf_HungarianDoubleCompression_HungarianCulture()
{
Expand Down Expand Up @@ -4849,7 +4852,7 @@ public static IEnumerable<object[]> StartsWith_StringComparison_TestData()
yield return new object[] { "", "", StringComparison.CurrentCulture, true };
yield return new object[] { "", "hello", StringComparison.CurrentCulture, false };

if (PlatformDetection.IsNotInvariantGlobalization)
if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnOSX)
yield return new object[] { "Hello", SoftHyphen + "Hel", StringComparison.CurrentCulture, true };

// CurrentCultureIgnoreCase
Expand All @@ -4861,7 +4864,7 @@ public static IEnumerable<object[]> StartsWith_StringComparison_TestData()
yield return new object[] { "", "", StringComparison.CurrentCultureIgnoreCase, true };
yield return new object[] { "", "hello", StringComparison.CurrentCultureIgnoreCase, false };

if (PlatformDetection.IsNotInvariantGlobalization)
if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnOSX)
yield return new object[] { "Hello", SoftHyphen + "Hel", StringComparison.CurrentCultureIgnoreCase, true };

// InvariantCulture
Expand All @@ -4873,7 +4876,7 @@ public static IEnumerable<object[]> StartsWith_StringComparison_TestData()
yield return new object[] { "", "", StringComparison.InvariantCulture, true };
yield return new object[] { "", "hello", StringComparison.InvariantCulture, false };

if (PlatformDetection.IsNotInvariantGlobalization)
if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnOSX)
yield return new object[] { "Hello", SoftHyphen + "Hel", StringComparison.InvariantCulture, true };

// InvariantCultureIgnoreCase
Expand All @@ -4885,7 +4888,7 @@ public static IEnumerable<object[]> StartsWith_StringComparison_TestData()
yield return new object[] { "", "", StringComparison.InvariantCultureIgnoreCase, true };
yield return new object[] { "", "hello", StringComparison.InvariantCultureIgnoreCase, false };

if (PlatformDetection.IsNotInvariantGlobalization)
if (PlatformDetection.IsNotInvariantGlobalization && PlatformDetection.IsNotHybridGlobalizationOnOSX)
yield return new object[] { "Hello", SoftHyphen + "Hel", StringComparison.InvariantCultureIgnoreCase, true };

// Ordinal
Expand Down Expand Up @@ -5342,6 +5345,7 @@ private static IEnumerable<object[]> ToLower_Culture_TestData()
}

[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotInvariantGlobalization))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/95338", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnOSX))]
public static void Test_ToLower_Culture()
{
foreach (object[] testdata in ToLower_Culture_TestData())
Expand Down Expand Up @@ -5857,6 +5861,7 @@ public static IEnumerable<object[]> ToUpper_Culture_TestData()
}

[ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotInvariantGlobalization))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/95338", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnOSX))]
[MemberData(nameof(ToUpper_Culture_TestData))]
public static void Test_ToUpper_Culture(string actual, string expected, CultureInfo culture)
{
Expand Down Expand Up @@ -5955,7 +5960,7 @@ public static void ToUpper_TurkishI_EnglishUSCulture(string s, string expected)
new KeyValuePair<char, char>('\u0130', '\u0130'),
new KeyValuePair<char, char>('\u0131', '\u0131'));

[ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotInvariantGlobalization))]
[ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotInvariantGlobalization), nameof(PlatformDetection.IsNotHybridGlobalizationOnOSX))]
[MemberData(nameof(ToUpper_TurkishI_InvariantCulture_MemberData))]
public static void ToUpper_TurkishI_InvariantCulture(string s, string expected)
{
Expand Down Expand Up @@ -7225,6 +7230,7 @@ public static void StartsWithMatchDifferentSpans_StringComparison()
}

[Fact]
[ActiveIssue("https://github.com/dotnet/runtime/issues/95338", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnOSX))]
public static void StartsWithNoMatch_StringComparison()
{
for (int length = 1; length < 150; length++)
Expand Down
Expand Up @@ -700,14 +700,36 @@ private unsafe SortKey IcuCreateSortKey(string source, CompareOptions options)
byte[] keyData;
fixed (char* pSource = source)
{
int sortKeyLength = Interop.Globalization.GetSortKey(_sortHandle, pSource, source.Length, null, 0, options);
int sortKeyLength;
#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
if (GlobalizationMode.Hybrid)
{
sortKeyLength = Interop.Globalization.GetSortKeyNative(m_name, m_name.Length, pSource, source.Length, null, 0, options);
}
else
#endif
{
sortKeyLength = Interop.Globalization.GetSortKey(_sortHandle, pSource, source.Length, null, 0, options);
}
keyData = new byte[sortKeyLength];

fixed (byte* pSortKey = keyData)
{
if (Interop.Globalization.GetSortKey(_sortHandle, pSource, source.Length, pSortKey, sortKeyLength, options) != sortKeyLength)
#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
if (GlobalizationMode.Hybrid)
{
throw new ArgumentException(SR.Arg_ExternalException);
if (Interop.Globalization.GetSortKeyNative(m_name, m_name.Length, pSource, source.Length, pSortKey, sortKeyLength, options) != sortKeyLength)
{
throw new ArgumentException(SR.Arg_ExternalException);
}
}
else
#endif
{
if (Interop.Globalization.GetSortKey(_sortHandle, pSource, source.Length, pSortKey, sortKeyLength, options) != sortKeyLength)
{
throw new ArgumentException(SR.Arg_ExternalException);
}
}
}
}
Expand All @@ -728,7 +750,16 @@ private unsafe int IcuGetSortKey(ReadOnlySpan<char> source, Span<byte> destinati
fixed (char* pSource = &MemoryMarshal.GetReference(source))
fixed (byte* pDest = &MemoryMarshal.GetReference(destination))
{
actualSortKeyLength = Interop.Globalization.GetSortKey(_sortHandle, pSource, source.Length, pDest, destination.Length, options);
#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
if (GlobalizationMode.Hybrid)
{
actualSortKeyLength = Interop.Globalization.GetSortKeyNative(m_name, m_name.Length, pSource, source.Length, pDest, destination.Length, options);
}
else
#endif
{
actualSortKeyLength = Interop.Globalization.GetSortKey(_sortHandle, pSource, source.Length, pDest, destination.Length, options);
}
}

// The check below also handles errors due to negative values / overflow being returned.
Expand Down Expand Up @@ -758,7 +789,16 @@ private unsafe int IcuGetSortKeyLength(ReadOnlySpan<char> source, CompareOptions

fixed (char* pSource = &MemoryMarshal.GetReference(source))
{
return Interop.Globalization.GetSortKey(_sortHandle, pSource, source.Length, null, 0, options);
#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
if (GlobalizationMode.Hybrid)
{
return Interop.Globalization.GetSortKeyNative(m_name, m_name.Length, pSource, source.Length, null, 0, options);
}
else
#endif
{
return Interop.Globalization.GetSortKey(_sortHandle, pSource, source.Length, null, 0, options);
}
}
}

Expand Down Expand Up @@ -809,7 +849,16 @@ private unsafe int IcuGetHashCodeOfString(ReadOnlySpan<char> source, CompareOpti
{
fixed (byte* pSortKey = &MemoryMarshal.GetReference(sortKey))
{
sortKeyLength = Interop.Globalization.GetSortKey(_sortHandle, pSource, source.Length, pSortKey, sortKey.Length, options);
#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
if (GlobalizationMode.Hybrid)
{
sortKeyLength = Interop.Globalization.GetSortKeyNative(m_name, m_name.Length, pSource, source.Length, pSortKey, sortKey.Length, options);
}
else
#endif
{
sortKeyLength = Interop.Globalization.GetSortKey(_sortHandle, pSource, source.Length, pSortKey, sortKey.Length, options);
}
}

if (sortKeyLength > sortKey.Length) // slow path for big strings
Expand All @@ -823,7 +872,16 @@ private unsafe int IcuGetHashCodeOfString(ReadOnlySpan<char> source, CompareOpti

fixed (byte* pSortKey = &MemoryMarshal.GetReference(sortKey))
{
sortKeyLength = Interop.Globalization.GetSortKey(_sortHandle, pSource, source.Length, pSortKey, sortKey.Length, options);
#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
if (GlobalizationMode.Hybrid)
{
sortKeyLength = Interop.Globalization.GetSortKeyNative(m_name, m_name.Length, pSource, source.Length, pSortKey, sortKey.Length, options);
}
else
#endif
{
sortKeyLength = Interop.Globalization.GetSortKey(_sortHandle, pSource, source.Length, pSortKey, sortKey.Length, options);
}
}
}
}
Expand Down
Expand Up @@ -1450,7 +1450,7 @@ public SortKey GetSortKey(string source)
private SortKey CreateSortKeyCore(string source, CompareOptions options) =>
GlobalizationMode.UseNls ?
NlsCreateSortKey(source, options) :
#if TARGET_BROWSER || TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
#if TARGET_BROWSER
GlobalizationMode.Hybrid ?
throw new PlatformNotSupportedException(GetPNSEText("SortKey")) :
#endif
Expand Down Expand Up @@ -1493,7 +1493,7 @@ public int GetSortKey(ReadOnlySpan<char> source, Span<byte> destination, Compare
private int GetSortKeyCore(ReadOnlySpan<char> source, Span<byte> destination, CompareOptions options) =>
GlobalizationMode.UseNls ?
NlsGetSortKey(source, destination, options) :
#if TARGET_BROWSER || TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
#if TARGET_BROWSER
GlobalizationMode.Hybrid ?
throw new PlatformNotSupportedException(GetPNSEText("SortKey")) :
#endif
Expand Down Expand Up @@ -1530,7 +1530,7 @@ public int GetSortKeyLength(ReadOnlySpan<char> source, CompareOptions options =
private int GetSortKeyLengthCore(ReadOnlySpan<char> source, CompareOptions options) =>
GlobalizationMode.UseNls ?
NlsGetSortKeyLength(source, options) :
#if TARGET_BROWSER || TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
#if TARGET_BROWSER
GlobalizationMode.Hybrid ?
throw new PlatformNotSupportedException(GetPNSEText("SortKey")) :
#endif
Expand Down Expand Up @@ -1607,7 +1607,7 @@ public int GetHashCode(ReadOnlySpan<char> source, CompareOptions options)
private unsafe int GetHashCodeOfStringCore(ReadOnlySpan<char> source, CompareOptions options) =>
GlobalizationMode.UseNls ?
NlsGetHashCodeOfString(source, options) :
#if TARGET_BROWSER || TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
#if TARGET_BROWSER
GlobalizationMode.Hybrid ?
throw new PlatformNotSupportedException(GetPNSEText("HashCode")) :
#endif
Expand Down

0 comments on commit b345e2d

Please sign in to comment.