Browse Source

added core/hashed_string

Michele Caini 8 years ago
parent
commit
c630cb1de2
2 changed files with 156 additions and 0 deletions
  1. 119 0
      src/entt/core/hashed_string.hpp
  2. 37 0
      test/entt/core/hashed_string.cpp

+ 119 - 0
src/entt/core/hashed_string.hpp

@@ -0,0 +1,119 @@
+#ifndef ENTT_CORE_HASHED_STRING_HPP
+#define ENTT_CORE_HASHED_STRING_HPP
+
+
+#include <cstdint>
+
+
+namespace entt {
+
+
+/**
+ * @brief Zero overhead resource identifier.
+ *
+ * A hashed string is a compile-time tool that allows users to use
+ * human-readable identifers in the codebase while using their numeric
+ * counterparts at runtime.<br/>
+ * Because of that, a hashed string can also be used in constant expressions if
+ * required.
+ */
+class HashedString final {
+    struct ConstCharWrapper final {
+        // non-explicit constructor on purpose
+        constexpr ConstCharWrapper(const char *str) noexcept: str{str} {}
+        const char *str;
+    };
+
+    static constexpr std::uint64_t offset = 14695981039346656037u;
+    static constexpr std::uint64_t prime = 1099511628211u;
+
+    // Fowler–Noll–Vo hash function v. 1a - the good
+    static constexpr std::uint64_t helper(std::uint64_t partial, const char *str) noexcept {
+        return str[0] == 0 ? partial : helper((partial^str[0])*prime, str+1);
+    }
+
+public:
+    /*! @brief Unsigned integer type. */
+    using hash_type = std::uint64_t;
+
+    /**
+     * @brief Constructs a hashed string from an array of const chars.
+     *
+     * Forcing template resolution avoids implicit conversions. An
+     * human-readable identifier can be anything but a plain, old bunch of
+     * characters.<br/>
+     * Example of use:
+     * @code{.cpp}
+     * HashedString sh{"my.png"};
+     * @endcode
+     *
+     * @tparam N Number of characters of the identifier.
+     * @param str Human-readable identifer.
+     */
+    template <std::size_t N>
+    constexpr HashedString(const char (&str)[N]) noexcept
+        : hash{helper(offset, str)}, str{str}
+    {}
+
+    /**
+     * @brief Explicit constructor on purpose to avoid constructing a hashed
+     * string directly from a `const char *`.
+     *
+     * @param wrapper Helps achieving the purpose by relying on overloading.
+     */
+    explicit constexpr HashedString(ConstCharWrapper wrapper) noexcept
+        : hash{helper(offset, wrapper.str)}, str{wrapper.str}
+    {}
+
+    /*! @brief Default copy constructor. */
+    constexpr HashedString(const HashedString &) noexcept = default;
+    /*! @brief Default move constructor. */
+    constexpr HashedString(HashedString &&) noexcept = default;
+
+    /*! @brief Default copy assignment operator. @return This hashed string. */
+    constexpr HashedString & operator=(const HashedString &) noexcept = default;
+    /*! @brief Default move assignment operator. @return This hashed string. */
+    constexpr HashedString & operator=(HashedString &&) noexcept = default;
+
+    /**
+     * @brief Returns the human-readable representation of a hashed string.
+     * @return The string used to initialize the instance.
+     */
+    constexpr operator const char *() const noexcept { return str; }
+
+    /**
+     * @brief Returns the numeric representation of a hashed string.
+     * @return The numeric representation of the instance.
+     */
+    constexpr operator hash_type() const noexcept { return hash; }
+
+    /**
+     * @brief Compares two hashed strings.
+     * @param other Hashed string with which to compare.
+     * @return True if the two hashed strings are identical, false otherwise.
+     */
+    constexpr bool operator==(const HashedString &other) const noexcept {
+        return hash == other.hash;
+    }
+
+private:
+    hash_type hash;
+    const char *str;
+};
+
+
+/**
+ * @brief Compares two hashed strings.
+ * @param lhs A valid hashed string.
+ * @param rhs A valid hashed string.
+ * @return True if the two hashed strings are identical, false otherwise.
+ */
+constexpr bool operator!=(const HashedString &lhs, const HashedString &rhs) noexcept {
+    return !(lhs == rhs);
+}
+
+
+}
+
+
+#endif // ENTT_CORE_HASHED_STRING_HPP

+ 37 - 0
test/entt/core/hashed_string.cpp

@@ -0,0 +1,37 @@
+#include <gtest/gtest.h>
+#include <entt/core/hashed_string.hpp>
+
+constexpr bool check(const char *str) {
+    using hash_type = entt::HashedString::hash_type;
+
+    return (static_cast<hash_type>(entt::HashedString{str}) == entt::HashedString{str}
+            && static_cast<const char *>(entt::HashedString{str}) == str
+            && entt::HashedString{str} == entt::HashedString{str}
+            && !(entt::HashedString{str} != entt::HashedString{str}));
+}
+
+TEST(HashedString, Constexprness) {
+    // how would you test a constepxr otherwise?
+    static_assert(check("foobar"), "!");
+    ASSERT_TRUE(true);
+}
+
+TEST(HashedString, Functionalities) {
+    using hash_type = entt::HashedString::hash_type;
+
+    const char *bar = "bar";
+
+    auto fooHs = entt::HashedString("foo");
+    auto barHs = entt::HashedString(bar);
+
+    ASSERT_NE(static_cast<hash_type>(fooHs), static_cast<hash_type>(barHs));
+    ASSERT_EQ(static_cast<const char *>(fooHs), "foo");
+    ASSERT_EQ(static_cast<const char *>(barHs), bar);
+
+    ASSERT_TRUE(fooHs == fooHs);
+    ASSERT_FALSE(fooHs == barHs);
+
+    entt::HashedString hs{"foobar"};
+
+    ASSERT_EQ(static_cast<hash_type>(hs), 0x85944171f73967e8);
+}