RemoveHotKeySpecifier.cs 4.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. using System.Text;
  2. using BenchmarkDotNet.Attributes;
  3. using Tui = Terminal.Gui;
  4. namespace Terminal.Gui.Benchmarks.Text.TextFormatter;
  5. /// <summary>
  6. /// Benchmarks for <see cref="Tui.TextFormatter.RemoveHotKeySpecifier"/> performance fine-tuning.
  7. /// </summary>
  8. [MemoryDiagnoser]
  9. [BenchmarkCategory (nameof(Tui.TextFormatter))]
  10. public class RemoveHotKeySpecifier
  11. {
  12. // Omit from summary table.
  13. private static readonly Rune HotkeySpecifier = (Rune)'_';
  14. /// <summary>
  15. /// Benchmark for previous implementation.
  16. /// </summary>
  17. [Benchmark]
  18. [ArgumentsSource (nameof (DataSource))]
  19. public string Previous (string text, int hotPos)
  20. {
  21. return StringConcatLoop (text, hotPos, HotkeySpecifier);
  22. }
  23. /// <summary>
  24. /// Benchmark for current implementation with stackalloc char buffer and fallback to rented array.
  25. /// </summary>
  26. [Benchmark (Baseline = true)]
  27. [ArgumentsSource (nameof (DataSource))]
  28. public string Current (string text, int hotPos)
  29. {
  30. return Tui.TextFormatter.RemoveHotKeySpecifier (text, hotPos, HotkeySpecifier);
  31. }
  32. /// <summary>
  33. /// Previous implementation with string concatenation in a loop.
  34. /// </summary>
  35. public static string StringConcatLoop (string text, int hotPos, Rune hotKeySpecifier)
  36. {
  37. if (string.IsNullOrEmpty (text))
  38. {
  39. return text;
  40. }
  41. // Scan
  42. var start = string.Empty;
  43. var i = 0;
  44. foreach (Rune c in text.EnumerateRunes ())
  45. {
  46. if (c == hotKeySpecifier && i == hotPos)
  47. {
  48. i++;
  49. continue;
  50. }
  51. start += c;
  52. i++;
  53. }
  54. return start;
  55. }
  56. public IEnumerable<object []> DataSource ()
  57. {
  58. string[] texts = [
  59. "",
  60. // Typical scenario.
  61. "_Save file (Ctrl+S)",
  62. // Medium text, hotkey specifier somewhere in the middle.
  63. "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla sed euismod metus. _Phasellus lectus metus, ultricies a commodo quis, facilisis vitae nulla.",
  64. // Long text, hotkey specifier almost at the beginning.
  65. "Ĺόŕéḿ íṕśúḿ d́όĺόŕ śít́ áḿét́, ćόńśéćt́ét́úŕ ád́íṕíśćíńǵ éĺít́. _Ṕŕáéśéńt́ q́úíś ĺúćt́úś éĺít́. Íńt́éǵéŕ út́ áŕćú éǵét́ d́όĺόŕ śćéĺéŕíśq́úé ḿát́t́íś áć ét́ d́íáḿ. " +
  66. "Ṕéĺĺéńt́éśq́úé śéd́ d́áṕíb́úś ḿáśśá, v́éĺ t́ŕíśt́íq́úé d́úí. Śéd́ v́ít́áé ńéq́úé éú v́éĺít́ όŕńáŕé áĺíq́úét́. Út́ q́úíś όŕćí t́éḿṕόŕ, t́éḿṕόŕ t́úŕṕíś íd́, t́éḿṕúś ńéq́úé. " +
  67. "Ṕŕáéśéńt́ śáṕíéń t́úŕṕíś, όŕńáŕé v́éĺ ḿáúŕíś át́, v́áŕíúś śúśćíṕít́ áńt́é. Út́ ṕúĺv́íńáŕ t́úŕṕíś ḿáśśá, q́úíś ćúŕśúś áŕćú f́áúćíb́úś íń.",
  68. // Long text, hotkey specifier almost at the end.
  69. "Ĺόŕéḿ íṕśúḿ d́όĺόŕ śít́ áḿét́, ćόńśéćt́ét́úŕ ád́íṕíśćíńǵ éĺít́. Ṕŕáéśéńt́ q́úíś ĺúćt́úś éĺít́. Íńt́éǵéŕ út́ áŕćú éǵét́ d́όĺόŕ śćéĺéŕíśq́úé ḿát́t́íś áć ét́ d́íáḿ. " +
  70. "Ṕéĺĺéńt́éśq́úé śéd́ d́áṕíb́úś ḿáśśá, v́éĺ t́ŕíśt́íq́úé d́úí. Śéd́ v́ít́áé ńéq́úé éú v́éĺít́ όŕńáŕé áĺíq́úét́. Út́ q́úíś όŕćí t́éḿṕόŕ, t́éḿṕόŕ t́úŕṕíś íd́, t́éḿṕúś ńéq́úé. " +
  71. "Ṕŕáéśéńt́ śáṕíéń t́úŕṕíś, όŕńáŕé v́éĺ ḿáúŕíś át́, v́áŕíúś śúśćíṕít́ áńt́é. _Út́ ṕúĺv́íńáŕ t́úŕṕíś ḿáśśá, q́úíś ćúŕśúś áŕćú f́áúćíb́úś íń.",
  72. ];
  73. foreach (string text in texts)
  74. {
  75. int hotPos = text.EnumerateRunes()
  76. .Select((r, i) => r == HotkeySpecifier ? i : -1)
  77. .FirstOrDefault(i => i > -1, -1);
  78. yield return [text, hotPos];
  79. }
  80. // Typical scenario but without hotkey and with misleading position.
  81. yield return ["Save file (Ctrl+S)", 3];
  82. }
  83. }