Browse Source

Merge pull request #2309 from eugeneko/hash-reverse

Add StringHash reverse support.
Eugene Kozlov 7 years ago
parent
commit
47aba2aaa2

+ 1 - 0
CMake/Modules/UrhoCommon.cmake

@@ -185,6 +185,7 @@ if (CMAKE_PROJECT_NAME STREQUAL Urho3D)
         # It is not possible to turn SSE off on 64-bit MSVC and it appears it is also not able to do so safely on 64-bit GCC
         cmake_dependent_option (URHO3D_SSE "Enable SIMD instruction set (32-bit Web and Intel platforms only, including Android on Intel Atom); default to true on Intel and false on Web platform; the effective SSE level could be higher, see also URHO3D_DEPLOYMENT_TARGET and CMAKE_OSX_DEPLOYMENT_TARGET build options" "${URHO3D_DEFAULT_SIMD}" "NOT URHO3D_64BIT" TRUE)
     endif ()
+    option (URHO3D_HASH_DEBUG "Enable StringHash reversing and hash collision detection at the expense of memory and performance penalty" FALSE)
     cmake_dependent_option (URHO3D_3DNOW "Enable 3DNow! instruction set (Linux platform only); should only be used for older CPU with (legacy) 3DNow! support" "${HAVE_3DNOW}" "X86 AND CMAKE_SYSTEM_NAME STREQUAL Linux AND NOT URHO3D_SSE" FALSE)
     cmake_dependent_option (URHO3D_MMX "Enable MMX instruction set (32-bit Linux platform only); the MMX is effectively enabled when 3DNow! or SSE is enabled; should only be used for older CPU with MMX support" "${HAVE_MMX}" "X86 AND CMAKE_SYSTEM_NAME STREQUAL Linux AND NOT URHO3D_64BIT AND NOT URHO3D_SSE AND NOT URHO3D_3DNOW" FALSE)
     # For completeness sake - this option is intentionally not documented as we do not officially support PowerPC (yet)

+ 1 - 0
Docs/GettingStarted.dox

@@ -129,6 +129,7 @@ A number of build options can be defined when invoking the build scripts or when
 |URHO3D_SSE           |*|Enable SIMD instruction set (32-bit Web and Intel platforms only, including Android on Intel Atom); default to true on Intel and false on Web platform; the effective SSE level could be higher, see also URHO3D_DEPLOYMENT_TARGET and CMAKE_OSX_DEPLOYMENT_TARGET build options|
 |URHO3D_MINIDUMPS     |1|Enable minidumps on crash (VS only)|
 |URHO3D_FILEWATCHER   |1|Enable filewatcher support|
+|URHO3D_HASH_DEBUG    |0|Enable StringHash reversing and hash collision detection at the expense of memory and performance penalty|
 |URHO3D_PACKAGING     |0|Enable resources packaging support|
 |URHO3D_PROFILING     |1|Enable profiling support|
 |URHO3D_LOGGING       |1|Enable logging support|

+ 1 - 1
Source/Urho3D/AngelScript/CoreAPI.cpp

@@ -950,7 +950,7 @@ static bool HasSubscribedToSenderEvent(Object* sender, const String& eventType)
 
 static void RegisterEventName(const String& eventName)
 {
-    EventNameRegistrar::RegisterEventName(eventName.CString());
+    GetEventNameRegister().RegisterString(eventName.CString());
 }
 
 static Object* GetEventSender()

+ 3 - 0
Source/Urho3D/CMakeLists.txt

@@ -101,6 +101,9 @@ if (WIN32)
         add_definitions (-DHAVE_RTL_OSVERSIONINFOW)
     endif ()
 endif ()
+if (URHO3D_HASH_DEBUG)
+    add_definitions (-DURHO3D_HASH_DEBUG)
+endif ()
 
 # Define source files
 foreach (DIR IK Navigation Network Physics Urho2D WebP)

+ 6 - 0
Source/Urho3D/Container/Str.h

@@ -36,6 +36,12 @@ static const int MATRIX_CONVERSION_BUFFER_LENGTH = 256;
 
 class WString;
 
+class StringHash;
+template <class T, class U> class HashMap;
+
+/// Map of strings.
+using StringMap = HashMap<StringHash, String>;
+
 /// %String class.
 class URHO3D_API String
 {

+ 1 - 1
Source/Urho3D/Core/EventProfiler.h

@@ -33,7 +33,7 @@ class URHO3D_API EventProfilerBlock : public ProfilerBlock
 public:
     /// Construct with the specified parent block and event ID.
     EventProfilerBlock(EventProfilerBlock* parent, StringHash eventID) :
-        ProfilerBlock(parent, EventNameRegistrar::GetEventName(eventID).CString()),
+        ProfilerBlock(parent, GetEventNameRegister().GetString(eventID).CString()),
         eventID_(eventID)
     {
     }

+ 3 - 16
Source/Urho3D/Core/Object.cpp

@@ -530,23 +530,10 @@ void Object::RemoveEventSender(Object* sender)
     }
 }
 
-Urho3D::StringHash EventNameRegistrar::RegisterEventName(const char* eventName)
+StringHashRegister& GetEventNameRegister()
 {
-    StringHash id(eventName);
-    GetEventNameMap()[id] = eventName;
-    return id;
-}
-
-const String& EventNameRegistrar::GetEventName(StringHash eventID)
-{
-    HashMap<StringHash, String>::ConstIterator it = GetEventNameMap().Find(eventID);
-    return  it != GetEventNameMap().End() ? it->second_ : String::EMPTY ;
-}
-
-HashMap<StringHash, String>& EventNameRegistrar::GetEventNameMap()
-{
-    static HashMap<StringHash, String> eventNames_;
-    return eventNames_;
+    static StringHashRegister eventNameRegister(false /*non thread safe*/);
+    return eventNameRegister;
 }
 
 }

+ 4 - 11
Source/Urho3D/Core/Object.h

@@ -23,6 +23,7 @@
 #pragma once
 
 #include "../Container/LinkedList.h"
+#include "../Core/StringHashRegister.h"
 #include "../Core/Variant.h"
 #include <functional>
 #include <utility>
@@ -354,19 +355,11 @@ private:
     std::function<void(StringHash, VariantMap&)> function_;
 };
 
-/// Register event names.
-struct URHO3D_API EventNameRegistrar
-{
-    /// Register an event name for hash reverse mapping.
-    static StringHash RegisterEventName(const char* eventName);
-    /// Return Event name or empty string if not found.
-    static const String& GetEventName(StringHash eventID);
-    /// Return Event name map.
-    static HashMap<StringHash, String>& GetEventNameMap();
-};
+/// Get register of event names.
+URHO3D_API StringHashRegister& GetEventNameRegister();
 
 /// Describe an event's hash ID and begin a namespace in which to define its parameters.
-#define URHO3D_EVENT(eventID, eventName) static const Urho3D::StringHash eventID(Urho3D::EventNameRegistrar::RegisterEventName(#eventName)); namespace eventName
+#define URHO3D_EVENT(eventID, eventName) static const Urho3D::StringHash eventID(Urho3D::GetEventNameRegister().RegisterString(#eventName)); namespace eventName
 /// Describe an event's parameter hash ID. Should be used inside an event namespace.
 #define URHO3D_PARAM(paramID, paramName) static const Urho3D::StringHash paramID(#paramName)
 /// Convenience macro to construct an EventHandler that points to a receiver object and its member function.

+ 108 - 0
Source/Urho3D/Core/StringHashRegister.cpp

@@ -0,0 +1,108 @@
+//
+// Copyright (c) 2008-2018 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "../Precompiled.h"
+
+#include "../Core/StringHashRegister.h"
+#include "../Core/Mutex.h"
+#include "../IO/Log.h"
+
+#include <cstdio>
+
+#include "../DebugNew.h"
+
+namespace Urho3D
+{
+
+StringHashRegister::StringHashRegister(bool threadSafe)
+{
+    if (threadSafe)
+        mutex_ = MakeUnique<Mutex>();
+}
+
+
+StringHashRegister::~StringHashRegister()       // NOLINT(hicpp-use-equals-default, modernize-use-equals-default)
+{
+    // Keep destructor here to let mutex_ destruct
+}
+
+StringHash StringHashRegister::RegisterString(const StringHash& hash, const char* string)
+{
+    if (mutex_)
+        mutex_->Acquire();
+
+    auto iter = map_.Find(hash);
+    if (iter == map_.End())
+    {
+        map_.Populate(hash, string);
+    }
+    else if (iter->second_.Compare(string, false) != 0)
+    {
+        URHO3D_LOGWARNINGF("StringHash collision detected! Both \"%s\" and \"%s\" have hash #%s",
+            string, iter->second_.CString(), hash.ToString().CString());
+    }
+
+    if (mutex_)
+        mutex_->Release();
+
+    return hash;
+}
+
+StringHash StringHashRegister::RegisterString(const char* string)
+{
+    StringHash hash(string);
+    return RegisterString(hash, string);
+}
+
+String StringHashRegister::GetStringCopy(const StringHash& hash) const
+{
+    if (mutex_)
+        mutex_->Acquire();
+
+    const String copy = GetString(hash);
+
+    if (mutex_)
+        mutex_->Release();
+
+    return copy;
+}
+
+bool StringHashRegister::Contains(const StringHash& hash) const
+{
+    if (mutex_)
+        mutex_->Acquire();
+
+    const bool contains = map_.Contains(hash);
+
+    if (mutex_)
+        mutex_->Release();
+
+    return contains;
+}
+
+const String& StringHashRegister::GetString(const StringHash& hash) const
+{
+    auto iter = map_.Find(hash);
+    return iter == map_.End() ? String::EMPTY : iter->second_;
+}
+
+}

+ 64 - 0
Source/Urho3D/Core/StringHashRegister.h

@@ -0,0 +1,64 @@
+//
+// Copyright (c) 2008-2018 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "../Container/HashMap.h"
+#include "../Container/Ptr.h"
+#include "../Math/StringHash.h"
+
+namespace Urho3D
+{
+
+class Mutex;
+
+/// Helper class used for StringHash reversing.
+class URHO3D_API StringHashRegister
+{
+public:
+    /// Construct. threadSafe controls whether the RegisterString and GetStringCopy are thread-safe.
+    StringHashRegister(bool threadSafe);
+    /// Destruct.
+    ~StringHashRegister();
+
+    /// Register string for hash reverse mapping. Could be used from StringHash ctor.
+    StringHash RegisterString(const StringHash& hash, const char* string);
+    /// Register string for hash reverse mapping.
+    StringHash RegisterString(const char* string);
+    /// Return string for given StringHash. Return empty string if not found.
+    String GetStringCopy(const StringHash& hash) const;
+    /// Return whether the string in contained in the register.
+    bool Contains(const StringHash& hash) const;
+
+    /// Return String for given StringHash. Return value is unsafe to use if RegisterString is called from other threads.
+    const String& GetString(const StringHash& hash) const;
+    /// Return map of hashes. Return value is unsafe to use if RegisterString is called from other threads.
+    const StringMap& GetInternalMap() const { return map_; }
+
+private:
+    /// Hash to string map.
+    StringMap map_;
+    /// Mutex.
+    UniquePtr<Mutex> mutex_;
+};
+
+}

+ 1 - 1
Source/Urho3D/LuaScript/pkgs/LuaScript/LuaScript.pkg

@@ -26,7 +26,7 @@ ${
 
 static void RegisterEventName(const String eventName)
 {
-    EventNameRegistrar::RegisterEventName(eventName.CString());
+    GetEventNameRegister().RegisterString(eventName.CString());
 }
 
 static LuaScript* GetLuaScript(lua_State* L)

+ 42 - 0
Source/Urho3D/Math/StringHash.cpp

@@ -24,6 +24,9 @@
 
 #include "../Math/MathDefs.h"
 #include "../Math/StringHash.h"
+#include "../Container/HashMap.h"
+#include "../Core/StringHashRegister.h"
+#include "../IO/Log.h"
 
 #include <cstdio>
 
@@ -32,16 +35,37 @@
 namespace Urho3D
 {
 
+#ifdef URHO3D_HASH_DEBUG
+
+// Expose map to let Visual Studio debugger access it if Urho3D is linked statically.
+const StringMap* hashReverseMap = nullptr;
+
+// Hide static global variables in functions to ensure initialization order.
+static StringHashRegister& GetGlobalStringHashRegister()
+{
+    static StringHashRegister stringHashRegister(true /*thread safe*/ );
+    hashReverseMap = &stringHashRegister.GetInternalMap();
+    return stringHashRegister;
+}
+
+#endif
+
 const StringHash StringHash::ZERO;
 
 StringHash::StringHash(const char* str) noexcept :
     value_(Calculate(str))
 {
+#ifdef URHO3D_HASH_DEBUG
+    Urho3D::GetGlobalStringHashRegister().RegisterString(*this, str);
+#endif
 }
 
 StringHash::StringHash(const String& str) noexcept :
     value_(Calculate(str.CString()))
 {
+#ifdef URHO3D_HASH_DEBUG
+    Urho3D::GetGlobalStringHashRegister().RegisterString(*this, str.CString());
+#endif
 }
 
 unsigned StringHash::Calculate(const char* str, unsigned hash)
@@ -60,6 +84,15 @@ unsigned StringHash::Calculate(const char* str, unsigned hash)
     return hash;
 }
 
+StringHashRegister* StringHash::GetGlobalStringHashRegister()
+{
+#ifdef URHO3D_HASH_DEBUG
+    return &Urho3D::GetGlobalStringHashRegister();
+#else
+    return nullptr;
+#endif
+}
+
 String StringHash::ToString() const
 {
     char tempBuffer[CONVERSION_BUFFER_LENGTH];
@@ -67,4 +100,13 @@ String StringHash::ToString() const
     return String(tempBuffer);
 }
 
+String StringHash::Reverse() const
+{
+#ifdef URHO3D_HASH_DEBUG
+    return Urho3D::GetGlobalStringHashRegister().GetStringCopy(*this);
+#else
+    return String::EMPTY;
+#endif
+}
+
 }

+ 8 - 0
Source/Urho3D/Math/StringHash.h

@@ -27,6 +27,8 @@
 namespace Urho3D
 {
 
+class StringHashRegister;
+
 /// 32-bit hash value for a string.
 class URHO3D_API StringHash
 {
@@ -90,12 +92,18 @@ public:
     /// Return as string.
     String ToString() const;
 
+    /// Return string which has specific hash value. Return first string if many (in order of calculation). Use for debug purposes only. Return empty string if URHO3D_HASH_DEBUG is off.
+    String Reverse() const;
+
     /// Return hash value for HashSet & HashMap.
     unsigned ToHash() const { return value_; }
 
     /// Calculate hash value case-insensitively from a C string.
     static unsigned Calculate(const char* str, unsigned hash = 0);
 
+    /// Get global StringHashRegister. Use for debug purposes only. Return nullptr if URHO3D_HASH_DEBUG is off.
+    static StringHashRegister* GetGlobalStringHashRegister();
+
     /// Zero hash.
     static const StringHash ZERO;