/** \file G3D/HashTrait.h \maintainer Morgan McGuire, http://graphics.cs.williams.edu \created 2008-10-01 \edited 2011-06-09 Copyright 2000-2012, Morgan McGuire. All rights reserved. */ #ifndef G3D_HashTrait_h #define G3D_HashTrait_h #include "G3D/platform.h" #include "G3D/Crypto.h" #include "G3D/g3dmath.h" #include "G3D/uint128.h" #include #include #undef get16bits #if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) #define get16bits(d) (*((const uint16_t *) (d))) #endif #if !defined (get16bits) #define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\ +(uint32_t)(((const uint8_t *)(d))[0]) ) #endif namespace G3D { /** \brief A hash function that is faster than CRC32 for arbitrary long strings \cite From http://www.azillionmonkeys.com/qed/hash.html by Paul Hsieh*/ inline uint32_t superFastHash (const void* _data, size_t numBytes) { const char* data = (const char*)_data; uint32_t hash = (uint32_t)numBytes; uint32_t tmp; int rem; if ((numBytes <= 0) || (data == NULL)) { return 0; } rem = numBytes & 3; numBytes >>= 2; /* Main loop */ for (;numBytes > 0; --numBytes) { hash += get16bits (data); tmp = (get16bits (data+2) << 11) ^ hash; hash = (hash << 16) ^ tmp; data += 2*sizeof (uint16_t); hash += hash >> 11; } /* Handle end cases */ switch (rem) { case 3: hash += get16bits (data); hash ^= hash << 16; hash ^= data[sizeof (uint16_t)] << 18; hash += hash >> 11; break; case 2: hash += get16bits (data); hash ^= hash << 11; hash += hash >> 17; break; case 1: hash += *data; hash ^= hash << 10; hash += hash >> 1; } /* Force "avalanching" of final 127 bits */ hash ^= hash << 3; hash += hash >> 5; hash ^= hash << 4; hash += hash >> 17; hash ^= hash << 25; hash += hash >> 6; return hash; } /** Thomas Wang's 64-to-32-bit mix hash based on Robert Jenkin's hash http://www.concentric.net/~ttwang/tech/inthash.htm Found by Morgan to produce the best net performance for building tables from Vector4int16 */ inline uint32_t wangHash6432Shift(int64 key) { key = (~key) + (key << 18); // key = (key << 18) - key - 1; key = key ^ (key >> 31); key = key * 21; // key = (key + (key << 2)) + (key << 4); key = key ^ (key >> 11); key = key + (key << 6); return uint32_t(key) ^ uint32_t(key >> 22); } } #undef get16bits /** Must be specialized for custom types. @see G3D::Table for specialization requirements. */ template struct HashTrait{}; template struct HashTrait { static size_t hashCode(const void* k) { return reinterpret_cast(k) >> 1; } }; /** For use with \code Table \endcode. */ template <> struct HashTrait { static size_t hashCode(const std::type_info* const t) { # ifdef _MSC_VER return t->hash_code(); # else return reinterpret_cast(t) >> 1; # endif } }; template <> struct HashTrait { static size_t hashCode(G3D::int16 k) { return static_cast(k); } }; template <> struct HashTrait { static size_t hashCode(G3D::uint16 k) { return static_cast(k); } }; template <> struct HashTrait { static size_t hashCode(G3D::int32 k) { return static_cast(k); } }; template <> struct HashTrait { static size_t hashCode(G3D::uint32 k) { return static_cast(k); } }; #ifdef G3D_OSX template <> struct HashTrait { static size_t hashCode(G3D::uint32 k) { return static_cast(k); } }; #endif template <> struct HashTrait { static size_t hashCode(G3D::uint64 k) { return static_cast(k) ^ static_cast(k >> 32); } }; template <> struct HashTrait { static size_t hashCode(G3D::int64 k) { return HashTrait::hashCode(G3D::uint64(k)); } }; template <> struct HashTrait { static size_t hashCode(const std::string& k) { return G3D::superFastHash(k.c_str(), k.size()); //return static_cast(G3D::Crypto::crc32(k.c_str(), k.size())); } }; template <> struct HashTrait { static size_t hashCode(G3D::uint128 key) { return G3D::superFastHash(&key, sizeof(key)); //return HashTrait::hashCode(key.hi) ^ HashTrait::hashCode(key.lo); #if 0 // Really slow under gcc // Use the FNV-1 hash (http://isthe.com/chongo/tech/comp/fnv/#FNV-1). static const G3D::uint128 FNV_PRIME_128(1 << 24, 0x159); static const G3D::uint128 FNV_OFFSET_128(0xCF470AAC6CB293D2ULL, 0xF52F88BF32307F8FULL); G3D::uint128 hash = FNV_OFFSET_128; G3D::uint128 mask(0, 0xFF); for (int i = 0; i < 16; ++i) { hash *= FNV_PRIME_128; hash ^= (mask & key); key >>= 8; } G3D::uint64 foldedHash = hash.hi ^ hash.lo; return static_cast((foldedHash >> 32) ^ (foldedHash & 0xFFFFFFFF)); #endif } }; #endif