Browse Source

Mostly finished probing hash map

Kajetan Johannes Hammerle 10 months ago
parent
commit
3450e54fc5
13 changed files with 371 additions and 318 deletions
  1. 4 16
      data/HashMap.h
  2. 1 1
      data/List.h
  3. 150 118
      data/ProbingHashMap.h
  4. 1 2
      meson.build
  5. 26 2
      performance/Main.cpp
  6. 6 6
      tests/HashMapTests.cpp
  7. 81 89
      tests/ProbingHashMapTests.cpp
  8. 5 3
      utils/ArrayString.h
  9. 0 60
      utils/HashCode.cpp
  10. 85 12
      utils/HashCode.h
  11. 10 9
      utils/Logger.h
  12. 1 0
      utils/Utility.cpp
  13. 1 0
      utils/Utility.h

+ 4 - 16
data/HashMap.h

@@ -106,10 +106,6 @@ namespace Core {
             }
         };
 
-        using EntryIteratorAdapter = IteratorAdapter<HashMap, EntryIterator>;
-        using ConstEntryIteratorAdapter =
-            IteratorAdapter<const HashMap, ConstEntryIterator>;
-
         using ValueIteratorAdapter = IteratorAdapter<HashMap, ValueIterator>;
         using ConstValueIteratorAdapter =
             IteratorAdapter<const HashMap, ConstValueIterator>;
@@ -137,7 +133,7 @@ namespace Core {
                 return Error::NONE;
             }
             HashMap<K, V> map;
-            int l = 1 << Math::roundUpLog2(Core::Math::max(minCapacity, 8));
+            int l = Core::Math::max(1 << Math::roundUpLog2(minCapacity), 8);
             CORE_RETURN_ERROR(map.nodePointers.resize(l));
             for(NodePointerList& list : nodePointers) {
                 for(NodePointer& n : list) {
@@ -225,23 +221,15 @@ namespace Core {
             return *this;
         }
 
-        EntryIteratorAdapter entries() {
-            return {*this};
-        }
-
-        ConstEntryIteratorAdapter entries() const {
-            return {*this};
-        }
-
-        ConstKeyIteratorAdapter keys() const {
+        ConstKeyIteratorAdapter getKeys() const {
             return {*this};
         }
 
-        ValueIteratorAdapter values() {
+        ValueIteratorAdapter getValues() {
             return {*this};
         }
 
-        ConstValueIteratorAdapter values() const {
+        ConstValueIteratorAdapter getValues() const {
             return {*this};
         }
 

+ 1 - 1
data/List.h

@@ -22,7 +22,7 @@ namespace Core {
 
         ~List() {
             clear();
-            delete[] reinterpret_cast<char*>(data);
+            delete[] reinterpret_cast<Aligned*>(data);
         }
 
         List& operator=(const List& other) = delete;

+ 150 - 118
data/ProbingHashMap.h

@@ -9,13 +9,14 @@
 namespace Core {
     template<typename K, typename V>
     struct ProbingHashMap final {
+        template<typename Value>
         class Node final {
             friend ProbingHashMap;
             friend List<Node>;
             K key;
 
         public:
-            V& value;
+            Value& value;
 
             const K& getKey() const {
                 return key;
@@ -29,157 +30,194 @@ namespace Core {
             }
 
         private:
-            Node(const K& key_, V& value_) : key(key_), value(value_) {
+            Node(const K& key_, Value& value_) : key(key_), value(value_) {
             }
         };
 
     private:
-        /*template<typename N, typename I, typename R, R& (*A)(I&)>
+        template<typename Value, typename R, R (*A)(const K&, Value&)>
         class Iterator final {
-            N iterator;
+            const K* currentKey;
+            const K* endKey;
+            Value* currentValue;
 
         public:
-            Iterator(N iterator_) : iterator(iterator_) {
+            Iterator(const K* key, const K* endKey_, Value* value)
+                : currentKey(key), endKey(endKey_), currentValue(value) {
+                skip();
             }
 
             Iterator& operator++() {
-                ++iterator;
+                ++currentKey;
+                ++currentValue;
+                skip();
                 return *this;
             }
 
             bool operator!=(const Iterator& other) const {
-                return iterator != other.iterator;
+                return currentKey != other.currentKey;
             }
 
-            R& operator*() const {
-                return A(*iterator);
+            R operator*() const {
+                return A(*currentKey, *currentValue);
+            }
+
+        private:
+            void skip() {
+                while(currentKey != endKey && *currentKey == emptyValue<K>()) {
+                    ++currentKey;
+                    ++currentValue;
+                }
             }
         };
 
-        template<typename R>
-        static R& access(R& node) {
-            return node;
+        template<typename Value>
+        static Node<Value> access(const K& key, Value& value) {
+            return Node<Value>(key, value);
         }
 
-        template<typename I, typename R>
-        static R& accessValue(I& node) {
-            return node.value;
+        template<typename Value>
+        static Value& accessValue(const K&, Value& value) {
+            return value;
         }
 
-        static const K& accessKey(const Node& node) {
-            return node.getKey();
+        static const K& accessKey(const K& key, const V&) {
+            return key;
         }
 
-        template<typename N, typename R>
-        using BaseEntryIterator = Iterator<N, R, R, access<R>>;
-        using EntryIterator = BaseEntryIterator<NodeIterator, Node>;
-        using ConstEntryIterator =
-            BaseEntryIterator<ConstNodeIterator, const Node>;
+        template<typename Value>
+        using BaseEntryIterator = Iterator<Value, Node<Value>, access<Value>>;
+        using EntryIterator = BaseEntryIterator<V>;
+        using ConstEntryIterator = BaseEntryIterator<const V>;
 
-        template<typename N, typename I, typename R>
-        using BaseValueIterator = Iterator<N, I, R, accessValue<I, R>>;
-        using ValueIterator = BaseValueIterator<NodeIterator, Node, V>;
-        using ConstValueIterator =
-            BaseValueIterator<ConstNodeIterator, const Node, const V>;
+        template<typename Value>
+        using BaseValueIterator = Iterator<Value, Value&, accessValue<Value>>;
+        using ValueIterator = BaseValueIterator<V>;
+        using ConstValueIterator = BaseValueIterator<const V>;
 
-        using ConstKeyIterator =
-            Iterator<ConstNodeIterator, const Node, const K, accessKey>;
+        using ConstKeyIterator = Iterator<const V, const K&, accessKey>;
 
         template<typename M, typename I>
         struct IteratorAdapter final {
             M& map;
 
             I begin() const {
-                return I(map.nodes.begin());
+                return {map.keys.begin(), map.keys.end(), map.values};
             }
 
             I end() const {
-                return I(map.nodes.end());
+                return {map.keys.end(), map.keys.end(), nullptr};
             }
         };
 
-        using EntryIteratorAdapter =
-            IteratorAdapter<ProbingHashMap, EntryIterator>;
-        using ConstEntryIteratorAdapter =
-            IteratorAdapter<const ProbingHashMap, ConstEntryIterator>;
-
         using ValueIteratorAdapter =
             IteratorAdapter<ProbingHashMap, ValueIterator>;
         using ConstValueIteratorAdapter =
             IteratorAdapter<const ProbingHashMap, ConstValueIterator>;
 
         using ConstKeyIteratorAdapter =
-            IteratorAdapter<const ProbingHashMap, ConstKeyIterator>;*/
+            IteratorAdapter<const ProbingHashMap, ConstKeyIterator>;
 
     private:
+        struct alignas(V) AlignedValue final {
+            char data[sizeof(V)];
+        };
+
         List<K> keys;
-        List<V> values;
-        K emptyKey;
+        V* values;
         int entries;
 
     public:
-        ProbingHashMap(const K& emptyKey_) : emptyKey(emptyKey_), entries(0) {
+        ProbingHashMap() : values(nullptr), entries(0) {
+        }
+
+        ProbingHashMap(const ProbingHashMap& other) = delete;
+
+        ProbingHashMap(ProbingHashMap&& other) : ProbingHashMap() {
+            swap(other);
+        }
+
+        ~ProbingHashMap() {
+            for(int i = 0; i < keys.getLength(); i++) {
+                if(keys[i] != emptyValue<K>()) {
+                    values[i].~V();
+                }
+            }
+            delete[] reinterpret_cast<AlignedValue*>(values);
+        }
+
+        ProbingHashMap& operator=(const ProbingHashMap& other) = delete;
+
+        ProbingHashMap& operator=(ProbingHashMap&& other) {
+            swap(other);
+            return *this;
         }
 
-        /*check_return Error copyFrom(const ProbingHashMap& other) {
+        check_return Error copyFrom(const ProbingHashMap& other) {
             ProbingHashMap copy;
-            for(const auto& en : other) {
-                CORE_RETURN_ERROR(copy.add(en.getKey(), en.value));
+            for(const auto& e : other) {
+                CORE_RETURN_ERROR(copy.add(e.getKey(), e.value));
             }
-            swap(copy.nodes, nodes);
-            swap(copy.nodePointers, nodePointers);
+            swap(copy);
             return Error::NONE;
-        }*/
+        }
 
         check_return Error rehash(int minCapacity) {
-            minCapacity *= 2;
             if(minCapacity <= keys.getLength()) {
                 return Error::NONE;
             }
-            ProbingHashMap<K, V> map(emptyKey);
-            int l = 1 << Math::roundUpLog2(Core::Math::max(minCapacity, 8));
-            CORE_RETURN_ERROR(map.keys.resize(l, emptyKey));
-            CORE_RETURN_ERROR(map.values.resize(l));
+            ProbingHashMap<K, V> map;
+            int l = Core::Math::max(1 << Math::roundUpLog2(minCapacity), 8);
+            CORE_RETURN_ERROR(map.keys.resize(l, emptyValue<K>()));
+            map.values = reinterpret_cast<V*>(new AlignedValue[l]);
+            if(map.values == nullptr) {
+                return Error::OUT_OF_MEMORY;
+            }
             for(int i = 0; i < keys.getLength(); i++) {
-                if(keys[i] != emptyKey) {
+                if(keys[i] != emptyValue<K>()) {
                     CORE_RETURN_ERROR(map.add(keys[i], values[i]));
                 }
             }
-            Core::swap(map.keys, keys);
-            Core::swap(map.values, values);
+            swap(map);
             return Error::NONE;
         }
 
-        /*template<typename... Args>
+        template<typename... Args>
         check_return Error tryEmplace(V*& v, const K& key, Args&&... args) {
-            CORE_RETURN_ERROR(rehash(nodes.getLength() + 1));
-            int h = hashIndex(key);
-            v = searchList(key, h);
-            if(v != nullptr) {
-                return Error::EXISTING_KEY;
+            if(key == emptyValue<K>()) {
+                return Error::INVALID_ARGUMENT;
             }
-            NodePointer np = nullptr;
-            CORE_RETURN_ERROR(nodes.put(np, key, Core::forward<Args>(args)...));
-            Error e = Error::NONE;
-            if(checkError(e, nodePointers[h].add(np))) {
-                nodes.remove(np);
-                return e;
+            CORE_RETURN_ERROR(rehash(entries * 2 + 1));
+            int index = searchSlot(key);
+            if(index < 0) {
+                return Error::CAPACITY_REACHED;
+            } else if(keys[index] == key) {
+                return Error::EXISTING_KEY;
             }
-            v = &(np->data.value);
+            keys[index] = key;
+            v = new(values + index) V(Core::forward<Args>(args)...);
+            entries++;
             return Error::NONE;
-        }*/
+        }
 
         template<typename VA>
         check_return Error put(V*& v, const K& key, VA&& value) {
-            CORE_RETURN_ERROR(rehash(entries + 1));
-            int index = searchList(key);
+            if(key == emptyValue<K>()) {
+                return Error::INVALID_ARGUMENT;
+            }
+            CORE_RETURN_ERROR(rehash(entries * 2 + 1));
+            int index = searchSlot(key);
             if(index < 0) {
                 return Error::CAPACITY_REACHED;
             }
-            entries += keys[index] != key;
+            if(keys[index] == key) {
+                values[index] = Core::forward<VA>(value);
+            } else {
+                new(values + index) V(Core::forward<VA>(value));
+                entries++;
+            }
             keys[index] = key;
-            values[index] = Core::forward<VA>(value);
-            v = &(values[index]);
+            v = reinterpret_cast<V*>(values + index);
             return Error::NONE;
         }
 
@@ -189,97 +227,91 @@ namespace Core {
             return put(v, key, Core::forward<VA>(value));
         }
 
-        /*check_return Error remove(const K& key) {
-            NodePointerList& list = nodePointers[hashIndex(key)];
-            for(int i = 0; i < list.getLength(); i++) {
-                if(list[i]->data.key == key) {
-                    nodes.remove(list[i]);
-                    return list.removeBySwap(i);
-                }
-            }
-            return Error::NOT_FOUND;
-        }*/
-
         const V* search(const K& key) const {
-            int i = searchList(key);
-            return i < 0 || keys[i] == emptyKey ? nullptr : &(values[i]);
+            return searchValue<const V>(key);
         }
 
         V* search(const K& key) {
-            int i = searchList(key);
-            return i < 0 || keys[i] == emptyKey ? nullptr : &(values[i]);
+            return searchValue<V>(key);
         }
 
         bool contains(const K& key) const {
             return search(key) != nullptr;
         }
 
-        /*ProbingHashMap& clear() {
-            nodes.clear();
-            for(NodePointerList& n : nodePointers) {
-                n.clear();
-            }
+        ProbingHashMap& clear() {
+            ProbingHashMap<K, V> map;
+            swap(map);
             return *this;
-        }*/
+        }
 
-        /*EntryIteratorAdapter entries() {
+        ConstKeyIteratorAdapter getKeys() const {
             return {*this};
         }
 
-        ConstEntryIteratorAdapter entries() const {
+        ValueIteratorAdapter getValues() {
             return {*this};
-        }*/
-
-        /*ConstKeyIteratorAdapter keys() const {
-            return keys.begin();
         }
 
-        ValueIteratorAdapter values() {
-            return values.begin();
+        ConstValueIteratorAdapter getValues() const {
+            return {*this};
         }
 
-        ConstValueIteratorAdapter values() const {
-            return values.begin();
-        }*/
-
-        /*EntryIterator begin() {
-            return EntryIterator(nodes.begin());
+        EntryIterator begin() {
+            return {keys.begin(), keys.end(), values};
         }
 
         EntryIterator end() {
-            return EntryIterator(nodes.end());
+            return {keys.end(), keys.end(), nullptr};
         }
 
         ConstEntryIterator begin() const {
-            return ConstEntryIterator(nodes.begin());
+            return {keys.begin(), keys.end(), values};
         }
 
         ConstEntryIterator end() const {
-            return ConstEntryIterator(nodes.end());
+            return {keys.end(), keys.end(), nullptr};
         }
 
         template<typename String>
         check_return Error toString(String& s) const {
             return Core::toString(s, *this);
-        }*/
+        }
+
+        void swap(ProbingHashMap& o) {
+            Core::swap(o.keys, keys);
+            Core::swap(o.values, values);
+            Core::swap(o.entries, entries);
+        }
 
     private:
-        int searchList(const K& key) const {
+        int searchSlot(const K& key) const {
             int baseHash = static_cast<int>(hashCode(key));
             int end = keys.getLength() - 1;
             for(int i = 0; i <= end; i++) {
                 int hash = (baseHash + i) & end;
-                if(keys[hash] == emptyKey || keys[hash] == key) {
+                if(keys[hash] == emptyValue<K>() || keys[hash] == key) {
                     return hash;
                 }
             }
             return -1;
         }
 
-        /*V* searchList(const K& key, int h) {
-            return const_cast<V*>(
-                static_cast<const ProbingHashMap*>(this)->searchList(key, h));
-        }*/
+        template<typename Value>
+        Value* searchValue(const K& key) const {
+            int baseHash = static_cast<int>(hashCode(key));
+            int end = keys.getLength() - 1;
+            for(int i = 0; i <= end; i++) {
+                int hash = (baseHash + i) & end;
+                if(keys[hash] == key) //[[likely]]
+                {
+                    return values + hash;
+                } else if(keys[hash] == emptyValue<K>()) {
+                    return nullptr;
+                }
+            }
+            return nullptr;
+        }
     };
 }
 

+ 1 - 2
meson.build

@@ -3,7 +3,6 @@ project('core', 'cpp', default_options : ['cpp_std=c++2a'])
 src = [
     'utils/Logger.cpp',
     'utils/Utility.cpp',
-    'utils/HashCode.cpp',
     'utils/Buffer.cpp',
     'utils/Clock.cpp',
     'utils/Random.cpp',
@@ -98,7 +97,7 @@ core_include = [include_directories('.')]
 core = static_library('core', 
     sources: src,
     include_directories: core_include,
-    cpp_args: error_args + compile_args,
+    cpp_args: error_args + compile_args + ['-DCORE_LOG_LEVEL=4'],
     link_args: link_args)
 
 core_dep = declare_dependency(

+ 26 - 2
performance/Main.cpp

@@ -2,6 +2,7 @@
 #include "data/ProbingHashMap.h"
 #include "test/Test.h"
 #include "utils/Clock.h"
+#include "utils/Random.h"
 
 template<typename Map>
 static void test(const Map& m) {
@@ -24,9 +25,9 @@ static void test(const Map& m) {
     CORE_LOG_INFO("# | # ns", sum, nanos2 - nanos);
 }
 
-int main() {
+static void order() {
     Core::HashMap<int, int> m;
-    Core::ProbingHashMap<int, int> m2(1 << 30);
+    Core::ProbingHashMap<int, int> m2;
     for(int i = 0; i < 10000; i++) {
         CORE_TEST_ERROR(m.add(i, i * i));
         CORE_TEST_ERROR(m2.add(i, i * i));
@@ -39,6 +40,29 @@ int main() {
     test(m2);
     test(m2);
     test(m2);
+}
+
+static void chaos() {
+    Core::Random random(0);
+    Core::HashMap<int, int> m;
+    Core::ProbingHashMap<int, int> m2;
+    for(int i = 0; i < 10000; i++) {
+        int r = random.next(0, 9999);
+        CORE_TEST_ERROR(m.add(r, r * r));
+        CORE_TEST_ERROR(m2.add(r, r * r));
+    }
+
+    test(m);
+    test(m);
+    test(m);
+
+    test(m2);
+    test(m2);
+    test(m2);
+}
 
+int main() {
+    order();
+    chaos();
     return 0;
 }

+ 6 - 6
tests/HashMapTests.cpp

@@ -239,14 +239,14 @@ static void testEntryForEach() {
     CORE_TEST_ERROR(map.add(15, 2));
 
     int counter = 0;
-    for(auto& entry : map.entries()) {
+    for(auto& entry : map) {
         counter += entry.getKey() + entry.value;
     }
     CORE_TEST_EQUAL(39, counter);
 
     const IntMap& cmap = map;
     counter = 0;
-    for(const auto& entry : cmap.entries()) {
+    for(const auto& entry : cmap) {
         counter += entry.getKey() + entry.value;
     }
     CORE_TEST_EQUAL(39, counter);
@@ -259,14 +259,14 @@ static void testKeyForEach() {
     CORE_TEST_ERROR(map.add(15, 2));
 
     int counter = 0;
-    for(const int& key : map.keys()) {
+    for(const int& key : map.getKeys()) {
         counter += key;
     }
     CORE_TEST_EQUAL(30, counter);
 
     const IntMap& cmap = map;
     counter = 0;
-    for(const int& key : cmap.keys()) {
+    for(const int& key : cmap.getKeys()) {
         counter += key;
     }
     CORE_TEST_EQUAL(30, counter);
@@ -279,14 +279,14 @@ static void testValueForEach() {
     CORE_TEST_ERROR(map.add(15, 2));
 
     int counter = 0;
-    for(int& value : map.values()) {
+    for(int& value : map.getValues()) {
         counter += value;
     }
     CORE_TEST_EQUAL(9, counter);
 
     const IntMap& cmap = map;
     counter = 0;
-    for(const int& value : cmap.values()) {
+    for(const int& value : cmap.getValues()) {
         counter += value;
     }
     CORE_TEST_EQUAL(9, counter);

+ 81 - 89
tests/ProbingHashMapTests.cpp

@@ -8,7 +8,7 @@
 using IntMap = Core::ProbingHashMap<int, int>;
 
 static void testAdd() {
-    IntMap map(1 << 30);
+    IntMap map;
     CORE_TEST_ERROR(map.add(5, 4));
     int* value = map.search(5);
     CORE_TEST_NOT_NULL(value);
@@ -18,7 +18,7 @@ static void testAdd() {
 }
 
 static void testMultipleAdd() {
-    IntMap map(1 << 30);
+    IntMap map;
     CORE_TEST_ERROR(map.add(5, 4));
     CORE_TEST_ERROR(map.add(10, 3));
     CORE_TEST_ERROR(map.add(15, 2));
@@ -38,13 +38,13 @@ static void testMultipleAdd() {
     }
 }
 
-/*static void testSearch() {
-    IntMap map(1 << 30);
+static void testSearch() {
+    IntMap map;
     CORE_TEST_NULL(map.search(6));
 }
 
 static void testAddReplace() {
-    IntMap map(1 << 30);
+    IntMap map;
     CORE_TEST_ERROR(map.add(5, 4));
     CORE_TEST_ERROR(map.add(5, 10));
     CORE_TEST_TRUE(map.contains(5));
@@ -56,7 +56,7 @@ static void testAddReplace() {
 }
 
 static void testClear() {
-    IntMap map(1 << 30);
+    IntMap map;
     CORE_TEST_ERROR(map.add(5, 4));
     CORE_TEST_ERROR(map.add(4, 10));
     map.clear();
@@ -65,7 +65,7 @@ static void testClear() {
 }
 
 static void testOverflow() {
-    IntMap map(1 << 30);
+    IntMap map;
     for(int i = 0; i < 100000; i++) {
         CORE_TEST_ERROR(map.add(i, i));
     }
@@ -74,18 +74,30 @@ static void testOverflow() {
     }
 }
 
+static int aInstances = 0;
+
 struct A final {
     int a;
     int b;
 
     A(int a_, int b_) : a(a_), b(b_) {
+        aInstances++;
+    }
+
+    A(const A& o) : a(o.a), b(o.b) {
+        aInstances++;
+    }
+
+    A(A&& o) : a(o.a), b(o.b) {
+        aInstances++;
+    }
+
+    ~A() {
+        aInstances--;
     }
 
-    // none of these should be needed for the hashmap
-    A(const A&) = delete;
-    A(A&&) = delete;
-    A& operator=(const A&) = delete;
-    A& operator=(A&&) = delete;
+    A& operator=(const A& o) = default;
+    A& operator=(A&& o) = default;
 
     bool operator==(const A& other) const {
         return a == other.a && b == other.b;
@@ -103,32 +115,36 @@ struct A final {
 };
 
 static void testEmplace() {
-    Core::HashMap<int, A> map;
-
-    A* ar = nullptr;
-    CORE_TEST_ERROR(map.tryEmplace(ar, 0, 3, 4));
-    CORE_TEST_ERROR(map.tryEmplace(ar, 3, 4, 5));
-    CORE_TEST_ERROR(map.tryEmplace(ar, 20, 5, 6));
-    CORE_TEST_EQUAL(Core::Error::EXISTING_KEY, map.tryEmplace(ar, 3, 6, 7));
-    CORE_TEST_EQUAL(Core::Error::EXISTING_KEY, map.tryEmplace(ar, 20, 7, 8));
-
-    A* a = map.search(0);
-    A* b = map.search(3);
-    A* c = map.search(20);
-
-    CORE_TEST_NOT_NULL(a);
-    CORE_TEST_NOT_NULL(b);
-    CORE_TEST_NOT_NULL(c);
-
-    if(a != nullptr && b != nullptr && c != nullptr) {
-        CORE_TEST_EQUAL(A(3, 4), *a);
-        CORE_TEST_EQUAL(A(4, 5), *b);
-        CORE_TEST_EQUAL(A(5, 6), *c);
+    {
+        Core::ProbingHashMap<int, A> map;
+
+        A* ar = nullptr;
+        CORE_TEST_ERROR(map.tryEmplace(ar, 0, 3, 4));
+        CORE_TEST_ERROR(map.tryEmplace(ar, 3, 4, 5));
+        CORE_TEST_ERROR(map.tryEmplace(ar, 20, 5, 6));
+        CORE_TEST_EQUAL(Core::Error::EXISTING_KEY, map.tryEmplace(ar, 3, 6, 7));
+        CORE_TEST_EQUAL(Core::Error::EXISTING_KEY,
+                        map.tryEmplace(ar, 20, 7, 8));
+
+        A* a = map.search(0);
+        A* b = map.search(3);
+        A* c = map.search(20);
+
+        CORE_TEST_NOT_NULL(a);
+        CORE_TEST_NOT_NULL(b);
+        CORE_TEST_NOT_NULL(c);
+
+        if(a != nullptr && b != nullptr && c != nullptr) {
+            CORE_TEST_EQUAL(A(3, 4), *a);
+            CORE_TEST_EQUAL(A(4, 5), *b);
+            CORE_TEST_EQUAL(A(5, 6), *c);
+        }
     }
+    CORE_TEST_EQUAL(0, aInstances);
 }
 
 static void testToString1() {
-    IntMap map(1 << 30);
+    IntMap map;
     CORE_TEST_ERROR(map.add(1, 3));
     CORE_TEST_ERROR(map.add(2, 4));
     CORE_TEST_ERROR(map.add(3, 5));
@@ -136,18 +152,18 @@ static void testToString1() {
 }
 
 static void testToString2() {
-    IntMap map(1 << 30);
+    IntMap map;
     CORE_TEST_ERROR(map.add(1, 3));
     CORE_TEST_STRING("[1 = 3]", map);
 }
 
 static void testToString3() {
-    IntMap map(1 << 30);
+    IntMap map;
     CORE_TEST_STRING("[]", map);
 }
 
 static void testCopy() {
-    IntMap map(1 << 30);
+    IntMap map;
     CORE_TEST_ERROR(map.add(1, 3));
     CORE_TEST_ERROR(map.add(2, 4));
     CORE_TEST_ERROR(map.add(3, 5));
@@ -166,7 +182,7 @@ static void testCopy() {
 }
 
 static void testMove() {
-    IntMap map(1 << 30);
+    IntMap map;
     CORE_TEST_ERROR(map.add(1, 3));
     CORE_TEST_ERROR(map.add(2, 4));
     CORE_TEST_ERROR(map.add(3, 5));
@@ -188,7 +204,7 @@ static void testMove() {
 }
 
 static void testMoveAssignment() {
-    IntMap map(1 << 30);
+    IntMap map;
     CORE_TEST_ERROR(map.add(1, 3));
     CORE_TEST_ERROR(map.add(2, 4));
     CORE_TEST_ERROR(map.add(3, 5));
@@ -211,84 +227,61 @@ static void testMoveAssignment() {
     }
 }
 
-static void testRemove() {
-    IntMap map(1 << 30);
-    CORE_TEST_ERROR(map.add(1, 3));
-    CORE_TEST_ERROR(map.add(2, 4));
-    CORE_TEST_ERROR(map.add(3, 5));
-
-    CORE_TEST_ERROR(map.remove(2));
-    CORE_TEST_EQUAL(Core::Error::NOT_FOUND, map.remove(7));
-
-    int* a = map.search(1);
-    int* b = map.search(2);
-    int* c = map.search(3);
-
-    CORE_TEST_NOT_NULL(a);
-    CORE_TEST_NULL(b);
-    CORE_TEST_NOT_NULL(c);
-
-    if(a != nullptr && c != nullptr) {
-        CORE_TEST_EQUAL(3, *a);
-        CORE_TEST_EQUAL(5, *c);
-    }
-}
-
 static void testEntryForEach() {
-    IntMap map(1 << 30);
+    IntMap map;
     CORE_TEST_ERROR(map.add(5, 4));
     CORE_TEST_ERROR(map.add(10, 3));
     CORE_TEST_ERROR(map.add(15, 2));
 
     int counter = 0;
-    for(auto& entry : map.entries()) {
+    for(auto entry : map) {
         counter += entry.getKey() + entry.value;
     }
     CORE_TEST_EQUAL(39, counter);
 
     const IntMap& cmap = map;
     counter = 0;
-    for(const auto& entry : cmap.entries()) {
+    for(auto entry : cmap) {
         counter += entry.getKey() + entry.value;
     }
     CORE_TEST_EQUAL(39, counter);
 }
 
 static void testKeyForEach() {
-    IntMap map(1 << 30);
+    IntMap map;
     CORE_TEST_ERROR(map.add(5, 4));
     CORE_TEST_ERROR(map.add(10, 3));
     CORE_TEST_ERROR(map.add(15, 2));
 
     int counter = 0;
-    for(const int& key : map.keys()) {
+    for(const int& key : map.getKeys()) {
         counter += key;
     }
     CORE_TEST_EQUAL(30, counter);
 
     const IntMap& cmap = map;
     counter = 0;
-    for(const int& key : cmap.keys()) {
+    for(const int& key : cmap.getKeys()) {
         counter += key;
     }
     CORE_TEST_EQUAL(30, counter);
 }
 
 static void testValueForEach() {
-    IntMap map(1 << 30);
+    IntMap map;
     CORE_TEST_ERROR(map.add(5, 4));
     CORE_TEST_ERROR(map.add(10, 3));
     CORE_TEST_ERROR(map.add(15, 2));
 
     int counter = 0;
-    for(int& value : map.values()) {
+    for(int& value : map.getValues()) {
         counter += value;
     }
     CORE_TEST_EQUAL(9, counter);
 
     const IntMap& cmap = map;
     counter = 0;
-    for(const int& value : cmap.values()) {
+    for(const int& value : cmap.getValues()) {
         counter += value;
     }
     CORE_TEST_EQUAL(9, counter);
@@ -296,7 +289,7 @@ static void testValueForEach() {
 
 template<typename T>
 static void testType() {
-    Core::HashMap<T, int> m;
+    Core::ProbingHashMap<T, int> m;
     CORE_TEST_ERROR(m.add(T(), 3));
 }
 
@@ -312,25 +305,24 @@ static void testTypes() {
     testType<unsigned int>();
     testType<unsigned long>();
     testType<unsigned long long>();
-}*/
+}
 
 void Core::ProbingHashMapTests::test() {
     testAdd();
     testMultipleAdd();
-    // testSearch();
-    // testAddReplace();
-    // testClear();
-    // testOverflow();
-    // testEmplace();
-    // testToString1();
-    // testToString2();
-    // testToString3();
-    // testCopy();
-    // testMove();
-    // testMoveAssignment();
-    // testRemove();
-    // testEntryForEach();
-    // testKeyForEach();
-    // testValueForEach();
-    // testTypes();
+    testSearch();
+    testAddReplace();
+    testClear();
+    testOverflow();
+    testEmplace();
+    testToString1();
+    testToString2();
+    testToString3();
+    testCopy();
+    testMove();
+    testMoveAssignment();
+    testEntryForEach();
+    testKeyForEach();
+    testValueForEach();
+    testTypes();
 }

+ 5 - 3
utils/ArrayString.h

@@ -240,9 +240,11 @@ namespace Core {
         template<typename... Args>
         check_return Error format(Args&&... args) {
             ArrayString s;
-            CORE_RETURN_ERROR(formatBuffer(s, 0, Core::forward<Args>(args)...));
-            *this = s;
-            return Error::NONE;
+            Error e = formatBuffer(s, 0, Core::forward<Args>(args)...);
+            if(e == Error::NONE || e == Error::CAPACITY_REACHED) {
+                *this = s;
+            }
+            return e;
         }
 
         template<int L>

+ 0 - 60
utils/HashCode.cpp

@@ -1,60 +0,0 @@
-#include "utils/HashCode.h"
-
-#include "utils/Utility.h"
-
-template<typename T>
-static u32 hashNumber(T t) {
-    constexpr u32 L = sizeof(T) / 4 + (sizeof(T) % 4 != 0);
-    u32 parts[L];
-    Core::memorySet(parts, 0, sizeof(parts));
-    Core::memoryCopy(parts, &t, sizeof(T));
-    u32 hash = 0;
-    for(u32 i = 0; i < L; i++) {
-        hash ^= parts[i];
-    }
-    return hash;
-}
-
-u32 Core::hashCode(char key) {
-    return hashNumber(key);
-}
-
-u32 Core::hashCode(signed char key) {
-    return hashNumber(key);
-}
-
-u32 Core::hashCode(signed short key) {
-    return hashNumber(key);
-}
-
-u32 Core::hashCode(signed int key) {
-    return hashNumber(key);
-}
-
-u32 Core::hashCode(signed long key) {
-    return hashNumber(key);
-}
-
-u32 Core::hashCode(signed long long key) {
-    return hashNumber(key);
-}
-
-u32 Core::hashCode(unsigned char key) {
-    return hashNumber(key);
-}
-
-u32 Core::hashCode(unsigned short key) {
-    return hashNumber(key);
-}
-
-u32 Core::hashCode(unsigned int key) {
-    return hashNumber(key);
-}
-
-u32 Core::hashCode(unsigned long key) {
-    return hashNumber(key);
-}
-
-u32 Core::hashCode(unsigned long long key) {
-    return hashNumber(key);
-}

+ 85 - 12
utils/HashCode.h

@@ -1,25 +1,98 @@
 #ifndef CORE_HASH_CODE_H
 #define CORE_HASH_CODE_H
 
+#include <limits.h>
+
 #include "utils/Utility.h"
 
 namespace Core {
     template<typename H>
-    u32 hashCode(const H& key) {
+    inline u32 hashCode(const H& key) {
         return key.hashCode();
     }
 
-    u32 hashCode(char key);
-    u32 hashCode(signed char key);
-    u32 hashCode(signed short key);
-    u32 hashCode(signed int key);
-    u32 hashCode(signed long key);
-    u32 hashCode(signed long long key);
-    u32 hashCode(unsigned char key);
-    u32 hashCode(unsigned short key);
-    u32 hashCode(unsigned int key);
-    u32 hashCode(unsigned long key);
-    u32 hashCode(unsigned long long key);
+    template<typename T>
+    inline u32 hashNumber(T t) {
+        static_assert(sizeof(t) <= 8);
+        if constexpr(sizeof(t) <= 4) {
+            return static_cast<u32>(t);
+        }
+        return static_cast<u32>(static_cast<u64>(t) ^
+                                (static_cast<u64>(t) >> 32));
+    }
+
+    inline u32 hashCode(char key) {
+        return hashNumber(key);
+    }
+
+    inline u32 hashCode(signed char key) {
+        return hashNumber(key);
+    }
+
+    inline u32 hashCode(signed short key) {
+        return hashNumber(key);
+    }
+
+    inline u32 hashCode(signed int key) {
+        return hashNumber(key);
+    }
+
+    inline u32 hashCode(signed long key) {
+        return hashNumber(key);
+    }
+
+    inline u32 hashCode(signed long long key) {
+        return hashNumber(key);
+    }
+
+    inline u32 hashCode(unsigned char key) {
+        return hashNumber(key);
+    }
+
+    inline u32 hashCode(unsigned short key) {
+        return hashNumber(key);
+    }
+
+    inline u32 hashCode(unsigned int key) {
+        return hashNumber(key);
+    }
+
+    inline u32 hashCode(unsigned long key) {
+        return hashNumber(key);
+    }
+
+    inline u32 hashCode(unsigned long long key) {
+        return hashNumber(key);
+    }
+
+    template<typename T>
+    inline consteval T emptyValue() {
+        if constexpr(Core::IsSame<char, T>) {
+            return CHAR_MAX;
+        } else if constexpr(Core::IsSame<signed char, T>) {
+            return SCHAR_MAX;
+        } else if constexpr(Core::IsSame<signed short, T>) {
+            return SHRT_MAX;
+        } else if constexpr(Core::IsSame<signed int, T>) {
+            return INT_MAX;
+        } else if constexpr(Core::IsSame<signed long, T>) {
+            return LONG_MAX;
+        } else if constexpr(Core::IsSame<signed long long, T>) {
+            return LLONG_MAX;
+        } else if constexpr(Core::IsSame<unsigned char, T>) {
+            return UCHAR_MAX;
+        } else if constexpr(Core::IsSame<unsigned short, T>) {
+            return USHRT_MAX;
+        } else if constexpr(Core::IsSame<unsigned int, T>) {
+            return UINT_MAX;
+        } else if constexpr(Core::IsSame<unsigned long, T>) {
+            return ULONG_MAX;
+        } else if constexpr(Core::IsSame<unsigned long long, T>) {
+            return ULLONG_MAX;
+        } else {
+            return T::emptyValue();
+        }
+    }
 }
 
 #endif

+ 10 - 9
utils/Logger.h

@@ -1,12 +1,17 @@
 #ifndef CORE_LOGGER_H
 #define CORE_LOGGER_H
 
-#include "utils/ArrayString.h"
+#include <stdio.h>
 
+#include "utils/ArrayString.h"
 namespace Core::Logger {
     enum class Level { ERROR, WARNING, INFO, DEBUG };
     extern Level level;
 
+    inline bool filterError(Error e) {
+        return e != Error::NONE && e != Error::CAPACITY_REACHED;
+    }
+
     // aborts on critical logging failure
     template<typename... Args>
     void log(Level l, const char* file, int line, const char* start,
@@ -16,14 +21,10 @@ namespace Core::Logger {
         }
         file = Core::getFileName(file);
         Core::String32<2048> s;
-        Error e = Error::NONE;
-        if(checkError(e, s.append(start)) ||
-           checkError(e, s.append("#:# | ")) ||
-           checkError(e, s.format(file, line)) ||
-           checkError(e, s.append(format)) ||
-           checkError(e, s.format(Core::forward<Args>(args)...)) ||
-           checkError(e, s.append("\33[39;49m\n")) ||
-           checkError(e, s.print())) {
+        if(filterError(s.append(start)) || filterError(s.append("#:# | ")) ||
+           filterError(s.format(file, line)) || filterError(s.append(format)) ||
+           filterError(s.format(Core::forward<Args>(args)...)) ||
+           filterError(s.append("\33[39;49m\n")) || filterError(s.print())) {
             CORE_EXIT(1);
         }
     }

+ 1 - 0
utils/Utility.cpp

@@ -18,6 +18,7 @@ const char* Core::getErrorName(Error e) {
         case Error::NOT_FOUND: return "NOT_FOUND";
         case Error::INVALID_STATE: return "INVALID_STATE";
         case Error::INVALID_INDEX: return "INVALID_INDEX";
+        case Error::INVALID_ARGUMENT: return "INVALID_ARGUMENT";
         case Error::TIME_NOT_AVAILABLE: return "TIME_NOT_AVAILABLE";
         case Error::SLEEP_INTERRUPTED: return "SLEEP_INTERRUPTED";
         case Error::THREAD_ERROR: return "THREAD_ERROR";

+ 1 - 0
utils/Utility.h

@@ -16,6 +16,7 @@ namespace Core {
         NOT_FOUND,
         INVALID_STATE,
         INVALID_INDEX,
+        INVALID_ARGUMENT,
         TIME_NOT_AVAILABLE,
         SLEEP_INTERRUPTED,
         THREAD_ERROR,