Repository: carbon-language/carbon-lang
File: common/raw_hashtable_benchmark_helpers.h (C/C++)

Code before:
// in the may keys array. If the lookup size is larger than the main size, the
// lookup sequence will contain duplicates.
//
// For a given size, this will return the same arrays. This uses unsynchronized
// global state, and so is thread hostile and must not be called before main.
template <typename T>
auto GetKeysAndHitKeys(ssize_t table_keys_size, ssize_t lookup_keys_size)
    -> std::pair<llvm::ArrayRef<T>, llvm::ArrayRef<T>>;

// Dump statistics about hashing the given keys.
template <typename T>
auto DumpHashStatistics(llvm::ArrayRef<T> keys) -> void;

// A type that works like an `int` but shifting in the specified number of low
// zero bits. This is only intended for testing hash tables with especially
// difficult to hash integer values, it isn't meant to be used otherwise.
template <int LowZeroBits>
struct LowZeroBitInt {
  int64_t shifted_value = 0;

  constexpr LowZeroBitInt() = default;
  // NOLINTNEXTLINE(google-explicit-constructor): Ease use in existing code.
  constexpr LowZeroBitInt(int64_t value)
      : shifted_value(value << LowZeroBits) {}
  // NOLINTNEXTLINE(google-explicit-constructor): Ease use in existing code
  constexpr operator int64_t() const { return shifted_value; }

  constexpr auto operator==(const LowZeroBitInt& rhs) const -> bool = default;
  constexpr auto operator<=>(const LowZeroBitInt& rhs) const
      -> std::strong_ordering = default;

  friend auto CarbonHashValue(const LowZeroBitInt& value, uint64_t seed)
      -> HashCode {
    return HashValue(value.shifted_value, seed);
  }

  template <typename H>
  friend auto AbslHashValue(H h, const LowZeroBitInt& value) -> H {
    return H::combine(std::move(h), value.shifted_value);
  }

  friend auto hash_value(const LowZeroBitInt& value) -> size_t {
    boost::hash<int64_t> hasher;
    return hasher(value.shifted_value);
  }
};

// Convert values used in hashtable benchmarking to a bool. This is used to form
// dependencies between values stored in the hashtable between benchmark
// iterations.
template <typename T>

Review comment (suggestion):
I was looking at this, trying to figure out what the existing code was, had you considered specifically:

```suggestion
  explicit constexpr LowZeroBitInt() = default;
  explicit constexpr LowZeroBitInt(int64_t value)
      : shifted_value(value << LowZeroBits) {}

  friend auto operator<<(llvm::raw_ostream& out,
                         const LowZeroBitInt& value) -> llvm::raw_ostream& {
    return out << value.shifted_value;
  }
```

And elsewhere in this PR:

- `static auto getEmptyKey() -> IntT { return -1; }` -> `static auto getEmptyKey() -> IntT { return IntT(-1); }`
- `key = index + 1;` -> `key = IntT(index + 1;)`
  - This in particular assumes you add a similar IntT typedef

Perhaps this is lower friction than you were expecting? Note the ostream operator is in place of the implicit conversion because I think you have a spot that just wants to be able to stream the value.

If you prefer to keep this as is, note line 71 should have a period at the end (68 has a period, I guess you didn't copy the full line).

Code after:
// in the may keys array. If the lookup size is larger than the main size, the
// lookup sequence will contain duplicates.
//
// For a given size, this will return the same arrays. This uses unsynchronized
// global state, and so is thread hostile and must not be called before main.
template <typename T>
auto GetKeysAndHitKeys(ssize_t table_keys_size, ssize_t lookup_keys_size)
    -> std::pair<llvm::ArrayRef<T>, llvm::ArrayRef<T>>;

// Dump statistics about hashing the given keys.
template <typename T>
auto DumpHashStatistics(llvm::ArrayRef<T> keys) -> void;

// A type that works like an `int` but shifting in the specified number of low
// zero bits. This is only intended for testing hash tables with especially
// difficult to hash integer values, it isn't meant to be used otherwise.
template <int LowZeroBits>
struct LowZeroBitInt {
  int64_t shifted_value = 0;

  explicit constexpr LowZeroBitInt() = default;
  explicit constexpr LowZeroBitInt(int64_t value)
      : shifted_value(value << LowZeroBits) {}

  friend auto operator<<(llvm::raw_ostream& out, const LowZeroBitInt& value)
      -> llvm::raw_ostream& {
    return out << value.shifted_value;
  }

  constexpr auto operator==(const LowZeroBitInt& rhs) const -> bool = default;
  constexpr auto operator<=>(const LowZeroBitInt& rhs) const
      -> std::strong_ordering = default;

  friend auto CarbonHashValue(const LowZeroBitInt& value, uint64_t seed)
      -> HashCode {
    return HashValue(value.shifted_value, seed);
  }

  template <typename H>
  friend auto AbslHashValue(H h, const LowZeroBitInt& value) -> H {
    return H::combine(std::move(h), value.shifted_value);
  }

  friend auto hash_value(const LowZeroBitInt& value) -> size_t {
    boost::hash<int64_t> hasher;
    return hasher(value.shifted_value);
  }
};

// Convert values used in hashtable benchmarking to a bool. This is used to form
// dependencies between values stored in the hashtable between benchmark