2 Commits a8989829d7 ... 911e5a1c36

Author SHA1 Message Date
  Kajetan Johannes Hammerle 911e5a1c36 Remove hash from normal strings 2 weeks ago
  Kajetan Johannes Hammerle e18779e368 Hashed string for maps 2 weeks ago

+ 1 - 0
CMakeLists.txt

@@ -61,6 +61,7 @@ set(SRC_TESTS
     "test/modules/FileReaderTests.cpp"
     "test/modules/ErrorTests.cpp"
     "test/modules/NewTests.cpp"
+    "test/modules/HashedStringTests.cpp"
 )
 
 set(SRC_PERFORMANCE

+ 2 - 0
include/core/data/List.hpp

@@ -1,8 +1,10 @@
 #ifndef CORE_LIST_HPP
 #define CORE_LIST_HPP
 
+#include "core/math/Math.hpp"
 #include "core/utils/AlignedData.hpp"
 #include "core/utils/ArrayString.hpp"
+#include "core/utils/New.hpp"
 
 namespace Core {
     template<typename T>

+ 0 - 14
include/core/utils/ArrayString.hpp

@@ -1,8 +1,6 @@
 #ifndef CORE_ARRAY_STRING_HPP
 #define CORE_ARRAY_STRING_HPP
 
-#include "core/math/Math.hpp"
-#include "core/utils/Check.hpp"
 #include "core/utils/Error.hpp"
 #include "core/utils/Meta.hpp"
 #include "core/utils/Types.hpp"
@@ -61,7 +59,6 @@ namespace Core {
     protected:
         i32 length;
         i32 capacity;
-        u32 hash;
         char* data;
 
     public:
@@ -95,7 +92,6 @@ namespace Core {
         CError toString(CharString& s) const;
         CError toString(Char32String& s) const;
         void clear();
-        u32 hashCode() const;
         CError print() const;
         CError printLine() const;
 
@@ -115,16 +111,12 @@ namespace Core {
                        const CharString& replace);
         void replace(char search, char replace);
         operator const char*() const;
-
-    private:
-        void addToHash(c32 u);
     };
 
     class Char32String {
     protected:
         i32 length;
         i32 capacity;
-        u32 hash;
         c32* data;
 
     public:
@@ -158,7 +150,6 @@ namespace Core {
         CError toString(CharString& s) const;
         CError toString(Char32String& s) const;
         void clear();
-        u32 hashCode() const;
         CError print() const;
         CError printLine() const;
 
@@ -178,9 +169,6 @@ namespace Core {
                        const Char32String& replace);
         void replace(c32 search, c32 replace);
         operator const c32*() const;
-
-    private:
-        void addToHash(c32 u);
     };
 
     template<int N, typename C, typename B>
@@ -195,14 +183,12 @@ namespace Core {
         ArrayString(const ArrayString& other) : B(data, N) {
             Core::memoryCopy(data, other.data, sizeof(data));
             B::length = other.length;
-            B::hash = other.hash;
         }
 
         ArrayString& operator=(const ArrayString& other) {
             if(this != &other) {
                 Core::memoryCopy(data, other.data, sizeof(data));
                 B::length = other.length;
-                B::hash = other.hash;
             }
             return *this;
         }

+ 65 - 0
include/core/utils/HashedString.hpp

@@ -0,0 +1,65 @@
+#ifndef CORE_HASHED_STRING_HPP
+#define CORE_HASHED_STRING_HPP
+
+#include <string.h>
+
+#include "core/utils/Types.hpp"
+
+namespace Core {
+    struct StringHash {
+        i32 length = 0;
+        u32 hash = 0;
+    };
+
+    constexpr StringHash hashString(const char* s) {
+        StringHash h;
+        while(*s != '\0') {
+            h.length++;
+            h.hash = 2120251889u * h.hash + static_cast<u32>(*(s++));
+        }
+        return h;
+    }
+
+    template<i32 N>
+    class HashedString {
+        static_assert(N > 0, "length of hashed string must be positive");
+        StringHash hash;
+        char data[N];
+
+    public:
+        HashedString() : hash() {
+        }
+
+        HashedString(const char* s) : hash(hashString(s)) {
+            strncpy(data, s, N);
+        }
+
+        bool operator==(const HashedString& other) const {
+            return hash.length == other.hash.length &&
+                   hash.hash == other.hash.hash &&
+                   strcmp(data, other.data) == 0;
+        }
+
+        bool operator!=(const HashedString& other) const {
+            return !(*this == other);
+        }
+
+        i32 getLength() const {
+            return hash.length;
+        }
+
+        constexpr i32 getCapacity() const {
+            return N - 1;
+        }
+
+        u32 hashCode() const {
+            return hash.hash;
+        }
+
+        operator const char*() const {
+            return data;
+        }
+    };
+}
+
+#endif

+ 1 - 2
include/core/utils/Types.hpp

@@ -1,7 +1,6 @@
 #ifndef CORE_TYPES_HPP
 #define CORE_TYPES_HPP
 
-#include <limits.h>
 #include <stdint.h>
 
 using i64 = int64_t;
@@ -14,4 +13,4 @@ using u16 = uint16_t;
 using u8 = uint8_t;
 using c32 = char32_t;
 
-#endif
+#endif

+ 3 - 26
src/ArrayString.cpp

@@ -1,5 +1,6 @@
 #include "core/utils/ArrayString.hpp"
 
+#include "core/math/Math.hpp"
 #include "core/utils/Error.hpp"
 
 using CharString = Core::CharString;
@@ -85,7 +86,7 @@ static Error printChar(c32 u, u32 shift, u32 a, u32 o) {
 }
 
 CharString::CharString(char* buffer, i32 bufferSize)
-    : length(0), capacity(bufferSize), hash(0), data(buffer) {
+    : length(0), capacity(bufferSize), data(buffer) {
     data[0] = '\0';
 }
 
@@ -141,7 +142,6 @@ Error CharString::append(char c) {
     }
     data[length++] = c;
     data[length] = '\0';
-    addToHash(static_cast<c32>(c));
     return ErrorCode::NONE;
 }
 
@@ -205,14 +205,9 @@ Error CharString::toString(Char32String& s) const {
 
 void CharString::clear() {
     length = 0;
-    hash = 0;
     data[0] = '\0';
 }
 
-u32 CharString::hashCode() const {
-    return hash;
-}
-
 Error CharString::print() const {
     for(int i = 0; i < length; i++) {
         CORE_RETURN_ERROR(Core::putChar(data[i]));
@@ -294,12 +289,10 @@ Error CharString::replace(CharString& s, const CharString& search,
 }
 
 void CharString::replace(char search, char replace) {
-    hash = 0;
     for(int i = 0; i < length; i++) {
         if(data[i] == search) {
             data[i] = replace;
         }
-        addToHash(static_cast<c32>(data[i]));
     }
 }
 
@@ -307,12 +300,8 @@ CharString::operator const char*() const {
     return data;
 }
 
-void CharString::addToHash(c32 u) {
-    hash = static_cast<u32>(2120251889) * hash + static_cast<u32>(u);
-}
-
 Char32String::Char32String(c32* buffer, i32 bufferSize)
-    : length(0), capacity(bufferSize), hash(0), data(buffer) {
+    : length(0), capacity(bufferSize), data(buffer) {
     data[0] = '\0';
 }
 
@@ -384,7 +373,6 @@ Error Char32String::append(c32 c) {
     }
     data[length++] = c;
     data[length] = '\0';
-    addToHash(static_cast<c32>(c));
     return ErrorCode::NONE;
 }
 
@@ -437,14 +425,9 @@ Error Char32String::toString(Char32String& s) const {
 
 void Char32String::clear() {
     length = 0;
-    hash = 0;
     data[0] = '\0';
 }
 
-u32 Char32String::hashCode() const {
-    return hash;
-}
-
 Error Char32String::print() const {
     for(int i = 0; i < length; i++) {
         c32 c = data[i];
@@ -541,19 +524,13 @@ Error Char32String::replace(Char32String& s, const Char32String& search,
 }
 
 void Char32String::replace(c32 search, c32 replace) {
-    hash = 0;
     for(int i = 0; i < length; i++) {
         if(data[i] == search) {
             data[i] = replace;
         }
-        addToHash(static_cast<c32>(data[i]));
     }
 }
 
 Char32String::operator const c32*() const {
     return data;
 }
-
-void Char32String::addToHash(c32 u) {
-    hash = static_cast<u32>(2120251889) * hash + static_cast<u32>(u);
-}

+ 1 - 0
test/Main.cpp

@@ -38,6 +38,7 @@ int main(int argAmount, const char** args) {
     Core::testError();
     Core::testFileReader();
     Core::testFrustum();
+    Core::testHashedString();
     Core::testHashMap(light, outOfMemoryTest);
     Core::testLinkedList(light, outOfMemoryTest);
     Core::testList(light);

+ 3 - 2
test/Test.cpp

@@ -20,8 +20,9 @@ void Core::Test::finalize() {
         const char* color = e.value.successTests == e.value.tests
                                 ? Logger::COLOR_GREEN
                                 : Logger::COLOR_RED;
-        Logger::log(color, "# - # / # tests succeeded", e.getKey(),
-                    e.value.successTests, e.value.tests);
+        Logger::log(color, "# - # / # tests succeeded",
+                    static_cast<const char*>(e.getKey()), e.value.successTests,
+                    e.value.tests);
     }
     Internal::results.clear();
 }

+ 6 - 9
test/Test.hpp

@@ -3,6 +3,7 @@
 
 #include "core/data/HashMap.hpp"
 #include "core/math/Vector.hpp"
+#include "core/utils/HashedString.hpp"
 #include "core/utils/Logger.hpp"
 
 namespace Core::Test {
@@ -11,7 +12,7 @@ namespace Core::Test {
             int tests = 0;
             int successTests = 0;
         };
-        using FileName = String32<256>;
+        using FileName = HashedString<256>;
         extern HashMap<FileName, Result> results;
 
         void warn(const char* file, int line, Error e);
@@ -21,11 +22,7 @@ namespace Core::Test {
                    bool c) {
             file = Logger::getFileName(file);
             Error e = ErrorCode::NONE;
-            FileName fileName;
-            if(checkError(e, fileName.append(file))) {
-                warn(file, line, e);
-                return false;
-            }
+            FileName fileName(file);
             Result* result = results.search(fileName);
             if(result == nullptr &&
                checkError(e, results.tryEmplace(result, fileName))) {
@@ -37,9 +34,9 @@ namespace Core::Test {
                 result->successTests++;
                 return true;
             }
-            Core::Logger::log(Core::Logger::COLOR_RED,
-                              "#:# - expected '#' got '#'", fileName, line,
-                              wanted, actual);
+            Core::Logger::log(
+                Core::Logger::COLOR_RED, "#:# - expected '#' got '#'",
+                static_cast<const char*>(fileName), line, wanted, actual);
             return false;
         }
 

+ 2 - 1
test/Tests.hpp

@@ -17,6 +17,7 @@ namespace Core {
     void testError();
     void testFileReader();
     void testFrustum();
+    void testHashedString();
     void testHashMap(bool light, bool outOfMemoryTest);
     void testLinkedList(bool light, bool outOfMemoryTest);
     void testList(bool light);
@@ -37,4 +38,4 @@ namespace Core {
     void testView();
 }
 
-#endif
+#endif

+ 0 - 94
test/modules/ArrayStringTests.cpp

@@ -222,18 +222,6 @@ static void testClear8() {
     CORE_TEST_EQUAL(build("wusi1234"), s);
 }
 
-static void testHashCode8() {
-    String8 s;
-    CORE_TEST_ERROR(s.append("a"));
-    CORE_TEST_ERROR(s.append("bc"));
-    CORE_TEST_ERROR(s.append(20));
-    CORE_TEST_ERROR(s.append(25.5f));
-    CORE_TEST_ERROR(s.append(true));
-    CORE_TEST_EQUAL(build("abc2025.50true").hashCode(), s.hashCode());
-    s.clear();
-    CORE_TEST_EQUAL(String8().hashCode(), s.hashCode());
-}
-
 static void testAddSelf8() {
     String8 s;
     CORE_TEST_ERROR(s.append("test1"));
@@ -242,29 +230,6 @@ static void testAddSelf8() {
     CORE_TEST_EQUAL(build("test1test1test1test1"), s);
 }
 
-static void testAsHashMapKey8() {
-    Core::HashMap<String8, int> map;
-    CORE_TEST_ERROR(map.add(build("wusi"), 3));
-    CORE_TEST_ERROR(map.add(build("hiThere"), 7));
-    CORE_TEST_ERROR(map.add(build("baum123"), 5));
-
-    int* a = map.search(build("wusi"));
-    int* b = map.search(build("hiThere"));
-    int* c = map.search(build("baum123"));
-    int* d = map.search(build("423hifd"));
-
-    CORE_TEST_NOT_NULL(a);
-    CORE_TEST_NOT_NULL(b);
-    CORE_TEST_NOT_NULL(c);
-    CORE_TEST_NULL(d);
-
-    if(a != nullptr && b != nullptr && c != nullptr) {
-        CORE_TEST_EQUAL(3, *a);
-        CORE_TEST_EQUAL(7, *b);
-        CORE_TEST_EQUAL(5, *c);
-    }
-}
-
 static void testStartsWith8() {
     String8 s;
     CORE_TEST_ERROR(s.append("0123456789"));
@@ -410,14 +375,8 @@ static void testReplace8() {
 
     String8 replace;
     CORE_TEST_ERROR(replace.append("ABCD"));
-
     CORE_TEST_ERROR(s.replace(search, replace));
-
     CORE_TEST_STRING("0ABCDABCDä1üABCD3ä", s);
-
-    String8 s2;
-    CORE_TEST_ERROR(s2.append("0ABCDABCDä1üABCD3ä"));
-    CORE_TEST_EQUAL(s2.hashCode(), s.hashCode());
 }
 
 static void testReplaceChar8() {
@@ -433,10 +392,6 @@ static void testReplaceChar8() {
     CORE_TEST_STRING("ABCDD3D", s);
     s.replace('3', 'E');
     CORE_TEST_STRING("ABCDDED", s);
-
-    String8 s2;
-    CORE_TEST_ERROR(s2.append("ABCDDED"));
-    CORE_TEST_EQUAL(s2.hashCode(), s.hashCode());
 }
 
 static void testCastAppendSelf8() {
@@ -704,18 +659,6 @@ static void testClear32() {
     CORE_TEST_EQUAL(build(U"wusi1234"), s);
 }
 
-static void testHashCode32() {
-    String32 s;
-    CORE_TEST_ERROR(s.append(U"a"));
-    CORE_TEST_ERROR(s.append(U"bc"));
-    CORE_TEST_ERROR(s.append(20));
-    CORE_TEST_ERROR(s.append(25.5f));
-    CORE_TEST_ERROR(s.append(true));
-    CORE_TEST_EQUAL(build(U"abc2025.50true").hashCode(), s.hashCode());
-    s.clear();
-    CORE_TEST_EQUAL(String32().hashCode(), s.hashCode());
-}
-
 static void testAddSelf32() {
     String32 s;
     CORE_TEST_ERROR(s.append(U"test1"));
@@ -724,29 +667,6 @@ static void testAddSelf32() {
     CORE_TEST_EQUAL(build(U"test1test1test1test1"), s);
 }
 
-static void testAsHashMapKey32() {
-    Core::HashMap<String32, int> map;
-    CORE_TEST_ERROR(map.add(build(U"wusi"), 3));
-    CORE_TEST_ERROR(map.add(build(U"hiThere"), 7));
-    CORE_TEST_ERROR(map.add(build(U"baum123"), 5));
-
-    int* a = map.search(build(U"wusi"));
-    int* b = map.search(build(U"hiThere"));
-    int* c = map.search(build(U"baum123"));
-    int* d = map.search(build(U"423hifd"));
-
-    CORE_TEST_NOT_NULL(a);
-    CORE_TEST_NOT_NULL(b);
-    CORE_TEST_NOT_NULL(c);
-    CORE_TEST_NULL(d);
-
-    if(a != nullptr && b != nullptr && c != nullptr) {
-        CORE_TEST_EQUAL(3, *a);
-        CORE_TEST_EQUAL(7, *b);
-        CORE_TEST_EQUAL(5, *c);
-    }
-}
-
 static void testStartsWith32() {
     String32 s;
     CORE_TEST_ERROR(s.append(U"0123456789"));
@@ -903,14 +823,8 @@ static void testReplace32() {
 
     String32 replace;
     CORE_TEST_ERROR(replace.append(U"ABCD"));
-
     CORE_TEST_ERROR(s.replace(search, replace));
-
     CORE_TEST_STRING(U"0ABCDABCDä1üABCD3ä", s);
-
-    String32 s2;
-    CORE_TEST_ERROR(s2.append(U"0ABCDABCDä1üABCD3ä"));
-    CORE_TEST_EQUAL(s2.hashCode(), s.hashCode());
 }
 
 static void testReplaceChar32() {
@@ -926,10 +840,6 @@ static void testReplaceChar32() {
     CORE_TEST_STRING(U"ABCDD3D", s);
     s.replace(U'3', U'E');
     CORE_TEST_STRING(U"ABCDDED", s);
-
-    String32 s2;
-    CORE_TEST_ERROR(s2.append(U"ABCDDED"));
-    CORE_TEST_EQUAL(s2.hashCode(), s.hashCode());
 }
 
 static void testCastAppendSelf32() {
@@ -1043,9 +953,7 @@ void Core::testArrayString() {
     testIntOverflow8();
     testUnicode8();
     testClear8();
-    testHashCode8();
     testAddSelf8();
-    testAsHashMapKey8();
     testStartsWith8();
     testSearch8();
     testContains8();
@@ -1087,9 +995,7 @@ void Core::testArrayString() {
     testIntOverflow32();
     testUnicode32();
     testClear32();
-    testHashCode32();
     testAddSelf32();
-    testAsHashMapKey32();
     testStartsWith32();
     testSearch32();
     testContains32();

+ 73 - 0
test/modules/HashedStringTests.cpp

@@ -0,0 +1,73 @@
+#include "../Tests.hpp"
+#include "core/data/HashMap.hpp"
+#include "core/utils/HashedString.hpp"
+
+template class Core::HashedString<32>;
+
+using HString = Core::HashedString<32>;
+
+static void testComparison() {
+    HString a("test");
+    HString b("testA");
+    HString c("");
+    HString d("Btest");
+
+    CORE_TEST_TRUE(a == a);
+    CORE_TEST_TRUE(b == b);
+    CORE_TEST_TRUE(c == c);
+    CORE_TEST_TRUE(d == d);
+    CORE_TEST_TRUE(a != b);
+    CORE_TEST_TRUE(a != c);
+    CORE_TEST_TRUE(a != d);
+    CORE_TEST_TRUE(b != a);
+    CORE_TEST_TRUE(b != c);
+    CORE_TEST_TRUE(b != d);
+    CORE_TEST_TRUE(c != a);
+    CORE_TEST_TRUE(c != b);
+    CORE_TEST_TRUE(c != d);
+    CORE_TEST_TRUE(d != a);
+    CORE_TEST_TRUE(d != b);
+    CORE_TEST_TRUE(d != c);
+}
+
+static void testLength() {
+    HString s("test");
+    CORE_TEST_EQUAL(4, s.getLength());
+}
+
+static void testHashCode() {
+    HString a;
+    HString b("wusi");
+    CORE_TEST_EQUAL(a.hashCode(), 0u);
+    CORE_TEST_TRUE(b.hashCode() != 0u);
+}
+
+static void testAsHashMapKey() {
+    Core::HashMap<HString, int> map;
+    CORE_TEST_ERROR(map.add("wusi", 3));
+    CORE_TEST_ERROR(map.add("hiThere", 7));
+    CORE_TEST_ERROR(map.add("baum123", 5));
+
+    int* a = map.search("wusi");
+    int* b = map.search("hiThere");
+    int* c = map.search("baum123");
+    int* d = map.search("423hifd");
+
+    CORE_TEST_NOT_NULL(a);
+    CORE_TEST_NOT_NULL(b);
+    CORE_TEST_NOT_NULL(c);
+    CORE_TEST_NULL(d);
+
+    if(a != nullptr && b != nullptr && c != nullptr) {
+        CORE_TEST_EQUAL(3, *a);
+        CORE_TEST_EQUAL(7, *b);
+        CORE_TEST_EQUAL(5, *c);
+    }
+}
+
+void Core::testHashedString() {
+    testComparison();
+    testLength();
+    testHashCode();
+    testAsHashMapKey();
+}