Skip to content

Commit

Permalink
Reduced allocations in string.MaxLength
Browse files Browse the repository at this point in the history
  • Loading branch information
Kaoticz committed Sep 16, 2023
1 parent c91219c commit 32f281a
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 9 deletions.
10 changes: 10 additions & 0 deletions Kotz.Extensions/ReadOnlySpanCharExt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,14 @@ public static bool HasFirstWordOf(this ReadOnlySpan<char> text, ReadOnlySpan<cha

return firstTextWord.Equals(firstSampleWord, comparisonType);
}

/// <summary>
/// Truncates the string to the maximum specified length.
/// </summary>
/// <param name="text">This string.</param>
/// <param name="maxLength">The maximum length the string should have.</param>
/// <returns>This string with length equal to or lower than <paramref name="maxLength"/>.</returns>
/// <exception cref="ArgumentOutOfRangeException">Occurs when <paramref name="maxLength"/> is less than zero.</exception>
public static ReadOnlySpan<char> MaxLength(this ReadOnlySpan<char> text, int maxLength)
=> text[..Math.Min(text.Length, maxLength)];
}
37 changes: 32 additions & 5 deletions Kotz.Extensions/StringExt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,18 @@ public static bool HasFirstWordOf(this string text, string sample, StringCompari
/// <exception cref="ArgumentOutOfRangeException">Occurs when <paramref name="maxLength"/> is less than zero.</exception>
[return: NotNullIfNotNull("text")]
public static string? MaxLength(this string? text, int maxLength)
=> text?[..Math.Min(text.Length, maxLength)];
{
if (maxLength < 0)
throw new ArgumentOutOfRangeException(nameof(maxLength), maxLength, "Maximum length cannot be less than 0.");
else if (string.IsNullOrWhiteSpace(text))
return text;

var result = ReadOnlySpanCharExt.MaxLength(text, maxLength);

return (result.Length == text.Length)
? text
: result.ToString();
}

/// <summary>
/// Truncates the string to the maximum specified length.
Expand All @@ -83,12 +94,28 @@ public static bool HasFirstWordOf(this string text, string sample, StringCompari
/// <param name="append">The string to be appended to the end of the truncated string.</param>
/// <remarks>The <paramref name="append"/> only gets added to the truncated string if this string exceeds <paramref name="maxLength"/> in length.</remarks>
/// <returns>This string with length equal to or lower than <paramref name="maxLength"/>.</returns>
/// <exception cref="ArgumentOutOfRangeException">Occurs when <paramref name="maxLength"/> is less than zero.</exception>
/// <exception cref="ArgumentOutOfRangeException">
/// Occurs when <paramref name="append"/> exceeds or is equal to <paramref name="maxLength"/>
/// or when <paramref name="maxLength"/> is less than zero.
/// </exception>
[return: NotNullIfNotNull("text")]
public static string? MaxLength(this string? text, int maxLength, string append)
=> (text is null || text.Length <= maxLength)
? text
: (text.MaxLength(Math.Max(0, maxLength - append.Length)) + append)[..maxLength];
{
if (text is null || text.Length <= maxLength)
return text;
else if (maxLength < 0)
throw new ArgumentOutOfRangeException(nameof(maxLength), maxLength, "Maximum length cannot be less than 0.");
else if (string.IsNullOrEmpty(append))
return text.MaxLength(maxLength);
else if (append.Length >= maxLength)
throw new ArgumentOutOfRangeException(nameof(append), append.Length, "Append exceeds or is equal to maximum length size.");

var truncatedText = ReadOnlySpanCharExt.MaxLength(text, maxLength);

return (truncatedText.Length == maxLength && truncatedText.Length - append.Length > 0)
? truncatedText[..^append.Length].ToString() + append
: truncatedText.ToString();
}

/// <summary>
/// Converts a string to the "Title Case" format.
Expand Down
20 changes: 16 additions & 4 deletions Kotz.Tests/Extensions/StringExtTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,32 @@ public sealed class StringExtTest
[InlineData(5, "hello", "hello")]
[InlineData(5, "avocado", "avoca")]
[InlineData(0, "banana", "")]
[InlineData(0, "", "")]
[InlineData(5, null, null)]
internal void MaxLengthTest(int length, string source, string result)
=> Assert.Equal(result, source.MaxLength(length));

[Theory]
[InlineData(11, "banana cheesecake", "banana[...]", "[...]")]
[InlineData(11, "banana chee", "banana chee", "[...]")]
[InlineData(11, "banana chees", "banana[...]", "[...]")]
[InlineData(0, "banana", "", "[...]")]
[InlineData(11, "banana chee", "banana chee", "[...]")]
[InlineData(6, "banana cheesecake", "b[...]", "[...]")]
[InlineData(6, "banana", "banana", "[...]")]
[InlineData(1, "banana", "b", null)]
[InlineData(5, "", "", "[...]")]
[InlineData(5, "a", "a", "[...]")]
[InlineData(5, "a", "a", null)]
internal void MaxLengthWithAppendTest(int length, string source, string result, string append)
=> Assert.Equal(result, source.MaxLength(length, append));
[InlineData(5, null, null, null)]
[InlineData(5, null, null, "")]
internal void MaxLengthWithAppendTest(int maxLength, string source, string result, string append)
=> Assert.Equal(result, source.MaxLength(maxLength, append));

[Theory]
[InlineData(0, "banana", "[...]")]
[InlineData(1, "banana", "[...]")]
[InlineData(5, "banana", "[...]")]
internal void MaxLengthWithAppendErrorTest(int maxLength, string source, string append)
=> Assert.Throws<ArgumentOutOfRangeException>(() => source.MaxLength(maxLength, append));

[Theory]
[InlineData(null, null)]
Expand Down

0 comments on commit 32f281a

Please # to comment.