Skip to content

Commit

Permalink
Support System.Text.Rune (#219)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Joy-less authored Jan 11, 2025
1 parent 0c9e17d commit 8a87e6a
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 12 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,21 @@ All notable changes to **ValueStringBuilder** will be documented in this file. T

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.

### Added

- Added `Append(Rune)` overload
- Added `AppendJoin(Rune, IEnumerable<string?>)` overload
- Added `AppendJoin<T>(Rune, IEnumerable<T>)` overload

### Removed

- Support for `net6.0` and `net7.0` was removed.

### Changed

- 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).
- Optimised `AppendLine(scoped ReadOnlySpan<char>)` by avoiding allocating a new string
- Removed erroneous null check in `AppendJoin<T>(ReadOnlySpan<char>, IEnumerable<T>)`

## [1.22.0] - 2024-12-18

Expand Down
20 changes: 18 additions & 2 deletions src/LinkDotNet.StringBuilder/ValueStringBuilder.Append.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;

namespace LinkDotNet.StringBuilder;

Expand Down Expand Up @@ -117,6 +118,20 @@ public void Append(char value)
bufferPosition++;
}

/// <summary>
/// Appends a single rune to the string builder.
/// </summary>
/// <param name="value">Rune to add.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Append(Rune value)
{
Span<char> valueChars = stackalloc char[2];
int valueCharsWritten = value.EncodeToUtf16(valueChars);
ReadOnlySpan<char> valueCharsReadOnly = valueChars[..valueCharsWritten];

Append(valueCharsReadOnly);
}

/// <summary>
/// Adds the default new line separator.
/// </summary>
Expand All @@ -129,11 +144,12 @@ public void AppendLine()
/// <summary>
/// Does the same as <see cref="Append(char)"/> but adds a newline at the end.
/// </summary>
/// <param name="str">String, which will be added to this builder.</param>
/// <param name="str">String to be added to this builder.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AppendLine(scoped ReadOnlySpan<char> str)
{
Append(string.Concat(str, Environment.NewLine));
Append(str);
Append(Environment.NewLine);
}

/// <summary>
Expand Down
60 changes: 50 additions & 10 deletions src/LinkDotNet.StringBuilder/ValueStringBuilder.AppendJoin.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Runtime.CompilerServices;
using System.Text;

namespace LinkDotNet.StringBuilder;

Expand All @@ -8,7 +9,7 @@ public ref partial struct ValueStringBuilder
/// Concatenates and appends all values with the given separator between each entry at the end of the string.
/// </summary>
/// <param name="separator">String used as separator between the entries.</param>
/// <param name="values">Array of strings, which will be concatenated.</param>
/// <param name="values">Enumerable of strings to be concatenated.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AppendJoin(ReadOnlySpan<char> separator, IEnumerable<string?> values)
=> AppendJoinInternalString(separator, values);
Expand All @@ -17,17 +18,26 @@ public void AppendJoin(ReadOnlySpan<char> separator, IEnumerable<string?> values
/// Concatenates and appends all values with the given separator between each entry at the end of the string.
/// </summary>
/// <param name="separator">Character used as separator between the entries.</param>
/// <param name="values">Array of strings, which will be concatenated.</param>
/// <param name="values">Enumerable of strings to be concatenated.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AppendJoin(char separator, IEnumerable<string?> values)
=> AppendJoinInternalChar(separator, values);

/// <summary>
/// Concatenates and appends all values with the given separator between each entry at the end of the string.
/// </summary>
/// <param name="separator">Rune used as separator between the entries.</param>
/// <param name="values">Enumerable of strings to be concatenated.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AppendJoin(Rune separator, IEnumerable<string?> values)
=> AppendJoinInternalRune(separator, values);

/// <summary>
/// Concatenates and appends all values with the given separator between each entry at the end of the string.
/// </summary>
/// <param name="separator">String used as separator between the entries.</param>
/// <param name="values">Array of strings, which will be concatenated.</param>
/// <typeparam name="T">Type of the given array.</typeparam>
/// <param name="values">Enumerable to be concatenated.</param>
/// <typeparam name="T">Type of the given enumerable.</typeparam>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AppendJoin<T>(ReadOnlySpan<char> separator, IEnumerable<T> values)
=> AppendJoinInternalString(separator, values);
Expand All @@ -36,12 +46,22 @@ public void AppendJoin<T>(ReadOnlySpan<char> separator, IEnumerable<T> values)
/// Concatenates and appends all values with the given separator between each entry at the end of the string.
/// </summary>
/// <param name="separator">Character used as separator between the entries.</param>
/// <param name="values">Array of strings, which will be concatenated.</param>
/// <typeparam name="T">Type of the given array.</typeparam>
/// <param name="values">Enumerable to be concatenated.</param>
/// <typeparam name="T">Type of the given enumerable.</typeparam>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AppendJoin<T>(char separator, IEnumerable<T> values)
=> AppendJoinInternalChar(separator, values);

/// <summary>
/// Concatenates and appends all values with the given separator between each entry at the end of the string.
/// </summary>
/// <param name="separator">Rune used as separator between the entries.</param>
/// <param name="values">Enumerable to be concatenated.</param>
/// <typeparam name="T">Type of the given enumerable.</typeparam>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AppendJoin<T>(Rune separator, IEnumerable<T> values)
=> AppendJoinInternalRune(separator, values);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void AppendJoinInternalString<T>(ReadOnlySpan<char> separator, IEnumerable<T> values)
{
Expand All @@ -55,10 +75,7 @@ private void AppendJoinInternalString<T>(ReadOnlySpan<char> separator, IEnumerab
}

var current = enumerator.Current;
if (current is not null)
{
AppendInternal(current);
}
AppendInternal(current);

while (enumerator.MoveNext())
{
Expand Down Expand Up @@ -91,6 +108,29 @@ private void AppendJoinInternalChar<T>(char separator, IEnumerable<T> values)
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void AppendJoinInternalRune<T>(Rune separator, IEnumerable<T> values)
{
ArgumentNullException.ThrowIfNull(values);

using var enumerator = values.GetEnumerator();

if (!enumerator.MoveNext())
{
return;
}

var current = enumerator.Current;
AppendInternal(current);

while (enumerator.MoveNext())
{
Append(separator);
current = enumerator.Current;
AppendInternal(current);
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void AppendInternal<T>(T value)
{
Expand Down

0 comments on commit 8a87e6a

Please sign in to comment.