public class LowercaseBenchmark
{
[Params("benchmarks", "BenchMarks")]
public string TestString { get; set; }
[Benchmark]
public string BareImplementation()
{
ReadOnlySpan<char> span = TestString;
Span<char> chars = stackalloc char[span.Length];
for (int i = 0; i < span.Length; i++)
{
char c = span[i];
chars[i] = (c >= 'A' && c <= 'Z') ? (char)(c + 32) : c;
}
return new string(chars);
}
[Benchmark]
public string BareImplementationWithCheck()
{
ReadOnlySpan<char> span = TestString;
bool hasUppercase = false;
for (int i = 0; i < span.Length; i++)
{
char c = span[i];
if (c >= 'A' && c <= 'Z')
{
hasUppercase = true;
break;
}
}
if (!hasUppercase)
return new string(span);
Span<char> chars = stackalloc char[span.Length];
for (int i = 0; i < span.Length; i++)
{
char c = span[i];
chars[i] = (c >= 'A' && c <= 'Z') ? (char)(c + 32) : c;
}
return new string(chars);
}
[Benchmark]
public string AsciiToLower()
{
ReadOnlySpan<char> span = TestString;
Span<char> chars = stackalloc char[span.Length];
Ascii.ToLower(span, chars, out _);
return new string(chars);
}
[Benchmark]
public string ToLowerInvariant()
{
ReadOnlySpan<char> span = TestString;
Span<char> chars = stackalloc char[span.Length];
span.ToLowerInvariant(chars);
return new string(chars);
}
[Benchmark]
public string DeferredCreation()
{
ReadOnlySpan<char> span = TestString;
unsafe
{
var ptr = &span;
return String.Create(span.Length, (nint)ptr, static (buffer, ptr) =>
{
var span = *(ReadOnlySpan<char>*)ptr;
for (int i = 0; i < span.Length; i++)
{
char c = span[i];
buffer[i] = (c >= 'A' && c <= 'Z') ? (char)(c + 32) : c;
}
});
}
}
}