diff --git a/StringExtension/StringExtension.cs b/StringExtension/StringExtension.cs
index a5cb5d0..e4bfa4a 100644
--- a/StringExtension/StringExtension.cs
+++ b/StringExtension/StringExtension.cs
@@ -1,4 +1,5 @@
-using System.Text.RegularExpressions;
+using System.Buffers;
+using System.Text.RegularExpressions;
namespace StringExtension;
@@ -7,6 +8,12 @@ namespace StringExtension;
///
public static partial class StringExtension
{
+ ///
+ /// Above this length, buffers are rented from instead
+ /// of stack-allocated, to avoid excessive stack usage for large inputs.
+ ///
+ private const int StackAllocThreshold = 256;
+
///
/// Represents a regular expression that can be used to validate an email address.
///
@@ -27,6 +34,7 @@ public static partial class StringExtension
/// The input string.
/// An array of characters to remove.
/// A new string with specified characters removed.
+ /// Returns if is .
public static string RemoveCharacters(this string input, char[] charactersToRemove)
{
if (string.IsNullOrEmpty(input) || charactersToRemove is null || charactersToRemove.Length == 0)
@@ -50,18 +58,31 @@ public static string RemoveCharacters(this ReadOnlySpan input, ReadOnlySpa
return input.ToString();
}
- Span buffer = input.Length <= 256 ? stackalloc char[input.Length] : new char[input.Length];
- var count = 0;
+ char[]? pooledBuffer = null;
+ Span buffer = (uint)input.Length <= StackAllocThreshold
+ ? stackalloc char[input.Length]
+ : (pooledBuffer = ArrayPool.Shared.Rent(input.Length));
- foreach (var c in input)
+ try
+ {
+ var count = 0;
+ foreach (var c in input)
+ {
+ if (charactersToRemove.IndexOf(c) < 0)
+ {
+ buffer[count++] = c;
+ }
+ }
+
+ return new string(buffer[..count]);
+ }
+ finally
{
- if (charactersToRemove.IndexOf(c) < 0)
+ if (pooledBuffer is not null)
{
- buffer[count++] = c;
+ ArrayPool.Shared.Return(pooledBuffer);
}
}
-
- return new string(buffer[..count]);
}
///
@@ -112,11 +133,6 @@ public static bool IsValidPhoneNumber(this ReadOnlySpan phoneNumber)
/// The number of occurrences of the substring in the input string.
public static int CountSubstring(this string input, string substring)
{
- if (string.IsNullOrEmpty(input) || string.IsNullOrEmpty(substring))
- {
- return 0;
- }
-
return CountSubstring(input.AsSpan(), substring.AsSpan());
}
@@ -142,6 +158,7 @@ public static int CountSubstring(this ReadOnlySpan input, ReadOnlySpan
/// The input to reverse words.
/// The input string with the order of words reversed.
+ /// Returns if is .
public static string ReverseWords(this string input)
{
if (string.IsNullOrEmpty(input))
@@ -149,8 +166,6 @@ public static string ReverseWords(this string input)
return input;
}
- // string.Create needs the length up front, and it is preserved by a
- // simple word swap around single-space separators.
return string.Create(input.Length, input, static (span, source) =>
{
foreach (Range word in source.AsSpan().Split(' '))
@@ -179,7 +194,7 @@ public static string ReverseWords(this string input)
/// true if the string is a palindrome; otherwise, false.
public static bool IsPalindrome(this string input)
{
- return !string.IsNullOrEmpty(input) && IsPalindrome(input.AsSpan());
+ return IsPalindrome(input.AsSpan());
}
///
@@ -231,11 +246,6 @@ public static bool IsPalindrome(this ReadOnlySpan input)
/// The number of letters in the input string.
public static int CountLetters(this string input)
{
- if (string.IsNullOrEmpty(input))
- {
- return 0;
- }
-
return CountLetters(input.AsSpan());
}
@@ -263,6 +273,7 @@ public static int CountLetters(this ReadOnlySpan input)
///
/// The input string.
/// A new string with duplicate characters removed.
+ /// Returns if is .
public static string RemoveDuplicateCharacters(this string input)
{
if (string.IsNullOrEmpty(input))
@@ -286,19 +297,33 @@ public static string RemoveDuplicateCharacters(this ReadOnlySpan input)
return string.Empty;
}
- Span buffer = input.Length <= 256 ? stackalloc char[input.Length] : new char[input.Length];
- var seen = new HashSet(input.Length);
- var count = 0;
+ char[]? pooledBuffer = null;
+ Span buffer = (uint)input.Length <= StackAllocThreshold
+ ? stackalloc char[input.Length]
+ : (pooledBuffer = ArrayPool.Shared.Rent(input.Length));
- foreach (var c in input)
+ try
{
- if (seen.Add(c))
+ var seen = new HashSet(input.Length);
+ var count = 0;
+
+ foreach (var c in input)
{
- buffer[count++] = c;
+ if (seen.Add(c))
+ {
+ buffer[count++] = c;
+ }
}
- }
- return new string(buffer[..count]);
+ return new string(buffer[..count]);
+ }
+ finally
+ {
+ if (pooledBuffer is not null)
+ {
+ ArrayPool.Shared.Return(pooledBuffer);
+ }
+ }
}
///
@@ -308,11 +333,6 @@ public static string RemoveDuplicateCharacters(this ReadOnlySpan input)
/// The input string converted to camel case.
public static string ToCamelCase(this string input)
{
- if (string.IsNullOrEmpty(input))
- {
- return string.Empty;
- }
-
return ToCamelCase(input.AsSpan());
}
@@ -328,30 +348,43 @@ public static string ToCamelCase(this ReadOnlySpan input)
return string.Empty;
}
- Span buffer = input.Length <= 256 ? stackalloc char[input.Length] : new char[input.Length];
-
- var count = 0;
- var shouldCapitalize = false;
+ char[]? pooledBuffer = null;
+ Span buffer = (uint)input.Length <= StackAllocThreshold
+ ? stackalloc char[input.Length]
+ : (pooledBuffer = ArrayPool.Shared.Rent(input.Length));
- foreach (var c in input)
+ try
{
- if (char.IsWhiteSpace(c) || c == '_')
+ var count = 0;
+ var shouldCapitalize = false;
+
+ foreach (var c in input)
{
- shouldCapitalize = true;
- continue;
+ if (char.IsWhiteSpace(c) || c == '_')
+ {
+ shouldCapitalize = true;
+ continue;
+ }
+
+ buffer[count++] = shouldCapitalize ? char.ToUpperInvariant(c) : char.ToLowerInvariant(c);
+ shouldCapitalize = false;
}
- buffer[count++] = shouldCapitalize ? char.ToUpperInvariant(c) : char.ToLowerInvariant(c);
- shouldCapitalize = false;
- }
+ // The very first character is always lowercase, even if the input
+ // started with a separator (e.g. "_hello" gives "hello", not "Hello").
+ if (count > 0)
+ {
+ buffer[0] = char.ToLowerInvariant(buffer[0]);
+ }
- // The very first character is always lowercase, even if the input
- // started with a separator (e.g. "_hello" gives "hello", not "Hello").
- if (count > 0)
+ return new string(buffer[..count]);
+ }
+ finally
{
- buffer[0] = char.ToLowerInvariant(buffer[0]);
+ if (pooledBuffer is not null)
+ {
+ ArrayPool.Shared.Return(pooledBuffer);
+ }
}
-
- return new string(buffer[..count]);
}
}
\ No newline at end of file