#ifndef __Hash__
#define __Hash__
#include <utility>
#include <functional>
#include <type_traits>
#include <cstdint>
#include "Forwards.hpp"
#include "Kernel/Unit.hpp"
#include "Lib/Option.hpp"
static const unsigned FNV32_PRIME = 16777619;
static const unsigned FNV32_OFFSET_BASIS = 2166136261;
namespace Lib {
struct HashUtils
{
static unsigned combine(unsigned h1, unsigned h2) { return h1 ^ (h2 + 0x9e3779b9 + (h1 << 6) + (h1 >> 2)); }
static unsigned combine(unsigned h1) { return h1; }
static unsigned combine() { return combine(0, 1); }
template<class... Ts> static unsigned combine(unsigned h1, unsigned h2, unsigned h3, Ts... ts)
{ return combine(h1, combine(h2, h3, ts...)); }
};
struct IdentityHash
{
template<typename T>
static bool equals(T o1, T o2)
{ return o1 == o2; }
template<typename T>
static unsigned hash(T val)
{ return static_cast<unsigned>(val); }
};
struct StlHash {
template<class T>
static bool equals(const T& lhs, const T& rhs)
{ return lhs == rhs; }
template<class T>
static unsigned hash(const T& self)
{ return std::hash<T>{}(self); }
};
template<class InnerHash>
struct DerefPtrHash {
template<class T>
static bool equals(const T* lhs, const T* rhs)
{ return InnerHash::equals(*lhs, *rhs); }
template<class T>
static unsigned hash(const T* self)
{ return InnerHash::hash(*self); }
};
template<class ElementHash>
struct StackHash {
template<typename T>
static unsigned hash(const Stack<T>& s, unsigned hash = FNV32_OFFSET_BASIS) {
for (auto& x : s) {
hash = HashUtils::combine(hash, ElementHash::hash(x));
}
return hash;
}
};
template<class ElementHash>
struct VectorHash {
template<typename T>
static unsigned hash(const Vector<T>& s) {
unsigned res = FNV32_OFFSET_BASIS;
for (unsigned i = 0; i < s.length(); i++) {
res = HashUtils::combine(res, ElementHash::hash(s[i]));
}
return res;
}
};
template<class InnerHash>
struct TupleHash
{
template<typename... T>
static unsigned hash(std::tuple<T...> const& s)
{ return std::apply([](auto... args) { return HashUtils::combine(InnerHash::hash(args)...); }, s); }
};
class DefaultHash
{
public:
template<typename T>
static bool equals(const T &o1, const T &o2)
{ return o1 == o2; }
template<typename T>
static typename std::enable_if<
std::is_same<
typename std::invoke_result<decltype(&T::defaultHash), T>::type,
unsigned
>::value,
unsigned
>::type hash(const T &ref) {
return ref.defaultHash();
}
template<typename T>
static typename std::enable_if<
std::is_base_of<Kernel::Unit, T>::value,
unsigned
>::type hash(T *unit)
{ return hash(unit ? unit->number() : 0); }
template<typename T>
static typename std::enable_if<
!std::is_base_of<Kernel::Unit, T>::value,
unsigned
>::type hash(T* ptr, unsigned hash = FNV32_OFFSET_BASIS) {
return hashBytes(
reinterpret_cast<const unsigned char*>(&ptr),
sizeof(ptr),
hash
);
}
template<typename T>
static typename std::enable_if<
std::is_arithmetic<T>::value || std::is_enum<T>::value,
unsigned
>::type hash(T val, unsigned hash = FNV32_OFFSET_BASIS) {
return hashBytes(
reinterpret_cast<const unsigned char *>(&val),
sizeof(val),
hash
);
}
static unsigned hash(const std::string& str)
{ return DefaultHash::hashNulTerminated(str.c_str()); }
template<typename T>
static unsigned hash(const Vector<T> &obj)
{ return VectorHash<DefaultHash>::hash(obj); }
template<typename T>
static unsigned hash(const Stack<T> &obj, unsigned hash = FNV32_OFFSET_BASIS)
{ return StackHash<DefaultHash>::hash(obj, hash); }
template<typename T, typename U>
static unsigned hash(const std::pair<T,U> &obj) {
return HashUtils::combine(
DefaultHash::hash(obj.first),
DefaultHash::hash(obj.second)
);
}
static unsigned hashBytes(
const unsigned char *val,
size_t size,
unsigned hash = FNV32_OFFSET_BASIS
) {
for (size_t i = 0; i < size; i++) {
hash = (hash ^ val[i]) * FNV32_PRIME;
}
return hash;
}
template<class Iter>
static unsigned hashIter(
Iter iter,
unsigned hash = FNV32_OFFSET_BASIS
) {
while (iter.hasNext()) {
hash = (hash ^ iter.next()) * FNV32_PRIME;
}
return hash;
}
static unsigned hashNulTerminated(const char* val) {
unsigned hash = FNV32_OFFSET_BASIS;
while (*val) {
hash = (hash ^ *val) * FNV32_PRIME;
val++;
}
return hash;
}
template<typename... T>
static unsigned hash(std::tuple<T...> const& s)
{ return TupleHash<DefaultHash>::hash(s); }
template<typename T>
static unsigned hash(Lib::Option<T> const& o)
{ return o.isSome() ? Lib::DefaultHash::hash(*o)
: Lib::DefaultHash::hash(0); }
};
class DefaultHash2 {
public:
template<typename T>
static typename std::enable_if<
std::is_same<
typename std::invoke_result<decltype(&T::defaultHash2), T>::type,
unsigned
>::value,
unsigned
>::type hash(const T &ref) {
return ref.defaultHash2();
}
template<typename T>
static typename std::enable_if<
std::is_base_of<Kernel::Unit, T>::value,
unsigned
>::type hash(T *unit)
{ return unit ? unit->number() : 0; }
template<typename T>
static typename std::enable_if<
!std::is_base_of<Kernel::Unit, T>::value,
unsigned
>::type hash(T* ptr) {
return static_cast<unsigned>(reinterpret_cast<uintptr_t>(ptr));
}
template<typename T> static typename std::enable_if<
std::is_fundamental<T>::value || std::is_enum<T>::value,
unsigned
>::type hash(T val) {
return static_cast<unsigned>(val);
}
static unsigned hash(const std::string &str) {
return str.length();
}
template<typename T> static unsigned hash(const Stack<T> &stack) {
return stack.length();
}
template<typename T> static unsigned hash(const Vector<T> &vector) {
return vector.length();
}
template<typename T, typename U>
static unsigned hash(const std::pair<T, U> &pp) {
return HashUtils::combine(
DefaultHash2::hash(pp.first),
DefaultHash2::hash(pp.second)
);
}
template<typename... T>
static unsigned hash(std::tuple<T...> const& s)
{ return TupleHash<DefaultHash2>::hash(s); }
template<typename T>
static unsigned hash(Lib::Option<T> const& o)
{ return o.isSome() ? Lib::DefaultHash2::hash(*o)
: Lib::DefaultHash2::hash(0); }
};
}
namespace std {
template<class T> struct hash<Lib::Stack<T>>
{
size_t operator()(Lib::Stack<T> const& s) const
{ return Lib::StackHash<Lib::StlHash>::hash(s); }
};
template<class... T> struct hash<std::tuple<T...>>
{
size_t operator()(std::tuple<T...> const& s) const
{ return Lib::DefaultHash::hash(s); }
};
}
#endif