ordered_pairs.h 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. #pragma once
  2. #include <util/generic/algorithm.h>
  3. #include <util/generic/array_ref.h>
  4. #include <util/generic/strbuf.h>
  5. #include <array>
  6. #include <functional>
  7. namespace NEnumSerializationRuntime {
  8. enum class ESortOrder: int {
  9. Unordered = 0, // беспорядок
  10. Ascending = 1, // можно искать бинарным поиском, но есть эквивалентные ключи. Гланый ключ находится через lower_bound
  11. StrictlyAscending = 2, // + все ключи уникальны
  12. DirectMapping = 3, // последовательность целых чисел без пропусков, индекс элемента можно вычислить из его значения не делая поиск
  13. };
  14. template <typename TEnumRepresentationType>
  15. struct TEnumStringPair {
  16. TEnumRepresentationType Key;
  17. TStringBuf Name;
  18. };
  19. template <typename TEnumRepresentationType>
  20. constexpr ESortOrder GetKeyFieldSortOrder(const TArrayRef<const TEnumStringPair<TEnumRepresentationType>> initializer) {
  21. if (initializer.empty()) {
  22. return ESortOrder::DirectMapping;
  23. }
  24. bool direct = true;
  25. bool strict = true;
  26. bool sorted = true;
  27. const auto* data = initializer.data();
  28. const size_t size = initializer.size();
  29. TEnumRepresentationType expected = data[0].Key;
  30. for (size_t i = 1; i < size; ++i) {
  31. const auto& prev = data[i - 1].Key;
  32. const auto& next = data[i - 0].Key;
  33. if (++expected != next) {
  34. direct = false;
  35. }
  36. if (prev >= next) {
  37. strict = false;
  38. }
  39. if (prev > next) {
  40. sorted = false;
  41. break;
  42. }
  43. }
  44. return direct ? ESortOrder::DirectMapping
  45. : strict ? ESortOrder::StrictlyAscending
  46. : sorted ? ESortOrder::Ascending
  47. : ESortOrder::Unordered;
  48. }
  49. template <typename TEnumRepresentationType>
  50. constexpr ESortOrder GetNameFieldSortOrder(const TArrayRef<const TEnumStringPair<TEnumRepresentationType>> initializer) {
  51. if (initializer.empty()) {
  52. return ESortOrder::DirectMapping;
  53. }
  54. bool strict = true;
  55. bool sorted = true;
  56. const auto* data = initializer.data();
  57. const size_t size = initializer.size();
  58. for (size_t i = 1; i < size; ++i) {
  59. const std::string_view& prev = data[i - 1].Name;
  60. const std::string_view& next = data[i - 0].Name;
  61. const int cmp = prev.compare(next);
  62. if (cmp >= 0) {
  63. strict = false;
  64. }
  65. if (cmp > 0) {
  66. sorted = false;
  67. break;
  68. }
  69. }
  70. return strict ? ESortOrder::StrictlyAscending
  71. : sorted ? ESortOrder::Ascending
  72. : ESortOrder::Unordered;
  73. }
  74. #if defined(__cpp_lib_array_constexpr) && defined(__cpp_lib_constexpr_algorithms) && defined(__cpp_lib_constexpr_functional)
  75. // Функция должна состоять из единственного вызова
  76. // std::stable_sort(v.begin(), v.end(), [](const T& a, const T& b) { return a.Key < b.Key; });
  77. // и возврата отсортированного массива.
  78. // Но в C++20 stable_sort ещё не имеет спецификатора constexpr и не может использоваться тут.
  79. // Поэтому в текущей реализации вместо этого делается обычная нестабильная сортировка пар {ключ элемента, положение элемента}.
  80. template <class T, size_t N>
  81. constexpr std::array<T, N> TryStableSortKeys(std::array<T, N> v) {
  82. // Компилятор обычно ограничивает число шагов, которые можно сделать при вычислении constexpr-функции (см. опции `-fconstexpr-steps=N` или `/constexpr:stepsN`).
  83. // Число же шагов, необходимых для сортировки, зависит не только от длины массива,
  84. // но и от используемого алгоритма, от числа вложенных функций в его реализации, и от наличия assert'ов в ней.
  85. // Что также означает, что число шагов может меняться в несколько раз при смене реализации STL или при сборке с NDEBUG и без.
  86. //
  87. // Исчерпание бюджета на действия приведёт к ошибки компиляции без возможности восстановления.
  88. // То есть без возможности обнаружить эту ситуацию и переключится на другой алгоритм.
  89. //
  90. // Поэтому максимальный размер сортируемого массива заранее ограничивается безопасной константой.
  91. // А все массивы большего размера досортировываются уже во время исполнения программы.
  92. constexpr size_t MAX_COMPILE_TIME_SORT_ARRAY_SIZE = 2'000;
  93. if (v.size() > MAX_COMPILE_TIME_SORT_ARRAY_SIZE) {
  94. return v;
  95. }
  96. // Многие перечисления уже отсортированы. Но текущая реализация constexpr std::sort в libcxx не проверяет этот случай и всегда работает за время Θ(NlogN)
  97. if (IsSortedBy(v.begin(), v.end(), std::mem_fn(&T::Key))) {
  98. return v;
  99. }
  100. std::array<const T*, N> ptrs;
  101. Iota(ptrs.begin(), ptrs.end(), &v[0]);
  102. auto cmpKeyPointersFn = [](const T* a, const T* b) {
  103. if (a->Key != b->Key) {
  104. return a->Key < b->Key;
  105. }
  106. return a < b; // ensure stable sort order
  107. };
  108. Sort(ptrs.begin(), ptrs.end(), cmpKeyPointersFn);
  109. std::array<T, N> r;
  110. for (size_t i = 0; i < N; ++i) {
  111. r[i] = *ptrs[i];
  112. }
  113. return r;
  114. }
  115. #else
  116. template <class T, size_t N>
  117. constexpr std::array<T, N> TryStableSortKeys(std::array<T, N> v) {
  118. return v; // skip sort in case of old language version
  119. }
  120. #endif
  121. }