diff --git a/include/bx/hash.h b/include/bx/hash.h index 3f68f5d..cb6fe18 100644 --- a/include/bx/hash.h +++ b/include/bx/hash.h @@ -11,45 +11,7 @@ namespace bx { - /// MurmurHash2 was written by Austin Appleby, and is placed in the public - /// domain. The author hereby disclaims copyright to this source code. - /// - class HashMurmur2A - { - public: - /// - void begin(uint32_t _seed = 0); - - /// - void add(const void* _data, int _len); - - /// - template - void add(Ty _value); - - /// - uint32_t end(); - - private: - /// - void addAligned(const void* _data, int _len); - - /// - void addUnaligned(const void* _data, int _len); - - /// - static void readUnaligned(const void* _data, uint32_t& _out); - - /// - void mixTail(const uint8_t*& _data, int& _len); - - uint32_t m_hash; - uint32_t m_tail; - uint32_t m_count; - uint32_t m_size; - }; - - /// + /// 32-bit Adler checksum hash. class HashAdler32 { public: @@ -57,11 +19,17 @@ namespace bx void begin(); /// - void add(const void* _data, int _len); + void add(const void* _data, int32_t _len); + + /// + void add(const char* _data); + + /// + void add(const StringView& _data); /// template - void add(Ty _value); + void add(const Ty& _data); /// uint32_t end(); @@ -71,7 +39,7 @@ namespace bx uint32_t m_b; }; - /// + /// 32-bit cyclic redundancy checksum hash. class HashCrc32 { public: @@ -88,11 +56,17 @@ namespace bx void begin(Enum _type = Ieee); /// - void add(const void* _data, int _len); + void add(const void* _data, int32_t _len); + + /// + void add(const char* _data); + + /// + void add(const StringView& _data); /// template - void add(Ty _value); + void add(const Ty& _data); /// uint32_t end(); @@ -102,21 +76,51 @@ namespace bx uint32_t m_hash; }; + /// 32-bit multiply and rotate hash. + class HashMurmur2A + { + public: + /// + void begin(uint32_t _seed = 0); + + /// + void add(const void* _data, int32_t _len); + + /// + void add(const char* _data); + + /// + void add(const StringView& _data); + + /// + template + void add(const Ty& _data); + + /// + uint32_t end(); + + private: + uint32_t m_hash; + uint32_t m_tail; + uint32_t m_count; + uint32_t m_size; + }; + /// template uint32_t hash(const void* _data, uint32_t _size); /// - template - uint32_t hash(const Ty& _data); + template + uint32_t hash(const char* _data); /// template uint32_t hash(const StringView& _data); /// - template - uint32_t hash(const char* _data); + template + uint32_t hash(const Ty& _data); } // namespace bx diff --git a/include/bx/inline/hash.inl b/include/bx/inline/hash.inl index 8f1f9b4..bad8ba0 100644 --- a/include/bx/inline/hash.inl +++ b/include/bx/inline/hash.inl @@ -9,142 +9,16 @@ namespace bx { -#define MURMUR_M 0x5bd1e995 -#define MURMUR_R 24 -#define mmix(_h, _k) { _k *= MURMUR_M; _k ^= _k >> MURMUR_R; _k *= MURMUR_M; _h *= MURMUR_M; _h ^= _k; } - - inline void HashMurmur2A::begin(uint32_t _seed) - { - m_hash = _seed; - m_tail = 0; - m_count = 0; - m_size = 0; - } - - inline void HashMurmur2A::add(const void* _data, int _len) - { - if (BX_UNLIKELY(!isAligned(_data, 4) ) ) - { - addUnaligned(_data, _len); - return; - } - - addAligned(_data, _len); - } - - inline void HashMurmur2A::addAligned(const void* _data, int _len) - { - const uint8_t* data = (const uint8_t*)_data; - m_size += _len; - - mixTail(data, _len); - - while(_len >= 4) - { - uint32_t kk = *(uint32_t*)data; - - mmix(m_hash, kk); - - data += 4; - _len -= 4; - } - - mixTail(data, _len); - } - - inline void HashMurmur2A::addUnaligned(const void* _data, int _len) - { - const uint8_t* data = (const uint8_t*)_data; - m_size += _len; - - mixTail(data, _len); - - while(_len >= 4) - { - uint32_t kk; - readUnaligned(data, kk); - - mmix(m_hash, kk); - - data += 4; - _len -= 4; - } - - mixTail(data, _len); - } - - template - inline void HashMurmur2A::add(Ty _value) - { - add(&_value, sizeof(Ty) ); - } - - inline uint32_t HashMurmur2A::end() - { - mmix(m_hash, m_tail); - mmix(m_hash, m_size); - - m_hash ^= m_hash >> 13; - m_hash *= MURMUR_M; - m_hash ^= m_hash >> 15; - - return m_hash; - } - - inline void HashMurmur2A::readUnaligned(const void* _data, uint32_t& _out) - { - const uint8_t* data = (const uint8_t*)_data; - if (BX_ENABLED(BX_CPU_ENDIAN_BIG) ) - { - _out = 0 - | data[0]<<24 - | data[1]<<16 - | data[2]<<8 - | data[3] - ; - } - else - { - _out = 0 - | data[0] - | data[1]<<8 - | data[2]<<16 - | data[3]<<24 - ; - } - } - - inline void HashMurmur2A::mixTail(const uint8_t*& _data, int& _len) - { - while( _len && ((_len<4) || m_count) ) - { - m_tail |= (*_data++) << (m_count * 8); - - m_count++; - _len--; - - if(m_count == 4) - { - mmix(m_hash, m_tail); - m_tail = 0; - m_count = 0; - } - } - } - -#undef MURMUR_M -#undef MURMUR_R -#undef mmix - inline void HashAdler32::begin() { m_a = 1; m_b = 0; } - inline void HashAdler32::add(const void* _data, int _len) + inline void HashAdler32::add(const void* _data, int32_t _len) { - const uint32_t kModAdler = 65521; + constexpr uint32_t kModAdler = 65521; + const uint8_t* data = (const uint8_t*)_data; for (; _len != 0; --_len) { @@ -153,10 +27,20 @@ namespace bx } } - template - inline void HashAdler32::add(Ty _value) + inline void HashAdler32::add(const char* _data) { - add(&_value, sizeof(Ty) ); + return add(StringView(_data) ); + } + + inline void HashAdler32::add(const StringView& _data) + { + return add(_data.getPtr(), _data.getLength() ); + } + + template + inline void HashAdler32::add(const Ty& _data) + { + add(&_data, sizeof(Ty) ); } inline uint32_t HashAdler32::end() @@ -164,10 +48,20 @@ namespace bx return m_a | (m_b<<16); } - template - inline void HashCrc32::add(Ty _value) + inline void HashCrc32::add(const char* _data) { - add(&_value, sizeof(Ty) ); + return add(StringView(_data) ); + } + + inline void HashCrc32::add(const StringView& _data) + { + return add(_data.getPtr(), _data.getLength() ); + } + + template + inline void HashCrc32::add(const Ty& _data) + { + add(&_data, sizeof(Ty) ); } inline uint32_t HashCrc32::end() @@ -176,15 +70,51 @@ namespace bx return m_hash; } + inline void HashMurmur2A::begin(uint32_t _seed) + { + m_hash = _seed; + m_tail = 0; + m_count = 0; + m_size = 0; + } + + inline void HashMurmur2A::add(const char* _data) + { + return add(StringView(_data) ); + } + + inline void HashMurmur2A::add(const StringView& _data) + { + return add(_data.getPtr(), _data.getLength() ); + } + + template + inline void HashMurmur2A::add(const Ty& _data) + { + add(&_data, sizeof(Ty) ); + } + template inline uint32_t hash(const void* _data, uint32_t _size) { HashT hh; hh.begin(); - hh.add(_data, (int)_size); + hh.add(_data, (int32_t)_size); return hh.end(); } + template + inline uint32_t hash(const char* _data) + { + return hash(StringView(_data) ); + } + + template + inline uint32_t hash(const StringView& _data) + { + return hash(_data.getPtr(), _data.getLength() ); + } + template inline uint32_t hash(const Ty& _data) { @@ -192,16 +122,4 @@ namespace bx return hash(&_data, sizeof(Ty) ); } - template - inline uint32_t hash(const StringView& _data) - { - return hash(_data.getPtr(), _data.getLength() ); - } - - template - inline uint32_t hash(const char* _data) - { - return hash(StringView(_data) ); - } - } // namespace bx diff --git a/src/hash.cpp b/src/hash.cpp index dfab8a6..d61d8df 100644 --- a/src/hash.cpp +++ b/src/hash.cpp @@ -133,7 +133,7 @@ void HashCrc32::begin(Enum _type) m_table = s_crcTable[_type]; } -void HashCrc32::add(const void* _data, int _len) +void HashCrc32::add(const void* _data, int32_t _len) { const uint8_t* data = (const uint8_t*)_data; @@ -147,4 +147,136 @@ void HashCrc32::add(const void* _data, int _len) m_hash = hash; } +struct HashMurmur2APod +{ + uint32_t m_hash; + uint32_t m_tail; + uint32_t m_count; + uint32_t m_size; +}; +BX_STATIC_ASSERT(sizeof(HashMurmur2A) == sizeof(HashMurmur2APod) ); + +BX_FORCE_INLINE void mmix(uint32_t& _h, uint32_t& _k) +{ + constexpr uint32_t kMurmurMul = 0x5bd1e995; + constexpr uint32_t kMurmurRightShift = 24; + + _k *= kMurmurMul; + _k ^= _k >> kMurmurRightShift; + _k *= kMurmurMul; + _h *= kMurmurMul; + _h ^= _k; +} + +static void mixTail(HashMurmur2APod& _self, const uint8_t*& _data, int32_t& _len) +{ + while (_len + && ( (_len<4) || _self.m_count) + ) + { + _self.m_tail |= (*_data++) << (_self.m_count * 8); + + _self.m_count++; + _len--; + + if (_self.m_count == 4) + { + mmix(_self.m_hash, _self.m_tail); + _self.m_tail = 0; + _self.m_count = 0; + } + } +} + +static void addAligned(HashMurmur2APod& _self, const void* _data, int32_t _len) +{ + const uint8_t* data = (const uint8_t*)_data; + _self.m_size += _len; + + mixTail(_self, data, _len); + + while(_len >= 4) + { + uint32_t kk = *(uint32_t*)data; + + mmix(_self.m_hash, kk); + + data += 4; + _len -= 4; + } + + mixTail(_self, data, _len); +} + +BX_FORCE_INLINE void readUnaligned(const void* _data, uint32_t& _out) +{ + const uint8_t* data = (const uint8_t*)_data; + if (BX_ENABLED(BX_CPU_ENDIAN_BIG) ) + { + _out = 0 + | data[0]<<24 + | data[1]<<16 + | data[2]<<8 + | data[3] + ; + } + else + { + _out = 0 + | data[0] + | data[1]<<8 + | data[2]<<16 + | data[3]<<24 + ; + } +} + +static void addUnaligned(HashMurmur2APod& _self, const void* _data, int32_t _len) +{ + const uint8_t* data = (const uint8_t*)_data; + _self.m_size += _len; + + mixTail(_self, data, _len); + + while(_len >= 4) + { + uint32_t kk; + readUnaligned(data, kk); + + mmix(_self.m_hash, kk); + + data += 4; + _len -= 4; + } + + mixTail(_self, data, _len); +} + +void HashMurmur2A::add(const void* _data, int32_t _len) +{ + HashMurmur2APod& self = *(HashMurmur2APod*)this; + + if (BX_UNLIKELY(!isAligned(_data, 4) ) ) + { + addUnaligned(self, _data, _len); + return; + } + + addAligned(self, _data, _len); +} + +uint32_t HashMurmur2A::end() +{ + constexpr uint32_t kMurmurMul = 0x5bd1e995; + + mmix(m_hash, m_tail); + mmix(m_hash, m_size); + + m_hash ^= m_hash >> 13; + m_hash *= kMurmurMul; + m_hash ^= m_hash >> 15; + + return m_hash; +} + } // namespace bx diff --git a/tests/hash_test.cpp b/tests/hash_test.cpp index 0aa8324..4a7ee81 100644 --- a/tests/hash_test.cpp +++ b/tests/hash_test.cpp @@ -93,7 +93,7 @@ TEST_CASE("HashAdler32", "") #define mmix(h,k) { k *= m; k ^= k >> r; k *= m; h *= m; h ^= k; } -uint32_t MurmurHash2A ( const void * key, int len, uint32_t seed ) +uint32_t MurmurHash2A(const void * key, int len, uint32_t seed = 0) { const uint32_t m = 0x5bd1e995; const int r = 24; @@ -148,3 +148,12 @@ TEST_CASE("HashMurmur2A", "") REQUIRE(test.murmur2a == MurmurHash2A(test.input, bx::strLen(test.input), seed) ); } } + +TEST_CASE("HashMurmur2A-Separate-Add", "") +{ + bx::HashMurmur2A hash; + hash.begin(); + hash.add("0123456789"); + hash.add("abvgd012345"); + REQUIRE(MurmurHash2A("0123456789abvgd012345", 21) == hash.end() ); +}