Skip to content

Commit 0bccdce

Browse files
authored
Improve some string Rune helpers (#127939)
1 parent 5001116 commit 0bccdce

3 files changed

Lines changed: 53 additions & 55 deletions

File tree

src/libraries/System.Private.CoreLib/src/System/String.Comparison.cs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -523,7 +523,14 @@ public bool EndsWith(char value, StringComparison comparisonType)
523523
/// <returns><see langword="true"/> if <paramref name="value"/> matches the end of this instance; otherwise, <see langword="false"/>.</returns>
524524
public bool EndsWith(Rune value)
525525
{
526-
return EndsWith(value, StringComparison.Ordinal);
526+
if (value.IsBmp)
527+
{
528+
return EndsWith((char)value.Value);
529+
}
530+
531+
UnicodeUtility.GetUtf16SurrogatesFromSupplementaryPlaneScalar((uint)value.Value, out char highSurrogate, out char lowSurrogate);
532+
533+
return Length > 1 && this[^2] == highSurrogate && this[^1] == lowSurrogate;
527534
}
528535

529536
/// <summary>
@@ -1134,7 +1141,14 @@ public bool StartsWith(char value, StringComparison comparisonType)
11341141
/// <returns><see langword="true"/> if value matches the beginning of this string; otherwise, <see langword="false"/>.</returns>
11351142
public bool StartsWith(Rune value)
11361143
{
1137-
return StartsWith(value, StringComparison.Ordinal);
1144+
if (value.IsBmp)
1145+
{
1146+
return StartsWith((char)value.Value);
1147+
}
1148+
1149+
UnicodeUtility.GetUtf16SurrogatesFromSupplementaryPlaneScalar((uint)value.Value, out char highSurrogate, out char lowSurrogate);
1150+
1151+
return Length > 1 && _firstChar == highSurrogate && this[1] == lowSurrogate;
11381152
}
11391153

11401154
/// <summary>

src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs

Lines changed: 32 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1456,9 +1456,9 @@ private string ReplaceHelper(int oldValueLength, string newValue, ReadOnlySpan<i
14561456
/// </returns>
14571457
public unsafe string Replace(Rune oldRune, Rune newRune)
14581458
{
1459-
if (Length == 0)
1459+
if (oldRune.IsBmp && newRune.IsBmp)
14601460
{
1461-
return this;
1461+
return Replace((char)oldRune.Value, (char)newRune.Value);
14621462
}
14631463

14641464
ReadOnlySpan<char> oldChars = oldRune.AsSpan(stackalloc char[Rune.MaxUtf16CharsPerRune]);
@@ -1684,19 +1684,23 @@ public string[] Split(Rune separator, StringSplitOptions options = StringSplitOp
16841684
/// <returns>An array whose elements contain the substrings from this instance that are delimited by <paramref name="separator"/>.</returns>
16851685
public unsafe string[] Split(Rune separator, int count, StringSplitOptions options = StringSplitOptions.None)
16861686
{
1687-
ReadOnlySpan<char> separatorSpan = separator.AsSpan(stackalloc char[Rune.MaxUtf16CharsPerRune]);
1688-
1689-
if (separatorSpan.Length == 1)
1687+
if (separator.IsBmp)
16901688
{
1691-
return Split(separatorSpan[0], count, options);
1689+
return Split((char)separator.Value, count, options);
16921690
}
16931691

16941692
ArgumentOutOfRangeException.ThrowIfNegative(count);
16951693

16961694
CheckStringSplitOptions(options);
16971695

16981696
// Ensure matching the string separator overload.
1699-
return (count <= 1 || Length == 0) ? CreateSplitArrayOfThisAsSoleValue(options, count) : Split(separatorSpan, count, options);
1697+
if (count <= 1 || Length == 0)
1698+
{
1699+
return CreateSplitArrayOfThisAsSoleValue(options, count);
1700+
}
1701+
1702+
ReadOnlySpan<char> separatorSpan = separator.AsSpan(stackalloc char[Rune.MaxUtf16CharsPerRune]);
1703+
return Split(separatorSpan, count, options);
17001704
}
17011705

17021706
// Creates an array of strings by splitting this string at each
@@ -2413,39 +2417,28 @@ public unsafe string Trim(char trimChar)
24132417
/// </returns>
24142418
public unsafe string Trim(Rune trimRune)
24152419
{
2416-
if (Length == 0)
2420+
if (trimRune.IsBmp)
24172421
{
2418-
return this;
2422+
return Trim((char)trimRune.Value);
24192423
}
24202424

2421-
// Convert trimRune to span
2422-
ReadOnlySpan<char> trimChars = trimRune.AsSpan(stackalloc char[Rune.MaxUtf16CharsPerRune]);
2425+
UnicodeUtility.GetUtf16SurrogatesFromSupplementaryPlaneScalar((uint)trimRune.Value, out char highSurrogate, out char lowSurrogate);
24232426

24242427
// Trim start
24252428
int index = 0;
2426-
while (index < Length && this.AsSpan(index).StartsWith(trimChars))
2429+
while ((uint)(index + 1) < (uint)Length && this[index] == highSurrogate && this[index + 1] == lowSurrogate)
24272430
{
2428-
index += trimChars.Length;
2429-
}
2430-
2431-
if (index >= Length)
2432-
{
2433-
return Empty;
2431+
index += 2;
24342432
}
24352433

24362434
// Trim end
2437-
int endIndex = Length - 1;
2438-
while (endIndex >= index && this.AsSpan(index..(endIndex + 1)).EndsWith(trimChars))
2439-
{
2440-
endIndex -= trimChars.Length;
2441-
}
2442-
2443-
if (endIndex < index)
2435+
int endIndex = Length - 2;
2436+
while (endIndex > index && this[endIndex] == highSurrogate && this[endIndex + 1] == lowSurrogate)
24442437
{
2445-
return Empty;
2438+
endIndex -= 2;
24462439
}
24472440

2448-
return this[index..(endIndex + 1)];
2441+
return this[index..(endIndex + 2)];
24492442
}
24502443

24512444
// Removes a set of characters from the beginning and end of this string.
@@ -2499,24 +2492,17 @@ public unsafe string Trim(params ReadOnlySpan<char> trimChars)
24992492
/// </returns>
25002493
public unsafe string TrimStart(Rune trimRune)
25012494
{
2502-
if (Length == 0)
2495+
if (trimRune.IsBmp)
25032496
{
2504-
return this;
2497+
return TrimStart((char)trimRune.Value);
25052498
}
25062499

2507-
// Convert trimRune to span
2508-
ReadOnlySpan<char> trimChars = trimRune.AsSpan(stackalloc char[Rune.MaxUtf16CharsPerRune]);
2500+
UnicodeUtility.GetUtf16SurrogatesFromSupplementaryPlaneScalar((uint)trimRune.Value, out char highSurrogate, out char lowSurrogate);
25092501

2510-
// Trim start
25112502
int index = 0;
2512-
while (index < Length && this.AsSpan(index).StartsWith(trimChars))
2503+
while ((uint)(index + 1) < (uint)Length && this[index] == highSurrogate && this[index + 1] == lowSurrogate)
25132504
{
2514-
index += trimChars.Length;
2515-
}
2516-
2517-
if (index >= Length)
2518-
{
2519-
return Empty;
2505+
index += 2;
25202506
}
25212507

25222508
return this[index..];
@@ -2573,27 +2559,20 @@ public unsafe string TrimStart(params ReadOnlySpan<char> trimChars)
25732559
/// </returns>
25742560
public unsafe string TrimEnd(Rune trimRune)
25752561
{
2576-
if (Length == 0)
2562+
if (trimRune.IsBmp)
25772563
{
2578-
return this;
2564+
return TrimEnd((char)trimRune.Value);
25792565
}
25802566

2581-
// Convert trimRune to span
2582-
ReadOnlySpan<char> trimChars = trimRune.AsSpan(stackalloc char[Rune.MaxUtf16CharsPerRune]);
2567+
UnicodeUtility.GetUtf16SurrogatesFromSupplementaryPlaneScalar((uint)trimRune.Value, out char highSurrogate, out char lowSurrogate);
25832568

2584-
// Trim end
2585-
int endIndex = Length - 1;
2586-
while (endIndex >= 0 && this.AsSpan(..(endIndex + 1)).EndsWith(trimChars))
2569+
int endIndex = Length - 2;
2570+
while ((uint)endIndex < (uint)Length && this[endIndex] == highSurrogate && this[endIndex + 1] == lowSurrogate)
25872571
{
2588-
endIndex -= trimChars.Length;
2589-
}
2590-
2591-
if (endIndex < 0)
2592-
{
2593-
return Empty;
2572+
endIndex -= 2;
25942573
}
25952574

2596-
return this[..(endIndex + 1)];
2575+
return this[..(endIndex + 2)];
25972576
}
25982577

25992578
// Removes a set of characters from the end of this string.

src/libraries/System.Private.CoreLib/src/System/String.Searching.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ public bool Contains(char value, StringComparison comparisonType)
5252
/// <returns><see langword="true"/> if <paramref name="value"/> occurs within this string; otherwise, <see langword="false"/>.</returns>
5353
public bool Contains(Rune value)
5454
{
55+
if (value.IsBmp)
56+
{
57+
return Contains((char)value.Value);
58+
}
59+
5560
return Contains(value, StringComparison.Ordinal);
5661
}
5762

0 commit comments

Comments
 (0)