Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace relevant Substring use with AsSpan #1505

Merged
merged 1 commit into from
May 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Humanizer/Humanizer.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<DebugType Condition=" '$(BuildingForLiveUnitTesting)' != 'true' ">embedded</DebugType>
<NoWarn>CS1573;CS1591</NoWarn>
<IsTrimmable>true</IsTrimmable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Using Remove="System.Net.Http" />
Expand Down
4 changes: 2 additions & 2 deletions src/Humanizer/Inflections/Vocabulary.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Humanizer;
namespace Humanizer;

/// <summary>
/// A container for exceptions to simple pluralization/singularization rules.
Expand Down Expand Up @@ -185,7 +185,7 @@ bool IsUncountable(string word) =>

static string MatchUpperCase(string word, string replacement) =>
char.IsUpper(word[0]) &&
char.IsLower(replacement[0]) ? char.ToUpper(replacement[0]) + replacement.Substring(1) : replacement;
char.IsLower(replacement[0]) ? StringHumanizeExtensions.Concat(char.ToUpper(replacement[0]), replacement.AsSpan(1)) : replacement;

/// <summary>
/// If the word is the letter s, singular or plural, return the letter s singular
Expand Down
8 changes: 4 additions & 4 deletions src/Humanizer/InflectorExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//The Inflector class was cloned from Inflector (https://github.com/srkirkland/Inflector)
//The Inflector class was cloned from Inflector (https://github.com/srkirkland/Inflector)

//The MIT License (MIT)

Expand Down Expand Up @@ -65,9 +65,9 @@ public static string Camelize(this string input)
{
var word = input.Pascalize();
return word.Length > 0
? word
.Substring(0, 1)
.ToLower() + word.Substring(1)
? StringHumanizeExtensions.Concat(
char.ToLower(word[0]),
word.AsSpan(1))
: word;
}

Expand Down
10 changes: 5 additions & 5 deletions src/Humanizer/Localisation/Formatters/LuxembourgishFormatter.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Humanizer;
namespace Humanizer;

class LuxembourgishFormatter(CultureInfo culture) :
DefaultFormatter(culture)
Expand All @@ -18,12 +18,12 @@ public static string ApplyEifelerRule(string word)
=> word.TrimEnd(EifelerRuleSuffix);

public static string CheckForAndApplyEifelerRule(string word, string nextWord)
=> DoesEifelerRuleApply(nextWord)
=> DoesEifelerRuleApply(nextWord.AsSpan())
? word.TrimEnd(EifelerRuleSuffix)
: word;

public static bool DoesEifelerRuleApply(string nextWord)
=> !string.IsNullOrWhiteSpace(nextWord)
public static bool DoesEifelerRuleApply(CharSpan nextWord)
=> !nextWord.IsWhiteSpace()
&& !EifelerRuleCharacters.Contains(nextWord[0]);

protected override string Format(TimeUnit unit, string resourceKey, int number, bool toWords = false)
Expand All @@ -33,7 +33,7 @@ protected override string Format(TimeUnit unit, string resourceKey, int number,

return string.Format(resourceString,
toWords ? numberAsWord : number,
DoesEifelerRuleApply(numberAsWord) ? "" : EifelerRuleSuffix);
DoesEifelerRuleApply(numberAsWord.AsSpan()) ? "" : EifelerRuleSuffix);
}

protected override string GetResourceKey(string resourceKey, int number)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Humanizer;
namespace Humanizer;

class ArabicNumberToWordsConverter :
GenderedNumberToWordsConverter
Expand Down Expand Up @@ -268,7 +268,9 @@ static string ParseNumber(string word, int number, GrammaticalGender gender)
foreach (var kv in ordinals.Where(kv => word.EndsWith(kv.Key)))
{
// replace word with exception
return word.Substring(0, word.Length - kv.Key.Length) + kv.Value;
return StringHumanizeExtensions.Concat(
word.AsSpan(0, word.Length - kv.Key.Length),
kv.Value.AsSpan());
}
}
else if (number is > 10 and < 100)
Expand All @@ -286,7 +288,9 @@ static string ParseNumber(string word, int number, GrammaticalGender gender)
foreach (var kv in ordinals.Where(kv => oldPart.EndsWith(kv.Key)))
{
// replace word with exception
newPart = oldPart.Substring(0, oldPart.Length - kv.Key.Length) + kv.Value;
newPart = StringHumanizeExtensions.Concat(
oldPart.AsSpan(0, oldPart.Length - kv.Key.Length),
kv.Value.AsSpan());
}

if (number > 19 && newPart == oldPart && oldPart.Length > 1)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Humanizer;
namespace Humanizer;

class AzerbaijaniNumberToWordsConverter :
GenderlessNumberToWordsConverter
Expand Down Expand Up @@ -116,7 +116,7 @@ public override string ConvertToOrdinal(int number)

if (word[^1] == 't')
{
word = word.Substring(0, word.Length - 1) + 'd';
word = StringHumanizeExtensions.Concat(word.AsSpan(0, word.Length - 1), 'd');
}

if (suffixFoundOnLastVowel)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Humanizer;
namespace Humanizer;

class BrazilianPortugueseNumberToWordsConverter :
GenderedNumberToWordsConverter
Expand Down Expand Up @@ -166,17 +166,17 @@ static string ApplyGender(string toWords, GrammaticalGender gender)

if (toWords.EndsWith("os"))
{
return toWords.Substring(0, toWords.Length - 2) + "as";
return StringHumanizeExtensions.Concat(toWords.AsSpan(0, toWords.Length - 2), "as".AsSpan());
}

if (toWords.EndsWith("um"))
{
return toWords.Substring(0, toWords.Length - 2) + "uma";
return StringHumanizeExtensions.Concat(toWords.AsSpan(0, toWords.Length - 2), "uma".AsSpan());
}

if (toWords.EndsWith("dois"))
{
return toWords.Substring(0, toWords.Length - 4) + "duas";
return StringHumanizeExtensions.Concat(toWords.AsSpan(0, toWords.Length - 4), "duas".AsSpan());
}

return toWords;
Expand All @@ -186,7 +186,9 @@ static string ApplyOrdinalGender(string toWords, GrammaticalGender gender)
{
if (gender == GrammaticalGender.Feminine)
{
return toWords.TrimEnd('o') + 'a';
return StringHumanizeExtensions.Concat(
toWords.AsSpan().TrimEnd('o'),
'a');
}

return toWords;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Humanizer;
namespace Humanizer;

/// <summary>
/// Dutch spelling of numbers is not really officially regulated.
Expand Down Expand Up @@ -170,7 +170,7 @@ public override string ConvertToOrdinal(int number)
foreach (var kv in OrdinalExceptions.Where(kv => word.EndsWith(kv.Key)))
{
// replace word with exception
return word.Substring(0, word.Length - kv.Key.Length) + kv.Value;
return StringHumanizeExtensions.Concat(word.AsSpan(0, word.Length - kv.Key.Length), kv.Value.AsSpan());
}

// achtste
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Humanizer;
namespace Humanizer;

class LuxembourgishNumberToWordsConverter : GenderedNumberToWordsConverter
{
Expand Down Expand Up @@ -166,7 +166,7 @@ private static string GetEndingForGender(GrammaticalGender gender) =>
private string GetPartWithEifelerRule(string pluralFormat, long number, GrammaticalGender gender)
{
var nextWord = pluralFormat
.Substring(3, pluralFormat.Length - 3)
.AsSpan(3, pluralFormat.Length - 3)
.TrimStart();
var wordForm = LuxembourgishFormatter.DoesEifelerRuleApply(nextWord)
? WordForm.Eifeler
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Humanizer;
namespace Humanizer;

class PortugueseNumberToWordsConverter : GenderedNumberToWordsConverter
{
Expand Down Expand Up @@ -165,17 +165,17 @@ static string ApplyGender(string toWords, GrammaticalGender gender)

if (toWords.EndsWith("os"))
{
return toWords.Substring(0, toWords.Length - 2) + "as";
return StringHumanizeExtensions.Concat(toWords.AsSpan(0, toWords.Length - 2), "as".AsSpan());
}

if (toWords.EndsWith("um"))
{
return toWords.Substring(0, toWords.Length - 2) + "uma";
return StringHumanizeExtensions.Concat(toWords.AsSpan(0, toWords.Length - 2), "uma".AsSpan());
}

if (toWords.EndsWith("dois"))
{
return toWords.Substring(0, toWords.Length - 4) + "duas";
return StringHumanizeExtensions.Concat(toWords.AsSpan(0, toWords.Length - 4), "duas".AsSpan());
}

return toWords;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Humanizer;
namespace Humanizer;

class RomanianOrdinalNumberConverter
{
Expand Down Expand Up @@ -72,7 +72,7 @@ public string Convert(int number, GrammaticalGender gender)

if (gender == GrammaticalGender.Feminine && words.EndsWith("zeci"))
{
words = words.Substring(0, words.Length - 4) + "zece";
words = StringHumanizeExtensions.Concat(words.AsSpan(0, words.Length - 4), "zece".AsSpan());
}
else if (gender == GrammaticalGender.Feminine && words.Contains("zeci") && (words.Contains("milioane") || words.Contains("miliarde")))
{
Expand All @@ -82,15 +82,16 @@ public string Convert(int number, GrammaticalGender gender)
if (gender == GrammaticalGender.Feminine && words.StartsWith("un "))
{
words = words
.Substring(2)
.TrimStart();
.AsSpan(2)
.TrimStart()
.ToString();
}

if (words.EndsWith("milioane"))
{
if (gender == GrammaticalGender.Feminine)
{
words = words.Substring(0, words.Length - 8) + "milioana";
words = StringHumanizeExtensions.Concat(words.AsSpan(0, words.Length - 8), "milioana".AsSpan());
}
}

Expand All @@ -99,7 +100,7 @@ public string Convert(int number, GrammaticalGender gender)
{
if (gender == GrammaticalGender.Feminine)
{
words = words.Substring(0, words.Length - 6) + "milioana";
words = StringHumanizeExtensions.Concat(words.AsSpan(0, words.Length - 6), "milioana".AsSpan());
}
else
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Humanizer;
namespace Humanizer;

class TurkishNumberToWordConverter :
GenderlessNumberToWordsConverter
Expand Down Expand Up @@ -154,7 +154,7 @@ public override string ConvertToOrdinal(int number)

if (word[^1] == 't')
{
word = word.Substring(0, word.Length - 1) + 'd';
word = StringHumanizeExtensions.Concat(word.AsSpan(0, word.Length - 1), 'd');
}

if (suffixFoundOnLastVowel)
Expand Down
37 changes: 34 additions & 3 deletions src/Humanizer/StringHumanizeExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
namespace Humanizer;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace Humanizer;

/// <summary>
/// Contains extension methods for humanizing string values.
Expand Down Expand Up @@ -49,8 +53,7 @@ static string FromPascalCase(string input)
}

return result.Length > 0
? char.ToUpper(result[0]) +
result.Substring(1, result.Length - 1)
? Concat(char.ToUpper(result[0]), result.AsSpan(1, result.Length - 1))
: result;
}

Expand Down Expand Up @@ -90,4 +93,32 @@ public static string Humanize(this string input, LetterCasing casing) =>
input
.Humanize()
.ApplyCase(casing);

#if NET
internal static string Concat(CharSpan left, CharSpan right) =>
string.Concat(left, right);

internal static string Concat(char left, CharSpan right) =>
string.Concat(MemoryMarshal.CreateReadOnlySpan(ref left, 1), right);

internal static string Concat(CharSpan left, char right) =>
string.Concat(left, MemoryMarshal.CreateReadOnlySpan(ref right, 1));
#else
internal static unsafe string Concat(CharSpan left, CharSpan right)
{
var result = new string('\0', left.Length + right.Length);
fixed (char* pResult = result)
{
left.CopyTo(new Span<char>(pResult, left.Length));
right.CopyTo(new Span<char>(pResult + left.Length, right.Length));
}
return result;
}

internal static unsafe string Concat(char left, CharSpan right) =>
Concat(new CharSpan(&left, 1), right);

internal static unsafe string Concat(CharSpan left, char right) =>
Concat(left, new CharSpan(&right, 1));
#endif
}
9 changes: 7 additions & 2 deletions src/Humanizer/Transformer/ToSentenceCase.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Humanizer;
namespace Humanizer;

class ToSentenceCase : ICulturedStringTransformer
{
Expand All @@ -11,7 +11,12 @@ public string Transform(string input, CultureInfo? culture)

if (input.Length >= 1)
{
return culture.TextInfo.ToUpper(input[0]) + input.Substring(1);
if (char.IsUpper(input[0]))
{
return input;
}

return StringHumanizeExtensions.Concat(culture.TextInfo.ToUpper(input[0]), input.AsSpan(1));
}

return culture.TextInfo.ToUpper(input);
Expand Down
6 changes: 3 additions & 3 deletions src/Humanizer/Truncation/FixedLengthTruncator.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Humanizer;
namespace Humanizer;

/// <summary>
/// Truncate a string to a fixed length
Expand Down Expand Up @@ -28,12 +28,12 @@ class FixedLengthTruncator : ITruncator
if (truncateFrom == TruncateFrom.Left)
{
return value.Length > length
? truncationString + value.Substring(value.Length - length + truncationString.Length)
? StringHumanizeExtensions.Concat(truncationString.AsSpan(), value.AsSpan(value.Length - length + truncationString.Length))
: value;
}

return value.Length > length
? value.Substring(0, length - truncationString.Length) + truncationString
? StringHumanizeExtensions.Concat(value.AsSpan(0, length - truncationString.Length), truncationString.AsSpan())
: value;
}
}
4 changes: 2 additions & 2 deletions src/Humanizer/Truncation/FixedNumberOfCharactersTruncator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class FixedNumberOfCharactersTruncator : ITruncator

if (alphaNumericalCharactersProcessed + truncationString.Length == length)
{
return truncationString + value.Substring(i);
return StringHumanizeExtensions.Concat(truncationString.AsSpan(), value.AsSpan(i));
}
}
}
Expand All @@ -57,7 +57,7 @@ class FixedNumberOfCharactersTruncator : ITruncator

if (alphaNumericalCharactersProcessed + truncationString.Length == length)
{
return value.Substring(0, i + 1) + truncationString;
return StringHumanizeExtensions.Concat(value.AsSpan(0, i + 1), truncationString.AsSpan());
}
}

Expand Down
Loading