]> git.tdb.fi Git - libs/core.git/blobdiff - source/core/hash.h
Add move semantics to Variant
[libs/core.git] / source / core / hash.h
index d696cae5b63542e65d6718f1012d0606b9f55833..e2201cf22110280172828fff9021d4e1daa36036 100644 (file)
 #ifndef MSP_CORE_HASH_H_
 #define MSP_CORE_HASH_H_
 
+#include <cstdint>
+#include <limits>
 #include <string>
-#include "inttypes.h"
 
 namespace Msp {
 
+template<unsigned N, unsigned L = 3+(N>8)+(N>16)+(N>32)+(N>64)>
+struct HashTraits;
+
+template<>
+struct HashTraits<64, 6>
+{
+       using HashType = uint64_t;
+       using ResultType = uint64_t;
+       static constexpr HashType prime = 1099511628211ULL;
+       static constexpr HashType offset = 14695981039346656037ULL;
+       static constexpr bool folded = false;
+};
+
+template<>
+struct HashTraits<32, 5>
+{
+       using HashType = uint32_t;
+       using ResultType = uint32_t;
+       static constexpr HashType prime = 16777619;
+       static constexpr HashType offset = 2166136261U;
+       static constexpr bool folded = false;
+};
+
+template<unsigned N>
+struct HashTraits<N, 6>
+{
+       using HashType = uint64_t;
+       using ResultType = uint64_t;
+       static constexpr bool folded = true;
+};
+
+template<unsigned N>
+struct HashTraits<N, 5>
+{
+       using HashType = uint32_t;
+       using ResultType = uint32_t;
+       static constexpr bool folded = true;
+};
+
+template<unsigned N>
+struct HashTraits<N, 4>
+{
+       using HashType = uint32_t;
+       using ResultType = uint16_t;
+       static constexpr bool folded = true;
+};
+
+template<unsigned N>
+struct HashTraits<N, 3>
+{
+       using HashType = uint32_t;
+       using ResultType = uint8_t;
+       static constexpr bool folded = true;
+};
+
 /**
-Computes a 32-bit Fowler-Noll-Vo (FNV-1a) hash.  The number of bits can be
-limited to less than 32, in which case XOR-folding is used to reduce the hash
-size.
+Computes a single round of a hash, adding one byte of data.  N must be a native
+hash size.
 */
-UInt32 hash32(const void *, unsigned, unsigned = 32);
+template<unsigned N>
+typename HashTraits<N>::ResultType hash_round(typename HashTraits<N>::HashType result, uint8_t byte)
+{
+       constexpr typename HashTraits<N>::HashType prime = HashTraits<N>::prime;
+       return (result^byte)*prime;
+}
 
 /**
-Convenience function to compute a 32-bit hash of a string.
+Updates a previously computed hash by adding bytes to it.  N must be a native
+hash size.
 */
-inline unsigned hash32(const std::string &s, unsigned b = 32)
-{ return hash32(s.data(), s.size(), b); }
+template<unsigned N>
+typename HashTraits<N>::ResultType hash_update(typename HashTraits<N>::HashType result, const void *data, std::size_t size)
+{
+       const uint8_t *ptr = static_cast<const uint8_t *>(data);
+       for(unsigned i=0; i<size; ++i)
+               result = hash_round<N>(result, *ptr++);
+       return result;
+}
+
+template<unsigned N>
+typename HashTraits<N>::ResultType hash_update(typename HashTraits<N>::HashType result, const std::string &str)
+{ return hash_update<N>(result, str.data(), str.size()); }
+
+// For some reason this causes an ambiguity error without the enable_if.
+template<unsigned N, typename T>
+typename std::enable_if<!std::is_same<T, std::string>::value, typename HashTraits<N>::ResultType>::type hash_update(typename HashTraits<N>::HashType result, const T &obj)
+{ return hash_update<N>(result, &obj, sizeof(obj)); }
 
 /**
-Computes a 64-bit Fowler-Noll-Vo (FNV-1a) hash.  Note that even if bits is
-limited to 32 or less, this does not produce the same result as hash32.
+Manually folds a hash to a smaller size.
 */
-UInt64 hash64(const void *, unsigned, unsigned = 64);
+template<unsigned N, typename T>
+typename std::enable_if<N!=std::numeric_limits<T>::digits, typename HashTraits<N>::ResultType>::type hash_fold(T hash)
+{ return (hash>>N ^ hash) & ((T(1)<<N)-1); }
+
+template<unsigned N, typename T>
+typename std::enable_if<N==std::numeric_limits<T>::digits, typename HashTraits<N>::ResultType>::type hash_fold(T hash)
+{ return hash; }
 
 /**
-Convenience function to compute a 64-bit hash of a string.
+Computes an N-bit Fowler-Noll-Vo (FNV-1a) hash.  If N is not a native hash
+size, the next larger native hash is computed and XOR-folding is applied to
+the result.
+
+http://en.wikipedia.org/wiki/Fowler-Noll-Vo_hash_function
+http://www.isthe.com/chongo/tech/comp/fnv/index.html
 */
-inline UInt64 hash64(const std::string &s, unsigned b = 64)
-{ return hash64(s.data(), s.size(), b); }
+template<unsigned N>
+typename HashTraits<N>::ResultType hash(const void *data, std::size_t size)
+{
+       constexpr unsigned hash_bits = std::numeric_limits<typename HashTraits<N>::HashType>::digits;
+       typename HashTraits<N>::HashType result = hash_update<hash_bits>(HashTraits<hash_bits>::offset, data, size);
+       if(HashTraits<N>::folded)
+               result = hash_fold<N>(result);
+       return result;
+}
 
-inline UInt32 fold32(UInt64 hash)
-{ return hash^(hash>>32); }
+/**
+Convenience function to compute an N-bit hash of a string.
+*/
+template<unsigned N>
+typename HashTraits<N>::ResultType hash(const std::string &str)
+{ return hash<N>(str.data(), str.size()); }
 
-inline UInt16 fold16(UInt64 hash)
-{ return hash^(hash>>16)^(hash>>32)^(hash>>48); }
+/**
+Convenience function to compute an N-bit hash of any object.  The entire object
+representation is hashed, including any padding bytes.
+*/
+template<unsigned N, typename T>
+typename HashTraits<N>::ResultType hash(const T &obj)
+{ return hash<N>(&obj, sizeof(obj)); }
 
 } // namespace Msp