Skip to content

Instantly share code, notes, and snippets.

@antonfirsov
Last active July 3, 2025 20:59
Show Gist options
  • Save antonfirsov/b705943e8278d9207e019cc5bc94a47a to your computer and use it in GitHub Desktop.
Save antonfirsov/b705943e8278d9207e019cc5bc94a47a to your computer and use it in GitHub Desktop.
Replace Dangerous Characters Benchmark
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using System.Buffers;
using System.Text;
BenchmarkRunner.Run<HeaderDecoder>();
public class HeaderDecoder
{
private static readonly SearchValues<char> s_dangerousCharacters = SearchValues.Create('\0', '\r', '\n');
private static readonly SearchValues<byte> s_dangerousCharacterBytes = SearchValues.Create((byte)'\0', (byte)'\r', (byte)'\n');
private static readonly string NoSpecialCharsTest = "qwertzuiopö ASDFGHJKLü yxcvbnmé 123456789 qwertzuiop ASDFGHJKL yxcvbnm 123456789 qwertzuiop ASDFGHJKL yxcvbnm 123456789";
private static readonly string HasSpecialCharsTest = "qwertzuiopö\0ASDFGHJKLü\nyxcvbnmé\r123456789 qwertzuiop\0ASDFGHJKL\nyxcvbnm\r123456789 qwertzuiop\0ASDFGHJKL\nyxcvbnm\r123456789";
[Params(true, false)]
public bool Dangerous { get; set; }
[Params(true, false)]
public bool UseUtf8 { get; set; }
private Encoding _encoding;
private byte[] _input;
[GlobalSetup]
public void Setup()
{
string str = Dangerous ? HasSpecialCharsTest : NoSpecialCharsTest;
_input = Encoding.ASCII.GetBytes(str);
_encoding = UseUtf8 ? Encoding.UTF8 : Encoding.Latin1;
}
[Benchmark]
public string GetString()
{
ReadOnlySpan<byte> input = _input;
return _encoding.GetString(input);
}
[Benchmark]
public string StringCreate()
{
ReadOnlySpan<byte> input = _input;
int length = _encoding.GetCharCount(input);
return string.Create(length, new CreateHeaderState(input, _encoding), static (chars, s) =>
{
s.Encoding.GetChars(s.Bytes, chars);
});
}
[Benchmark]
public string StringCreate_ReplaceDangerous()
{
ReadOnlySpan<byte> input = _input;
int length = _encoding.GetCharCount(input);
return string.Create(length, new CreateHeaderState(input, _encoding), static (chars, s) =>
{
s.Encoding.GetChars(s.Bytes, chars);
chars.ReplaceAny(s_dangerousCharacters, ' ');
});
}
[Benchmark]
public string StringCreate_ReplaceDangerous_IfAny()
{
ReadOnlySpan<byte> input = _input;
bool replaceDangerous = input.IndexOfAny(s_dangerousCharacterBytes) >= 0;
int length = _encoding.GetCharCount(input);
return string.Create(length, new CreateHeaderState(input, _encoding, replaceDangerous), static (chars, s) =>
{
s.Encoding.GetChars(s.Bytes, chars);
if (s.ReplaceDangerous) chars.ReplaceAny(s_dangerousCharacters, ' ');
});
}
private readonly ref struct CreateHeaderState
{
public readonly ReadOnlySpan<byte> Bytes;
public readonly Encoding Encoding;
public readonly bool ReplaceDangerous;
public CreateHeaderState(ReadOnlySpan<byte> bytes, Encoding encoding, bool replaceDangerous = false)
{
Bytes = bytes;
Encoding = encoding;
ReplaceDangerous = replaceDangerous;
}
}
}
public static class Extensions
{
public static void ReplaceAny<T>(this Span<T> span, SearchValues<T> values, T newValue) where T : IEquatable<T>?
{
int pos;
while ((pos = span.IndexOfAny(values)) >= 0)
{
span[pos] = newValue;
span = span.Slice(pos + 1);
}
}
}
Method Dangerous UseUtf8 Mean Error StdDev
GetString False False 21.06 ns 0.463 ns 0.433 ns
StringCreate False False 19.47 ns 0.433 ns 0.362 ns
StringCreate_ReplaceDangerous False False 28.47 ns 0.603 ns 0.865 ns
StringCreate_ReplaceDangerous_IfAny False False 25.24 ns 0.484 ns 0.452 ns
GetString False True 29.44 ns 0.638 ns 0.682 ns
StringCreate False True 29.16 ns 0.504 ns 0.421 ns
StringCreate_ReplaceDangerous False True 38.52 ns 0.783 ns 0.654 ns
StringCreate_ReplaceDangerous_IfAny False True 33.03 ns 0.590 ns 0.523 ns
GetString True False 20.33 ns 0.311 ns 0.276 ns
StringCreate True False 19.06 ns 0.269 ns 0.239 ns
StringCreate_ReplaceDangerous True False 75.23 ns 1.555 ns 2.022 ns
StringCreate_ReplaceDangerous_IfAny True False 77.28 ns 1.552 ns 1.787 ns
GetString True True 29.27 ns 0.612 ns 0.573 ns
StringCreate True True 27.59 ns 0.594 ns 0.993 ns
StringCreate_ReplaceDangerous True True 81.58 ns 1.627 ns 1.670 ns
StringCreate_ReplaceDangerous_IfAny True True 84.19 ns 1.720 ns 1.980 ns
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment