2
0

AlphanumericComparator.cs 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. using System;
  2. using System.Collections.Generic;
  3. namespace Jellyfin.Extensions
  4. {
  5. /// <summary>
  6. /// Alphanumeric <see cref="IComparer{T}" />.
  7. /// </summary>
  8. public class AlphanumericComparator : IComparer<string?>
  9. {
  10. /// <summary>
  11. /// Compares two objects and returns a value indicating whether one is less than, equal to, or greater than the other.
  12. /// </summary>
  13. /// <param name="s1">The first object to compare.</param>
  14. /// <param name="s2">The second object to compare.</param>
  15. /// <returns>A signed integer that indicates the relative values of <c>x</c> and <c>y</c>.</returns>
  16. public static int CompareValues(string? s1, string? s2)
  17. {
  18. if (s1 is null && s2 is null)
  19. {
  20. return 0;
  21. }
  22. if (s1 is null)
  23. {
  24. return -1;
  25. }
  26. if (s2 is null)
  27. {
  28. return 1;
  29. }
  30. int len1 = s1.Length;
  31. int len2 = s2.Length;
  32. // Early return for empty strings
  33. if (len1 == 0 && len2 == 0)
  34. {
  35. return 0;
  36. }
  37. if (len1 == 0)
  38. {
  39. return -1;
  40. }
  41. if (len2 == 0)
  42. {
  43. return 1;
  44. }
  45. int pos1 = 0;
  46. int pos2 = 0;
  47. do
  48. {
  49. int start1 = pos1;
  50. int start2 = pos2;
  51. bool isNum1 = char.IsDigit(s1[pos1++]);
  52. bool isNum2 = char.IsDigit(s2[pos2++]);
  53. while (pos1 < len1 && char.IsDigit(s1[pos1]) == isNum1)
  54. {
  55. pos1++;
  56. }
  57. while (pos2 < len2 && char.IsDigit(s2[pos2]) == isNum2)
  58. {
  59. pos2++;
  60. }
  61. var span1 = s1.AsSpan(start1, pos1 - start1);
  62. var span2 = s2.AsSpan(start2, pos2 - start2);
  63. if (isNum1 && isNum2)
  64. {
  65. // Trim leading zeros so we can compare the length
  66. // of the strings to find the largest number
  67. span1 = span1.TrimStart('0');
  68. span2 = span2.TrimStart('0');
  69. var span1Len = span1.Length;
  70. var span2Len = span2.Length;
  71. if (span1Len < span2Len)
  72. {
  73. return -1;
  74. }
  75. if (span1Len > span2Len)
  76. {
  77. return 1;
  78. }
  79. }
  80. int result = span1.CompareTo(span2, StringComparison.InvariantCulture);
  81. if (result != 0)
  82. {
  83. return result;
  84. }
  85. } while (pos1 < len1 && pos2 < len2);
  86. return len1 - len2;
  87. }
  88. /// <inheritdoc />
  89. public int Compare(string? x, string? y)
  90. {
  91. return CompareValues(x, y);
  92. }
  93. }
  94. }