Skip to content

Commit 8a87e6a

Browse files
authored
Support System.Text.Rune (#219)
* Add Append(Rune) * Add AppendJoin(Rune, IEnumerable<T>) * Avoid string allocation in AppendLine(ReadOnlySpan<char>) * Add missing AggressiveInlining attribute * Update CHANGELOG.md * Restructure CHANGELOG.md changes
1 parent 0c9e17d commit 8a87e6a

File tree

3 files changed

+78
-12
lines changed

3 files changed

+78
-12
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,21 @@ All notable changes to **ValueStringBuilder** will be documented in this file. T
88

99
This is the `v2` release of the **ValueStringBuilder**. There aren't any noticeable changes. Only old framework versions were removed to make further development easier.
1010

11+
### Added
12+
13+
- Added `Append(Rune)` overload
14+
- Added `AppendJoin(Rune, IEnumerable<string?>)` overload
15+
- Added `AppendJoin<T>(Rune, IEnumerable<T>)` overload
16+
1117
### Removed
18+
1219
- Support for `net6.0` and `net7.0` was removed.
1320

1421
### Changed
22+
1523
- Added `OverloadResolutionPriority` for `Span` overload for the ctor to keep the current behavior. Reported by [@nsentinel])(https://github.com/nsentinel) in [#210](https://github.com/linkdotnet/StringBuilder/issues/210).
24+
- Optimised `AppendLine(scoped ReadOnlySpan<char>)` by avoiding allocating a new string
25+
- Removed erroneous null check in `AppendJoin<T>(ReadOnlySpan<char>, IEnumerable<T>)`
1626

1727
## [1.22.0] - 2024-12-18
1828

src/LinkDotNet.StringBuilder/ValueStringBuilder.Append.cs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Runtime.CompilerServices;
22
using System.Runtime.InteropServices;
3+
using System.Text;
34

45
namespace LinkDotNet.StringBuilder;
56

@@ -117,6 +118,20 @@ public void Append(char value)
117118
bufferPosition++;
118119
}
119120

121+
/// <summary>
122+
/// Appends a single rune to the string builder.
123+
/// </summary>
124+
/// <param name="value">Rune to add.</param>
125+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
126+
public void Append(Rune value)
127+
{
128+
Span<char> valueChars = stackalloc char[2];
129+
int valueCharsWritten = value.EncodeToUtf16(valueChars);
130+
ReadOnlySpan<char> valueCharsReadOnly = valueChars[..valueCharsWritten];
131+
132+
Append(valueCharsReadOnly);
133+
}
134+
120135
/// <summary>
121136
/// Adds the default new line separator.
122137
/// </summary>
@@ -129,11 +144,12 @@ public void AppendLine()
129144
/// <summary>
130145
/// Does the same as <see cref="Append(char)"/> but adds a newline at the end.
131146
/// </summary>
132-
/// <param name="str">String, which will be added to this builder.</param>
147+
/// <param name="str">String to be added to this builder.</param>
133148
[MethodImpl(MethodImplOptions.AggressiveInlining)]
134149
public void AppendLine(scoped ReadOnlySpan<char> str)
135150
{
136-
Append(string.Concat(str, Environment.NewLine));
151+
Append(str);
152+
Append(Environment.NewLine);
137153
}
138154

139155
/// <summary>

src/LinkDotNet.StringBuilder/ValueStringBuilder.AppendJoin.cs

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Runtime.CompilerServices;
2+
using System.Text;
23

34
namespace LinkDotNet.StringBuilder;
45

@@ -8,7 +9,7 @@ public ref partial struct ValueStringBuilder
89
/// Concatenates and appends all values with the given separator between each entry at the end of the string.
910
/// </summary>
1011
/// <param name="separator">String used as separator between the entries.</param>
11-
/// <param name="values">Array of strings, which will be concatenated.</param>
12+
/// <param name="values">Enumerable of strings to be concatenated.</param>
1213
[MethodImpl(MethodImplOptions.AggressiveInlining)]
1314
public void AppendJoin(ReadOnlySpan<char> separator, IEnumerable<string?> values)
1415
=> AppendJoinInternalString(separator, values);
@@ -17,17 +18,26 @@ public void AppendJoin(ReadOnlySpan<char> separator, IEnumerable<string?> values
1718
/// Concatenates and appends all values with the given separator between each entry at the end of the string.
1819
/// </summary>
1920
/// <param name="separator">Character used as separator between the entries.</param>
20-
/// <param name="values">Array of strings, which will be concatenated.</param>
21+
/// <param name="values">Enumerable of strings to be concatenated.</param>
2122
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2223
public void AppendJoin(char separator, IEnumerable<string?> values)
2324
=> AppendJoinInternalChar(separator, values);
2425

26+
/// <summary>
27+
/// Concatenates and appends all values with the given separator between each entry at the end of the string.
28+
/// </summary>
29+
/// <param name="separator">Rune used as separator between the entries.</param>
30+
/// <param name="values">Enumerable of strings to be concatenated.</param>
31+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
32+
public void AppendJoin(Rune separator, IEnumerable<string?> values)
33+
=> AppendJoinInternalRune(separator, values);
34+
2535
/// <summary>
2636
/// Concatenates and appends all values with the given separator between each entry at the end of the string.
2737
/// </summary>
2838
/// <param name="separator">String used as separator between the entries.</param>
29-
/// <param name="values">Array of strings, which will be concatenated.</param>
30-
/// <typeparam name="T">Type of the given array.</typeparam>
39+
/// <param name="values">Enumerable to be concatenated.</param>
40+
/// <typeparam name="T">Type of the given enumerable.</typeparam>
3141
[MethodImpl(MethodImplOptions.AggressiveInlining)]
3242
public void AppendJoin<T>(ReadOnlySpan<char> separator, IEnumerable<T> values)
3343
=> AppendJoinInternalString(separator, values);
@@ -36,12 +46,22 @@ public void AppendJoin<T>(ReadOnlySpan<char> separator, IEnumerable<T> values)
3646
/// Concatenates and appends all values with the given separator between each entry at the end of the string.
3747
/// </summary>
3848
/// <param name="separator">Character used as separator between the entries.</param>
39-
/// <param name="values">Array of strings, which will be concatenated.</param>
40-
/// <typeparam name="T">Type of the given array.</typeparam>
49+
/// <param name="values">Enumerable to be concatenated.</param>
50+
/// <typeparam name="T">Type of the given enumerable.</typeparam>
4151
[MethodImpl(MethodImplOptions.AggressiveInlining)]
4252
public void AppendJoin<T>(char separator, IEnumerable<T> values)
4353
=> AppendJoinInternalChar(separator, values);
4454

55+
/// <summary>
56+
/// Concatenates and appends all values with the given separator between each entry at the end of the string.
57+
/// </summary>
58+
/// <param name="separator">Rune used as separator between the entries.</param>
59+
/// <param name="values">Enumerable to be concatenated.</param>
60+
/// <typeparam name="T">Type of the given enumerable.</typeparam>
61+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
62+
public void AppendJoin<T>(Rune separator, IEnumerable<T> values)
63+
=> AppendJoinInternalRune(separator, values);
64+
4565
[MethodImpl(MethodImplOptions.AggressiveInlining)]
4666
private void AppendJoinInternalString<T>(ReadOnlySpan<char> separator, IEnumerable<T> values)
4767
{
@@ -55,10 +75,7 @@ private void AppendJoinInternalString<T>(ReadOnlySpan<char> separator, IEnumerab
5575
}
5676

5777
var current = enumerator.Current;
58-
if (current is not null)
59-
{
60-
AppendInternal(current);
61-
}
78+
AppendInternal(current);
6279

6380
while (enumerator.MoveNext())
6481
{
@@ -91,6 +108,29 @@ private void AppendJoinInternalChar<T>(char separator, IEnumerable<T> values)
91108
}
92109
}
93110

111+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
112+
private void AppendJoinInternalRune<T>(Rune separator, IEnumerable<T> values)
113+
{
114+
ArgumentNullException.ThrowIfNull(values);
115+
116+
using var enumerator = values.GetEnumerator();
117+
118+
if (!enumerator.MoveNext())
119+
{
120+
return;
121+
}
122+
123+
var current = enumerator.Current;
124+
AppendInternal(current);
125+
126+
while (enumerator.MoveNext())
127+
{
128+
Append(separator);
129+
current = enumerator.Current;
130+
AppendInternal(current);
131+
}
132+
}
133+
94134
[MethodImpl(MethodImplOptions.AggressiveInlining)]
95135
private void AppendInternal<T>(T value)
96136
{

0 commit comments

Comments
 (0)