Browse Source

Mostly finished probing hash map

Kajetan Johannes Hammerle 11 tháng trước cách đây
mục cha
commit
3450e54fc5
13 tập tin đã thay đổi với 371 bổ sung318 xóa
  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,