raw_hashtable_test_helpers.h 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. // Exceptions. See /LICENSE for license information.
  3. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. #ifndef CARBON_COMMON_RAW_HASHTABLE_TEST_HELPERS_H_
  5. #define CARBON_COMMON_RAW_HASHTABLE_TEST_HELPERS_H_
  6. #include <compare>
  7. #include "common/check.h"
  8. #include "common/hashing.h"
  9. #include "common/hashtable_key_context.h"
  10. #include "common/ostream.h"
  11. namespace Carbon::RawHashtable {
  12. // Non-trivial type for testing.
  13. struct TestData : Printable<TestData> {
  14. int value;
  15. // NOLINTNEXTLINE: google-explicit-constructor
  16. TestData(int v) : value(v) { CARBON_CHECK(value >= 0); }
  17. ~TestData() {
  18. CARBON_CHECK(value >= 0);
  19. value = -1;
  20. }
  21. TestData(const TestData& other) : TestData(other.value) {}
  22. TestData(TestData&& other) noexcept : TestData(other.value) {
  23. other.value = 0;
  24. }
  25. auto Print(llvm::raw_ostream& out) const -> void { out << value; }
  26. friend auto operator==(TestData lhs, TestData rhs) -> bool {
  27. return lhs.value == rhs.value;
  28. }
  29. friend auto operator<=>(TestData lhs, TestData rhs) -> std::strong_ordering {
  30. return lhs.value <=> rhs.value;
  31. }
  32. friend auto CarbonHashValue(TestData data, uint64_t seed) -> HashCode {
  33. return Carbon::HashValue(data.value, seed);
  34. }
  35. };
  36. static_assert(std::is_copy_constructible_v<TestData>);
  37. inline auto CarbonHashtableEq(int lhs, TestData rhs) -> bool {
  38. return lhs == rhs;
  39. }
  40. // Non-trivial type for testing.
  41. struct MoveOnlyTestData : Printable<TestData> {
  42. int value;
  43. // NOLINTNEXTLINE: google-explicit-constructor
  44. MoveOnlyTestData(int v) : value(v) { CARBON_CHECK(value >= 0); }
  45. ~MoveOnlyTestData() {
  46. CARBON_CHECK(value >= 0);
  47. value = -1;
  48. }
  49. MoveOnlyTestData(MoveOnlyTestData&& other) noexcept
  50. : MoveOnlyTestData(other.value) {
  51. other.value = 0;
  52. }
  53. auto operator=(MoveOnlyTestData&& other) noexcept -> MoveOnlyTestData& {
  54. value = other.value;
  55. other.value = 0;
  56. return *this;
  57. }
  58. auto Print(llvm::raw_ostream& out) const -> void { out << value; }
  59. friend auto operator==(const MoveOnlyTestData& lhs,
  60. const MoveOnlyTestData& rhs) -> bool {
  61. return lhs.value == rhs.value;
  62. }
  63. friend auto operator<=>(const MoveOnlyTestData& lhs,
  64. const MoveOnlyTestData& rhs) -> std::strong_ordering {
  65. return lhs.value <=> rhs.value;
  66. }
  67. friend auto CarbonHashValue(const MoveOnlyTestData& data, uint64_t seed)
  68. -> HashCode {
  69. return Carbon::HashValue(data.value, seed);
  70. }
  71. };
  72. static_assert(!std::is_copy_constructible_v<MoveOnlyTestData>);
  73. static_assert(std::is_move_constructible_v<MoveOnlyTestData>);
  74. inline auto CarbonHashtableEq(int lhs, const MoveOnlyTestData& rhs) -> bool {
  75. return lhs == rhs;
  76. }
  77. // Test stateless key context that produces different hashes from normal.
  78. // Changing the hash values should result in test failures if the context ever
  79. // fails to be used.
  80. struct TestKeyContext : DefaultKeyContext {
  81. template <typename KeyT>
  82. auto HashKey(const KeyT& key, uint64_t seed) const -> HashCode {
  83. Hasher hash(seed);
  84. // Inject some other data to the hash.
  85. hash.HashRaw(42);
  86. hash.HashRaw(HashValue(key));
  87. return static_cast<HashCode>(hash);
  88. }
  89. };
  90. // Hostile fixed hashing key context used for stress testing. Allows control
  91. // over which parts of the hash will be forced to collide, and the values they
  92. // are coerced to. Note that this relies on implementation details and internals
  93. // of `HashCode`.
  94. template <int TagBits, bool FixIndexBits, bool FixTagBits, uint64_t FixedVal>
  95. struct FixedHashKeyContext : DefaultKeyContext {
  96. template <typename KeyT>
  97. auto HashKey(const KeyT& key, uint64_t seed) const -> HashCode {
  98. HashCode original_hash = HashValue(key, seed);
  99. auto raw_hash = static_cast<uint64_t>(original_hash);
  100. constexpr uint64_t TagMask = (1U << TagBits) - 1;
  101. if (FixIndexBits) {
  102. raw_hash &= TagMask;
  103. raw_hash |= FixedVal << TagBits;
  104. CARBON_DCHECK(HashCode(raw_hash).ExtractIndexAndTag<TagBits>().first ==
  105. (FixedVal & (~static_cast<uint64_t>(0) >> TagBits)));
  106. }
  107. if (FixTagBits) {
  108. raw_hash &= ~TagMask;
  109. raw_hash |= FixedVal & TagMask;
  110. CARBON_DCHECK(HashCode(raw_hash).ExtractIndexAndTag<TagBits>().second ==
  111. (FixedVal & TagMask));
  112. }
  113. return HashCode(raw_hash);
  114. }
  115. };
  116. template <typename T>
  117. class IndexKeyContext : public TranslatingKeyContext<IndexKeyContext<T>> {
  118. using Base = TranslatingKeyContext<IndexKeyContext>;
  119. public:
  120. explicit IndexKeyContext(llvm::ArrayRef<T> array) : array_(array) {}
  121. auto TranslateKey(ssize_t index) const -> const T& { return array_[index]; }
  122. // Override the CRTP approach when we have two indices as we can optimize that
  123. // approach.
  124. using Base::KeyEq;
  125. auto KeyEq(ssize_t lhs_index, ssize_t rhs_index) const -> bool {
  126. // No need to compare the elements, if the indices are equal, the values
  127. // must be.
  128. return lhs_index == rhs_index;
  129. }
  130. private:
  131. llvm::ArrayRef<T> array_;
  132. };
  133. } // namespace Carbon::RawHashtable
  134. #endif // CARBON_COMMON_RAW_HASHTABLE_TEST_HELPERS_H_