<commit_before>#ifndef VAST_CONCEPT_HASHABLE_HASH_APPEND_HPP
#define VAST_CONCEPT_HASHABLE_HASH_APPEND_HPP

#include <cstddef>
#include <cstdint>
#include <cstring>

#include <array>
#include <memory>
#include <string>
#include <tuple>
#include <vector>
#include <type_traits>

#include <caf/detail/type_traits.hpp>
#include <caf/meta/save_callback.hpp>
#include <caf/meta/type_name.hpp>

#include "vast/detail/endian.hpp"
#include "vast/detail/type_traits.hpp"

// In order to be hashable, a type T must satisfy at least one of the following
// requirements:
//
//     1. There exists an overload with the following signature:
//
//         template <class Hasher, class T>
//         void hash_append(Hasher& h, T const& x);
//
//     2. T is contiguously hashable under. That is, for all combinations of
//        two instances of T, say `x` and `y`, if `x == y`, then it must also
//        be true that `memcmp(addressof(x), addressof(y), sizeof(T)) == 0`.
//        That is, if `x == y`, then `x` and `y` have the same bit pattern
//        representation.
//
// See https://isocpp.org/files/papers/n3980.html for details.

namespace vast {
namespace detail {

// -- is_uniquely_represented ------------------------------------------------

template <class T>
struct is_uniquely_represented
  : std::integral_constant<bool, std::is_integral<T>{}
                                 || std::is_enum<T> {}
                                 || std::is_pointer<T>{}> {};

template <class T>
struct is_uniquely_represented<T const> : is_uniquely_represented<T> {};

template <class T, class U>
struct is_uniquely_represented<std::pair<T, U>>
  : std::integral_constant<
      bool,
      is_uniquely_represented<T>{}
        && is_uniquely_represented<U>{}
        && sizeof(T) + sizeof(U) == sizeof(std::pair<T, U>)
     > {};

template <class ...T>
struct is_uniquely_represented<std::tuple<T...>>
  : std::integral_constant<
      bool,
      detail::conjunction<is_uniquely_represented<T>::value...>{}
        && detail::sum<sizeof(T)...>{} == sizeof(std::tuple<T...>)
    > {};

template <class T, size_t N>
struct is_uniquely_represented<T[N]> : is_uniquely_represented<T> {};

template <class T, size_t N>
struct is_uniquely_represented<std::array<T, N>>
  : std::integral_constant<
      bool,
      is_uniquely_represented<T>{} && sizeof(T) * N == sizeof(std::array<T, N>)
    > {};

// -- helpers ----------------------------------------------------------------

template <class T>
constexpr void reverse_bytes(T& x) {
  auto ptr = std::memmove(std::addressof(x), std::addressof(x), sizeof(T));
  unsigned char* bytes = static_cast<unsigned char*>(ptr);
  for (unsigned i = 0; i < sizeof(T) / 2; ++i)
    std::swap(bytes[i], bytes[sizeof(T) - 1 - i]);
}

template <class T, class Hasher>
constexpr std::enable_if_t<Hasher::endian == host_endian>
maybe_reverse_bytes(T&, Hasher&) {
  // nop
}

template <class T, class Hasher>
constexpr std::enable_if_t<Hasher::endian != host_endian>
maybe_reverse_bytes(T& x, Hasher&) {
  reverse_bytes(x);
}

// -- is_contiguously_hashable -----------------------------------------------

template <class T, class Hasher>
struct is_contiguously_hashable
  : std::integral_constant<
      bool,
      is_uniquely_represented<T>{}
        && (sizeof(T) == 1 || Hasher::endian == host_endian)
    > {};

template <class T, size_t N, class Hasher>
struct is_contiguously_hashable<T[N], Hasher>
  : std::integral_constant<
      bool,
      is_uniquely_represented<T[N]>{}
        && (sizeof(T) == 1 || Hasher::endian == host_endian)
    > {};

} // namespace detail

template <class Hasher, class T>
std::enable_if_t<detail::is_contiguously_hashable<T, Hasher>{}>
hash_append(Hasher& h, T const& x) noexcept {
  h(std::addressof(x), sizeof(x));
}

// -- Scalars -----------------------------------------------------------------

template <class Hasher, class T>
std::enable_if_t<
  !detail::is_contiguously_hashable<T, Hasher>{}
    && (std::is_integral<T>{} || std::is_pointer<T>{} || std::is_enum<T>{})
>
hash_append(Hasher& h, T x) noexcept {
  detail::reverse_bytes(x);
  h(std::addressof(x), sizeof(x));
}

template <class Hasher, class T>
std::enable_if_t<std::is_floating_point<T>{}>
hash_append(Hasher& h, T x) noexcept {
  // When hashing, we treat -0 and 0 the same.
  if (x == 0)
    x = 0;
  detail::maybe_reverse_bytes(x, h);
  h(&x, sizeof(x));
}

template <class Hasher>
void hash_append(Hasher& h, std::nullptr_t) noexcept {
  void const* p = nullptr;
  detail::maybe_reverse_bytes(p, h);
  h(&p, sizeof(p));
}

// -- Forward declarations to enable ADL --------------------------------------

template <class Hasher, class T, size_t N>
std::enable_if_t<!detail::is_contiguously_hashable<T, Hasher>{}>
hash_append(Hasher& h, T (&a)[N]) noexcept;

template <class Hasher, class CharT, class Traits, class Alloc>
std::enable_if_t<!detail::is_contiguously_hashable<CharT, Hasher>{}>
hash_append(Hasher& h, std::basic_string<CharT, Traits, Alloc> const& s) noexcept;

template <class Hasher, class CharT, class Traits, class Alloc>
std::enable_if_t<detail::is_contiguously_hashable<CharT, Hasher>{}>
hash_append(Hasher& h, std::basic_string<CharT, Traits, Alloc> const& s) noexcept;

template <class Hasher, class T, class U>
std::enable_if_t<!detail::is_contiguously_hashable<std::pair<T, U>, Hasher>{}>
hash_append (Hasher& h, std::pair<T, U> const& p) noexcept;

template <class Hasher, class T, class Alloc>
std::enable_if_t<!detail::is_contiguously_hashable<T, Hasher>{}>
hash_append(Hasher& h, std::vector<T, Alloc> const& v) noexcept;

template <class Hasher, class T, class Alloc>
std::enable_if_t<detail::is_contiguously_hashable<T, Hasher>{}>
hash_append(Hasher& h, std::vector<T, Alloc> const& v) noexcept;

template <class Hasher, class T, size_t N>
std::enable_if_t<!detail::is_contiguously_hashable<std::array<T, N>, Hasher>{}>
hash_append(Hasher& h, std::array<T, N> const& a) noexcept;

template <class Hasher, class ...T>
std::enable_if_t<!detail::is_contiguously_hashable<std::tuple<T...>, Hasher>{}>
hash_append(Hasher& h, std::tuple<T...> const& t) noexcept;

template <class Hasher, class T0, class T1, class ...T>
void hash_append(Hasher& h, T0 const& t0, T1 const& t1, T const& ...t) noexcept;

// -- C array -----------------------------------------------------------------

template <class Hasher, class T, size_t N>
std::enable_if_t<!detail::is_contiguously_hashable<T, Hasher>{}>
hash_append(Hasher& h, T (&a)[N]) noexcept {
  for (auto const& x : a)
    hash_append(h, x);
}

// -- string ------------------------------------------------------------------

template <class Hasher, class CharT, class Traits, class Alloc>
std::enable_if_t<!detail::is_contiguously_hashable<CharT, Hasher>{} >
hash_append(Hasher& h,
            std::basic_string<CharT, Traits, Alloc> const& s) noexcept {
  for (auto c : s)
    hash_append(h, c);
  hash_append(h, s.size());
}

template <class Hasher, class CharT, class Traits, class Alloc>
std::enable_if_t<detail::is_contiguously_hashable<CharT, Hasher>{}>
hash_append(Hasher& h,
            std::basic_string<CharT, Traits, Alloc> const& s) noexcept {
  h(s.data(), s.size() * sizeof(CharT));
  hash_append(h, s.size());
}

// -- pair --------------------------------------------------------------------


template <class Hasher, class T, class U>
std::enable_if_t<!detail::is_contiguously_hashable<std::pair<T, U>, Hasher>{}>
hash_append(Hasher& h, std::pair<T, U> const& p) noexcept {
  hash_append(h, p.first, p.second);
}

// -- vector ------------------------------------------------------------------

template <class Hasher, class T, class Alloc>
std::enable_if_t<!detail::is_contiguously_hashable<T, Hasher>{}>
hash_append(Hasher& h, std::vector<T, Alloc> const& v) noexcept {
  for (auto const& t : v)
    hash_append(h, t);
  hash_append(h, v.size());
}

template <class Hasher, class T, class Alloc>
std::enable_if_t<detail::is_contiguously_hashable<T, Hasher>{}>
hash_append(Hasher& h, std::vector<T, Alloc> const& v) noexcept {
  h(v.data(), v.size() * sizeof(T));
  hash_append(h, v.size());
}

// -- array -------------------------------------------------------------------

template <class Hasher, class T, size_t N>
std::enable_if_t<!detail::is_contiguously_hashable<std::array<T, N>, Hasher>{}>
hash_append(Hasher& h, std::array<T, N> const& a) noexcept {
  for (auto const& t : a)
    hash_append(h, t);
}

// -- tuple -------------------------------------------------------------------

namespace detail {

inline void for_each_item(...) noexcept {
}

template <class Hasher, class T>
int hash_one(Hasher& h, T const& t) noexcept {
  hash_append(h, t);
  return 0;
}

template <class Hasher, class ...T, size_t ...I>
void tuple_hash(Hasher& h, std::tuple<T...> const& t,
                std::index_sequence<I...>) noexcept {
  for_each_item(hash_one(h, std::get<I>(t))...);
}

} // namespace detail

template <class Hasher, class ...T>
std::enable_if_t<!detail::is_contiguously_hashable<std::tuple<T...>, Hasher>{}>
hash_append(Hasher& h, std::tuple<T...> const& t) noexcept {
  detail::tuple_hash(h, t, std::index_sequence_for<T...>{});
}

// -- variadic ----------------------------------------------------------------

template <class Hasher, class T0, class T1, class... Ts>
void hash_append(Hasher& h, T0 const& x0, T1 const& x1,
                 Ts const& ...xs) noexcept {
  hash_append(h, x0);
  hash_append(h, x1, xs...);
}

// -- inspectable -------------------------------------------------------------

namespace detail {

template <class Hasher>
struct hash_inspector {
  using result_type = void;

  static constexpr bool reads_state = true;

  hash_inspector(Hasher& h) : h_{h} {
  }

  result_type operator()() const noexcept {
    // End of recursion.
  }

  template <class... Ts>
  result_type operator()(caf::meta::type_name_t x, Ts&&... xs) const {
    // Figure out the actual bytes to hash.
    auto ptr = x.value;
    while (*ptr != '\0')
      ++ptr;
    h_(x.value, ptr - x.value);
    (*this)(std::forward<Ts>(xs)...);
  }

  template <class F, class... Ts>
  result_type operator()(caf::meta::save_callback_t<F> x, Ts&&... xs) const {
    x.fun();
    (*this)(std::forward<Ts>(xs)...);
  }

  template <class T, class... Ts>
  std::enable_if_t<caf::meta::is_annotation<T>::value, result_type>
  operator()(T&&, Ts&&... xs) const noexcept {
    (*this)(std::forward<Ts>(xs)...);
  }

  template <class T, class... Ts>
  std::enable_if_t<!caf::meta::is_annotation<T>::value, result_type>
  operator()(T&& x, Ts&&... xs) const noexcept {
    hash_append(h_, x);
    (*this)(std::forward<Ts>(xs)...);
  }

  Hasher& h_;
};

} // namespace detail

template <class Hasher, class T><commit_msg>Make more type standard types hashable<commit_after>#ifndef VAST_CONCEPT_HASHABLE_HASH_APPEND_HPP
#define VAST_CONCEPT_HASHABLE_HASH_APPEND_HPP

#include <cstddef>
#include <cstdint>
#include <cstring>

#include <array>
#include <chrono>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#include <vector>

#include <caf/detail/type_traits.hpp>
#include <caf/meta/save_callback.hpp>
#include <caf/meta/type_name.hpp>

#include "vast/detail/endian.hpp"
#include "vast/detail/type_traits.hpp"

// In order to be hashable, a type T must satisfy at least one of the following
// requirements:
//
//     1. There exists an overload with the following signature:
//
//         template <class Hasher, class T>
//         void hash_append(Hasher& h, T const& x);
//
//     2. T is contiguously hashable under. That is, for all combinations of
//        two instances of T, say `x` and `y`, if `x == y`, then it must also
//        be true that `memcmp(addressof(x), addressof(y), sizeof(T)) == 0`.
//        That is, if `x == y`, then `x` and `y` have the same bit pattern
//        representation.
//
// See https://isocpp.org/files/papers/n3980.html for details.

namespace vast {
namespace detail {

// -- is_uniquely_represented ------------------------------------------------

template <class T>
struct is_uniquely_represented
  : std::integral_constant<bool, std::is_integral<T>{}
                                 || std::is_enum<T> {}
                                 || std::is_pointer<T>{}> {};

template <class T>
struct is_uniquely_represented<T const> : is_uniquely_represented<T> {};

template <class T, class U>
struct is_uniquely_represented<std::pair<T, U>>
  : std::integral_constant<
      bool,
      is_uniquely_represented<T>{}
        && is_uniquely_represented<U>{}
        && sizeof(T) + sizeof(U) == sizeof(std::pair<T, U>)
     > {};

template <class ...T>
struct is_uniquely_represented<std::tuple<T...>>
  : std::integral_constant<
      bool,
      detail::conjunction<is_uniquely_represented<T>::value...>{}
        && detail::sum<sizeof(T)...>{} == sizeof(std::tuple<T...>)
    > {};

template <class T, size_t N>
struct is_uniquely_represented<T[N]> : is_uniquely_represented<T> {};

template <class T, size_t N>
struct is_uniquely_represented<std::array<T, N>>
  : std::integral_constant<
      bool,
      is_uniquely_represented<T>{} && sizeof(T) * N == sizeof(std::array<T, N>)
    > {};

// -- helpers ----------------------------------------------------------------

template <class T>
constexpr void reverse_bytes(T& x) {
  auto ptr = std::memmove(std::addressof(x), std::addressof(x), sizeof(T));
  unsigned char* bytes = static_cast<unsigned char*>(ptr);
  for (unsigned i = 0; i < sizeof(T) / 2; ++i)
    std::swap(bytes[i], bytes[sizeof(T) - 1 - i]);
}

template <class T, class Hasher>
constexpr std::enable_if_t<Hasher::endian == host_endian>
maybe_reverse_bytes(T&, Hasher&) {
  // nop
}

template <class T, class Hasher>
constexpr std::enable_if_t<Hasher::endian != host_endian>
maybe_reverse_bytes(T& x, Hasher&) {
  reverse_bytes(x);
}

// -- is_contiguously_hashable -----------------------------------------------

template <class T, class Hasher>
struct is_contiguously_hashable
  : std::integral_constant<
      bool,
      is_uniquely_represented<T>{}
        && (sizeof(T) == 1 || Hasher::endian == host_endian)
    > {};

template <class T, size_t N, class Hasher>
struct is_contiguously_hashable<T[N], Hasher>
  : std::integral_constant<
      bool,
      is_uniquely_represented<T[N]>{}
        && (sizeof(T) == 1 || Hasher::endian == host_endian)
    > {};

} // namespace detail

template <class Hasher, class T>
std::enable_if_t<detail::is_contiguously_hashable<T, Hasher>{}>
hash_append(Hasher& h, T const& x) noexcept {
  h(std::addressof(x), sizeof(x));
}

// -- Scalars -----------------------------------------------------------------

template <class Hasher, class T>
std::enable_if_t<
  !detail::is_contiguously_hashable<T, Hasher>{}
    && (std::is_integral<T>{} || std::is_pointer<T>{} || std::is_enum<T>{})
>
hash_append(Hasher& h, T x) noexcept {
  detail::reverse_bytes(x);
  h(std::addressof(x), sizeof(x));
}

template <class Hasher, class T>
std::enable_if_t<std::is_floating_point<T>{}>
hash_append(Hasher& h, T x) noexcept {
  // When hashing, we treat -0 and 0 the same.
  if (x == 0)
    x = 0;
  detail::maybe_reverse_bytes(x, h);
  h(&x, sizeof(x));
}

template <class Hasher>
void hash_append(Hasher& h, std::nullptr_t) noexcept {
  void const* p = nullptr;
  detail::maybe_reverse_bytes(p, h);
  h(&p, sizeof(p));
}

// -- chrono ------------------------------------------------------------------

template <class Hasher, class Rep, class Period>
void hash_append(Hasher& h, std::chrono::duration<Rep, Period> d) {
  hash_append(h, d.count());
}

template <class Hasher, class Clock, class Duration>
void hash_append(Hasher& h, std::chrono::time_point<Clock, Duration> t) {
  hash_append(h, t.time_since_epoch());
}

// -- empty types -------------------------------------------------------------

template <class Hasher, class T>
std::enable_if_t<std::is_empty<T>::value>
hash_append(Hasher& h, T) noexcept {
  hash_append(h, 0);
}

// -- forward declarations to enable ADL --------------------------------------

template <class Hasher, class T, size_t N>
std::enable_if_t<!detail::is_contiguously_hashable<T, Hasher>{}>
hash_append(Hasher& h, T (&a)[N]) noexcept;

template <class Hasher, class CharT, class Traits, class Alloc>
std::enable_if_t<!detail::is_contiguously_hashable<CharT, Hasher>{}>
hash_append(Hasher& h, std::basic_string<CharT, Traits, Alloc> const& s) noexcept;

template <class Hasher, class CharT, class Traits, class Alloc>
std::enable_if_t<detail::is_contiguously_hashable<CharT, Hasher>{}>
hash_append(Hasher& h, std::basic_string<CharT, Traits, Alloc> const& s) noexcept;

template <class Hasher, class T, class U>
std::enable_if_t<!detail::is_contiguously_hashable<std::pair<T, U>, Hasher>{}>
hash_append (Hasher& h, std::pair<T, U> const& p) noexcept;

template <class Hasher, class T, size_t N>
std::enable_if_t<!detail::is_contiguously_hashable<std::array<T, N>, Hasher>{}>
hash_append(Hasher& h, std::array<T, N> const& a) noexcept;

template <class Hasher, class T, class Alloc>
std::enable_if_t<!detail::is_contiguously_hashable<T, Hasher>{}>
hash_append(Hasher& h, std::vector<T, Alloc> const& v) noexcept;

template <class Hasher, class T, class Alloc>
std::enable_if_t<detail::is_contiguously_hashable<T, Hasher>{}>
hash_append(Hasher& h, std::vector<T, Alloc> const& v) noexcept;

template <class Hasher, class Key, class Comp, class Alloc>
void hash_append(Hasher& h, std::set<Key, Comp, Alloc> const& s) noexcept;

template <class Hasher, class Key, class T, class Comp, class Alloc>
void hash_append(Hasher& h, std::map<Key, T, Comp, Alloc> const& m) noexcept;

template <class Hasher, class Key, class Hash, class Eq, class Alloc>
void hash_append(Hasher& h,
                 std::unordered_set<Key, Hash, Eq, Alloc> const& s) noexcept;

template <class Hasher, class K, class T, class Hash, class Eq, class Alloc>
void hash_append(Hasher& h,
                 std::unordered_map<K, T, Hash, Eq, Alloc> const& m) noexcept;

template <class Hasher, class ...T>
std::enable_if_t<!detail::is_contiguously_hashable<std::tuple<T...>, Hasher>{}>
hash_append(Hasher& h, std::tuple<T...> const& t) noexcept;

template <class Hasher, class T0, class T1, class ...T>
void hash_append(Hasher& h, T0 const& t0, T1 const& t1, T const& ...t) noexcept;

// -- C array -----------------------------------------------------------------

template <class Hasher, class T, size_t N>
std::enable_if_t<!detail::is_contiguously_hashable<T, Hasher>{}>
hash_append(Hasher& h, T (&a)[N]) noexcept {
  for (auto const& x : a)
    hash_append(h, x);
}

// -- string ------------------------------------------------------------------

template <class Hasher, class CharT, class Traits, class Alloc>
std::enable_if_t<!detail::is_contiguously_hashable<CharT, Hasher>{} >
hash_append(Hasher& h,
            std::basic_string<CharT, Traits, Alloc> const& s) noexcept {
  for (auto c : s)
    hash_append(h, c);
  hash_append(h, s.size());
}

template <class Hasher, class CharT, class Traits, class Alloc>
std::enable_if_t<detail::is_contiguously_hashable<CharT, Hasher>{}>
hash_append(Hasher& h,
            std::basic_string<CharT, Traits, Alloc> const& s) noexcept {
  h(s.data(), s.size() * sizeof(CharT));
  hash_append(h, s.size());
}

// -- pair --------------------------------------------------------------------

template <class Hasher, class T, class U>
std::enable_if_t<!detail::is_contiguously_hashable<std::pair<T, U>, Hasher>{}>
hash_append(Hasher& h, std::pair<T, U> const& p) noexcept {
  hash_append(h, p.first, p.second);
}

// -- array -------------------------------------------------------------------

template <class Hasher, class T, size_t N>
std::enable_if_t<!detail::is_contiguously_hashable<std::array<T, N>, Hasher>{}>
hash_append(Hasher& h, std::array<T, N> const& a) noexcept {
  for (auto const& t : a)
    hash_append(h, t);
}

// -- vector ------------------------------------------------------------------

template <class Hasher, class T, class Alloc>
std::enable_if_t<detail::is_contiguously_hashable<T, Hasher>{}>
hash_append(Hasher& h, std::vector<T, Alloc> const& v) noexcept {
  h(v.data(), v.size() * sizeof(T));
  hash_append(h, v.size());
}

template <class Hasher, class T, class Alloc>
std::enable_if_t<!detail::is_contiguously_hashable<T, Hasher>{}>
hash_append(Hasher& h, std::vector<T, Alloc> const& v) noexcept {
  for (auto const& t : v)
    hash_append(h, t);
  hash_append(h, v.size());
}

// -- set ---------------------------------------------------------------------

template <class Hasher, class Key, class Comp, class Alloc>
void hash_append(Hasher& h, std::set<Key, Comp, Alloc> const& s) noexcept {
  for (auto const& x : s)
    hash_append(h, x);
  hash_append(h, s.size());
}

// -- map ---------------------------------------------------------------------

template <class Hasher, class Key, class T, class Comp, class Alloc>
void hash_append(Hasher& h, std::map<Key, T, Comp, Alloc> const& m) noexcept {
  for (auto const& x : m)
    hash_append(h, x);
  hash_append(h, m.size());
}

// -- unordered_set -----------------------------------------------------------

template <class Hasher, class Key, class Hash, class Eq, class Alloc>
void hash_append(Hasher& h,
                 std::unordered_set<Key, Hash, Eq, Alloc> const& s) noexcept {
  for (auto const& x : s)
    hash_append(h, x);
  hash_append(h, s.size());
}

// -- unordered_map -----------------------------------------------------------

template <class Hasher, class K, class T, class Hash, class Eq, class Alloc>
void hash_append(Hasher& h,
                 std::unordered_map<K, T, Hash, Eq, Alloc> const& m) noexcept {
  for (auto const& x : m)
    hash_append(h, x);
  hash_append(h, m.size());
}

// -- tuple -------------------------------------------------------------------

namespace detail {

inline void for_each_item(...) noexcept {
}

template <class Hasher, class T>
int hash_one(Hasher& h, T const& t) noexcept {
  hash_append(h, t);
  return 0;
}

template <class Hasher, class ...T, size_t ...I>
void tuple_hash(Hasher& h, std::tuple<T...> const& t,
                std::index_sequence<I...>) noexcept {
  for_each_item(hash_one(h, std::get<I>(t))...);
}

} // namespace detail

template <class Hasher, class ...T>
std::enable_if_t<!detail::is_contiguously_hashable<std::tuple<T...>, Hasher>{}>
hash_append(Hasher& h, std::tuple<T...> const& t) noexcept {
  detail::tuple_hash(h, t, std::index_sequence_for<T...>{});
}

// -- variadic ----------------------------------------------------------------

template <class Hasher, class T0, class T1, class... Ts>
void hash_append(Hasher& h, T0 const& x0, T1 const& x1,
                 Ts const& ...xs) noexcept {
  hash_append(h, x0);
  hash_append(h, x1, xs...);
}

// -- inspectable -------------------------------------------------------------

namespace detail {

template <class Hasher>
struct hash_inspector {
  using result_type = void;

  static constexpr bool reads_state = true;

  hash_inspector(Hasher& h) : h_{h} {
  }

  result_type operator()() const noexcept {
    // End of recursion.
  }

  template <class... Ts>
  result_type operator()(caf::meta::type_name_t x, Ts&&... xs) const {
    // Figure out the actual bytes to hash.
    auto ptr = x.value;
    while (*ptr != '\0')
      ++ptr;
    h_(x.value, ptr - x.value);
    (*this)(std::forward<Ts>(xs)...);
  }

  template <class F, class... Ts>
  result_type operator()(caf::meta::save_callback_t<F> x, Ts&&... xs) const {
    x.fun();
    (*this)(std::forward<Ts>(xs)...);
  }

  template <class T, class... Ts>
  std::enable_if_t<caf::meta::is_annotation<T>::value, result_type>
  operator()(T&&, Ts&&... xs) const noexcept {
    (*this)(std::forward<Ts>(xs)...); // Ignore annotation.
  }

  template <class T, class... Ts>
  std::enable_if_t<!caf::meta::is_annotation<T>::value, result_type>
  operator()(T&& x, Ts&&... xs) const noexcept {
    hash_append(h_, x);
    (*this)(std::forward<Ts>(xs)...);
  }

  Hasher& h_;
};

} // namespace detail

template <class Hasher, class T>
{'fraction_non_alphanumeric': 0.23469598644235937, 'fraction_numerical': 0.0026867275658248252, 'mean_word_length': 2.909825468648998, 'pattern_counts': {'":': 0, '<': 357, '<?xml version=': 0, '>': 355, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5002875', 'n_tokens_mistral': 8508, 'n_tokens_neox': 8291, 'n_words': 2021}