raw_hashtable_test_helpers.h 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  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. // Non-trivial type for testing.
  38. struct MoveOnlyTestData : Printable<TestData> {
  39. int value;
  40. // NOLINTNEXTLINE: google-explicit-constructor
  41. MoveOnlyTestData(int v) : value(v) { CARBON_CHECK(value >= 0); }
  42. ~MoveOnlyTestData() {
  43. CARBON_CHECK(value >= 0);
  44. value = -1;
  45. }
  46. MoveOnlyTestData(MoveOnlyTestData&& other) noexcept
  47. : MoveOnlyTestData(other.value) {
  48. other.value = 0;
  49. }
  50. auto operator=(MoveOnlyTestData&& other) noexcept -> MoveOnlyTestData& {
  51. value = other.value;
  52. other.value = 0;
  53. return *this;
  54. }
  55. auto Print(llvm::raw_ostream& out) const -> void { out << value; }
  56. friend auto operator==(const MoveOnlyTestData& lhs,
  57. const MoveOnlyTestData& rhs) -> bool {
  58. return lhs.value == rhs.value;
  59. }
  60. friend auto operator<=>(const MoveOnlyTestData& lhs,
  61. const MoveOnlyTestData& rhs) -> std::strong_ordering {
  62. return lhs.value <=> rhs.value;
  63. }
  64. friend auto CarbonHashValue(const MoveOnlyTestData& data, uint64_t seed)
  65. -> HashCode {
  66. return Carbon::HashValue(data.value, seed);
  67. }
  68. };
  69. static_assert(!std::is_copy_constructible_v<MoveOnlyTestData>);
  70. static_assert(std::is_move_constructible_v<MoveOnlyTestData>);
  71. // Test stateless key context that produces different hashes from normal.
  72. // Changing the hash values should result in test failures if the context ever
  73. // fails to be used.
  74. struct TestKeyContext : DefaultKeyContext {
  75. template <typename KeyT>
  76. auto HashKey(const KeyT& key, uint64_t seed) const -> HashCode {
  77. Hasher hash(seed);
  78. // Inject some other data to the hash.
  79. hash.HashRaw(42);
  80. hash.HashRaw(HashValue(key));
  81. return static_cast<HashCode>(hash);
  82. }
  83. };
  84. // Hostile fixed hashing key context used for stress testing. Allows control
  85. // over which parts of the hash will be forced to collide, and the values they
  86. // are coerced to. Note that this relies on implementation details and internals
  87. // of `HashCode`.
  88. template <int TagBits, bool FixIndexBits, bool FixTagBits, uint64_t FixedVal>
  89. struct FixedHashKeyContext : DefaultKeyContext {
  90. template <typename KeyT>
  91. auto HashKey(const KeyT& key, uint64_t seed) const -> HashCode {
  92. HashCode original_hash = HashValue(key, seed);
  93. auto raw_hash = static_cast<uint64_t>(original_hash);
  94. constexpr uint64_t TagMask = (1U << TagBits) - 1;
  95. if (FixIndexBits) {
  96. raw_hash &= TagMask;
  97. raw_hash |= FixedVal << TagBits;
  98. CARBON_DCHECK(HashCode(raw_hash).ExtractIndexAndTag<TagBits>().first ==
  99. (FixedVal & (~static_cast<uint64_t>(0) >> TagBits)));
  100. }
  101. if (FixTagBits) {
  102. raw_hash &= ~TagMask;
  103. raw_hash |= FixedVal & TagMask;
  104. CARBON_DCHECK(HashCode(raw_hash).ExtractIndexAndTag<TagBits>().second ==
  105. (FixedVal & TagMask));
  106. }
  107. return HashCode(raw_hash);
  108. }
  109. };
  110. template <typename T>
  111. class IndexKeyContext : public TranslatingKeyContext<IndexKeyContext<T>> {
  112. using Base = TranslatingKeyContext<IndexKeyContext>;
  113. public:
  114. explicit IndexKeyContext(llvm::ArrayRef<T> array) : array_(array) {}
  115. auto TranslateKey(ssize_t index) const -> const T& { return array_[index]; }
  116. // Override the CRTP approach when we have two indices as we can optimize that
  117. // approach.
  118. using Base::KeyEq;
  119. auto KeyEq(ssize_t lhs_index, ssize_t rhs_index) const -> bool {
  120. // No need to compare the elements, if the indices are equal, the values
  121. // must be.
  122. return lhs_index == rhs_index;
  123. }
  124. private:
  125. llvm::ArrayRef<T> array_;
  126. };
  127. } // namespace Carbon::RawHashtable
  128. #endif // CARBON_COMMON_RAW_HASHTABLE_TEST_HELPERS_H_