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 8684386c6e66f5..e84524dc4ff9c3 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
@@ -1217,6 +1217,7 @@
+
diff --git a/src/libraries/System.Private.CoreLib/src/System/Char.cs b/src/libraries/System.Private.CoreLib/src/System/Char.cs
index ae2238d78dd4c1..6b7d11bda27386 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Char.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Char.cs
@@ -129,6 +129,19 @@ public bool Equals(char obj)
return m_value == obj;
}
+ internal bool Equals(char right, StringComparison comparisonType)
+ {
+ switch (comparisonType)
+ {
+ case StringComparison.Ordinal:
+ return Equals(right);
+ default:
+ ReadOnlySpan leftCharsSlice = [this];
+ ReadOnlySpan rightCharsSlice = [right];
+ return leftCharsSlice.Equals(rightCharsSlice, comparisonType);
+ }
+ }
+
// Compares this object to another object, returning an integer that
// indicates the relationship.
// Returns a value less than zero if this object
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 f95e54ba338d59..83686710940462 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.cs
@@ -180,6 +180,17 @@ public string ToLower(string str)
return ChangeCaseCommon(str);
}
+ internal void ToLower(ReadOnlySpan source, Span destination)
+ {
+ if (GlobalizationMode.Invariant)
+ {
+ InvariantModeCasing.ToLower(source, destination);
+ return;
+ }
+
+ ChangeCaseCommon(source, destination);
+ }
+
private unsafe char ChangeCase(char c, bool toUpper)
{
Debug.Assert(!GlobalizationMode.Invariant);
@@ -451,6 +462,17 @@ public string ToUpper(string str)
return ChangeCaseCommon(str);
}
+ internal void ToUpper(ReadOnlySpan source, Span destination)
+ {
+ if (GlobalizationMode.Invariant)
+ {
+ InvariantModeCasing.ToUpper(source, destination);
+ return;
+ }
+
+ ChangeCaseCommon(source, destination);
+ }
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static char ToUpperAsciiInvariant(char c)
{
@@ -461,6 +483,50 @@ internal static char ToUpperAsciiInvariant(char c)
return c;
}
+ ///
+ /// Converts the specified rune to lowercase.
+ ///
+ /// The rune to convert to lowercase.
+ /// The specified rune converted to lowercase.
+ public Rune ToLower(Rune value)
+ {
+ // Convert rune to span
+ ReadOnlySpan valueChars = value.AsSpan(stackalloc char[Rune.MaxUtf16CharsPerRune]);
+
+ // Change span to lower and convert to rune
+ if (valueChars.Length == 2)
+ {
+ Span lowerChars = stackalloc char[2];
+ ToLower(valueChars, lowerChars);
+ return new Rune(lowerChars[0], lowerChars[1]);
+ }
+
+ char lowerChar = ToLower(valueChars[0]);
+ return new Rune(lowerChar);
+ }
+
+ ///
+ /// Converts the specified rune to uppercase.
+ ///
+ /// The rune to convert to uppercase.
+ /// The specified rune converted to uppercase.
+ public Rune ToUpper(Rune value)
+ {
+ // Convert rune to span
+ ReadOnlySpan valueChars = value.AsSpan(stackalloc char[Rune.MaxUtf16CharsPerRune]);
+
+ // Change span to upper and convert to rune
+ if (valueChars.Length == 2)
+ {
+ Span upperChars = stackalloc char[2];
+ ToUpper(valueChars, upperChars);
+ return new Rune(upperChars[0], upperChars[1]);
+ }
+
+ char upperChar = ToUpper(valueChars[0]);
+ return new Rune(upperChar);
+ }
+
private bool IsAsciiCasingSameAsInvariant
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/TextWriter.cs b/src/libraries/System.Private.CoreLib/src/System/IO/TextWriter.cs
index 035664cc815f5e..fda09bb8802263 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/TextWriter.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/TextWriter.cs
@@ -125,6 +125,23 @@ public virtual void Write(char value)
{
}
+ ///
+ /// Writes a rune to the text stream.
+ ///
+ /// The rune to write to the text stream.
+ public virtual void Write(Rune value)
+ {
+ // Convert value to span
+ ReadOnlySpan valueChars = value.AsSpan(stackalloc char[Rune.MaxUtf16CharsPerRune]);
+
+ // Write span
+ Write(valueChars[0]);
+ if (valueChars.Length > 1)
+ {
+ Write(valueChars[1]);
+ }
+ }
+
// Writes a character array to the text stream. This default method calls
// Write(char) for each of the characters in the character array.
// If the character array is null, nothing is written.
@@ -343,6 +360,26 @@ public virtual void WriteLine(char value)
WriteLine();
}
+ ///
+ /// Writes a rune followed by a line terminator to the text stream.
+ ///
+ /// The rune to write to the text stream.
+ public virtual void WriteLine(Rune value)
+ {
+ // Convert value to span
+ ReadOnlySpan valueChars = value.AsSpan(stackalloc char[Rune.MaxUtf16CharsPerRune]);
+
+ if (valueChars.Length > 1)
+ {
+ Write(valueChars[0]);
+ WriteLine(valueChars[1]);
+ }
+ else
+ {
+ WriteLine(valueChars[0]);
+ }
+ }
+
// Writes an array of characters followed by a line terminator to the text
// stream.
//
@@ -542,6 +579,28 @@ public virtual Task WriteAsync(char value) =>
t.Item1.Write(t.Item2);
}, new TupleSlim(this, value), CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
+ ///
+ /// Writes a rune to the text stream asynchronously.
+ ///
+ /// The rune to write to the text stream.
+ /// A task that represents the asynchronous write operation.
+ public virtual Task WriteAsync(Rune value)
+ {
+ ReadOnlySpan valueChars = value.AsSpan(stackalloc char[Rune.MaxUtf16CharsPerRune]);
+
+ if (valueChars.Length > 1)
+ {
+ return Task.Factory.StartNew(static state =>
+ {
+ var t = (TupleSlim)state!;
+ t.Item1.Write(t.Item2);
+ t.Item1.Write(t.Item3);
+ }, new TupleSlim(this, valueChars[0], valueChars[1]), CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
+ }
+
+ return WriteAsync(valueChars[0]);
+ }
+
public virtual Task WriteAsync(string? value) =>
Task.Factory.StartNew(static state =>
{
@@ -605,6 +664,28 @@ public virtual Task WriteLineAsync(char value) =>
t.Item1.WriteLine(t.Item2);
}, new TupleSlim(this, value), CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
+ ///
+ /// Writes a rune followed by a line terminator to the text stream asynchronously.
+ ///
+ /// The rune to write to the text stream.
+ /// A task that represents the asynchronous write operation.
+ public virtual Task WriteLineAsync(Rune value)
+ {
+ ReadOnlySpan valueChars = value.AsSpan(stackalloc char[Rune.MaxUtf16CharsPerRune]);
+
+ if (valueChars.Length > 1)
+ {
+ return Task.Factory.StartNew(static state =>
+ {
+ var t = (TupleSlim)state!;
+ t.Item1.Write(t.Item2);
+ t.Item1.WriteLine(t.Item3);
+ }, new TupleSlim(this, valueChars[0], valueChars[1]), CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
+ }
+
+ return WriteLineAsync(valueChars[0]);
+ }
+
public virtual Task WriteLineAsync(string? value) =>
Task.Factory.StartNew(static state =>
{
diff --git a/src/libraries/System.Private.CoreLib/src/System/String.Comparison.cs b/src/libraries/System.Private.CoreLib/src/System/String.Comparison.cs
index 458aa815cd6560..3a927ed2cfbb1a 100644
--- a/src/libraries/System.Private.CoreLib/src/System/String.Comparison.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/String.Comparison.cs
@@ -9,6 +9,7 @@
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using System.Text;
using System.Text.Unicode;
namespace System
@@ -589,6 +590,44 @@ public bool EndsWith(char value)
return ((uint)lastPos < (uint)Length) && this[lastPos] == value;
}
+ ///
+ /// Determines whether the end of this string instance matches the specified character.
+ ///
+ /// The character to compare to the character at the end of this instance.
+ /// One of the enumeration values that specifies the rules to use in the comparison.
+ /// if matches the end of this instance; otherwise, .
+ public bool EndsWith(char value, StringComparison comparisonType)
+ {
+ // Convert value to span
+ ReadOnlySpan valueChars = [value];
+
+ return this.EndsWith(valueChars, comparisonType);
+ }
+
+ ///
+ /// Determines whether the end of this string instance matches the specified rune using an ordinal comparison.
+ ///
+ /// The character to compare to the character at the end of this instance.
+ /// if matches the end of this instance; otherwise, .
+ public bool EndsWith(Rune value)
+ {
+ return EndsWith(value, StringComparison.Ordinal);
+ }
+
+ ///
+ /// Determines whether the end of this string instance matches the specified rune when compared using the specified comparison option.
+ ///
+ /// The character to compare to the character at the end of this instance.
+ /// One of the enumeration values that specifies the rules to use in the comparison.
+ /// if matches the end of this instance; otherwise, .
+ public bool EndsWith(Rune value, StringComparison comparisonType)
+ {
+ // Convert value to span
+ ReadOnlySpan valueChars = value.AsSpan(stackalloc char[Rune.MaxUtf16CharsPerRune]);
+
+ return this.EndsWith(valueChars, comparisonType);
+ }
+
// Determines whether two strings match.
public override bool Equals([NotNullWhen(true)] object? obj)
{
@@ -1162,6 +1201,44 @@ public bool StartsWith(char value)
return Length != 0 && _firstChar == value;
}
+ ///
+ /// Determines whether the beginning of this string instance matches the specified character when compared using the specified comparison option.
+ ///
+ /// The character to compare.
+ /// One of the enumeration values that determines how this string and are compared.
+ /// if value matches the beginning of this string; otherwise, .
+ public bool StartsWith(char value, StringComparison comparisonType)
+ {
+ // Convert value to span
+ ReadOnlySpan valueChars = [value];
+
+ return this.StartsWith(valueChars, comparisonType);
+ }
+
+ ///
+ /// Determines whether the beginning of this string instance matches the specified rune using an ordinal comparison.
+ ///
+ /// The rune to compare.
+ /// if value matches the beginning of this string; otherwise, .
+ public bool StartsWith(Rune value)
+ {
+ return StartsWith(value, StringComparison.Ordinal);
+ }
+
+ ///
+ /// Determines whether the beginning of this string instance matches the specified rune when compared using the specified comparison option.
+ ///
+ /// The rune to compare.
+ /// One of the enumeration values that determines how this string and are compared.
+ /// if value matches the beginning of this string; otherwise, .
+ public bool StartsWith(Rune value, StringComparison comparisonType)
+ {
+ // Convert value to span
+ ReadOnlySpan valueChars = value.AsSpan(stackalloc char[Rune.MaxUtf16CharsPerRune]);
+
+ return this.StartsWith(valueChars, comparisonType);
+ }
+
internal static void CheckStringComparison(StringComparison comparisonType)
{
// Single comparison to check if comparisonType is within [CurrentCulture .. OrdinalIgnoreCase]
diff --git a/src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs b/src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs
index 46738b2c59bafe..774f5c49d46e2f 100644
--- a/src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs
@@ -1447,6 +1447,29 @@ private string ReplaceHelper(int oldValueLength, string newValue, ReadOnlySpan
+ /// Returns a new string in which all occurrences of a specified Unicode rune in this instance are replaced with another specified Unicode rune using an ordinal comparison.
+ ///
+ /// The Unicode character to be replaced.
+ /// The Unicode character to replace all occurrences of .
+ ///
+ /// A string that is equivalent to this instance except that all instances of are replaced with .
+ /// If is not found in the current instance, the method returns the current instance unchanged.
+ ///
+ public string Replace(Rune oldRune, Rune newRune)
+ {
+ if (Length == 0)
+ {
+ return this;
+ }
+
+ ReadOnlySpan oldChars = oldRune.AsSpan(stackalloc char[Rune.MaxUtf16CharsPerRune]);
+ ReadOnlySpan newChars = newRune.AsSpan(stackalloc char[Rune.MaxUtf16CharsPerRune]);
+
+ return ReplaceCore(this, oldChars, newChars, CompareInfo.Invariant, CompareOptions.Ordinal)
+ ?? this;
+ }
+
///
/// Replaces all newline sequences in the current string with .
///
@@ -1643,6 +1666,41 @@ public string[] Split(char separator, int count, StringSplitOptions options = St
return SplitInternal(new ReadOnlySpan(in separator), count, options);
}
+ ///
+ /// Splits a string into substrings based on a specified delimiting rune and, optionally, options.
+ ///
+ /// A character that delimits the substrings in this string.
+ /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
+ /// An array whose elements contain the substrings from this instance that are delimited by .
+ public string[] Split(Rune separator, StringSplitOptions options = StringSplitOptions.None)
+ {
+ return Split(separator, int.MaxValue, options);
+ }
+
+ ///
+ /// Splits a string into a maximum number of substrings based on the provided rune separator, optionally omitting empty substrings from the result.
+ ///
+ /// A character that delimits the substrings in this string.
+ /// The maximum number of elements expected in the array.
+ /// A bitwise combination of the enumeration values that specifies whether to trim substrings and include empty substrings.
+ /// An array whose elements contain the substrings from this instance that are delimited by .
+ public string[] Split(Rune separator, int count, StringSplitOptions options = StringSplitOptions.None)
+ {
+ ReadOnlySpan separatorSpan = separator.AsSpan(stackalloc char[Rune.MaxUtf16CharsPerRune]);
+
+ if (separatorSpan.Length == 1)
+ {
+ return Split(separatorSpan[0], count, options);
+ }
+
+ ArgumentOutOfRangeException.ThrowIfNegative(count);
+
+ CheckStringSplitOptions(options);
+
+ // Ensure matching the string separator overload.
+ return (count <= 1 || Length == 0) ? CreateSplitArrayOfThisAsSoleValue(options, count) : Split(separatorSpan, count, options);
+ }
+
// Creates an array of strings by splitting this string at each
// occurrence of a separator. The separator is searched for, and if found,
// the substring preceding the occurrence is stored as the first element in
@@ -1836,6 +1894,9 @@ private string[] CreateSplitArrayOfThisAsSoleValue(StringSplitOptions options, i
}
private string[] SplitInternal(string separator, int count, StringSplitOptions options)
+ => Split(separator.AsSpan(), count, options);
+
+ private string[] Split(ReadOnlySpan separator, int count, StringSplitOptions options)
{
var sepListBuilder = new ValueListBuilder(stackalloc int[StackallocIntBufferSizeLimit]);
@@ -2344,6 +2405,51 @@ public unsafe string Trim(char trimChar)
return TrimHelper(&trimChar, 1, TrimType.Both);
}
+ ///
+ /// Removes all leading and trailing instances of a rune from the current string.
+ ///
+ /// A Unicode rune to remove.
+ ///
+ /// The string that remains after all instances of the rune are removed from the start and end of the
+ /// current string. If no runes can be trimmed from the current instance, the method returns the current instance unchanged.
+ ///
+ public string Trim(Rune trimRune)
+ {
+ if (Length == 0)
+ {
+ return this;
+ }
+
+ // Convert trimRune to span
+ ReadOnlySpan trimChars = trimRune.AsSpan(stackalloc char[Rune.MaxUtf16CharsPerRune]);
+
+ // Trim start
+ int index = 0;
+ while (index < Length && this.AsSpan(index).StartsWith(trimChars))
+ {
+ index += trimChars.Length;
+ }
+
+ if (index >= Length)
+ {
+ return Empty;
+ }
+
+ // Trim end
+ int endIndex = Length - 1;
+ while (endIndex >= index && this.AsSpan(index..(endIndex + 1)).EndsWith(trimChars))
+ {
+ endIndex -= trimChars.Length;
+ }
+
+ if (endIndex < index)
+ {
+ return Empty;
+ }
+
+ return this[index..(endIndex + 1)];
+ }
+
// Removes a set of characters from the beginning and end of this string.
public unsafe string Trim(params char[]? trimChars)
{
@@ -2385,6 +2491,39 @@ public unsafe string Trim(params ReadOnlySpan trimChars)
// Removes a set of characters from the beginning of this string.
public unsafe string TrimStart(char trimChar) => TrimHelper(&trimChar, 1, TrimType.Head);
+ ///
+ /// Removes all leading instances of a rune from the current string.
+ ///
+ /// A Unicode rune to remove.
+ ///
+ /// The string that remains after all instances of the rune are removed from the start of the
+ /// current string. If no runes can be trimmed from the current instance, the method returns the current instance unchanged.
+ ///
+ public string TrimStart(Rune trimRune)
+ {
+ if (Length == 0)
+ {
+ return this;
+ }
+
+ // Convert trimRune to span
+ ReadOnlySpan trimChars = trimRune.AsSpan(stackalloc char[Rune.MaxUtf16CharsPerRune]);
+
+ // Trim start
+ int index = 0;
+ while (index < Length && this.AsSpan(index).StartsWith(trimChars))
+ {
+ index += trimChars.Length;
+ }
+
+ if (index >= Length)
+ {
+ return Empty;
+ }
+
+ return this[index..];
+ }
+
// Removes a set of characters from the beginning of this string.
public unsafe string TrimStart(params char[]? trimChars)
{
@@ -2426,6 +2565,39 @@ public unsafe string TrimStart(params ReadOnlySpan trimChars)
// Removes a set of characters from the end of this string.
public unsafe string TrimEnd(char trimChar) => TrimHelper(&trimChar, 1, TrimType.Tail);
+ ///
+ /// Removes all trailing instances of a rune from the current string.
+ ///
+ /// A Unicode rune to remove.
+ ///
+ /// The string that remains after all instances of the rune are removed from the end of the
+ /// current string. If no runes can be trimmed from the current instance, the method returns the current instance unchanged.
+ ///
+ public string TrimEnd(Rune trimRune)
+ {
+ if (Length == 0)
+ {
+ return this;
+ }
+
+ // Convert trimRune to span
+ ReadOnlySpan trimChars = trimRune.AsSpan(stackalloc char[Rune.MaxUtf16CharsPerRune]);
+
+ // Trim end
+ int endIndex = Length - 1;
+ while (endIndex >= 0 && this.AsSpan(..(endIndex + 1)).EndsWith(trimChars))
+ {
+ endIndex -= trimChars.Length;
+ }
+
+ if (endIndex < 0)
+ {
+ return Empty;
+ }
+
+ return this[..(endIndex + 1)];
+ }
+
// Removes a set of characters from the end of this string.
public unsafe string TrimEnd(params char[]? trimChars)
{
diff --git a/src/libraries/System.Private.CoreLib/src/System/String.Searching.cs b/src/libraries/System.Private.CoreLib/src/System/String.Searching.cs
index addc7386039357..11e1e950f9d973 100644
--- a/src/libraries/System.Private.CoreLib/src/System/String.Searching.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/String.Searching.cs
@@ -1,8 +1,10 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Buffers;
using System.Globalization;
using System.Runtime.CompilerServices;
+using System.Text;
namespace System
{
@@ -39,10 +41,31 @@ public bool Contains(char value)
public bool Contains(char value, StringComparison comparisonType)
{
#pragma warning disable CA2249 // Consider using 'string.Contains' instead of 'string.IndexOf'... this is the implementation of Contains!
- return IndexOf(value, comparisonType) != -1;
+ return IndexOf(value, comparisonType) >= 0;
#pragma warning restore CA2249
}
+ ///
+ /// Returns a value indicating whether a specified rune occurs within this string using an ordinal comparison.
+ ///
+ /// The rune to seek.
+ /// if occurs within this string; otherwise, .
+ public bool Contains(Rune value)
+ {
+ return Contains(value, StringComparison.Ordinal);
+ }
+
+ ///
+ /// Returns a value indicating whether a specified rune occurs within this string using the specified comparison option.
+ ///
+ /// The rune to seek.
+ /// One of the enumeration values that specifies the rules to use in the comparison.
+ /// if occurs within this string; otherwise, .
+ public bool Contains(Rune value, StringComparison comparisonType)
+ {
+ return IndexOf(value, comparisonType) >= 0;
+ }
+
// Returns the index of the first occurrence of a specified character in the current instance.
// The search starts at startIndex and runs thorough the next count characters.
public int IndexOf(char value) => SpanHelpers.IndexOfChar(ref _firstChar, value, Length);
@@ -262,6 +285,109 @@ public int IndexOf(string value, int startIndex, int count, StringComparison com
};
}
+ ///
+ /// Reports the zero-based index of the first occurrence of the specified rune in the current String object.
+ ///
+ /// The rune to seek.
+ ///
+ /// The zero-based index position of from the start of the current instance
+ /// if that rune is found, or -1 if it is not.
+ ///
+ public int IndexOf(Rune value)
+ {
+ return IndexOf(value, StringComparison.Ordinal);
+ }
+
+ ///
+ /// Reports the zero-based index of the first occurrence of the specified rune in the current String object.
+ /// A parameter specifies the starting search position in the current string.
+ ///
+ /// The rune to seek.
+ /// The search starting position.
+ ///
+ /// The zero-based index position of from the start of the current instance
+ /// if that rune is found, or -1 if it is not.
+ ///
+ public int IndexOf(Rune value, int startIndex)
+ {
+ return IndexOf(value, startIndex, StringComparison.Ordinal);
+ }
+
+ ///
+ /// Reports the zero-based index of the first occurrence of the specified rune in the current String object.
+ /// Parameters specify the starting search position in the current string and the number of characters in the
+ /// current string to search.
+ ///
+ /// The rune to seek.
+ /// The search starting position.
+ /// The number of character positions to examine.
+ ///
+ /// The zero-based index position of from the start of the current instance
+ /// if that rune is found, or -1 if it is not.
+ ///
+ public int IndexOf(Rune value, int startIndex, int count)
+ {
+ return IndexOf(value, startIndex, count, StringComparison.Ordinal);
+ }
+
+ ///
+ /// Reports the zero-based index of the first occurrence of the specified rune in the current String object.
+ /// A parameter specifies the type of search to use for the specified rune.
+ ///
+ /// The rune to seek.
+ /// One of the enumeration values that specifies the rules for the search.
+ ///
+ /// The zero-based index position of from the start of the current instance
+ /// if that rune is found, or -1 if it is not.
+ ///
+ public int IndexOf(Rune value, StringComparison comparisonType)
+ {
+ return IndexOf(value, 0, comparisonType);
+ }
+
+ ///
+ /// Reports the zero-based index of the first occurrence of the specified rune in the current String object.
+ /// Parameters specify the starting search position in the current string and the type of search to use for
+ /// the specified rune.
+ ///
+ /// The rune to seek.
+ /// The search starting position.
+ /// One of the enumeration values that specifies the rules for the search.
+ ///
+ /// The zero-based index position of from the start of the current instance
+ /// if that rune is found, or -1 if it is not.
+ ///
+ internal int IndexOf(Rune value, int startIndex, StringComparison comparisonType)
+ {
+ return IndexOf(value, startIndex, Length - startIndex, comparisonType);
+ }
+
+ ///
+ /// Reports the zero-based index of the first occurrence of the specified rune in the current String object.
+ /// Parameters specify the starting search position in the current string, the number of characters in the
+ /// current string to search, and the type of search to use for the specified rune.
+ ///
+ /// The rune to seek.
+ /// The search starting position.
+ /// The number of character positions to examine.
+ /// One of the enumeration values that specifies the rules for the search.
+ ///
+ /// The zero-based index position of from the start of the current instance
+ /// if that rune is found, or -1 if it is not.
+ ///
+ internal int IndexOf(Rune value, int startIndex, int count, StringComparison comparisonType)
+ {
+ ArgumentOutOfRangeException.ThrowIfLessThan(startIndex, 0);
+ ArgumentOutOfRangeException.ThrowIfLessThan(count, 0);
+ ArgumentOutOfRangeException.ThrowIfGreaterThan(startIndex + count, Length);
+
+ // Convert value to span
+ ReadOnlySpan valueChars = value.AsSpan(stackalloc char[Rune.MaxUtf16CharsPerRune]);
+
+ int subIndex = this.AsSpan(startIndex..(startIndex + count)).IndexOf(valueChars, comparisonType);
+ return subIndex < 0 ? subIndex : startIndex + subIndex;
+ }
+
// Returns the index of the last occurrence of a specified character in the current instance.
// The search starts at startIndex and runs backwards to startIndex - count + 1.
// The character at position startIndex is included in the search. startIndex is the larger
@@ -386,5 +512,111 @@ public int LastIndexOf(string value, int startIndex, int count, StringComparison
_ => throw (value is null ? new ArgumentNullException(nameof(value)) : new ArgumentException(SR.NotSupported_StringComparison, nameof(comparisonType))),
};
}
+
+ ///
+ /// Reports the zero-based index of the last occurrence of the specified rune in the current String object.
+ ///
+ /// The rune to seek.
+ ///
+ /// The zero-based index position of from the end of the current instance
+ /// if that rune is found, or -1 if it is not.
+ ///
+ public int LastIndexOf(Rune value)
+ {
+ return LastIndexOf(value, StringComparison.Ordinal);
+ }
+
+ ///
+ /// Reports the zero-based index of the last occurrence of the specified rune in the current String object.
+ /// A parameter specifies the starting search position in the current string.
+ ///
+ /// The rune to seek.
+ /// The search starting position. The search proceeds from toward the beginning of this instance.
+ ///
+ /// The zero-based index position of from the end of the current instance
+ /// if that rune is found, or -1 if it is not.
+ ///
+ public int LastIndexOf(Rune value, int startIndex)
+ {
+ return LastIndexOf(value, startIndex, StringComparison.Ordinal);
+ }
+
+ ///
+ /// Reports the zero-based index of the last occurrence of the specified rune in the current String object.
+ /// Parameters specify the starting search position in the current string and the number of characters in the
+ /// current string to search.
+ ///
+ /// The rune to seek.
+ /// The search starting position. The search proceeds from toward the beginning of this instance.
+ /// The number of character positions to examine.
+ ///
+ /// The zero-based index position of from the end of the current instance
+ /// if that rune is found, or -1 if it is not.
+ ///
+ public int LastIndexOf(Rune value, int startIndex, int count)
+ {
+ return LastIndexOf(value, startIndex, count, StringComparison.Ordinal);
+ }
+
+ ///
+ /// Reports the zero-based index of the last occurrence of the specified rune in the current String object.
+ /// A parameter specifies the type of search to use for the specified rune.
+ ///
+ /// The rune to seek.
+ /// One of the enumeration values that specifies the rules for the search.
+ ///
+ /// The zero-based index position of from the end of the current instance
+ /// if that rune is found, or -1 if it is not.
+ ///
+ public int LastIndexOf(Rune value, StringComparison comparisonType)
+ {
+ return LastIndexOf(value, Length - 1, comparisonType);
+ }
+
+ ///
+ /// Reports the zero-based index of the last occurrence of the specified rune in the current String object.
+ /// Parameters specify the starting search position in the current string and the type of search to use for
+ /// the specified rune.
+ ///
+ /// The rune to seek.
+ /// The search starting position. The search proceeds from toward the beginning of this instance.
+ /// One of the enumeration values that specifies the rules for the search.
+ ///
+ /// The zero-based index position of from the end of the current instance
+ /// if that rune is found, or -1 if it is not.
+ ///
+ internal int LastIndexOf(Rune value, int startIndex, StringComparison comparisonType)
+ {
+ return LastIndexOf(value, startIndex, startIndex + 1, comparisonType);
+ }
+
+ ///
+ /// Reports the zero-based index of the last occurrence of the specified rune in the current String object.
+ /// Parameters specify the starting search position in the current string, the number of characters in the
+ /// current string to search, and the type of search to use for the specified rune.
+ ///
+ /// The rune to seek.
+ /// The search starting position. The search proceeds from toward the beginning of this instance.
+ /// The number of character positions to examine.
+ /// One of the enumeration values that specifies the rules for the search.
+ ///
+ /// The zero-based index position of from the end of the current instance
+ /// if that rune is found, or -1 if it is not.
+ ///
+ internal int LastIndexOf(Rune value, int startIndex, int count, StringComparison comparisonType)
+ {
+ ArgumentOutOfRangeException.ThrowIfLessThan(startIndex, 0);
+ ArgumentOutOfRangeException.ThrowIfLessThan(count, 0);
+ ArgumentOutOfRangeException.ThrowIfLessThan(startIndex - count + 1, 0);
+ ArgumentOutOfRangeException.ThrowIfGreaterThan(startIndex, Length - 1);
+
+ // Convert value to span
+ ReadOnlySpan valueChars = value.AsSpan(stackalloc char[Rune.MaxUtf16CharsPerRune]);
+
+ int startIndexFromZero = startIndex - count + 1;
+
+ int subIndex = this.AsSpan(startIndexFromZero..(startIndexFromZero + count)).LastIndexOf(valueChars, comparisonType);
+ return subIndex < 0 ? subIndex : startIndexFromZero + subIndex;
+ }
}
}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/Rune.cs b/src/libraries/System.Private.CoreLib/src/System/Text/Rune.cs
index 2f0c00c1b4a55a..05c15e3fa841d1 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Text/Rune.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Text/Rune.cs
@@ -294,6 +294,13 @@ private static Rune ChangeCaseCultureAware(Rune rune, CultureInfo culture, bool
public int CompareTo(Rune other) => this.Value - other.Value; // values don't span entire 32-bit domain; won't integer overflow
+ internal ReadOnlySpan AsSpan(Span buffer)
+ {
+ Debug.Assert(buffer.Length >= MaxUtf16CharsPerRune);
+ int charsWritten = EncodeToUtf16(buffer);
+ return buffer.Slice(0, charsWritten);
+ }
+
///
/// Decodes the at the beginning of the provided UTF-16 source buffer.
///
@@ -780,6 +787,29 @@ public int EncodeToUtf8(Span destination)
public bool Equals(Rune other) => this == other;
+ ///
+ /// Returns a value that indicates whether the current instance and a specified rune are equal using the specified comparison option.
+ ///
+ /// The rune to compare with the current instance.
+ /// One of the enumeration values that specifies the rules to use in the comparison.
+ /// if the current instance and are equal; otherwise, .
+ public bool Equals(Rune other, StringComparison comparisonType)
+ {
+ if (comparisonType is StringComparison.Ordinal)
+ {
+ return this == other;
+ }
+
+ // Convert this to span
+ ReadOnlySpan thisChars = AsSpan(stackalloc char[MaxUtf16CharsPerRune]);
+
+ // Convert other to span
+ ReadOnlySpan otherChars = other.AsSpan(stackalloc char[MaxUtf16CharsPerRune]);
+
+ // Compare span equality
+ return thisChars.Equals(otherChars, comparisonType);
+ }
+
public override int GetHashCode() => Value;
#if SYSTEM_PRIVATE_CORELIB
diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/StringBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Text/StringBuilder.cs
index a159895c0ed721..0dcc60a825f986 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Text/StringBuilder.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Text/StringBuilder.cs
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Buffers;
+using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
@@ -645,6 +647,14 @@ public ManyChunkInfo(StringBuilder? stringBuilder, int chunkCount)
#endregion
}
+ ///
+ /// Returns an enumeration of from this builder.
+ ///
+ ///
+ /// Invalid sequences will be represented in the enumeration by .
+ ///
+ public StringBuilderRuneEnumerator EnumerateRunes() => new StringBuilderRuneEnumerator(this);
+
///
/// Appends a character 0 or more times to the end of this builder.
///
@@ -1027,6 +1037,20 @@ private void AppendWithExpansion(char value)
m_ChunkLength++;
}
+ ///
+ /// Appends the string representation of a specified to this instance.
+ ///
+ /// The UTF-32-encoded code unit to append.
+ /// A reference to this instance after the append operation has completed.
+ public StringBuilder Append(Rune value)
+ {
+ // Convert value to span
+ ReadOnlySpan valueChars = value.AsSpan(stackalloc char[Rune.MaxUtf16CharsPerRune]);
+
+ // Append span
+ return Append(valueChars);
+ }
+
[CLSCompliant(false)]
public StringBuilder Append(sbyte value) => AppendSpanFormattable(value);
@@ -1327,6 +1351,21 @@ public StringBuilder Insert(int index, char value)
return this;
}
+ ///
+ /// Inserts the string representation of a specified Unicode rune into this instance at the specified character position.
+ ///
+ /// The position in this instance where insertion begins.
+ /// The value to insert.
+ /// A reference to this instance after the insert operation has completed.
+ public StringBuilder Insert(int index, Rune value)
+ {
+ // Convert value to span
+ ReadOnlySpan valueChars = value.AsSpan(stackalloc char[Rune.MaxUtf16CharsPerRune]);
+
+ // Insert span
+ return Insert(index, valueChars);
+ }
+
public StringBuilder Insert(int index, char[]? value)
{
if ((uint)index > (uint)Length)
@@ -2248,6 +2287,40 @@ public StringBuilder Replace(char oldChar, char newChar, int startIndex, int cou
return this;
}
+ ///
+ /// Replaces all occurrences of a specified rune in this instance with another specified rune using an ordinal comparison.
+ ///
+ /// The rune to replace.
+ /// The rune that replaces .
+ /// A reference to this instance with replaced by .
+ public StringBuilder Replace(Rune oldRune, Rune newRune)
+ {
+ return Replace(oldRune, newRune, 0, Length);
+ }
+
+ ///
+ /// Replaces, within a substring of this instance, all occurrences of a specified rune with another specified rune using an ordinal comparison.
+ ///
+ /// The rune to replace.
+ /// The rune that replaces .
+ /// The position in this instance where the substring begins.
+ /// The length of the substring.
+ ///
+ /// A reference to this instance with replaced by in the range
+ /// from to + - 1.
+ ///
+ public StringBuilder Replace(Rune oldRune, Rune newRune, int startIndex, int count)
+ {
+ // Convert oldRune to span
+ ReadOnlySpan oldChars = oldRune.AsSpan(stackalloc char[Rune.MaxUtf16CharsPerRune]);
+
+ // Convert newRune to span
+ ReadOnlySpan newChars = newRune.AsSpan(stackalloc char[Rune.MaxUtf16CharsPerRune]);
+
+ // Replace span with span
+ return Replace(oldChars, newChars, startIndex, count);
+ }
+
///
/// Appends a character buffer to this builder.
///
@@ -2798,6 +2871,54 @@ private void Remove(int startIndex, int count, out StringBuilder chunk, out int
AssertInvariants();
}
+ ///
+ /// Gets the that begins at a specified position in this builder.
+ ///
+ /// The starting position in this builder at which to decode the rune.
+ /// The rune obtained from this builder at the specified .
+ /// The index is out of the range of the builder.
+ /// The rune at the specified index is not valid.
+ public Rune GetRuneAt(int index)
+ {
+ if (TryGetRuneAt(index, out Rune value))
+ {
+ return value;
+ }
+ ThrowHelper.ThrowArgumentException_CannotExtractScalar(ExceptionArgument.index);
+ return default;
+ }
+
+ ///
+ /// Attempts to get the that begins at a specified position in this builder, and return a value that indicates whether the operation succeeded.
+ ///
+ /// The starting position in this builder at which to decode the rune.
+ /// When this method returns, the decoded rune.
+ ///
+ /// if a scalar value was successfully extracted from the specified index;
+ /// if a value could not be extracted because of invalid data.
+ ///
+ /// The index is out of the range of the builder.
+ public bool TryGetRuneAt(int index, out Rune value)
+ {
+ ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(index, Length);
+ ArgumentOutOfRangeException.ThrowIfNegative(index);
+
+ // Get span at StringBuilder index
+ Span chars = index + 1 < Length
+ ? [this[index], this[index + 1]]
+ : [this[index]];
+
+ OperationStatus status = Rune.DecodeFromUtf16(chars, out Rune result, out _);
+ if (status is OperationStatus.Done)
+ {
+ value = result;
+ return true;
+ }
+
+ value = default;
+ return false;
+ }
+
/// Provides a handler used by the language compiler to append interpolated strings into instances.
[EditorBrowsable(EditorBrowsableState.Never)]
[InterpolatedStringHandler]
diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/StringBuilderRuneEnumerator.cs b/src/libraries/System.Private.CoreLib/src/System/Text/StringBuilderRuneEnumerator.cs
new file mode 100644
index 00000000000000..caa0f717d54d3a
--- /dev/null
+++ b/src/libraries/System.Private.CoreLib/src/System/Text/StringBuilderRuneEnumerator.cs
@@ -0,0 +1,105 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections;
+using System.Collections.Generic;
+
+namespace System.Text
+{
+ ///
+ /// An enumerator for retrieving instances from a .
+ ///
+ public struct StringBuilderRuneEnumerator : IEnumerable, IEnumerator
+ {
+ private readonly StringBuilder _stringBuilder;
+ private Rune _current;
+ private int _nextIndex;
+
+ internal StringBuilderRuneEnumerator(StringBuilder value)
+ {
+ _stringBuilder = value;
+ _current = default;
+ _nextIndex = 0;
+ }
+
+ ///
+ /// Gets the at the current position of the enumerator.
+ ///
+ public readonly Rune Current => _current;
+
+ ///
+ /// Returns the current enumerator instance.
+ ///
+ /// The current enumerator instance.
+ public readonly StringBuilderRuneEnumerator GetEnumerator() => this;
+
+ ///
+ /// Advances the enumerator to the next of the builder.
+ ///
+ ///
+ /// if the enumerator successfully advanced to the next item;
+ /// if the end of the builder has been reached.
+ ///
+ public bool MoveNext()
+ {
+ if ((uint)_nextIndex >= _stringBuilder.Length)
+ {
+ // reached the end of the string
+ _current = default;
+ return false;
+ }
+
+ if (!_stringBuilder.TryGetRuneAt(_nextIndex, out _current))
+ {
+ // replace invalid sequences with U+FFFD
+ _current = Rune.ReplacementChar;
+ }
+
+ // In UTF-16 specifically, invalid sequences always have length 1, which is the same
+ // length as the replacement character U+FFFD. This means that we can always bump the
+ // next index by the current scalar's UTF-16 sequence length. This optimization is not
+ // generally applicable; for example, enumerating scalars from UTF-8 cannot utilize
+ // this same trick.
+
+ _nextIndex += _current.Utf16SequenceLength;
+ return true;
+ }
+
+ ///
+ /// Gets the at the current position of the enumerator.
+ ///
+ readonly object? IEnumerator.Current => _current;
+
+ ///
+ /// Releases all resources used by the current instance.
+ ///
+ ///
+ /// This method performs no operation and produces no side effects.
+ ///
+ readonly void IDisposable.Dispose()
+ {
+ // no-op
+ }
+
+ ///
+ /// Returns the current enumerator instance.
+ ///
+ /// The current enumerator instance.
+ readonly IEnumerator IEnumerable.GetEnumerator() => this;
+
+ ///
+ /// Returns the current enumerator instance.
+ ///
+ /// The current enumerator instance.
+ readonly IEnumerator IEnumerable.GetEnumerator() => this;
+
+ ///
+ /// Resets the current instance to the beginning of the builder.
+ ///
+ void IEnumerator.Reset()
+ {
+ _current = default;
+ _nextIndex = 0;
+ }
+ }
+}
diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs
index 0807ee840135d4..9922faac9a38a3 100644
--- a/src/libraries/System.Runtime/ref/System.Runtime.cs
+++ b/src/libraries/System.Runtime/ref/System.Runtime.cs
@@ -5654,6 +5654,8 @@ public unsafe String(sbyte* value, int startIndex, int length, System.Text.Encod
public static string Concat(System.Collections.Generic.IEnumerable values) { throw null; }
public bool Contains(char value) { throw null; }
public bool Contains(char value, System.StringComparison comparisonType) { throw null; }
+ public bool Contains(System.Text.Rune value) { throw null; }
+ public bool Contains(System.Text.Rune value, System.StringComparison comparisonType) { throw null; }
public bool Contains(string value) { throw null; }
public bool Contains(string value, System.StringComparison comparisonType) { throw null; }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
@@ -5665,6 +5667,9 @@ public void CopyTo(System.Span destination) { }
public static string Create(System.IFormatProvider? provider, System.Span initialBuffer, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute(new string[]{ "provider", "initialBuffer"})] ref System.Runtime.CompilerServices.DefaultInterpolatedStringHandler handler) { throw null; }
public static string Create(int length, TState state, System.Buffers.SpanAction action) where TState : allows ref struct { throw null; }
public bool EndsWith(char value) { throw null; }
+ public bool EndsWith(char value, System.StringComparison comparisonType) { throw null; }
+ public bool EndsWith(System.Text.Rune value) { throw null; }
+ public bool EndsWith(System.Text.Rune value, System.StringComparison comparisonType) { throw null; }
public bool EndsWith(string value) { throw null; }
public bool EndsWith(string value, bool ignoreCase, System.Globalization.CultureInfo? culture) { throw null; }
public bool EndsWith(string value, System.StringComparison comparisonType) { throw null; }
@@ -5707,6 +5712,10 @@ public void CopyTo(System.Span destination) { }
public int IndexOf(string value, int startIndex, int count, System.StringComparison comparisonType) { throw null; }
public int IndexOf(string value, int startIndex, System.StringComparison comparisonType) { throw null; }
public int IndexOf(string value, System.StringComparison comparisonType) { throw null; }
+ public int IndexOf(System.Text.Rune value) { throw null; }
+ public int IndexOf(System.Text.Rune value, int startIndex) { throw null; }
+ public int IndexOf(System.Text.Rune value, int startIndex, int count) { throw null; }
+ public int IndexOf(System.Text.Rune value, System.StringComparison comparisonType) { throw null; }
public int IndexOfAny(char[] anyOf) { throw null; }
public int IndexOfAny(char[] anyOf, int startIndex) { throw null; }
public int IndexOfAny(char[] anyOf, int startIndex, int count) { throw null; }
@@ -5739,6 +5748,10 @@ public void CopyTo(System.Span destination) { }
public int LastIndexOf(string value, int startIndex, int count, System.StringComparison comparisonType) { throw null; }
public int LastIndexOf(string value, int startIndex, System.StringComparison comparisonType) { throw null; }
public int LastIndexOf(string value, System.StringComparison comparisonType) { throw null; }
+ public int LastIndexOf(System.Text.Rune value) { throw null; }
+ public int LastIndexOf(System.Text.Rune value, int startIndex) { throw null; }
+ public int LastIndexOf(System.Text.Rune value, int startIndex, int count) { throw null; }
+ public int LastIndexOf(System.Text.Rune value, System.StringComparison comparisonType) { throw null; }
public int LastIndexOfAny(char[] anyOf) { throw null; }
public int LastIndexOfAny(char[] anyOf, int startIndex) { throw null; }
public int LastIndexOfAny(char[] anyOf, int startIndex, int count) { throw null; }
@@ -5754,6 +5767,7 @@ public void CopyTo(System.Span destination) { }
public string Remove(int startIndex) { throw null; }
public string Remove(int startIndex, int count) { throw null; }
public string Replace(char oldChar, char newChar) { throw null; }
+ public string Replace(System.Text.Rune oldRune, System.Text.Rune newRune) { throw null; }
public string Replace(string oldValue, string? newValue) { throw null; }
public string Replace(string oldValue, string? newValue, bool ignoreCase, System.Globalization.CultureInfo? culture) { throw null; }
public string Replace(string oldValue, string? newValue, System.StringComparison comparisonType) { throw null; }
@@ -5761,6 +5775,8 @@ public void CopyTo(System.Span destination) { }
public string ReplaceLineEndings(string replacementText) { throw null; }
public string[] Split(char separator, int count, System.StringSplitOptions options = System.StringSplitOptions.None) { throw null; }
public string[] Split(char separator, System.StringSplitOptions options = System.StringSplitOptions.None) { throw null; }
+ public string[] Split(System.Text.Rune separator, int count, System.StringSplitOptions options = System.StringSplitOptions.None) { throw null; }
+ public string[] Split(System.Text.Rune separator, System.StringSplitOptions options = System.StringSplitOptions.None) { throw null; }
public string[] Split(params char[]? separator) { throw null; }
public string[] Split(params System.ReadOnlySpan separator) { throw null; }
public string[] Split(char[]? separator, int count) { throw null; }
@@ -5771,6 +5787,9 @@ public void CopyTo(System.Span destination) { }
public string[] Split(string[]? separator, int count, System.StringSplitOptions options) { throw null; }
public string[] Split(string[]? separator, System.StringSplitOptions options) { throw null; }
public bool StartsWith(char value) { throw null; }
+ public bool StartsWith(char value, System.StringComparison comparisonType) { throw null; }
+ public bool StartsWith(System.Text.Rune value) { throw null; }
+ public bool StartsWith(System.Text.Rune value, System.StringComparison comparisonType) { throw null; }
public bool StartsWith(string value) { throw null; }
public bool StartsWith(string value, bool ignoreCase, System.Globalization.CultureInfo? culture) { throw null; }
public bool StartsWith(string value, System.StringComparison comparisonType) { throw null; }
@@ -5809,12 +5828,15 @@ public void CopyTo(System.Span destination) { }
public string ToUpperInvariant() { throw null; }
public string Trim() { throw null; }
public string Trim(char trimChar) { throw null; }
+ public string Trim(System.Text.Rune trimRune) { throw null; }
public string Trim(params char[]? trimChars) { throw null; }
public string TrimEnd() { throw null; }
public string TrimEnd(char trimChar) { throw null; }
+ public string TrimEnd(System.Text.Rune trimRune) { throw null; }
public string TrimEnd(params char[]? trimChars) { throw null; }
public string TrimStart() { throw null; }
public string TrimStart(char trimChar) { throw null; }
+ public string TrimStart(System.Text.Rune trimRune) { throw null; }
public string TrimStart(params char[]? trimChars) { throw null; }
public bool TryCopyTo(System.Span destination) { throw null; }
}
@@ -9896,6 +9918,8 @@ void System.Runtime.Serialization.IDeserializationCallback.OnDeserialization(obj
public string ToTitleCase(string str) { throw null; }
public char ToUpper(char c) { throw null; }
public string ToUpper(string str) { throw null; }
+ public System.Text.Rune ToLower(System.Text.Rune value) { throw null; }
+ public System.Text.Rune ToUpper(System.Text.Rune value) { throw null; }
}
public partial class ThaiBuddhistCalendar : System.Globalization.Calendar
{
@@ -10990,6 +11014,7 @@ public virtual void Flush() { }
public static System.IO.TextWriter Synchronized(System.IO.TextWriter writer) { throw null; }
public virtual void Write(bool value) { }
public virtual void Write(char value) { }
+ public virtual void Write(System.Text.Rune value) { }
public virtual void Write(char[]? buffer) { }
public virtual void Write(char[] buffer, int index, int count) { }
public virtual void Write(decimal value) { }
@@ -11011,6 +11036,7 @@ public virtual void Write(uint value) { }
[System.CLSCompliantAttribute(false)]
public virtual void Write(ulong value) { }
public virtual System.Threading.Tasks.Task WriteAsync(char value) { throw null; }
+ public virtual System.Threading.Tasks.Task WriteAsync(System.Text.Rune value) { throw null; }
public System.Threading.Tasks.Task WriteAsync(char[]? buffer) { throw null; }
public virtual System.Threading.Tasks.Task WriteAsync(char[] buffer, int index, int count) { throw null; }
public virtual System.Threading.Tasks.Task WriteAsync(System.ReadOnlyMemory buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
@@ -11019,6 +11045,7 @@ public virtual void Write(ulong value) { }
public virtual void WriteLine() { }
public virtual void WriteLine(bool value) { }
public virtual void WriteLine(char value) { }
+ public virtual void WriteLine(System.Text.Rune value) { }
public virtual void WriteLine(char[]? buffer) { }
public virtual void WriteLine(char[] buffer, int index, int count) { }
public virtual void WriteLine(decimal value) { }
@@ -11041,6 +11068,7 @@ public virtual void WriteLine(uint value) { }
public virtual void WriteLine(ulong value) { }
public virtual System.Threading.Tasks.Task WriteLineAsync() { throw null; }
public virtual System.Threading.Tasks.Task WriteLineAsync(char value) { throw null; }
+ public virtual System.Threading.Tasks.Task WriteLineAsync(System.Text.Rune value) { throw null; }
public System.Threading.Tasks.Task WriteLineAsync(char[]? buffer) { throw null; }
public virtual System.Threading.Tasks.Task WriteLineAsync(char[] buffer, int index, int count) { throw null; }
public virtual System.Threading.Tasks.Task WriteLineAsync(System.ReadOnlyMemory buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
@@ -15672,6 +15700,7 @@ public enum NormalizationForm
public int EncodeToUtf8(System.Span destination) { throw null; }
public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; }
public bool Equals(System.Text.Rune other) { throw null; }
+ public bool Equals(System.Text.Rune other, System.StringComparison comparisonType) { throw null; }
public override int GetHashCode() { throw null; }
public static double GetNumericValue(System.Text.Rune value) { throw null; }
public static System.Text.Rune GetRuneAt(string input, int index) { throw null; }
@@ -15778,6 +15807,7 @@ public StringBuilder(string? value, int startIndex, int length, int capacity) {
public System.Text.StringBuilder Append(bool value) { throw null; }
public System.Text.StringBuilder Append(byte value) { throw null; }
public System.Text.StringBuilder Append(char value) { throw null; }
+ public System.Text.StringBuilder Append(System.Text.Rune value) { throw null; }
[System.CLSCompliantAttribute(false)]
public unsafe System.Text.StringBuilder Append(char* value, int valueCount) { throw null; }
public System.Text.StringBuilder Append(char value, int repeatCount) { throw null; }
@@ -15842,9 +15872,12 @@ public void CopyTo(int sourceIndex, System.Span destination, int count) {
public bool Equals(System.ReadOnlySpan span) { throw null; }
public bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] System.Text.StringBuilder? sb) { throw null; }
public System.Text.StringBuilder.ChunkEnumerator GetChunks() { throw null; }
+ public System.Text.StringBuilderRuneEnumerator EnumerateRunes() { throw null; }
+ public Rune GetRuneAt(int index) { throw null; }
public System.Text.StringBuilder Insert(int index, bool value) { throw null; }
public System.Text.StringBuilder Insert(int index, byte value) { throw null; }
public System.Text.StringBuilder Insert(int index, char value) { throw null; }
+ public System.Text.StringBuilder Insert(int index, System.Text.Rune value) { throw null; }
public System.Text.StringBuilder Insert(int index, char[]? value) { throw null; }
public System.Text.StringBuilder Insert(int index, char[]? value, int startIndex, int charCount) { throw null; }
public System.Text.StringBuilder Insert(int index, decimal value) { throw null; }
@@ -15868,6 +15901,8 @@ public void CopyTo(int sourceIndex, System.Span destination, int count) {
public System.Text.StringBuilder Remove(int startIndex, int length) { throw null; }
public System.Text.StringBuilder Replace(char oldChar, char newChar) { throw null; }
public System.Text.StringBuilder Replace(char oldChar, char newChar, int startIndex, int count) { throw null; }
+ public System.Text.StringBuilder Replace(System.Text.Rune oldRune, System.Text.Rune newRune) { throw null; }
+ public System.Text.StringBuilder Replace(System.Text.Rune oldRune, System.Text.Rune newRune, int startIndex, int count) { throw null; }
public System.Text.StringBuilder Replace(System.ReadOnlySpan oldValue, System.ReadOnlySpan newValue) { throw null; }
public System.Text.StringBuilder Replace(System.ReadOnlySpan oldValue, System.ReadOnlySpan newValue, int startIndex, int count) { throw null; }
public System.Text.StringBuilder Replace(string oldValue, string? newValue) { throw null; }
@@ -15875,6 +15910,7 @@ public void CopyTo(int sourceIndex, System.Span destination, int count) {
void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { }
public override string ToString() { throw null; }
public string ToString(int startIndex, int length) { throw null; }
+ public bool TryGetRuneAt(int index, out Rune value) { throw null; }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
[System.Runtime.CompilerServices.InterpolatedStringHandlerAttribute]
public partial struct AppendInterpolatedStringHandler
@@ -15904,6 +15940,19 @@ public partial struct ChunkEnumerator
public bool MoveNext() { throw null; }
}
}
+ public partial struct StringBuilderRuneEnumerator : System.Collections.Generic.IEnumerable, System.Collections.Generic.IEnumerator, System.Collections.IEnumerable, System.Collections.IEnumerator, System.IDisposable
+ {
+ private object _dummy;
+ private int _dummyPrimitive;
+ public System.Text.Rune Current { get { throw null; } }
+ object? System.Collections.IEnumerator.Current { get { throw null; } }
+ public System.Text.StringBuilderRuneEnumerator GetEnumerator() { throw null; }
+ public bool MoveNext() { throw null; }
+ System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() { throw null; }
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; }
+ void System.Collections.IEnumerator.Reset() { }
+ void System.IDisposable.Dispose() { }
+ }
public partial struct StringRuneEnumerator : System.Collections.Generic.IEnumerable, System.Collections.Generic.IEnumerator, System.Collections.IEnumerable, System.Collections.IEnumerator, System.IDisposable
{
private object _dummy;
diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Tests/System/Globalization/TextInfoTests.cs b/src/libraries/System.Runtime/tests/System.Globalization.Tests/System/Globalization/TextInfoTests.cs
index 924de536b6efbd..0afc990cba5db7 100644
--- a/src/libraries/System.Runtime/tests/System.Globalization.Tests/System/Globalization/TextInfoTests.cs
+++ b/src/libraries/System.Runtime/tests/System.Globalization.Tests/System/Globalization/TextInfoTests.cs
@@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Reflection;
+using System.Text;
using Xunit;
namespace System.Globalization.Tests
@@ -344,6 +345,25 @@ public void ToLower_Null_ThrowsArgumentNullException(string cultureName)
AssertExtensions.Throws("str", () => new CultureInfo(cultureName).TextInfo.ToLower(null));
}
+ public static IEnumerable