Skip to content
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
207 commits
Select commit Hold shift + click to select a range
5e94820
Add rune overloads
Joy-less Jan 24, 2025
ddecd49
Fix some compiler errors
Joy-less Jan 25, 2025
bedf656
More compiler error fixes
Joy-less Jan 25, 2025
b67dfb9
Even more compiler error fixes
Joy-less Jan 25, 2025
725d1ee
Fix more compiler errors
Joy-less Jan 25, 2025
b2d1828
Merge branch 'dotnet:main' into rune-overloads
Joy-less Jun 30, 2025
1f6e247
Various improvements & add refs
Joy-less Jun 30, 2025
b1e168f
Fix validation in LastIndexOf
Joy-less Jun 30, 2025
5a43a0e
Fix replace core usage
Joy-less Jun 30, 2025
180825f
Merge branch 'main' into rune-overloads
jeffhandley Sep 1, 2025
6c1bc2e
Fix TextInfo ToLower/ToUpper with 2-char runes
Joy-less Sep 5, 2025
74fbb49
Optimize `char.Equals` for Ordinal StringComparison
Joy-less Sep 5, 2025
8b30184
Add conditional compilation for SYSTEM_PRIVATE_CORLIB
Joy-less Sep 17, 2025
663459d
Add more conditional compilation for SYSTEM_PRIVATE_CORLIB
Joy-less Sep 17, 2025
20f24c8
Rename `r` to `value`
Joy-less Sep 17, 2025
d1e174b
Replace static Equals with instance methods
Joy-less Sep 17, 2025
658ca88
Rename `r` to `value` in refs
Joy-less Sep 17, 2025
df93e85
Fix ToLower & ToUpper bypassing invariant globalization check
Joy-less Sep 17, 2025
f931967
Add newline between ToLower, ToUpper
Joy-less Sep 17, 2025
af35f71
Optimize TextWriter.Write(Rune)
Joy-less Sep 17, 2025
1b9a1d3
Optimize TextWriter.WriteLine(Rune)
Joy-less Sep 17, 2025
beec5a0
Change TextWriter.WriteAsync(Rune)
Joy-less Sep 17, 2025
2676505
Change TextWriter.WriteLineAsync(Rune)
Joy-less Sep 17, 2025
23bfb48
Remove unnecessary slices in ToLower(Rune) & ToUpper(Rune)
Joy-less Sep 17, 2025
aaf8dd6
Revert `Split(char, options)`, `Split(char, int, options)` order
Joy-less Sep 17, 2025
c43c906
Flip Split(Rune, options) and Split(Rune, int, options) order
Joy-less Sep 17, 2025
6b3f6c9
Make `Equals(char, StringComparison)` helper internal
Joy-less Sep 17, 2025
8b3dec6
Add `StringBuilder.EnumerateRunes()` & `StringBuilder.RuneEnumerator`
Joy-less Sep 17, 2025
e363d74
Make `IndexOf`/`LastIndexOf` overload helpers internal
Joy-less Sep 17, 2025
b17d8c3
Fix WriteLineAsync(Rune)
Joy-less Sep 17, 2025
283045e
Rename left/right local variables to old/new
Joy-less Sep 17, 2025
94c6645
Remove unnecessary `else`
Joy-less Sep 17, 2025
0485c9b
Move StringBuilder.RuneEnumerator -> StringBuilderRuneEnumerator
Joy-less Sep 17, 2025
ca2a687
Add `ArgumentOutOfRangeException.ThrowIfNegative(index);`
Joy-less Sep 17, 2025
c2d909b
Replace exception with ThrowHelper.ThrowIndexOutOfRangeException();
Joy-less Sep 17, 2025
3b848a3
Add empty line between StringBuilder.Replace overloads
Joy-less Sep 17, 2025
1128ecb
Add doc comments to all added APIs
Joy-less Sep 18, 2025
1062fb5
Use invariant culture for string.Replace(Rune, Rune)
Joy-less Sep 18, 2025
c004b62
Reference `StringBuilderRuneEnumerator.cs` file
Joy-less Sep 18, 2025
5fa50d6
Update exceptions in string builder GetRuneAt
Joy-less Sep 19, 2025
f4b207f
Change string.Replace(Rune, Rune) compare options from None to Ordinal
Joy-less Sep 19, 2025
947439e
Use span comparison for StartsWith/EndsWith
Joy-less Sep 19, 2025
30e6f89
Make sure start index not bypassed in string.Trim(Rune)
Joy-less Sep 19, 2025
0224c91
Fix incorrect slice end index
Joy-less Sep 19, 2025
1ad80df
Replace IndexOf & LastIndexOf code with span conversion
Joy-less Sep 19, 2025
4bdde7f
Add `Contains_Rune` and `Contains_Rune_StringComparison` tests
Joy-less Sep 20, 2025
06dddac
Add string.Split tests
Joy-less Sep 20, 2025
bd91a3d
Add string.IndexOf & string.LastIndexOf tests
Joy-less Sep 20, 2025
a28dc9b
Add string.Replace tests
Joy-less Sep 20, 2025
1144b46
Fix string.Replace tests
Joy-less Sep 20, 2025
b377324
Add string.EndsWith tests
Joy-less Sep 20, 2025
72fe4d1
Add string.StartsWith tests
Joy-less Sep 20, 2025
ee27bf1
Fix bounds check in TrimStart(Rune)
Joy-less Sep 21, 2025
bd2f971
Fix bounds check in Trim(Rune)
Joy-less Sep 21, 2025
407cafb
Simplify string.Empty to Empty
Joy-less Sep 21, 2025
2925584
Use AsSpan helper method to simplify rune to span
Joy-less Sep 21, 2025
c64b0dd
Document ordinal comparisons
Joy-less Sep 21, 2025
6f47afc
Fix IndexOf/LastIndexOf startIndex
Joy-less Sep 21, 2025
c626e3c
Add Trim Rune tests
Joy-less Sep 21, 2025
76db11d
Add StringBuilder test to EnumerateRunes test
Joy-less Sep 21, 2025
b276ff1
Separate EnumerateRunes tests for string & StringBuilder
Joy-less Sep 21, 2025
ce8ea65
Add Rune.Equals(Rune, StringComparison) test
Joy-less Sep 21, 2025
be12d9e
Add StringBuilder Append Rune test
Joy-less Sep 21, 2025
04e655c
Add StringBuilder GetRuneAt/TryGetRuneAt tests
Joy-less Sep 21, 2025
6846bcf
Add StringBuilder Insert_Rune and Insert_Rune_Invalid tests
Joy-less Sep 21, 2025
730d609
Use consistent style with other tests
Joy-less Sep 21, 2025
e3c35e4
Add StringBuilder ReplaceRune tests
Joy-less Sep 21, 2025
08381f9
Add TextWriter rune tests
Joy-less Sep 21, 2025
b08bb26
Add TextInfo ToLower/ToUpper tests
Joy-less Sep 21, 2025
6b6fae9
Add rune tests to SplitCharSeparator
Joy-less Sep 21, 2025
3584b63
Fix sub index not offsetted in IndexOf/LastIndexOf
Joy-less Sep 21, 2025
4fb0122
Fix LastIndexOf searchIndex
Joy-less Sep 21, 2025
816c25c
Fix LastIndexOf
Joy-less Sep 22, 2025
4c61363
Remove `#if SYSTEM_PRIVATE_CORLIB`
Joy-less Sep 22, 2025
6179d56
Change code style for rune trimming
Joy-less Sep 22, 2025
eef1a03
Fix error in new code style
Joy-less Sep 22, 2025
6255a5b
Fix `Rune` not found in `TextInfoTests.cs`
Joy-less Sep 23, 2025
2deb262
Fix incorrect method name in `TextWriterTests`
Joy-less Sep 23, 2025
0e8585f
Fix incorrect StartsWith Rune test
Joy-less Sep 23, 2025
80f0598
Fix tests with Rune attribute arguments
Joy-less Sep 23, 2025
97f69b2
Fix `str` -> `value`
Joy-less Sep 23, 2025
dc35bbb
Fix concatenating rune array
Joy-less Sep 23, 2025
c935a5f
Add dedicated `SplitRuneSeparator` test
Joy-less Sep 24, 2025
9354640
Fix trim rune tests
Joy-less Sep 24, 2025
332a040
Fix invalid collection expressions in tests
Joy-less Sep 24, 2025
819a480
Fix InsertRune test
Joy-less Sep 24, 2025
7a4742f
Fix replace rune tests
Joy-less Sep 24, 2025
6104e54
Fix unused comparisonType parameter
Joy-less Sep 24, 2025
bb1c1e9
Fix index of test names
Joy-less Sep 24, 2025
c5ea928
Remove redundant test
Joy-less Sep 24, 2025
dc48f17
Fix missing using
Joy-less Sep 24, 2025
0dd15b7
Fix replace method called with string comparison argument
Joy-less Sep 24, 2025
bae28a1
Fix tried to test split with rune on span
Joy-less Sep 24, 2025
6bbea5b
Remove start index from LastIndexOf tests
Joy-less Sep 24, 2025
e098468
Remove non-existent comparisonType parameter
Joy-less Sep 24, 2025
5f4e8b8
Fix StartsWith test incorrect call
Joy-less Sep 24, 2025
f289829
Fix LastIndexOf behaviour
Joy-less Sep 24, 2025
9e883b3
Various rune test fixes
Joy-less Sep 24, 2025
c48ff26
Fix error in LastIndexOf test
Joy-less Sep 24, 2025
73df815
Some fixes and ensure the Runtime tests passes
tarekgh Sep 25, 2025
59c0f39
Merge branch 'rune-overloads' of https://github.com/Joy-less/runtime …
tarekgh Sep 25, 2025
01f80dc
Add brackets to ternary condition
Joy-less Sep 25, 2025
71abe12
Rename `runeSeparator` to `separatorSpan`
Joy-less Sep 25, 2025
00e670d
Add rune overloads
Joy-less Jan 24, 2025
f297561
Fix some compiler errors
Joy-less Jan 25, 2025
5cd72fd
More compiler error fixes
Joy-less Jan 25, 2025
d6f6a49
Even more compiler error fixes
Joy-less Jan 25, 2025
e6b21e4
Fix more compiler errors
Joy-less Jan 25, 2025
9b3b4a1
Various improvements & add refs
Joy-less Jun 30, 2025
fdc3103
Fix validation in LastIndexOf
Joy-less Jun 30, 2025
86b36bb
Fix replace core usage
Joy-less Jun 30, 2025
c52087d
Fix TextInfo ToLower/ToUpper with 2-char runes
Joy-less Sep 5, 2025
58f21e4
Optimize `char.Equals` for Ordinal StringComparison
Joy-less Sep 5, 2025
5a67feb
Add conditional compilation for SYSTEM_PRIVATE_CORLIB
Joy-less Sep 17, 2025
d00aea3
Add more conditional compilation for SYSTEM_PRIVATE_CORLIB
Joy-less Sep 17, 2025
d776654
Rename `r` to `value`
Joy-less Sep 17, 2025
b00e49d
Replace static Equals with instance methods
Joy-less Sep 17, 2025
e39de05
Rename `r` to `value` in refs
Joy-less Sep 17, 2025
73881d2
Fix ToLower & ToUpper bypassing invariant globalization check
Joy-less Sep 17, 2025
449f5ed
Add newline between ToLower, ToUpper
Joy-less Sep 17, 2025
b55687b
Optimize TextWriter.Write(Rune)
Joy-less Sep 17, 2025
94f0f34
Optimize TextWriter.WriteLine(Rune)
Joy-less Sep 17, 2025
25449bc
Change TextWriter.WriteAsync(Rune)
Joy-less Sep 17, 2025
86bfacb
Change TextWriter.WriteLineAsync(Rune)
Joy-less Sep 17, 2025
61477db
Remove unnecessary slices in ToLower(Rune) & ToUpper(Rune)
Joy-less Sep 17, 2025
3e9dd08
Revert `Split(char, options)`, `Split(char, int, options)` order
Joy-less Sep 17, 2025
0fef816
Flip Split(Rune, options) and Split(Rune, int, options) order
Joy-less Sep 17, 2025
ba997dd
Make `Equals(char, StringComparison)` helper internal
Joy-less Sep 17, 2025
da4dd9d
Add `StringBuilder.EnumerateRunes()` & `StringBuilder.RuneEnumerator`
Joy-less Sep 17, 2025
da6eca7
Make `IndexOf`/`LastIndexOf` overload helpers internal
Joy-less Sep 17, 2025
f2417f6
Fix WriteLineAsync(Rune)
Joy-less Sep 17, 2025
a41999a
Rename left/right local variables to old/new
Joy-less Sep 17, 2025
1405c65
Remove unnecessary `else`
Joy-less Sep 17, 2025
dd86940
Move StringBuilder.RuneEnumerator -> StringBuilderRuneEnumerator
Joy-less Sep 17, 2025
ccc5093
Add `ArgumentOutOfRangeException.ThrowIfNegative(index);`
Joy-less Sep 17, 2025
df98366
Replace exception with ThrowHelper.ThrowIndexOutOfRangeException();
Joy-less Sep 17, 2025
ff612f5
Add empty line between StringBuilder.Replace overloads
Joy-less Sep 17, 2025
842fd1d
Add doc comments to all added APIs
Joy-less Sep 18, 2025
9461ae0
Use invariant culture for string.Replace(Rune, Rune)
Joy-less Sep 18, 2025
f37a78f
Reference `StringBuilderRuneEnumerator.cs` file
Joy-less Sep 18, 2025
4905db3
Update exceptions in string builder GetRuneAt
Joy-less Sep 19, 2025
9a4c73f
Change string.Replace(Rune, Rune) compare options from None to Ordinal
Joy-less Sep 19, 2025
f974ebb
Use span comparison for StartsWith/EndsWith
Joy-less Sep 19, 2025
38e43dc
Make sure start index not bypassed in string.Trim(Rune)
Joy-less Sep 19, 2025
6eca7e4
Fix incorrect slice end index
Joy-less Sep 19, 2025
233c8a5
Replace IndexOf & LastIndexOf code with span conversion
Joy-less Sep 19, 2025
a49f934
Add `Contains_Rune` and `Contains_Rune_StringComparison` tests
Joy-less Sep 20, 2025
5369355
Add string.Split tests
Joy-less Sep 20, 2025
4d25199
Add string.IndexOf & string.LastIndexOf tests
Joy-less Sep 20, 2025
fb304d7
Add string.Replace tests
Joy-less Sep 20, 2025
69f10af
Fix string.Replace tests
Joy-less Sep 20, 2025
ad562b8
Add string.EndsWith tests
Joy-less Sep 20, 2025
c9e3ffd
Add string.StartsWith tests
Joy-less Sep 20, 2025
f055c19
Fix bounds check in TrimStart(Rune)
Joy-less Sep 21, 2025
ea406f1
Fix bounds check in Trim(Rune)
Joy-less Sep 21, 2025
f02a82a
Simplify string.Empty to Empty
Joy-less Sep 21, 2025
7c49a43
Use AsSpan helper method to simplify rune to span
Joy-less Sep 21, 2025
212d59d
Document ordinal comparisons
Joy-less Sep 21, 2025
229d6f7
Fix IndexOf/LastIndexOf startIndex
Joy-less Sep 21, 2025
7ffe7f6
Add Trim Rune tests
Joy-less Sep 21, 2025
65ed65a
Add StringBuilder test to EnumerateRunes test
Joy-less Sep 21, 2025
bb59326
Separate EnumerateRunes tests for string & StringBuilder
Joy-less Sep 21, 2025
84bbd87
Add Rune.Equals(Rune, StringComparison) test
Joy-less Sep 21, 2025
e4d2dac
Add StringBuilder Append Rune test
Joy-less Sep 21, 2025
6b3d8c1
Add StringBuilder GetRuneAt/TryGetRuneAt tests
Joy-less Sep 21, 2025
ae3d6a3
Add StringBuilder Insert_Rune and Insert_Rune_Invalid tests
Joy-less Sep 21, 2025
ffe4116
Use consistent style with other tests
Joy-less Sep 21, 2025
9b595bc
Add StringBuilder ReplaceRune tests
Joy-less Sep 21, 2025
82ba285
Add TextWriter rune tests
Joy-less Sep 21, 2025
759a921
Add TextInfo ToLower/ToUpper tests
Joy-less Sep 21, 2025
416192f
Add rune tests to SplitCharSeparator
Joy-less Sep 21, 2025
e6afb41
Fix sub index not offsetted in IndexOf/LastIndexOf
Joy-less Sep 21, 2025
9505162
Fix LastIndexOf searchIndex
Joy-less Sep 21, 2025
7ea8272
Fix LastIndexOf
Joy-less Sep 22, 2025
a0817cc
Remove `#if SYSTEM_PRIVATE_CORLIB`
Joy-less Sep 22, 2025
6de8dc0
Change code style for rune trimming
Joy-less Sep 22, 2025
99306c7
Fix error in new code style
Joy-less Sep 22, 2025
f7e7840
Fix `Rune` not found in `TextInfoTests.cs`
Joy-less Sep 23, 2025
09b0236
Fix incorrect method name in `TextWriterTests`
Joy-less Sep 23, 2025
dc24be3
Fix incorrect StartsWith Rune test
Joy-less Sep 23, 2025
c9e63de
Fix tests with Rune attribute arguments
Joy-less Sep 23, 2025
dff360e
Fix `str` -> `value`
Joy-less Sep 23, 2025
d705994
Fix concatenating rune array
Joy-less Sep 23, 2025
e8ab8fa
Add dedicated `SplitRuneSeparator` test
Joy-less Sep 24, 2025
8b7e4cc
Fix trim rune tests
Joy-less Sep 24, 2025
1fb541f
Fix invalid collection expressions in tests
Joy-less Sep 24, 2025
1cd8975
Fix InsertRune test
Joy-less Sep 24, 2025
238d186
Fix replace rune tests
Joy-less Sep 24, 2025
ec312ef
Fix unused comparisonType parameter
Joy-less Sep 24, 2025
0c24087
Fix index of test names
Joy-less Sep 24, 2025
0b916d0
Remove redundant test
Joy-less Sep 24, 2025
9877523
Fix missing using
Joy-less Sep 24, 2025
c8267b8
Fix replace method called with string comparison argument
Joy-less Sep 24, 2025
b48a801
Fix tried to test split with rune on span
Joy-less Sep 24, 2025
84bac61
Remove start index from LastIndexOf tests
Joy-less Sep 24, 2025
9d34125
Remove non-existent comparisonType parameter
Joy-less Sep 24, 2025
8f21b60
Fix StartsWith test incorrect call
Joy-less Sep 24, 2025
660a85a
Fix LastIndexOf behaviour
Joy-less Sep 24, 2025
50e2cc3
Various rune test fixes
Joy-less Sep 24, 2025
c5b4880
Some fixes and ensure the Runtime tests passes
tarekgh Sep 25, 2025
3b6d320
Add brackets to ternary condition
Joy-less Sep 25, 2025
83791c3
Rename `runeSeparator` to `separatorSpan`
Joy-less Sep 25, 2025
1851052
Final fixes touches.
tarekgh Sep 25, 2025
de1e84d
Merge branch 'rune-overloads' of https://github.com/Joy-less/runtime …
tarekgh Sep 25, 2025
5df6484
Merge branch 'main' into rune-overloads
tarekgh Sep 25, 2025
c7dc92b
Merge branch 'main' of https://github.com/dotnet/runtime into rune-ov…
tarekgh Sep 25, 2025
859d0a5
Merge branch 'rune-overloads' of https://github.com/Joy-less/runtime …
tarekgh Sep 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/libraries/System.Private.CoreLib/src/System/Char.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<char> leftCharsSlice = [this];
ReadOnlySpan<char> 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,17 @@ public string ToLower(string str)
return ChangeCaseCommon<ToLowerConversion>(str);
}

internal void ToLower(ReadOnlySpan<char> source, Span<char> destination)
{
if (GlobalizationMode.Invariant)
{
InvariantModeCasing.ToLower(source, destination);
return;
}

ChangeCaseCommon<ToLowerConversion>(source, destination);
}

private unsafe char ChangeCase(char c, bool toUpper)
{
Debug.Assert(!GlobalizationMode.Invariant);
Expand Down Expand Up @@ -451,6 +462,17 @@ public string ToUpper(string str)
return ChangeCaseCommon<ToUpperConversion>(str);
}

internal void ToUpper(ReadOnlySpan<char> source, Span<char> destination)
{
if (GlobalizationMode.Invariant)
{
InvariantModeCasing.ToUpper(source, destination);
return;
}

ChangeCaseCommon<ToUpperConversion>(source, destination);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static char ToUpperAsciiInvariant(char c)
{
Expand All @@ -461,6 +483,46 @@ internal static char ToUpperAsciiInvariant(char c)
return c;
}

public Rune ToLower(Rune value)
{
// Convert rune to span
Span<char> chars = stackalloc char[2];
int charsWritten = value.EncodeToUtf16(chars);

// Change span to lower and convert to rune
if (charsWritten == 2)
{
Span<char> lowerChars = stackalloc char[2];
ToLower(chars, lowerChars);
return new Rune(lowerChars[0], lowerChars[1]);
}
else
{
char lowerChar = ToLower(chars[0]);
return new Rune(lowerChar);
}
}

public Rune ToUpper(Rune value)
{
// Convert rune to span
Span<char> chars = stackalloc char[2];
int charsWritten = value.EncodeToUtf16(chars);

// Change span to upper and convert to rune
if (charsWritten == 2)
{
Span<char> upperChars = stackalloc char[2];
ToUpper(chars, upperChars);
return new Rune(upperChars[0], upperChars[1]);
}
else
{
char upperChar = ToUpper(chars[0]);
return new Rune(upperChar);
}
}

private bool IsAsciiCasingSameAsInvariant
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand Down
67 changes: 67 additions & 0 deletions src/libraries/System.Private.CoreLib/src/System/IO/TextWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,20 @@ public virtual void Write(char value)
{
}

public virtual void Write(Rune value)
{
// Convert value to span
Span<char> chars = stackalloc char[2];
int charsWritten = value.EncodeToUtf16(chars);

// Write span
Write(chars[0]);
if (charsWritten > 1)
{
Write(chars[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.
Expand Down Expand Up @@ -343,6 +357,23 @@ public virtual void WriteLine(char value)
WriteLine();
}

public virtual void WriteLine(Rune value)
{
// Convert value to span
Span<char> chars = stackalloc char[2];
int charsWritten = value.EncodeToUtf16(chars);

if (charsWritten > 1)
{
Write(chars[0]);
WriteLine(chars[1]);
}
else
{
WriteLine(chars[0]);
}
}

// Writes an array of characters followed by a line terminator to the text
// stream.
//
Expand Down Expand Up @@ -542,6 +573,24 @@ public virtual Task WriteAsync(char value) =>
t.Item1.Write(t.Item2);
}, new TupleSlim<TextWriter, char>(this, value), CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);

public virtual Task WriteAsync(Rune value)
{
Span<char> chars = stackalloc char[2];
int charsWritten = value.EncodeToUtf16(chars);

if (charsWritten > 1)
{
return Task.Factory.StartNew(static state =>
{
var t = (TupleSlim<TextWriter, char, char>)state!;
t.Item1.Write(t.Item2);
t.Item1.Write(t.Item3);
}, new TupleSlim<TextWriter, char, char>(this, chars[0], chars[1]), CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
}

return WriteAsync(chars[0]);
}

public virtual Task WriteAsync(string? value) =>
Task.Factory.StartNew(static state =>
{
Expand Down Expand Up @@ -605,6 +654,24 @@ public virtual Task WriteLineAsync(char value) =>
t.Item1.WriteLine(t.Item2);
}, new TupleSlim<TextWriter, char>(this, value), CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);

public virtual Task WriteLineAsync(Rune value)
{
Span<char> chars = stackalloc char[2];
int charsWritten = value.EncodeToUtf16(chars);

if (charsWritten > 1)
{
return Task.Factory.StartNew(static state =>
{
var t = (TupleSlim<TextWriter, char, char>)state!;
t.Item1.Write(t.Item2);
t.Item1.WriteLine(t.Item3);
}, new TupleSlim<TextWriter, char, char>(this, chars[0], chars[1]), CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
}

return WriteAsync(chars[0]);
}

public virtual Task WriteLineAsync(string? value) =>
Task.Factory.StartNew(static state =>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.Unicode;

namespace System
Expand Down Expand Up @@ -589,6 +590,29 @@ public bool EndsWith(char value)
return ((uint)lastPos < (uint)Length) && this[lastPos] == value;
}

public bool EndsWith(char value, StringComparison comparisonType)
{
if (Length == 0)
{
return false;
}
return this[^1].Equals(value, comparisonType);
}

public bool EndsWith(Rune value)
{
return EndsWith(value, StringComparison.Ordinal);
}

public bool EndsWith(Rune value, StringComparison comparisonType)
{
if (Rune.DecodeLastFromUtf16(this, out Rune result, out _) is OperationStatus.Done)
{
return result.Equals(value, comparisonType);
}
return false;
}

// Determines whether two strings match.
public override bool Equals([NotNullWhen(true)] object? obj)
{
Expand Down Expand Up @@ -1162,6 +1186,29 @@ public bool StartsWith(char value)
return Length != 0 && _firstChar == value;
}

public bool StartsWith(char value, StringComparison comparisonType)
{
if (this.Length == 0)
{
return false;
}
return this[0].Equals(value, comparisonType);
}

public bool StartsWith(Rune value)
{
return StartsWith(value, StringComparison.Ordinal);
}

public bool StartsWith(Rune value, StringComparison comparisonType)
{
if (Rune.DecodeFromUtf16(this, out Rune result, out _) is OperationStatus.Done)
{
return result.Equals(value, comparisonType);
}
return false;
}

internal static void CheckStringComparison(StringComparison comparisonType)
{
// Single comparison to check if comparisonType is within [CurrentCulture .. OrdinalIgnoreCase]
Expand Down
Loading
Loading