[PATCH] D106910: [Support]: Introduce the `HashBuilder` interface.

Duncan P. N. Exon Smith via Phabricator via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 23 12:40:19 PDT 2021


dexonsmith accepted this revision.
dexonsmith added a comment.
This revision is now accepted and ready to land.

Thanks for the working through this! LGTM if you remove the CRTP (see below, I don't think it's needed). See also a few more nits I found to pick.



================
Comment at: llvm/include/llvm/Support/HashBuilder.h:30-35
+template <template <typename, support::endianness, support::endianness>
+          class HashBuilder,
+          class HasherT, support::endianness Endianness,
+          support::endianness EffectiveEndianness>
+class HashBuilderBase {
+  using Derived = HashBuilder<HasherT, Endianness, EffectiveEndianness>;
----------------
Using CRTP blocks the compile-time speedup / code size wins. Can we skip HashBuilder/Endianness/EffectiveEndianness/Derived?


================
Comment at: llvm/include/llvm/Support/HashBuilder.h:45-48
+  auto &update(ArrayRef<uint8_t> Data) {
+    this->getHasher().update(Data);
+    return *static_cast<Derived *>(this);
+  }
----------------
I suggest dropping the return statement, for `void update(ArrayRef)`, a simple passthrough to HasherT::update.
- No need for the CRTP (faster compile-time / avoid code size bloat by factoring out a common instantiation for the base class)
- The same functionality is available as `HashBuilder& HasherBuilder::addRangeElements`, so there's no loss of convenience


================
Comment at: llvm/include/llvm/Support/HashBuilder.h:55
+  /// contraints.
+  auto &update(StringRef Data) {
+    return update(makeArrayRef(reinterpret_cast<const uint8_t *>(Data.data()),
----------------
(this would also return `void`)


================
Comment at: llvm/include/llvm/Support/HashBuilder.h:60-66
+  template <typename T> using HasFinalT = decltype(std::declval<T &>().final());
+  /// Forward to `HasherT::final()` if available.
+  template <typename HasherT_ = HasherT>
+  std::enable_if_t<is_detected<HasFinalT, HasherT_>::value, HasFinalT<HasherT_>>
+  final() {
+    return this->getHasher().final();
+  }
----------------
I don't think we need enable_if or return type deduction here. It seems like overkill to make "does HashBuilder::final work?" SFINAE-detectible.


================
Comment at: llvm/include/llvm/Support/HashBuilder.h:68-76
+  template <typename T>
+  using HasResultT = decltype(std::declval<T &>().result());
+  /// Forward to `HasherT::result()` if available.
+  template <typename HasherT_ = HasherT>
+  std::enable_if_t<is_detected<HasResultT, HasherT_>::value,
+                   HasResultT<HasherT_>>
+  result() {
----------------
Same as `final`.


================
Comment at: llvm/include/llvm/Support/HashBuilder.h:91
+
+/// Implementation of the `HashBuilder` interface.
+template <template <typename, support::endianness, support::endianness>
----------------
I think this comment should explain that `support::endianness::native` is not supported here but that it's handled by `HashBuilder`. Basically, explain the static assertion in text.


================
Comment at: llvm/include/llvm/Support/HashBuilder.h:100-102
+  using Base =
+      HashBuilderBase<HashBuilder, HasherT, Endianness, EffectiveEndianness>;
+  using Derived = HashBuilder<HasherT, Endianness, EffectiveEndianness>;
----------------
Using CRTP in `HashBuilderImpl` also increases compile-time / code size since it blocks sharing code between "native" and whatever the system endianness is. I suggest dropping CRTP and returning `HashBuilderImpl&` instead of `HashBuilder&` from `add*`. (If this needed CRTP, which I don't think it does, I'd suggest using an opaque `DerivedT` to avoid passing through details like EffectiveEndianness.)



================
Comment at: llvm/include/llvm/Support/HashBuilder.h:410-424
+template <typename HasherT, support::endianness Endianness,
+          support::endianness EffectiveEndianness =
+              (Endianness == support::endianness::native
+                   ? support::endian::system_endianness()
+                   : Endianness)>
+class HashBuilder : public HashBuilderImpl<HashBuilder, HasherT, Endianness,
+                                           EffectiveEndianness> {
----------------
Given that `add*` will return `HashBuilderImpl&`, might be reasonable to reduce this to a typedef:
```
lang=c++
template <class HasherT, support::endianness Endianness>
using HashBuilder =
    HashBuilderImpl<HasherT,
                    (Endianness == support::endianness::native
                         ? support::endian::system_endianness()
                         : Endianness)>;
```
Either way seems fine though.


================
Comment at: llvm/unittests/Support/HashBuilderTest.cpp:46-50
+template <typename HasherTAndEndianness, typename... Ts>
+static auto hashWithBuilder(const Ts &...Args) {
+  return static_cast<std::string>(
+      HashBuilder<HasherTAndEndianness>().add(Args...).final());
+}
----------------
I'd skip `auto` type deduction unless the type is hard to spell. Also you can use StringRef::str here.


================
Comment at: llvm/unittests/Support/HashBuilderTest.cpp:53-56
+static auto hashRangeWithBuilder(const Ts &...Args) {
+  return static_cast<std::string>(
+      HashBuilder<HasherTAndEndianness>().addRange(Args...).final());
+}
----------------
(same as above)


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D106910/new/

https://reviews.llvm.org/D106910



More information about the llvm-commits mailing list