diff --git a/headers/enduro2d/utils/_utils.hpp b/headers/enduro2d/utils/_utils.hpp index 1e858619..d89c54c3 100644 --- a/headers/enduro2d/utils/_utils.hpp +++ b/headers/enduro2d/utils/_utils.hpp @@ -69,3 +69,56 @@ namespace e2d ~noncopyable() = default; }; } + +namespace e2d { namespace utils +{ + // + // sdbm_hash + // + + namespace impl + { + // Inspired by: + // http://www.cse.yorku.ca/~oz/hash.html + + template < typename Char > + u32 sdbm_hash_impl(u32 init, const Char* str) noexcept { + while ( Char c = *str++ ) { + init = c + (init << 6u) + (init << 16u) - init; + } + return init; + } + + template < typename Char > + u32 sdbm_hash_impl(u32 init, const Char* begin, const Char* const end) noexcept { + while ( begin != end ) { + init = (*begin++) + (init << 6u) + (init << 16u) - init; + } + return init; + } + } + + template < typename Char > + u32 sdbm_hash(const Char* str) noexcept { + E2D_ASSERT(str); + return impl::sdbm_hash_impl(0u, str); + } + + template < typename Char > + u32 sdbm_hash(const Char* begin, const Char* const end) noexcept { + E2D_ASSERT(begin <= end); + return impl::sdbm_hash_impl(0u, begin, end); + } + + template < typename Char > + u32 sdbm_hash(u32 init, const Char* str) noexcept { + E2D_ASSERT(str); + return impl::sdbm_hash_impl(init, str); + } + + template < typename Char > + u32 sdbm_hash(u32 init, const Char* begin, const Char* const end) noexcept { + E2D_ASSERT(begin <= end); + return impl::sdbm_hash_impl(init, begin, end); + } +}} diff --git a/headers/enduro2d/utils/strings.inl b/headers/enduro2d/utils/strings.inl index 46bfbedd..917b1800 100644 --- a/headers/enduro2d/utils/strings.inl +++ b/headers/enduro2d/utils/strings.inl @@ -127,12 +127,7 @@ namespace e2d template < typename Char > u32 basic_string_hash::calculate_hash(basic_string_view str) noexcept { - // Inspired by: - // http://www.cse.yorku.ca/~oz/hash.html - u32 hash = 0; - for ( Char c : str ) { - hash = c + (hash << 6u) + (hash << 16u) - hash; - } + u32 hash = utils::sdbm_hash(str.cbegin(), str.cend()); debug_check_collisions(hash, str); return hash; } diff --git a/untests/sources/untests_utils/_utils.cpp b/untests/sources/untests_utils/_utils.cpp index b991aef8..a367e4ad 100644 --- a/untests/sources/untests_utils/_utils.cpp +++ b/untests/sources/untests_utils/_utils.cpp @@ -6,6 +6,27 @@ #define CATCH_CONFIG_MAIN #include "_utils.hpp" +using namespace e2d; TEST_CASE("utils") { + { + REQUIRE(utils::sdbm_hash("") == 0u); + REQUIRE(utils::sdbm_hash(1u, "") == 1u); + + REQUIRE(utils::sdbm_hash(nullptr, nullptr) == 0u); + REQUIRE(utils::sdbm_hash(1u, nullptr, nullptr) == 1u); + + const char* str1 = "hello"; + const char* str2 = "hello"; + + REQUIRE(utils::sdbm_hash(str1) == utils::sdbm_hash(str2)); + REQUIRE(utils::sdbm_hash(42u, str1) == utils::sdbm_hash(42u, str2)); + + REQUIRE(utils::sdbm_hash( + str1, str1 + std::strlen(str1) + ) == utils::sdbm_hash(str2)); + REQUIRE(utils::sdbm_hash( + 42u, str1, str1 + std::strlen(str1) + ) == utils::sdbm_hash(42u, str2)); + } }