diff --git a/CHANGELOG.md b/CHANGELOG.md index 61f24ea..03005d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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)` overload +- Added `AppendJoin(Rune, IEnumerable)` 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)` by avoiding allocating a new string +- Removed erroneous null check in `AppendJoin(ReadOnlySpan, IEnumerable)` ## [1.22.0] - 2024-12-18 diff --git a/src/LinkDotNet.StringBuilder/ValueStringBuilder.Append.cs b/src/LinkDotNet.StringBuilder/ValueStringBuilder.Append.cs index 0bbc6bb..c3259e0 100644 --- a/src/LinkDotNet.StringBuilder/ValueStringBuilder.Append.cs +++ b/src/LinkDotNet.StringBuilder/ValueStringBuilder.Append.cs @@ -1,5 +1,6 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Text; namespace LinkDotNet.StringBuilder; @@ -117,6 +118,20 @@ public void Append(char value) bufferPosition++; } + /// + /// Appends a single rune to the string builder. + /// + /// Rune to add. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Append(Rune value) + { + Span valueChars = stackalloc char[2]; + int valueCharsWritten = value.EncodeToUtf16(valueChars); + ReadOnlySpan valueCharsReadOnly = valueChars[..valueCharsWritten]; + + Append(valueCharsReadOnly); + } + /// /// Adds the default new line separator. /// @@ -129,11 +144,12 @@ public void AppendLine() /// /// Does the same as but adds a newline at the end. /// - /// String, which will be added to this builder. + /// String to be added to this builder. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendLine(scoped ReadOnlySpan str) { - Append(string.Concat(str, Environment.NewLine)); + Append(str); + Append(Environment.NewLine); } /// diff --git a/src/LinkDotNet.StringBuilder/ValueStringBuilder.AppendJoin.cs b/src/LinkDotNet.StringBuilder/ValueStringBuilder.AppendJoin.cs index a6241c5..b7bdb76 100644 --- a/src/LinkDotNet.StringBuilder/ValueStringBuilder.AppendJoin.cs +++ b/src/LinkDotNet.StringBuilder/ValueStringBuilder.AppendJoin.cs @@ -1,4 +1,5 @@ using System.Runtime.CompilerServices; +using System.Text; namespace LinkDotNet.StringBuilder; @@ -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. /// /// String used as separator between the entries. - /// Array of strings, which will be concatenated. + /// Enumerable of strings to be concatenated. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendJoin(ReadOnlySpan separator, IEnumerable values) => AppendJoinInternalString(separator, values); @@ -17,17 +18,26 @@ public void AppendJoin(ReadOnlySpan separator, IEnumerable values /// Concatenates and appends all values with the given separator between each entry at the end of the string. /// /// Character used as separator between the entries. - /// Array of strings, which will be concatenated. + /// Enumerable of strings to be concatenated. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendJoin(char separator, IEnumerable values) => AppendJoinInternalChar(separator, values); + /// + /// Concatenates and appends all values with the given separator between each entry at the end of the string. + /// + /// Rune used as separator between the entries. + /// Enumerable of strings to be concatenated. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AppendJoin(Rune separator, IEnumerable values) + => AppendJoinInternalRune(separator, values); + /// /// Concatenates and appends all values with the given separator between each entry at the end of the string. /// /// String used as separator between the entries. - /// Array of strings, which will be concatenated. - /// Type of the given array. + /// Enumerable to be concatenated. + /// Type of the given enumerable. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendJoin(ReadOnlySpan separator, IEnumerable values) => AppendJoinInternalString(separator, values); @@ -36,12 +46,22 @@ public void AppendJoin(ReadOnlySpan separator, IEnumerable values) /// Concatenates and appends all values with the given separator between each entry at the end of the string. /// /// Character used as separator between the entries. - /// Array of strings, which will be concatenated. - /// Type of the given array. + /// Enumerable to be concatenated. + /// Type of the given enumerable. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AppendJoin(char separator, IEnumerable values) => AppendJoinInternalChar(separator, values); + /// + /// Concatenates and appends all values with the given separator between each entry at the end of the string. + /// + /// Rune used as separator between the entries. + /// Enumerable to be concatenated. + /// Type of the given enumerable. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AppendJoin(Rune separator, IEnumerable values) + => AppendJoinInternalRune(separator, values); + [MethodImpl(MethodImplOptions.AggressiveInlining)] private void AppendJoinInternalString(ReadOnlySpan separator, IEnumerable values) { @@ -55,10 +75,7 @@ private void AppendJoinInternalString(ReadOnlySpan separator, IEnumerab } var current = enumerator.Current; - if (current is not null) - { - AppendInternal(current); - } + AppendInternal(current); while (enumerator.MoveNext()) { @@ -91,6 +108,29 @@ private void AppendJoinInternalChar(char separator, IEnumerable values) } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void AppendJoinInternalRune(Rune separator, IEnumerable 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 value) {