diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.iOS.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.iOS.cs index 3ad9df0fcc25eb..10ecbff90b2216 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.iOS.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.iOS.cs @@ -80,7 +80,7 @@ private static void AssertComparisonSupported(CompareOptions options) } private const CompareOptions SupportedCompareOptions = CompareOptions.None | CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace | - CompareOptions.IgnoreWidth | CompareOptions.StringSort | CompareOptions.IgnoreKanaType; + CompareOptions.IgnoreWidth | CompareOptions.StringSort | CompareOptions.IgnoreKanaType | CompareOptions.IgnoreSymbols; private static string GetPNSE(CompareOptions options) => SR.Format(SR.PlatformNotSupported_HybridGlobalizationWithCompareOptions, options); diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.Compare.cs b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.Compare.cs index c3e5fd0e586a81..a8e955e9a6eaa3 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.Compare.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.Compare.cs @@ -225,11 +225,7 @@ public static IEnumerable Compare_TestData() yield return new object[] { s_invariantCompare, "FooBar", "Foo\u0400Bar", CompareOptions.Ordinal, -1 }; yield return new object[] { s_invariantCompare, "FooBA\u0300R", "FooB\u00C0R", CompareOptions.IgnoreNonSpace, 0 }; - // In HybridGlobalization on Apple platforms IgnoreSymbols is not supported - if (PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) - { - yield return new object[] { s_invariantCompare, "Test's", "Tests", CompareOptions.IgnoreSymbols, 0 }; - } + yield return new object[] { s_invariantCompare, "Test's", "Tests", CompareOptions.IgnoreSymbols, 0 }; yield return new object[] { s_invariantCompare, "Test's", "Tests", CompareOptions.StringSort, -1 }; yield return new object[] { s_invariantCompare, null, "Tests", CompareOptions.None, -1 }; @@ -251,26 +247,15 @@ public static IEnumerable Compare_TestData() yield return new object[] { s_invariantCompare, "\uFF9E", "\u3099", CompareOptions.IgnoreCase, 0 }; yield return new object[] { s_invariantCompare, "\u20A9", "\uFFE6", CompareOptions.IgnoreCase, -1 }; yield return new object[] { s_invariantCompare, "\u20A9", "\uFFE6", CompareOptions.None, -1 }; - - // In HybridGlobalization mode on Apple platforms IgnoreSymbols is not supported - if (PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) - { - yield return new object[] { s_invariantCompare, "\u0021", "\uFF01", CompareOptions.IgnoreSymbols, 0 }; - yield return new object[] { s_invariantCompare, "\uFF65", "\u30FB", CompareOptions.IgnoreSymbols, 0 }; - yield return new object[] { s_invariantCompare, "\u00A2", "\uFFE0", CompareOptions.IgnoreSymbols, 0 }; - yield return new object[] { s_invariantCompare, "$", "&", CompareOptions.IgnoreSymbols, 0 }; - } + yield return new object[] { s_invariantCompare, "\u0021", "\uFF01", CompareOptions.IgnoreSymbols, 0 }; + yield return new object[] { s_invariantCompare, "\uFF65", "\u30FB", CompareOptions.IgnoreSymbols, 0 }; + yield return new object[] { s_invariantCompare, "\u00A2", "\uFFE0", CompareOptions.IgnoreSymbols, 0 }; + yield return new object[] { s_invariantCompare, "$", "&", CompareOptions.IgnoreSymbols, 0 }; yield return new object[] { s_invariantCompare, "\u0021", "\uFF01", CompareOptions.None, -1 }; yield return new object[] { s_invariantCompare, "\u20A9", "\uFFE6", CompareOptions.IgnoreWidth, 0 }; yield return new object[] { s_invariantCompare, "\u0021", "\uFF01", CompareOptions.IgnoreWidth, 0 }; yield return new object[] { s_invariantCompare, "\uFF66", "\u30F2", CompareOptions.IgnoreWidth, 0 }; - - // In HybridGlobalization mode on Apple platforms IgnoreSymbols is not supported - if(PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) - { - yield return new object[] { s_invariantCompare, "\uFF66", "\u30F2", CompareOptions.IgnoreSymbols, s_expectedHalfToFullFormsComparison }; - } - + yield return new object[] { s_invariantCompare, "\uFF66", "\u30F2", CompareOptions.IgnoreSymbols, s_expectedHalfToFullFormsComparison }; yield return new object[] { s_invariantCompare, "\uFF66", "\u30F2", CompareOptions.IgnoreCase, s_expectedHalfToFullFormsComparison }; yield return new object[] { s_invariantCompare, "\uFF66", "\u30F2", CompareOptions.IgnoreNonSpace, s_expectedHalfToFullFormsComparison }; yield return new object[] { s_invariantCompare, "\uFF66", "\u30F2", CompareOptions.None, s_expectedHalfToFullFormsComparison }; diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.IndexOf.cs b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.IndexOf.cs index 9e6e46db401f4e..73bf956baf5f84 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.IndexOf.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.IndexOf.cs @@ -83,8 +83,7 @@ public static IEnumerable IndexOf_TestData() yield return new object[] { s_invariantCompare, "hello", "\u200d", 1, 3, CompareOptions.IgnoreCase, 1, 0 }; // Ignore symbols - if (PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) // IgnoreSymbols are not supported - yield return new object[] { s_invariantCompare, "More Test's", "Tests", 0, 11, CompareOptions.IgnoreSymbols, 5, 6 }; + yield return new object[] { s_invariantCompare, "More Test's", "Tests", 0, 11, CompareOptions.IgnoreSymbols, 5, 6 }; yield return new object[] { s_invariantCompare, "More Test's", "Tests", 0, 11, CompareOptions.None, -1, 0 }; yield return new object[] { s_invariantCompare, "cbabababdbaba", "ab", 0, 13, CompareOptions.None, 2, 2 }; diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.IsPrefix.cs b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.IsPrefix.cs index 3fba71b155c171..9748687f19c035 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.IsPrefix.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.IsPrefix.cs @@ -72,29 +72,22 @@ public static IEnumerable IsPrefix_TestData() yield return new object[] { s_invariantCompare, "\uD800\uD800", "\uD800\uD800", CompareOptions.None, true, 2 }; // Ignore symbols - if (PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) - { - yield return new object[] { s_invariantCompare, "Test's can be interesting", "Tests", CompareOptions.IgnoreSymbols, true, 6 }; - yield return new object[] { s_invariantCompare, "Test's can be interesting", "Tests", CompareOptions.None, false, 0 }; - } + yield return new object[] { s_invariantCompare, "Test's can be interesting", "Tests", CompareOptions.IgnoreSymbols, true, 6 }; + yield return new object[] { s_invariantCompare, "Test's can be interesting", "Tests", CompareOptions.None, false, 0 }; // Platform differences if (PlatformDetection.IsNlsGlobalization) { - if (PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) - { - yield return new object[] { s_hungarianCompare, "dzsdzsfoobar", "ddzsf", CompareOptions.None, true, 7 }; - yield return new object[] { s_invariantCompare, "''Tests", "Tests", CompareOptions.IgnoreSymbols, true, 7 }; - yield return new object[] { s_frenchCompare, "\u0153", "oe", CompareOptions.None, true, 1 }; - } + yield return new object[] { s_hungarianCompare, "dzsdzsfoobar", "ddzsf", CompareOptions.None, true, 7 }; + yield return new object[] { s_invariantCompare, "''Tests", "Tests", CompareOptions.IgnoreSymbols, true, 7 }; + yield return new object[] { s_frenchCompare, "\u0153", "oe", CompareOptions.None, true, 1 }; yield return new object[] { s_invariantCompare, "\uD800\uDC00", "\uD800", CompareOptions.None, true, 1 }; yield return new object[] { s_invariantCompare, "\uD800\uDC00", "\uD800", CompareOptions.IgnoreCase, true, 1 }; } else { yield return new object[] { s_hungarianCompare, "dzsdzsfoobar", "ddzsf", CompareOptions.None, false, 0 }; - if (PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) - yield return new object[] { s_invariantCompare, "''Tests", "Tests", CompareOptions.IgnoreSymbols, false, 0 }; + yield return new object[] { s_invariantCompare, "''Tests", "Tests", CompareOptions.IgnoreSymbols, PlatformDetection.IsHybridGlobalizationOnApplePlatform ? true : false, 0 }; yield return new object[] { s_frenchCompare, "\u0153", "oe", CompareOptions.None, false, 0 }; if (PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) { diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.IsSuffix.cs b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.IsSuffix.cs index d545be88e67476..80c911a5a4a69b 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.IsSuffix.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.IsSuffix.cs @@ -80,11 +80,8 @@ public static IEnumerable IsSuffix_TestData() yield return new object[] { s_invariantCompare, "\uD800\uD800", "\uD800\uD800", CompareOptions.None, true, 2 }; // Ignore symbols - if (PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) - { - yield return new object[] { s_invariantCompare, "More Test's", "Tests", CompareOptions.IgnoreSymbols, true, 6 }; - yield return new object[] { s_invariantCompare, "More Test's", "Tests", CompareOptions.None, false, 0 }; - } + yield return new object[] { s_invariantCompare, "More Test's", "Tests", CompareOptions.IgnoreSymbols, true, 6 }; + yield return new object[] { s_invariantCompare, "More Test's", "Tests", CompareOptions.None, false, 0 }; // NULL character yield return new object[] { s_invariantCompare, "a\u0000b", "a\u0000b", CompareOptions.None, true, 3 }; diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.LastIndexOf.cs b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.LastIndexOf.cs index 83556e601faec7..b7bc089581a9e3 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.LastIndexOf.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.LastIndexOf.cs @@ -101,8 +101,7 @@ public static IEnumerable LastIndexOf_TestData() yield return new object[] { s_invariantCompare, "AA\u200DA", "\u200d", 3, 4, CompareOptions.None, 4, 0}; // Ignore symbols - if (PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) // IgnoreSymbols are not supported - yield return new object[] { s_invariantCompare, "More Test's", "Tests", 10, 11, CompareOptions.IgnoreSymbols, 5, 6 }; + yield return new object[] { s_invariantCompare, "More Test's", "Tests", 10, 11, CompareOptions.IgnoreSymbols, 5, 6 }; yield return new object[] { s_invariantCompare, "More Test's", "Tests", 10, 11, CompareOptions.None, -1, 0 }; yield return new object[] { s_invariantCompare, "cbabababdbaba", "ab", 12, 13, CompareOptions.None, 10, 2 }; diff --git a/src/native/libs/System.Globalization.Native/pal_collation.m b/src/native/libs/System.Globalization.Native/pal_collation.m index 56af941fb40033..260947b471de71 100644 --- a/src/native/libs/System.Globalization.Native/pal_collation.m +++ b/src/native/libs/System.Globalization.Native/pal_collation.m @@ -13,14 +13,15 @@ #if defined(APPLE_HYBRID_GLOBALIZATION) // Enum that corresponds to C# CompareOptions -typedef enum +typedef enum : int32_t { - None = 0, - IgnoreCase = 1, - IgnoreNonSpace = 2, - IgnoreKanaType = 8, - IgnoreWidth = 16, - StringSort = 536870912, + None = 0x00000000, + IgnoreCase = 0x00000001, + IgnoreNonSpace = 0x00000002, + IgnoreSymbols = 0x00000004, + IgnoreKanaType = 0x00000008, + IgnoreWidth = 0x00000010, + StringSort = 0x20000000, } CompareOptions; typedef enum @@ -45,15 +46,15 @@ return currentLocale; } -static bool IsComparisonOptionSupported(int32_t comparisonOptions) +static bool IsComparisonOptionSupported(CompareOptions comparisonOptions) { - int32_t supportedOptions = None | IgnoreCase | IgnoreNonSpace | IgnoreWidth | StringSort | IgnoreKanaType; + int32_t supportedOptions = None | IgnoreCase | IgnoreNonSpace | IgnoreSymbols | IgnoreWidth | StringSort | IgnoreKanaType; if ((comparisonOptions | supportedOptions) != supportedOptions) return false; return true; } -static NSStringCompareOptions ConvertFromCompareOptionsToNSStringCompareOptions(int32_t comparisonOptions, bool isLiteralSearchSupported) +static NSStringCompareOptions ConvertFromCompareOptionsToNSStringCompareOptions(CompareOptions comparisonOptions, bool isLiteralSearchSupported) { // To achieve an equivalent search behavior to the default in ICU, // NSLiteralSearch is employed as the default search option. @@ -78,6 +79,126 @@ static NSStringCompareOptions ConvertFromCompareOptionsToNSStringCompareOptions( return mutableString; } +/** + * Removes zero-width and other weightless characters such as U+200B (Zero Width Space), + * U+200C (Zero Width Non-Joiner), U+200D (Zero Width Joiner), U+FEFF (Zero Width No-Break Space), + * and the NUL character from the specified string. + */ +static NSString* RemoveWeightlessCharacters(NSString* source) +{ + NSError *error = nil; + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"[\u200B-\u200D\uFEFF\0]" options:NSRegularExpressionCaseInsensitive error:&error]; + + if (error != nil) + return source; + + NSString *modifiedString = [regex stringByReplacingMatchesInString:source options:0 range:NSMakeRange(0, [source length]) withTemplate:@""]; + + return modifiedString; +} + +// Structure to hold the modified string and character mapping +typedef struct { + NSString* modifiedString; + NSArray* mapping; // mapping[i] = original position j for character at position i in modifiedString +} StringWithMapping; + +/** + * Removes symbols including whitespace, punctuation, currency symbols, the percent sign, + * mathematical symbols, the ampersand, and similar characters from the specified string. + * This corresponds to .NET's CompareOptions.IgnoreSymbols behavior. + */ +static NSString* RemoveSymbols(NSString* source) +{ + NSError *error = nil; + // Unicode categories covered: + // \p{P} - Punctuation (all punctuation marks) + // \p{S} - Symbols (currency, mathematical, modifier, and other symbols) + // \p{Z} - Separators (space, line, and paragraph separators) + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"[\\p{P}\\p{S}\\p{Z}]" options:NSRegularExpressionCaseInsensitive error:&error]; + + if (error != nil) + return source; + + NSString *modifiedString = [regex stringByReplacingMatchesInString:source options:0 range:NSMakeRange(0, [source length]) withTemplate:@""]; + + return modifiedString; +} + +/** + * Removes symbols including whitespace, punctuation, currency symbols, the percent sign, + * mathematical symbols, the ampersand, and similar characters from the specified string. + * Returns both the modified string and a mapping array where mapping[i] contains the original + * position in the source string for the character at position i in the modified string. + * This corresponds to .NET's CompareOptions.IgnoreSymbols behavior. + */ +static StringWithMapping RemoveSymbolsWithMapping(NSString* source) +{ + StringWithMapping result = {source, nil}; + + NSError *error = nil; + // Unicode categories covered: + // \p{P} - Punctuation (all punctuation marks) + // \p{S} - Symbols (currency, mathematical, modifier, and other symbols) + // \p{Z} - Separators (space, line, and paragraph separators) + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"[\\p{P}\\p{S}\\p{Z}]" options:NSRegularExpressionCaseInsensitive error:&error]; + + if (error != nil) + return result; + + NSMutableString *modifiedString = [NSMutableString string]; + NSMutableArray *mapping = [NSMutableArray array]; + + NSUInteger sourceLength = [source length]; + for (NSUInteger i = 0; i < sourceLength; i++) + { + unichar ch = [source characterAtIndex:i]; + NSString *charString = [NSString stringWithCharacters:&ch length:1]; + + // Check if this character matches the symbol pattern + NSRange matchRange = [regex rangeOfFirstMatchInString:charString options:0 range:NSMakeRange(0, 1)]; + + if (matchRange.location == NSNotFound) + { + // Character is not a symbol, keep it + [modifiedString appendString:charString]; + [mapping addObject:@(i)]; + } + // If it's a symbol, skip it (don't add to modifiedString or mapping) + } + + result.modifiedString = [modifiedString copy]; + result.mapping = [mapping copy]; + return result; +} + +/** + * Maps a range from the modified string back to the original string using the provided mapping. + * Returns the mapped location and length in the original string coordinates. + * Returns a range with location = ERROR_INDEX_NOT_FOUND if mapping fails. + */ +static Range MapRangeToOriginalString(NSRange modifiedRange, NSArray* mapping) +{ + Range invalidRange = {ERROR_INDEX_NOT_FOUND, 0}; + + if (mapping == nil || mapping.count == 0 || modifiedRange.location >= mapping.count || modifiedRange.length == 0) + return invalidRange; + + int32_t mappedLocation = [mapping[(NSUInteger)modifiedRange.location] intValue]; + + // Calculate the mapped length by finding the end position in the original string + NSUInteger endIndex = modifiedRange.location + modifiedRange.length - 1; + + if (endIndex >= mapping.count) + return invalidRange; + + int32_t mappedEndLocation = [mapping[endIndex] intValue]; + int32_t mappedLength = mappedEndLocation - mappedLocation + 1; + + Range result = {mappedLocation, mappedLength}; + return result; +} + /* Function: CompareString @@ -101,6 +222,12 @@ int32_t GlobalizationNative_CompareStringNative(const uint16_t* localeName, int3 targetStrPrecomposed = ConvertToKatakana(targetStrPrecomposed); } + if (comparisonOptions & IgnoreSymbols) + { + sourceStrPrecomposed = RemoveSymbols(sourceStrPrecomposed); + targetStrPrecomposed = RemoveSymbols(targetStrPrecomposed); + } + if (comparisonOptions != 0 && comparisonOptions != StringSort) { NSStringCompareOptions options = ConvertFromCompareOptionsToNSStringCompareOptions(comparisonOptions, false); @@ -117,24 +244,6 @@ int32_t GlobalizationNative_CompareStringNative(const uint16_t* localeName, int3 } } -/** - * Removes zero-width and other weightless characters such as U+200B (Zero Width Space), - * U+200C (Zero Width Non-Joiner), U+200D (Zero Width Joiner), U+FEFF (Zero Width No-Break Space), - * and the NUL character from the specified string. - */ -static NSString* RemoveWeightlessCharacters(NSString* source) -{ - NSError *error = nil; - NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"[\u200B-\u200D\uFEFF\0]" options:NSRegularExpressionCaseInsensitive error:&error]; - - if (error != nil) - return source; - - NSString *modifiedString = [regex stringByReplacingMatchesInString:source options:0 range:NSMakeRange(0, [source length]) withTemplate:@""]; - - return modifiedString; -} - static int32_t IsIndexFound(int32_t fromBeginning, int32_t foundLocation, int32_t newLocation) { // last index @@ -169,12 +278,24 @@ Range GlobalizationNative_IndexOfNative(const uint16_t* localeName, int32_t lNam NSString *searchStrCleaned = RemoveWeightlessCharacters(searchString); NSString *sourceString = [NSString stringWithCharacters: lpSource length: (NSUInteger)cwSourceLength]; NSString *sourceStrCleaned = RemoveWeightlessCharacters(sourceString); + + // Store mapping information for the source string. + // This is used to map the found index back to the original string if symbols were removed. + StringWithMapping sourceMapping = {sourceStrCleaned, nil}; + if (comparisonOptions & IgnoreKanaType) { sourceStrCleaned = ConvertToKatakana(sourceStrCleaned); searchStrCleaned = ConvertToKatakana(searchStrCleaned); } + if (comparisonOptions & IgnoreSymbols) + { + sourceMapping = RemoveSymbolsWithMapping(sourceStrCleaned); + sourceStrCleaned = sourceMapping.modifiedString; + searchStrCleaned = RemoveSymbols(searchStrCleaned); + } + if (searchStrCleaned.length == 0) { result.location = fromBeginning ? 0 : (int32_t)sourceString.length; @@ -208,8 +329,16 @@ Range GlobalizationNative_IndexOfNative(const uint16_t* localeName, int32_t lNam if (nsRange.location != NSNotFound) { - result.location = (int32_t)nsRange.location; - result.length = (int32_t)nsRange.length; + result = (Range){(int32_t)nsRange.location, (int32_t)nsRange.length}; + if (comparisonOptions & IgnoreSymbols) + { + // Map the found location back to the original string if symbols were removed + Range mappedResult = MapRangeToOriginalString(nsRange, sourceMapping.mapping); + if (mappedResult.location != ERROR_INDEX_NOT_FOUND) { + result = mappedResult; + } + } + // in case of CompareOptions.IgnoreCase if letters have different representations in source and search strings // and case insensitive search appears more than one time in source string take last index for LastIndexOf and first index for IndexOf // e.g. new CultureInfo().CompareInfo.LastIndexOf("Is \u0055\u0308 or \u0075\u0308 the same as \u00DC or \u00FC?", "U\u0308", 25,18, CompareOptions.IgnoreCase); @@ -234,8 +363,16 @@ Range GlobalizationNative_IndexOfNative(const uint16_t* localeName, int32_t lNam if ((comparisonOptions & IgnoreCase) && IsIndexFound(fromBeginning, (int32_t)result.location, (int32_t)precomposedRange.location)) return result; - result.location = (int32_t)precomposedRange.location; - result.length = (int32_t)precomposedRange.length; + result = (Range){(int32_t)precomposedRange.location, (int32_t)precomposedRange.length}; + if (comparisonOptions & IgnoreSymbols) + { + // Map the found location back to the original string if symbols were removed + Range mappedResult = MapRangeToOriginalString(precomposedRange, sourceMapping.mapping); + if (mappedResult.location != ERROR_INDEX_NOT_FOUND) { + result = mappedResult; + } + } + if (!(comparisonOptions & IgnoreCase)) return result; } @@ -253,8 +390,16 @@ Range GlobalizationNative_IndexOfNative(const uint16_t* localeName, int32_t lNam if ((comparisonOptions & IgnoreCase) && IsIndexFound(fromBeginning, (int32_t)result.location, (int32_t)decomposedRange.location)) return result; - result.location = (int32_t)decomposedRange.location; - result.length = (int32_t)decomposedRange.length; + result = (Range){(int32_t)decomposedRange.location, (int32_t)decomposedRange.length}; + if (comparisonOptions & IgnoreSymbols) + { + // Map the found location back to the original string if symbols were removed + Range mappedResult = MapRangeToOriginalString(decomposedRange, sourceMapping.mapping); + if (mappedResult.location != ERROR_INDEX_NOT_FOUND) { + result = mappedResult; + } + } + return result; } @@ -284,6 +429,12 @@ int32_t GlobalizationNative_StartsWithNative(const uint16_t* localeName, int32_t sourceStrComposed = ConvertToKatakana(sourceStrComposed); } + if (comparisonOptions & IgnoreSymbols) + { + prefixStrComposed = RemoveSymbols(prefixStrComposed); + sourceStrComposed = RemoveSymbols(sourceStrComposed); + } + NSRange sourceRange = NSMakeRange(0, prefixStrComposed.length > sourceStrComposed.length ? sourceStrComposed.length : prefixStrComposed.length); int32_t result = (int32_t)[sourceStrComposed compare:prefixStrComposed @@ -315,6 +466,13 @@ int32_t GlobalizationNative_EndsWithNative(const uint16_t* localeName, int32_t l suffixStrComposed = ConvertToKatakana(suffixStrComposed); sourceStrComposed = ConvertToKatakana(sourceStrComposed); } + + if (comparisonOptions & IgnoreSymbols) + { + suffixStrComposed = RemoveSymbols(suffixStrComposed); + sourceStrComposed = RemoveSymbols(sourceStrComposed); + } + NSUInteger startIndex = suffixStrComposed.length > sourceStrComposed.length ? 0 : sourceStrComposed.length - suffixStrComposed.length; NSRange sourceRange = NSMakeRange(startIndex, sourceStrComposed.length - startIndex);