From cf2645fa040dde0476e7a6e5836371058cd716da Mon Sep 17 00:00:00 2001 From: Tonttu <15074459+TheTonttu@users.noreply.github.com> Date: Sun, 16 Mar 2025 07:31:33 +0200 Subject: [PATCH] StringExtensions.ToString(IEnumerable) use rented array as alternative buffer --- .../StringExtensions/ToStringEnumerable.cs | 2 +- Terminal.Gui/Text/StringExtensions.cs | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/Benchmarks/Text/StringExtensions/ToStringEnumerable.cs b/Benchmarks/Text/StringExtensions/ToStringEnumerable.cs index 8d5d55f41..b44f33ad5 100644 --- a/Benchmarks/Text/StringExtensions/ToStringEnumerable.cs +++ b/Benchmarks/Text/StringExtensions/ToStringEnumerable.cs @@ -22,7 +22,7 @@ public class ToStringEnumerable } /// - /// Benchmark for current implementation with stackalloc char buffer and + /// Benchmark for current implementation with char buffer and /// fallback to rune chars appending to StringBuilder. /// /// diff --git a/Terminal.Gui/Text/StringExtensions.cs b/Terminal.Gui/Text/StringExtensions.cs index 3167bcf30..0dc1acdec 100644 --- a/Terminal.Gui/Text/StringExtensions.cs +++ b/Terminal.Gui/Text/StringExtensions.cs @@ -127,7 +127,7 @@ public static class StringExtensions const int maxCharsPerRune = 2; const int maxStackallocTextBufferSize = 1048; // ~2 kB - // Use stackalloc buffer if rune count is easily available and the count is reasonable. + // If rune count is easily available use stackalloc buffer or alternatively rented array. if (runes.TryGetNonEnumeratedCount (out int count)) { if (count == 0) @@ -135,10 +135,14 @@ public static class StringExtensions return string.Empty; } - int maxRequiredTextBufferSize = count * maxCharsPerRune; - if (maxRequiredTextBufferSize <= maxStackallocTextBufferSize) + char[]? rentedBufferArray = null; + try { - Span textBuffer = stackalloc char[maxRequiredTextBufferSize]; + int maxRequiredTextBufferSize = count * maxCharsPerRune; + Span textBuffer = maxRequiredTextBufferSize <= maxStackallocTextBufferSize + ? stackalloc char[maxRequiredTextBufferSize] + : (rentedBufferArray = ArrayPool.Shared.Rent(maxRequiredTextBufferSize)); + Span remainingBuffer = textBuffer; foreach (Rune rune in runes) { @@ -149,6 +153,13 @@ public static class StringExtensions ReadOnlySpan text = textBuffer[..^remainingBuffer.Length]; return text.ToString (); } + finally + { + if (rentedBufferArray != null) + { + ArrayPool.Shared.Return (rentedBufferArray); + } + } } // Fallback to StringBuilder append.