Browse Source

Sync containers with new HashMap/HashSet, sync API headers.

bruvzg 3 years ago
parent
commit
e3119e7d05

File diff suppressed because it is too large
+ 431 - 515
godot-headers/extension_api.json


+ 3 - 0
godot-headers/godot/gdnative_interface.h

@@ -114,6 +114,7 @@ typedef enum {
 	GDNATIVE_VARIANT_OP_NEGATE,
 	GDNATIVE_VARIANT_OP_NEGATE,
 	GDNATIVE_VARIANT_OP_POSITIVE,
 	GDNATIVE_VARIANT_OP_POSITIVE,
 	GDNATIVE_VARIANT_OP_MODULE,
 	GDNATIVE_VARIANT_OP_MODULE,
+	GDNATIVE_VARIANT_OP_POWER,
 	/* bitwise */
 	/* bitwise */
 	GDNATIVE_VARIANT_OP_SHIFT_LEFT,
 	GDNATIVE_VARIANT_OP_SHIFT_LEFT,
 	GDNATIVE_VARIANT_OP_SHIFT_RIGHT,
 	GDNATIVE_VARIANT_OP_SHIFT_RIGHT,
@@ -412,6 +413,8 @@ typedef struct {
 	GDNativeBool (*variant_iter_init)(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeBool *r_valid);
 	GDNativeBool (*variant_iter_init)(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeBool *r_valid);
 	GDNativeBool (*variant_iter_next)(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeBool *r_valid);
 	GDNativeBool (*variant_iter_next)(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeBool *r_valid);
 	void (*variant_iter_get)(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeVariantPtr r_ret, GDNativeBool *r_valid);
 	void (*variant_iter_get)(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_iter, GDNativeVariantPtr r_ret, GDNativeBool *r_valid);
+	GDNativeInt (*variant_hash)(const GDNativeVariantPtr p_self);
+	GDNativeInt (*variant_recursive_hash)(const GDNativeVariantPtr p_self, GDNativeInt p_recursion_count);
 	GDNativeBool (*variant_hash_compare)(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_other);
 	GDNativeBool (*variant_hash_compare)(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_other);
 	GDNativeBool (*variant_booleanize)(const GDNativeVariantPtr p_self);
 	GDNativeBool (*variant_booleanize)(const GDNativeVariantPtr p_self);
 	void (*variant_sub)(const GDNativeVariantPtr p_a, const GDNativeVariantPtr p_b, GDNativeVariantPtr r_dst);
 	void (*variant_sub)(const GDNativeVariantPtr p_a, const GDNativeVariantPtr p_b, GDNativeVariantPtr r_dst);

+ 8 - 0
include/godot_cpp/core/memory.hpp

@@ -98,6 +98,14 @@ public:
 	_ALWAYS_INLINE_ static void free(void *p_ptr) { Memory::free_static(p_ptr); }
 	_ALWAYS_INLINE_ static void free(void *p_ptr) { Memory::free_static(p_ptr); }
 };
 };
 
 
+template <class T>
+class DefaultTypedAllocator {
+public:
+	template <class... Args>
+	_ALWAYS_INLINE_ T *new_allocation(const Args &&...p_args) { return memnew(T(p_args...)); }
+	_ALWAYS_INLINE_ void delete_allocation(T *p_allocation) { memdelete(p_allocation); }
+};
+
 template <class T>
 template <class T>
 void memdelete(T *p_class, typename std::enable_if<!std::is_base_of_v<godot::Wrapped, T>>::type * = 0) {
 void memdelete(T *p_class, typename std::enable_if<!std::is_base_of_v<godot::Wrapped, T>>::type * = 0) {
 	if (!__has_trivial_destructor(T)) {
 	if (!__has_trivial_destructor(T)) {

+ 407 - 373
include/godot_cpp/templates/hash_map.hpp

@@ -34,524 +34,558 @@
 #include <godot_cpp/core/error_macros.hpp>
 #include <godot_cpp/core/error_macros.hpp>
 #include <godot_cpp/core/memory.hpp>
 #include <godot_cpp/core/memory.hpp>
 #include <godot_cpp/templates/hashfuncs.hpp>
 #include <godot_cpp/templates/hashfuncs.hpp>
-#include <godot_cpp/templates/list.hpp>
+#include <godot_cpp/templates/pair.hpp>
+
+namespace godot {
 
 
 /**
 /**
- * @class HashMap
+ * A HashMap implementation that uses open addressing with Robin Hood hashing.
+ * Robin Hood hashing swaps out entries that have a smaller probing distance
+ * than the to-be-inserted entry, that evens out the average probing distance
+ * and enables faster lookups. Backward shift deletion is employed to further
+ * improve the performance and to avoid infinite loops in rare cases.
  *
  *
- * Implementation of a standard Hashing HashMap, for quick lookups of Data associated with a Key.
- * The implementation provides hashers for the default types, if you need a special kind of hasher, provide
- * your own.
- * @param TKey  Key, search is based on it, needs to be hasheable. It is unique in this container.
- * @param TData Data, data associated with the key
- * @param Hasher Hasher object, needs to provide a valid static hash function for TKey
- * @param Comparator comparator object, needs to be able to safely compare two TKey values.
- * It needs to ensure that x == x for any items inserted in the map. Bear in mind that nan != nan when implementing an equality check.
- * @param MIN_HASH_TABLE_POWER Miminum size of the hash table, as a power of two. You rarely need to change this parameter.
- * @param RELATIONSHIP Relationship at which the hash table is resized. if amount of elements is RELATIONSHIP
- * times bigger than the hash table, table is resized to solve this condition. if RELATIONSHIP is zero, table is always MIN_HASH_TABLE_POWER.
+ * Keys and values are stored in a double linked list by insertion order. This
+ * has a slight performance overhead on lookup, which can be mostly compensated
+ * using a paged allocator if required.
  *
  *
+ * The assignment operator copy the pairs from one map to the other.
  */
  */
 
 
-namespace godot {
+template <class TKey, class TValue>
+struct HashMapElement {
+	HashMapElement *next = nullptr;
+	HashMapElement *prev = nullptr;
+	KeyValue<TKey, TValue> data;
+	HashMapElement() {}
+	HashMapElement(const TKey &p_key, const TValue &p_value) :
+			data(p_key, p_value) {}
+};
 
 
-template <class TKey, class TData, class Hasher = HashMapHasherDefault, class Comparator = HashMapComparatorDefault<TKey>, uint8_t MIN_HASH_TABLE_POWER = 3, uint8_t RELATIONSHIP = 8>
+template <class TKey, class TValue,
+		class Hasher = HashMapHasherDefault,
+		class Comparator = HashMapComparatorDefault<TKey>,
+		class Allocator = DefaultTypedAllocator<HashMapElement<TKey, TValue>>>
 class HashMap {
 class HashMap {
 public:
 public:
-	struct Pair {
-		TKey key;
-		TData data;
+	const uint32_t MIN_CAPACITY_INDEX = 2; // Use a prime.
+	const float MAX_OCCUPANCY = 0.75;
+	const uint32_t EMPTY_HASH = 0;
 
 
-		Pair(const TKey &p_key) :
-				key(p_key),
-				data() {}
-		Pair(const TKey &p_key, const TData &p_data) :
-				key(p_key),
-				data(p_data) {
-		}
-	};
+private:
+	Allocator element_alloc;
+	HashMapElement<TKey, TValue> **elements = nullptr;
+	uint32_t *hashes = nullptr;
+	HashMapElement<TKey, TValue> *head_element = nullptr;
+	HashMapElement<TKey, TValue> *tail_element = nullptr;
 
 
-	struct Element {
-	private:
-		friend class HashMap;
+	uint32_t capacity_index = 0;
+	uint32_t num_elements = 0;
 
 
-		uint32_t hash = 0;
-		Element *next = nullptr;
-		Element() {}
-		Pair pair;
+	_FORCE_INLINE_ uint32_t _hash(const TKey &p_key) const {
+		uint32_t hash = Hasher::hash(p_key);
 
 
-	public:
-		const TKey &key() const {
-			return pair.key;
+		if (unlikely(hash == EMPTY_HASH)) {
+			hash = EMPTY_HASH + 1;
 		}
 		}
 
 
-		TData &value() {
-			return pair.data;
-		}
+		return hash;
+	}
 
 
-		const TData &value() const {
-			return pair.value();
+	_FORCE_INLINE_ uint32_t _get_probe_length(uint32_t p_pos, uint32_t p_hash, uint32_t p_capacity) const {
+		uint32_t original_pos = p_hash % p_capacity;
+		return (p_pos - original_pos + p_capacity) % p_capacity;
+	}
+
+	bool _lookup_pos(const TKey &p_key, uint32_t &r_pos) const {
+		if (elements == nullptr) {
+			return false; // Failed lookups, no elements
 		}
 		}
 
 
-		Element(const TKey &p_key) :
-				pair(p_key) {}
-		Element(const Element &p_other) :
-				hash(p_other.hash),
-				pair(p_other.pair.key, p_other.pair.data) {}
-	};
+		uint32_t capacity = hash_table_size_primes[capacity_index];
+		uint32_t hash = _hash(p_key);
+		uint32_t pos = hash % capacity;
+		uint32_t distance = 0;
 
 
-private:
-	Element **hash_table = nullptr;
-	uint8_t hash_table_power = 0;
-	uint32_t elements = 0;
+		while (true) {
+			if (hashes[pos] == EMPTY_HASH) {
+				return false;
+			}
 
 
-	void make_hash_table() {
-		ERR_FAIL_COND(hash_table);
+			if (distance > _get_probe_length(pos, hashes[pos], capacity)) {
+				return false;
+			}
 
 
-		hash_table = memnew_arr(Element *, (1 << MIN_HASH_TABLE_POWER));
+			if (hashes[pos] == hash && Comparator::compare(elements[pos]->data.key, p_key)) {
+				r_pos = pos;
+				return true;
+			}
 
 
-		hash_table_power = MIN_HASH_TABLE_POWER;
-		elements = 0;
-		for (int i = 0; i < (1 << MIN_HASH_TABLE_POWER); i++) {
-			hash_table[i] = nullptr;
+			pos = (pos + 1) % capacity;
+			distance++;
 		}
 		}
 	}
 	}
 
 
-	void erase_hash_table() {
-		ERR_FAIL_COND_MSG(elements, "Cannot erase hash table if there are still elements inside.");
-
-		memdelete_arr(hash_table);
-		hash_table = nullptr;
-		hash_table_power = 0;
-		elements = 0;
-	}
+	void _insert_with_hash(uint32_t p_hash, HashMapElement<TKey, TValue> *p_value) {
+		uint32_t capacity = hash_table_size_primes[capacity_index];
+		uint32_t hash = p_hash;
+		HashMapElement<TKey, TValue> *value = p_value;
+		uint32_t distance = 0;
+		uint32_t pos = hash % capacity;
 
 
-	void check_hash_table() {
-		int new_hash_table_power = -1;
+		while (true) {
+			if (hashes[pos] == EMPTY_HASH) {
+				elements[pos] = value;
+				hashes[pos] = hash;
 
 
-		if ((int)elements > ((1 << hash_table_power) * RELATIONSHIP)) {
-			/* rehash up */
-			new_hash_table_power = hash_table_power + 1;
+				num_elements++;
 
 
-			while ((int)elements > ((1 << new_hash_table_power) * RELATIONSHIP)) {
-				new_hash_table_power++;
+				return;
 			}
 			}
 
 
-		} else if ((hash_table_power > (int)MIN_HASH_TABLE_POWER) && ((int)elements < ((1 << (hash_table_power - 1)) * RELATIONSHIP))) {
-			/* rehash down */
-			new_hash_table_power = hash_table_power - 1;
-
-			while ((int)elements < ((1 << (new_hash_table_power - 1)) * RELATIONSHIP)) {
-				new_hash_table_power--;
+			// Not an empty slot, let's check the probing length of the existing one.
+			uint32_t existing_probe_len = _get_probe_length(pos, hashes[pos], capacity);
+			if (existing_probe_len < distance) {
+				SWAP(hash, hashes[pos]);
+				SWAP(value, elements[pos]);
+				distance = existing_probe_len;
 			}
 			}
 
 
-			if (new_hash_table_power < (int)MIN_HASH_TABLE_POWER) {
-				new_hash_table_power = MIN_HASH_TABLE_POWER;
-			}
+			pos = (pos + 1) % capacity;
+			distance++;
 		}
 		}
+	}
 
 
-		if (new_hash_table_power == -1) {
-			return;
-		}
+	void _resize_and_rehash(uint32_t p_new_capacity_index) {
+		uint32_t old_capacity = hash_table_size_primes[capacity_index];
 
 
-		Element **new_hash_table = memnew_arr(Element *, ((uint64_t)1 << new_hash_table_power));
-		ERR_FAIL_COND_MSG(!new_hash_table, "Out of memory.");
+		// Capacity can't be 0.
+		capacity_index = MAX((uint32_t)MIN_CAPACITY_INDEX, p_new_capacity_index);
 
 
-		for (int i = 0; i < (1 << new_hash_table_power); i++) {
-			new_hash_table[i] = nullptr;
-		}
+		uint32_t capacity = hash_table_size_primes[capacity_index];
 
 
-		if (hash_table) {
-			for (int i = 0; i < (1 << hash_table_power); i++) {
-				while (hash_table[i]) {
-					Element *se = hash_table[i];
-					hash_table[i] = se->next;
-					int new_pos = se->hash & ((1 << new_hash_table_power) - 1);
-					se->next = new_hash_table[new_pos];
-					new_hash_table[new_pos] = se;
-				}
-			}
+		HashMapElement<TKey, TValue> **old_elements = elements;
+		uint32_t *old_hashes = hashes;
 
 
-			memdelete_arr(hash_table);
-		}
-		hash_table = new_hash_table;
-		hash_table_power = new_hash_table_power;
-	}
+		num_elements = 0;
+		hashes = reinterpret_cast<uint32_t *>(Memory::alloc_static(sizeof(uint32_t) * capacity));
+		elements = reinterpret_cast<HashMapElement<TKey, TValue> **>(Memory::alloc_static(sizeof(HashMapElement<TKey, TValue> *) * capacity));
 
 
-	/* I want to have only one function.. */
-	_FORCE_INLINE_ const Element *get_element(const TKey &p_key) const {
-		uint32_t hash = Hasher::hash(p_key);
-		uint32_t index = hash & ((1 << hash_table_power) - 1);
+		for (uint32_t i = 0; i < capacity; i++) {
+			hashes[i] = 0;
+			elements[i] = nullptr;
+		}
 
 
-		Element *e = hash_table[index];
+		if (old_capacity == 0) {
+			// Nothing to do.
+			return;
+		}
 
 
-		while (e) {
-			/* checking hash first avoids comparing key, which may take longer */
-			if (e->hash == hash && Comparator::compare(e->pair.key, p_key)) {
-				/* the pair exists in this hashtable, so just update data */
-				return e;
+		for (uint32_t i = 0; i < old_capacity; i++) {
+			if (old_hashes[i] == EMPTY_HASH) {
+				continue;
 			}
 			}
 
 
-			e = e->next;
+			_insert_with_hash(old_hashes[i], old_elements[i]);
 		}
 		}
 
 
-		return nullptr;
-	}
-
-	Element *create_element(const TKey &p_key) {
-		/* if element doesn't exist, create it */
-		Element *e = memnew(Element(p_key));
-		ERR_FAIL_COND_V_MSG(!e, nullptr, "Out of memory.");
-		uint32_t hash = Hasher::hash(p_key);
-		uint32_t index = hash & ((1 << hash_table_power) - 1);
-		e->next = hash_table[index];
-		e->hash = hash;
-
-		hash_table[index] = e;
-		elements++;
-
-		return e;
+		Memory::free_static(old_elements);
+		Memory::free_static(old_hashes);
 	}
 	}
 
 
-	void copy_from(const HashMap &p_t) {
-		if (&p_t == this) {
-			return; /* much less bother with that */
-		}
+	_FORCE_INLINE_ HashMapElement<TKey, TValue> *_insert(const TKey &p_key, const TValue &p_value, bool p_front_insert = false) {
+		uint32_t capacity = hash_table_size_primes[capacity_index];
+		if (unlikely(elements == nullptr)) {
+			// Allocate on demand to save memory.
 
 
-		clear();
+			hashes = reinterpret_cast<uint32_t *>(Memory::alloc_static(sizeof(uint32_t) * capacity));
+			elements = reinterpret_cast<HashMapElement<TKey, TValue> **>(Memory::alloc_static(sizeof(HashMapElement<TKey, TValue> *) * capacity));
 
 
-		if (!p_t.hash_table || p_t.hash_table_power == 0) {
-			return; /* not copying from empty table */
+			for (uint32_t i = 0; i < capacity; i++) {
+				hashes[i] = EMPTY_HASH;
+				elements[i] = nullptr;
+			}
 		}
 		}
 
 
-		hash_table = memnew_arr(Element *, (uint64_t)1 << p_t.hash_table_power);
-		hash_table_power = p_t.hash_table_power;
-		elements = p_t.elements;
-
-		for (int i = 0; i < (1 << p_t.hash_table_power); i++) {
-			hash_table[i] = nullptr;
-
-			const Element *e = p_t.hash_table[i];
+		uint32_t pos = 0;
+		bool exists = _lookup_pos(p_key, pos);
 
 
-			while (e) {
-				Element *le = memnew(Element(*e)); /* local element */
+		if (exists) {
+			elements[pos]->data.value = p_value;
+			return elements[pos];
+		} else {
+			if (num_elements + 1 > MAX_OCCUPANCY * capacity) {
+				ERR_FAIL_COND_V_MSG(capacity_index + 1 == HASH_TABLE_SIZE_MAX, nullptr, "Hash table maximum capacity reached, aborting insertion.");
+				_resize_and_rehash(capacity_index + 1);
+			}
 
 
-				/* add to list and reassign pointers */
-				le->next = hash_table[i];
-				hash_table[i] = le;
+			HashMapElement<TKey, TValue> *elem = element_alloc.new_allocation(HashMapElement<TKey, TValue>(p_key, p_value));
 
 
-				e = e->next;
+			if (tail_element == nullptr) {
+				head_element = elem;
+				tail_element = elem;
+			} else if (p_front_insert) {
+				head_element->prev = elem;
+				elem->next = head_element;
+				head_element = elem;
+			} else {
+				tail_element->next = elem;
+				elem->prev = tail_element;
+				tail_element = elem;
 			}
 			}
+
+			uint32_t hash = _hash(p_key);
+			_insert_with_hash(hash, elem);
+			return elem;
 		}
 		}
 	}
 	}
 
 
 public:
 public:
-	Element *set(const TKey &p_key, const TData &p_data) {
-		return set(Pair(p_key, p_data));
-	}
+	_FORCE_INLINE_ uint32_t get_capacity() const { return hash_table_size_primes[capacity_index]; }
+	_FORCE_INLINE_ uint32_t size() const { return num_elements; }
 
 
-	Element *set(const Pair &p_pair) {
-		Element *e = nullptr;
-		if (!hash_table) {
-			make_hash_table(); // if no table, make one
-		} else {
-			e = const_cast<Element *>(get_element(p_pair.key));
-		}
+	/* Standard Godot Container API */
 
 
-		/* if we made it up to here, the pair doesn't exist, create and assign */
+	bool is_empty() const {
+		return num_elements == 0;
+	}
 
 
-		if (!e) {
-			e = create_element(p_pair.key);
-			if (!e) {
-				return nullptr;
-			}
-			check_hash_table(); // perform mantenience routine
+	void clear() {
+		if (elements == nullptr) {
+			return;
 		}
 		}
+		uint32_t capacity = hash_table_size_primes[capacity_index];
+		for (uint32_t i = 0; i < capacity; i++) {
+			if (hashes[i] == EMPTY_HASH) {
+				continue;
+			}
 
 
-		e->pair.data = p_pair.data;
-		return e;
-	}
+			hashes[i] = EMPTY_HASH;
+			element_alloc.delete_allocation(elements[i]);
+			elements[i] = nullptr;
+		}
 
 
-	bool has(const TKey &p_key) const {
-		return getptr(p_key) != nullptr;
+		tail_element = nullptr;
+		head_element = nullptr;
+		num_elements = 0;
 	}
 	}
 
 
-	/**
-	 * Get a key from data, return a const reference.
-	 * WARNING: this doesn't check errors, use either getptr and check nullptr, or check
-	 * first with has(key)
-	 */
-
-	const TData &get(const TKey &p_key) const {
-		const TData *res = getptr(p_key);
-		CRASH_COND_MSG(!res, "Map key not found.");
-		return *res;
+	TValue &get(const TKey &p_key) {
+		uint32_t pos = 0;
+		bool exists = _lookup_pos(p_key, pos);
+		CRASH_COND_MSG(!exists, "HashMap key not found.");
+		return elements[pos]->data.value;
 	}
 	}
 
 
-	TData &get(const TKey &p_key) {
-		TData *res = getptr(p_key);
-		CRASH_COND_MSG(!res, "Map key not found.");
-		return *res;
+	const TValue &get(const TKey &p_key) const {
+		uint32_t pos = 0;
+		bool exists = _lookup_pos(p_key, pos);
+		CRASH_COND_MSG(!exists, "HashMap key not found.");
+		return elements[pos]->data.value;
 	}
 	}
 
 
-	/**
-	 * Same as get, except it can return nullptr when item was not found.
-	 * This is mainly used for speed purposes.
-	 */
+	const TValue *getptr(const TKey &p_key) const {
+		uint32_t pos = 0;
+		bool exists = _lookup_pos(p_key, pos);
 
 
-	_FORCE_INLINE_ TData *getptr(const TKey &p_key) {
-		if (unlikely(!hash_table)) {
-			return nullptr;
+		if (exists) {
+			return &elements[pos]->data.value;
 		}
 		}
+		return nullptr;
+	}
 
 
-		Element *e = const_cast<Element *>(get_element(p_key));
+	TValue *getptr(const TKey &p_key) {
+		uint32_t pos = 0;
+		bool exists = _lookup_pos(p_key, pos);
 
 
-		if (e) {
-			return &e->pair.data;
+		if (exists) {
+			return &elements[pos]->data.value;
 		}
 		}
-
 		return nullptr;
 		return nullptr;
 	}
 	}
 
 
-	_FORCE_INLINE_ const TData *getptr(const TKey &p_key) const {
-		if (unlikely(!hash_table)) {
-			return nullptr;
-		}
+	_FORCE_INLINE_ bool has(const TKey &p_key) const {
+		uint32_t _pos = 0;
+		return _lookup_pos(p_key, _pos);
+	}
 
 
-		const Element *e = const_cast<Element *>(get_element(p_key));
+	bool erase(const TKey &p_key) {
+		uint32_t pos = 0;
+		bool exists = _lookup_pos(p_key, pos);
 
 
-		if (e) {
-			return &e->pair.data;
+		if (!exists) {
+			return false;
 		}
 		}
 
 
-		return nullptr;
-	}
+		uint32_t capacity = hash_table_size_primes[capacity_index];
+		uint32_t next_pos = (pos + 1) % capacity;
+		while (hashes[next_pos] != EMPTY_HASH && _get_probe_length(next_pos, hashes[next_pos], capacity) != 0) {
+			SWAP(hashes[next_pos], hashes[pos]);
+			SWAP(elements[next_pos], elements[pos]);
+			pos = next_pos;
+			next_pos = (pos + 1) % capacity;
+		}
 
 
-	/**
-	 * Same as get, except it can return nullptr when item was not found.
-	 * This version is custom, will take a hash and a custom key (that should support operator==()
-	 */
+		hashes[pos] = EMPTY_HASH;
 
 
-	template <class C>
-	_FORCE_INLINE_ TData *custom_getptr(C p_custom_key, uint32_t p_custom_hash) {
-		if (unlikely(!hash_table)) {
-			return nullptr;
+		if (head_element == elements[pos]) {
+			head_element = elements[pos]->next;
 		}
 		}
 
 
-		uint32_t hash = p_custom_hash;
-		uint32_t index = hash & ((1 << hash_table_power) - 1);
-
-		Element *e = hash_table[index];
+		if (tail_element == elements[pos]) {
+			tail_element = elements[pos]->prev;
+		}
 
 
-		while (e) {
-			/* checking hash first avoids comparing key, which may take longer */
-			if (e->hash == hash && Comparator::compare(e->pair.key, p_custom_key)) {
-				/* the pair exists in this hashtable, so just update data */
-				return &e->pair.data;
-			}
+		if (elements[pos]->prev) {
+			elements[pos]->prev->next = elements[pos]->next;
+		}
 
 
-			e = e->next;
+		if (elements[pos]->next) {
+			elements[pos]->next->prev = elements[pos]->prev;
 		}
 		}
 
 
-		return nullptr;
+		element_alloc.delete_allocation(elements[pos]);
+		elements[pos] = nullptr;
+
+		num_elements--;
+		return true;
 	}
 	}
 
 
-	template <class C>
-	_FORCE_INLINE_ const TData *custom_getptr(C p_custom_key, uint32_t p_custom_hash) const {
-		if (unlikely(!hash_table)) {
-			return nullptr;
+	// Reserves space for a number of elements, useful to avoid many resizes and rehashes.
+	// If adding a known (possibly large) number of elements at once, must be larger than old capacity.
+	void reserve(uint32_t p_new_capacity) {
+		uint32_t new_index = capacity_index;
+
+		while (hash_table_size_primes[new_index] < p_new_capacity) {
+			ERR_FAIL_COND_MSG(new_index + 1 == (uint32_t)HASH_TABLE_SIZE_MAX, nullptr);
+			new_index++;
 		}
 		}
 
 
-		uint32_t hash = p_custom_hash;
-		uint32_t index = hash & ((1 << hash_table_power) - 1);
+		if (new_index == capacity_index) {
+			return;
+		}
+
+		if (elements == nullptr) {
+			capacity_index = new_index;
+			return; // Unallocated yet.
+		}
+		_resize_and_rehash(new_index);
+	}
 
 
-		const Element *e = hash_table[index];
+	/** Iterator API **/
 
 
-		while (e) {
-			/* checking hash first avoids comparing key, which may take longer */
-			if (e->hash == hash && Comparator::compare(e->pair.key, p_custom_key)) {
-				/* the pair exists in this hashtable, so just update data */
-				return &e->pair.data;
+	struct ConstIterator {
+		_FORCE_INLINE_ const KeyValue<TKey, TValue> &operator*() const {
+			return E->data;
+		}
+		_FORCE_INLINE_ const KeyValue<TKey, TValue> *operator->() const { return &E->data; }
+		_FORCE_INLINE_ ConstIterator &operator++() {
+			if (E) {
+				E = E->next;
 			}
 			}
-
-			e = e->next;
+			return *this;
+		}
+		_FORCE_INLINE_ ConstIterator &operator--() {
+			if (E) {
+				E = E->prev;
+			}
+			return *this;
 		}
 		}
 
 
-		return nullptr;
-	}
+		_FORCE_INLINE_ bool operator==(const ConstIterator &b) const { return E == b.E; }
+		_FORCE_INLINE_ bool operator!=(const ConstIterator &b) const { return E != b.E; }
 
 
-	/**
-	 * Erase an item, return true if erasing was successful
-	 */
+		_FORCE_INLINE_ explicit operator bool() const {
+			return E != nullptr;
+		}
 
 
-	bool erase(const TKey &p_key) {
-		if (unlikely(!hash_table)) {
-			return false;
+		_FORCE_INLINE_ ConstIterator(const HashMapElement<TKey, TValue> *p_E) { E = p_E; }
+		_FORCE_INLINE_ ConstIterator() {}
+		_FORCE_INLINE_ ConstIterator(const ConstIterator &p_it) { E = p_it.E; }
+		_FORCE_INLINE_ void operator=(const ConstIterator &p_it) {
+			E = p_it.E;
 		}
 		}
 
 
-		uint32_t hash = Hasher::hash(p_key);
-		uint32_t index = hash & ((1 << hash_table_power) - 1);
-
-		Element *e = hash_table[index];
-		Element *p = nullptr;
-		while (e) {
-			/* checking hash first avoids comparing key, which may take longer */
-			if (e->hash == hash && Comparator::compare(e->pair.key, p_key)) {
-				if (p) {
-					p->next = e->next;
-				} else {
-					// begin of list
-					hash_table[index] = e->next;
-				}
-
-				memdelete(e);
-				elements--;
-
-				if (elements == 0) {
-					erase_hash_table();
-				} else {
-					check_hash_table();
-				}
-				return true;
+	private:
+		const HashMapElement<TKey, TValue> *E = nullptr;
+	};
+
+	struct Iterator {
+		_FORCE_INLINE_ KeyValue<TKey, TValue> &operator*() const {
+			return E->data;
+		}
+		_FORCE_INLINE_ KeyValue<TKey, TValue> *operator->() const { return &E->data; }
+		_FORCE_INLINE_ Iterator &operator++() {
+			if (E) {
+				E = E->next;
+			}
+			return *this;
+		}
+		_FORCE_INLINE_ Iterator &operator--() {
+			if (E) {
+				E = E->prev;
 			}
 			}
+			return *this;
+		}
+
+		_FORCE_INLINE_ bool operator==(const Iterator &b) const { return E == b.E; }
+		_FORCE_INLINE_ bool operator!=(const Iterator &b) const { return E != b.E; }
 
 
-			p = e;
-			e = e->next;
+		_FORCE_INLINE_ explicit operator bool() const {
+			return E != nullptr;
 		}
 		}
 
 
-		return false;
-	}
+		_FORCE_INLINE_ Iterator(HashMapElement<TKey, TValue> *p_E) { E = p_E; }
+		_FORCE_INLINE_ Iterator() {}
+		_FORCE_INLINE_ Iterator(const Iterator &p_it) { E = p_it.E; }
+		_FORCE_INLINE_ void operator=(const Iterator &p_it) {
+			E = p_it.E;
+		}
 
 
-	inline const TData &operator[](const TKey &p_key) const { // constref
+		operator ConstIterator() const {
+			return ConstIterator(E);
+		}
 
 
-		return get(p_key);
+	private:
+		HashMapElement<TKey, TValue> *E = nullptr;
+	};
+
+	_FORCE_INLINE_ Iterator begin() {
+		return Iterator(head_element);
+	}
+	_FORCE_INLINE_ Iterator end() {
+		return Iterator(nullptr);
+	}
+	_FORCE_INLINE_ Iterator last() {
+		return Iterator(tail_element);
 	}
 	}
-	inline TData &operator[](const TKey &p_key) { // assignment
 
 
-		Element *e = nullptr;
-		if (!hash_table) {
-			make_hash_table(); // if no table, make one
-		} else {
-			e = const_cast<Element *>(get_element(p_key));
+	_FORCE_INLINE_ Iterator find(const TKey &p_key) {
+		uint32_t pos = 0;
+		bool exists = _lookup_pos(p_key, pos);
+		if (!exists) {
+			return end();
 		}
 		}
+		return Iterator(elements[pos]);
+	}
 
 
-		/* if we made it up to here, the pair doesn't exist, create */
-		if (!e) {
-			e = create_element(p_key);
-			CRASH_COND(!e);
-			check_hash_table(); // perform mantenience routine
+	_FORCE_INLINE_ void remove(const Iterator &p_iter) {
+		if (p_iter) {
+			erase(p_iter->key);
 		}
 		}
+	}
 
 
-		return e->pair.data;
+	_FORCE_INLINE_ ConstIterator begin() const {
+		return ConstIterator(head_element);
+	}
+	_FORCE_INLINE_ ConstIterator end() const {
+		return ConstIterator(nullptr);
+	}
+	_FORCE_INLINE_ ConstIterator last() const {
+		return ConstIterator(tail_element);
 	}
 	}
 
 
-	/**
-	 * Get the next key to p_key, and the first key if p_key is null.
-	 * Returns a pointer to the next key if found, nullptr otherwise.
-	 * Adding/Removing elements while iterating will, of course, have unexpected results, don't do it.
-	 *
-	 * Example:
-	 *
-	 * 	const TKey *k=nullptr;
-	 *
-	 * 	while( (k=table.next(k)) ) {
-	 *
-	 * 		print( *k );
-	 * 	}
-	 *
-	 */
-	const TKey *next(const TKey *p_key) const {
-		if (unlikely(!hash_table)) {
-			return nullptr;
+	_FORCE_INLINE_ ConstIterator find(const TKey &p_key) const {
+		uint32_t pos = 0;
+		bool exists = _lookup_pos(p_key, pos);
+		if (!exists) {
+			return end();
 		}
 		}
+		return ConstIterator(elements[pos]);
+	}
 
 
-		if (!p_key) { /* get the first key */
-
-			for (int i = 0; i < (1 << hash_table_power); i++) {
-				if (hash_table[i]) {
-					return &hash_table[i]->pair.key;
-				}
-			}
-
-		} else { /* get the next key */
+	/* Indexing */
 
 
-			const Element *e = get_element(*p_key);
-			ERR_FAIL_COND_V_MSG(!e, nullptr, "Invalid key supplied.");
-			if (e->next) {
-				/* if there is a "next" in the list, return that */
-				return &e->next->pair.key;
-			} else {
-				/* go to next elements */
-				uint32_t index = e->hash & ((1 << hash_table_power) - 1);
-				index++;
-				for (int i = index; i < (1 << hash_table_power); i++) {
-					if (hash_table[i]) {
-						return &hash_table[i]->pair.key;
-					}
-				}
-			}
+	const TValue &operator[](const TKey &p_key) const {
+		uint32_t pos = 0;
+		bool exists = _lookup_pos(p_key, pos);
+		CRASH_COND(!exists);
+		return elements[pos]->data.value;
+	}
 
 
-			/* nothing found, was at end */
+	TValue &operator[](const TKey &p_key) {
+		uint32_t pos = 0;
+		bool exists = _lookup_pos(p_key, pos);
+		if (!exists) {
+			return _insert(p_key, TValue())->data.value;
+		} else {
+			return elements[pos]->data.value;
 		}
 		}
-
-		return nullptr; /* nothing found */
 	}
 	}
 
 
-	inline unsigned int size() const {
-		return elements;
-	}
+	/* Insert */
 
 
-	inline bool is_empty() const {
-		return elements == 0;
+	Iterator insert(const TKey &p_key, const TValue &p_value, bool p_front_insert = false) {
+		return Iterator(_insert(p_key, p_value, p_front_insert));
 	}
 	}
 
 
-	void clear() {
-		/* clean up */
-		if (hash_table) {
-			for (int i = 0; i < (1 << hash_table_power); i++) {
-				while (hash_table[i]) {
-					Element *e = hash_table[i];
-					hash_table[i] = e->next;
-					memdelete(e);
-				}
-			}
+	/* Constructors */
 
 
-			memdelete_arr(hash_table);
+	HashMap(const HashMap &p_other) {
+		reserve(hash_table_size_primes[p_other.capacity_index]);
+
+		if (p_other.num_elements == 0) {
+			return;
 		}
 		}
 
 
-		hash_table = nullptr;
-		hash_table_power = 0;
-		elements = 0;
+		for (const KeyValue<TKey, TValue> &E : p_other) {
+			insert(E.key, E.value);
+		}
 	}
 	}
 
 
-	void operator=(const HashMap &p_table) {
-		copy_from(p_table);
-	}
+	void operator=(const HashMap &p_other) {
+		if (this == &p_other) {
+			return; // Ignore self assignment.
+		}
+		if (num_elements != 0) {
+			clear();
+		}
 
 
-	void get_key_list(List<TKey> *r_keys) const {
-		if (unlikely(!hash_table)) {
-			return;
+		reserve(hash_table_size_primes[p_other.capacity_index]);
+
+		if (p_other.elements == nullptr) {
+			return; // Nothing to copy.
 		}
 		}
-		for (int i = 0; i < (1 << hash_table_power); i++) {
-			Element *e = hash_table[i];
-			while (e) {
-				r_keys->push_back(e->pair.key);
-				e = e->next;
-			}
+
+		for (const KeyValue<TKey, TValue> &E : p_other) {
+			insert(E.key, E.value);
 		}
 		}
 	}
 	}
 
 
-	HashMap() {}
+	HashMap(uint32_t p_initial_capacity) {
+		// Capacity can't be 0.
+		capacity_index = 0;
+		reserve(p_initial_capacity);
+	}
+	HashMap() {
+		capacity_index = MIN_CAPACITY_INDEX;
+	}
 
 
-	HashMap(const HashMap &p_table) {
-		copy_from(p_table);
+	uint32_t debug_get_hash(uint32_t p_index) {
+		if (num_elements == 0) {
+			return 0;
+		}
+		ERR_FAIL_INDEX_V(p_index, get_capacity(), 0);
+		return hashes[p_index];
+	}
+	Iterator debug_get_element(uint32_t p_index) {
+		if (num_elements == 0) {
+			return Iterator();
+		}
+		ERR_FAIL_INDEX_V(p_index, get_capacity(), Iterator());
+		return Iterator(elements[p_index]);
 	}
 	}
 
 
 	~HashMap() {
 	~HashMap() {
 		clear();
 		clear();
+
+		if (elements != nullptr) {
+			Memory::free_static(elements);
+			Memory::free_static(hashes);
+		}
 	}
 	}
 };
 };
 
 
 } // namespace godot
 } // namespace godot
 
 
-#endif // ! HASH_MAP_HPP
+#endif // HASH_MAP_HPP

+ 477 - 0
include/godot_cpp/templates/hash_set.hpp

@@ -0,0 +1,477 @@
+/*************************************************************************/
+/*  hash_set.hpp                                                         */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* 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.                */
+/*************************************************************************/
+
+#ifndef HASH_SET_HPP
+#define HASH_SET_HPP
+
+#include <godot_cpp/core/error_macros.hpp>
+#include <godot_cpp/core/memory.hpp>
+#include <godot_cpp/templates/hash_map.hpp>
+#include <godot_cpp/templates/hashfuncs.hpp>
+#include <godot_cpp/templates/pair.hpp>
+
+namespace godot {
+
+/**
+ * Implementation of Set using a bidi indexed hash map.
+ * Use RBSet instead of this only if the following conditions are met:
+ *
+ * - You need to keep an iterator or const pointer to Key and you intend to add/remove elements in the meantime.
+ * - Iteration order does matter (via operator<)
+ *
+ */
+
+template <class TKey,
+		class Hasher = HashMapHasherDefault,
+		class Comparator = HashMapComparatorDefault<TKey>>
+class HashSet {
+public:
+	static constexpr uint32_t MIN_CAPACITY_INDEX = 2; // Use a prime.
+	static constexpr float MAX_OCCUPANCY = 0.75;
+	static constexpr uint32_t EMPTY_HASH = 0;
+
+private:
+	TKey *keys = nullptr;
+	uint32_t *hash_to_key = nullptr;
+	uint32_t *key_to_hash = nullptr;
+	uint32_t *hashes = nullptr;
+
+	uint32_t capacity_index = 0;
+	uint32_t num_elements = 0;
+
+	_FORCE_INLINE_ uint32_t _hash(const TKey &p_key) const {
+		uint32_t hash = Hasher::hash(p_key);
+
+		if (unlikely(hash == EMPTY_HASH)) {
+			hash = EMPTY_HASH + 1;
+		}
+
+		return hash;
+	}
+
+	_FORCE_INLINE_ uint32_t _get_probe_length(uint32_t p_pos, uint32_t p_hash, uint32_t p_capacity) const {
+		uint32_t original_pos = p_hash % p_capacity;
+		return (p_pos - original_pos + p_capacity) % p_capacity;
+	}
+
+	bool _lookup_pos(const TKey &p_key, uint32_t &r_pos) const {
+		if (keys == nullptr) {
+			return false; // Failed lookups, no elements
+		}
+
+		uint32_t capacity = hash_table_size_primes[capacity_index];
+		uint32_t hash = _hash(p_key);
+		uint32_t pos = hash % capacity;
+		uint32_t distance = 0;
+
+		while (true) {
+			if (hashes[pos] == EMPTY_HASH) {
+				return false;
+			}
+
+			if (distance > _get_probe_length(pos, hashes[pos], capacity)) {
+				return false;
+			}
+
+			if (hashes[pos] == hash && Comparator::compare(keys[hash_to_key[pos]], p_key)) {
+				r_pos = hash_to_key[pos];
+				return true;
+			}
+
+			pos = (pos + 1) % capacity;
+			distance++;
+		}
+	}
+
+	uint32_t _insert_with_hash(uint32_t p_hash, uint32_t p_index) {
+		uint32_t capacity = hash_table_size_primes[capacity_index];
+		uint32_t hash = p_hash;
+		uint32_t index = p_index;
+		uint32_t distance = 0;
+		uint32_t pos = hash % capacity;
+
+		while (true) {
+			if (hashes[pos] == EMPTY_HASH) {
+				hashes[pos] = hash;
+				key_to_hash[index] = pos;
+				hash_to_key[pos] = index;
+				return pos;
+			}
+
+			// Not an empty slot, let's check the probing length of the existing one.
+			uint32_t existing_probe_len = _get_probe_length(pos, hashes[pos], capacity);
+			if (existing_probe_len < distance) {
+				key_to_hash[index] = pos;
+				SWAP(hash, hashes[pos]);
+				SWAP(index, hash_to_key[pos]);
+				distance = existing_probe_len;
+			}
+
+			pos = (pos + 1) % capacity;
+			distance++;
+		}
+	}
+
+	void _resize_and_rehash(uint32_t p_new_capacity_index) {
+		// Capacity can't be 0.
+		capacity_index = MAX((uint32_t)MIN_CAPACITY_INDEX, p_new_capacity_index);
+
+		uint32_t capacity = hash_table_size_primes[capacity_index];
+
+		uint32_t *old_hashes = hashes;
+		uint32_t *old_key_to_hash = key_to_hash;
+
+		hashes = reinterpret_cast<uint32_t *>(Memory::alloc_static(sizeof(uint32_t) * capacity));
+		keys = reinterpret_cast<TKey *>(Memory::realloc_static(keys, sizeof(TKey) * capacity));
+		key_to_hash = reinterpret_cast<uint32_t *>(Memory::alloc_static(sizeof(uint32_t) * capacity));
+		hash_to_key = reinterpret_cast<uint32_t *>(Memory::realloc_static(hash_to_key, sizeof(uint32_t) * capacity));
+
+		for (uint32_t i = 0; i < capacity; i++) {
+			hashes[i] = EMPTY_HASH;
+		}
+
+		for (uint32_t i = 0; i < num_elements; i++) {
+			uint32_t h = old_hashes[old_key_to_hash[i]];
+			_insert_with_hash(h, i);
+		}
+
+		Memory::free_static(old_hashes);
+		Memory::free_static(old_key_to_hash);
+	}
+
+	_FORCE_INLINE_ int32_t _insert(const TKey &p_key) {
+		uint32_t capacity = hash_table_size_primes[capacity_index];
+		if (unlikely(keys == nullptr)) {
+			// Allocate on demand to save memory.
+
+			hashes = reinterpret_cast<uint32_t *>(Memory::alloc_static(sizeof(uint32_t) * capacity));
+			keys = reinterpret_cast<TKey *>(Memory::alloc_static(sizeof(TKey) * capacity));
+			key_to_hash = reinterpret_cast<uint32_t *>(Memory::alloc_static(sizeof(uint32_t) * capacity));
+			hash_to_key = reinterpret_cast<uint32_t *>(Memory::alloc_static(sizeof(uint32_t) * capacity));
+
+			for (uint32_t i = 0; i < capacity; i++) {
+				hashes[i] = EMPTY_HASH;
+			}
+		}
+
+		uint32_t pos = 0;
+		bool exists = _lookup_pos(p_key, pos);
+
+		if (exists) {
+			return pos;
+		} else {
+			if (num_elements + 1 > MAX_OCCUPANCY * capacity) {
+				ERR_FAIL_COND_V_MSG(capacity_index + 1 == HASH_TABLE_SIZE_MAX, -1, "Hash table maximum capacity reached, aborting insertion.");
+				_resize_and_rehash(capacity_index + 1);
+			}
+
+			uint32_t hash = _hash(p_key);
+			memnew_placement(&keys[num_elements], TKey(p_key));
+			_insert_with_hash(hash, num_elements);
+			num_elements++;
+			return num_elements - 1;
+		}
+	}
+
+	void _init_from(const HashSet &p_other) {
+		capacity_index = p_other.capacity_index;
+		num_elements = p_other.num_elements;
+
+		if (p_other.num_elements == 0) {
+			return;
+		}
+
+		uint32_t capacity = hash_table_size_primes[capacity_index];
+
+		hashes = reinterpret_cast<uint32_t *>(Memory::alloc_static(sizeof(uint32_t) * capacity));
+		keys = reinterpret_cast<TKey *>(Memory::alloc_static(sizeof(TKey) * capacity));
+		key_to_hash = reinterpret_cast<uint32_t *>(Memory::alloc_static(sizeof(uint32_t) * capacity));
+		hash_to_key = reinterpret_cast<uint32_t *>(Memory::alloc_static(sizeof(uint32_t) * capacity));
+
+		for (uint32_t i = 0; i < num_elements; i++) {
+			memnew_placement(&keys[i], TKey(p_other.keys[i]));
+			key_to_hash[i] = p_other.key_to_hash[i];
+		}
+
+		for (uint32_t i = 0; i < capacity; i++) {
+			hashes[i] = p_other.hashes[i];
+			hash_to_key[i] = p_other.hash_to_key[i];
+		}
+	}
+
+public:
+	_FORCE_INLINE_ uint32_t get_capacity() const { return hash_table_size_primes[capacity_index]; }
+	_FORCE_INLINE_ uint32_t size() const { return num_elements; }
+
+	/* Standard Godot Container API */
+
+	bool is_empty() const {
+		return num_elements == 0;
+	}
+
+	void clear() {
+		if (keys == nullptr) {
+			return;
+		}
+		uint32_t capacity = hash_table_size_primes[capacity_index];
+		for (uint32_t i = 0; i < capacity; i++) {
+			hashes[i] = EMPTY_HASH;
+		}
+		for (uint32_t i = 0; i < num_elements; i++) {
+			keys[i].~TKey();
+		}
+
+		num_elements = 0;
+	}
+
+	_FORCE_INLINE_ bool has(const TKey &p_key) const {
+		uint32_t _pos = 0;
+		return _lookup_pos(p_key, _pos);
+	}
+
+	bool erase(const TKey &p_key) {
+		uint32_t pos = 0;
+		bool exists = _lookup_pos(p_key, pos);
+
+		if (!exists) {
+			return false;
+		}
+
+		uint32_t key_pos = pos;
+		pos = key_to_hash[pos]; // make hash pos
+
+		uint32_t capacity = hash_table_size_primes[capacity_index];
+		uint32_t next_pos = (pos + 1) % capacity;
+		while (hashes[next_pos] != EMPTY_HASH && _get_probe_length(next_pos, hashes[next_pos], capacity) != 0) {
+			uint32_t kpos = hash_to_key[pos];
+			uint32_t kpos_next = hash_to_key[next_pos];
+			SWAP(key_to_hash[kpos], key_to_hash[kpos_next]);
+			SWAP(hashes[next_pos], hashes[pos]);
+			SWAP(hash_to_key[next_pos], hash_to_key[pos]);
+
+			pos = next_pos;
+			next_pos = (pos + 1) % capacity;
+		}
+
+		hashes[pos] = EMPTY_HASH;
+		keys[key_pos].~TKey();
+		num_elements--;
+		if (key_pos < num_elements) {
+			// Not the last key, move the last one here to keep keys lineal
+			memnew_placement(&keys[key_pos], TKey(keys[num_elements]));
+			keys[num_elements].~TKey();
+			key_to_hash[key_pos] = key_to_hash[num_elements];
+			hash_to_key[key_to_hash[num_elements]] = key_pos;
+		}
+
+		return true;
+	}
+
+	// Reserves space for a number of elements, useful to avoid many resizes and rehashes.
+	// If adding a known (possibly large) number of elements at once, must be larger than old capacity.
+	void reserve(uint32_t p_new_capacity) {
+		uint32_t new_index = capacity_index;
+
+		while (hash_table_size_primes[new_index] < p_new_capacity) {
+			ERR_FAIL_COND_MSG(new_index + 1 == (uint32_t)HASH_TABLE_SIZE_MAX, nullptr);
+			new_index++;
+		}
+
+		if (new_index == capacity_index) {
+			return;
+		}
+
+		if (keys == nullptr) {
+			capacity_index = new_index;
+			return; // Unallocated yet.
+		}
+		_resize_and_rehash(new_index);
+	}
+
+	/** Iterator API **/
+
+	struct Iterator {
+		_FORCE_INLINE_ const TKey &operator*() const {
+			return keys[index];
+		}
+		_FORCE_INLINE_ const TKey *operator->() const {
+			return &keys[index];
+		}
+		_FORCE_INLINE_ Iterator &operator++() {
+			index++;
+			if (index >= (int32_t)num_keys) {
+				index = -1;
+				keys = nullptr;
+				num_keys = 0;
+			}
+			return *this;
+		}
+		_FORCE_INLINE_ Iterator &operator--() {
+			index--;
+			if (index < 0) {
+				index = -1;
+				keys = nullptr;
+				num_keys = 0;
+			}
+			return *this;
+		}
+
+		_FORCE_INLINE_ bool operator==(const Iterator &b) const { return keys == b.keys && index == b.index; }
+		_FORCE_INLINE_ bool operator!=(const Iterator &b) const { return keys != b.keys || index != b.index; }
+
+		_FORCE_INLINE_ explicit operator bool() const {
+			return keys != nullptr;
+		}
+
+		_FORCE_INLINE_ Iterator(const TKey *p_keys, uint32_t p_num_keys, int32_t p_index = -1) {
+			keys = p_keys;
+			num_keys = p_num_keys;
+			index = p_index;
+		}
+		_FORCE_INLINE_ Iterator() {}
+		_FORCE_INLINE_ Iterator(const Iterator &p_it) {
+			keys = p_it.keys;
+			num_keys = p_it.num_keys;
+			index = p_it.index;
+		}
+		_FORCE_INLINE_ void operator=(const Iterator &p_it) {
+			keys = p_it.keys;
+			num_keys = p_it.num_keys;
+			index = p_it.index;
+		}
+
+	private:
+		const TKey *keys = nullptr;
+		uint32_t num_keys = 0;
+		int32_t index = -1;
+	};
+
+	_FORCE_INLINE_ Iterator begin() const {
+		return num_elements ? Iterator(keys, num_elements, 0) : Iterator();
+	}
+	_FORCE_INLINE_ Iterator end() const {
+		return Iterator();
+	}
+	_FORCE_INLINE_ Iterator last() const {
+		if (num_elements == 0) {
+			return Iterator();
+		}
+		return Iterator(keys, num_elements, num_elements - 1);
+	}
+
+	_FORCE_INLINE_ Iterator find(const TKey &p_key) const {
+		uint32_t pos = 0;
+		bool exists = _lookup_pos(p_key, pos);
+		if (!exists) {
+			return end();
+		}
+		return Iterator(keys, num_elements, pos);
+	}
+
+	_FORCE_INLINE_ void remove(const Iterator &p_iter) {
+		if (p_iter) {
+			erase(*p_iter);
+		}
+	}
+
+	/* Insert */
+
+	Iterator insert(const TKey &p_key) {
+		uint32_t pos = _insert(p_key);
+		return Iterator(keys, num_elements, pos);
+	}
+
+	/* Constructors */
+
+	HashSet(const HashSet &p_other) {
+		_init_from(p_other);
+	}
+
+	void operator=(const HashSet &p_other) {
+		if (this == &p_other) {
+			return; // Ignore self assignment.
+		}
+
+		clear();
+
+		if (keys != nullptr) {
+			Memory::free_static(keys);
+			Memory::free_static(key_to_hash);
+			Memory::free_static(hash_to_key);
+			Memory::free_static(hashes);
+			keys = nullptr;
+			hashes = nullptr;
+			hash_to_key = nullptr;
+			key_to_hash = nullptr;
+		}
+
+		_init_from(p_other);
+	}
+
+	HashSet(uint32_t p_initial_capacity) {
+		// Capacity can't be 0.
+		capacity_index = 0;
+		reserve(p_initial_capacity);
+	}
+	HashSet() {
+		capacity_index = MIN_CAPACITY_INDEX;
+	}
+
+	void reset() {
+		clear();
+
+		if (keys != nullptr) {
+			Memory::free_static(keys);
+			Memory::free_static(key_to_hash);
+			Memory::free_static(hash_to_key);
+			Memory::free_static(hashes);
+			keys = nullptr;
+			hashes = nullptr;
+			hash_to_key = nullptr;
+			key_to_hash = nullptr;
+		}
+		capacity_index = MIN_CAPACITY_INDEX;
+	}
+
+	~HashSet() {
+		clear();
+
+		if (keys != nullptr) {
+			Memory::free_static(keys);
+			Memory::free_static(key_to_hash);
+			Memory::free_static(hash_to_key);
+			Memory::free_static(hashes);
+		}
+	}
+};
+
+} // namespace godot
+
+#endif // HASH_SET_HPP

+ 128 - 3
include/godot_cpp/templates/hashfuncs.hpp

@@ -32,7 +32,19 @@
 #define HASHFUNCS_HPP
 #define HASHFUNCS_HPP
 
 
 #include <godot_cpp/core/math.hpp>
 #include <godot_cpp/core/math.hpp>
+#include <godot_cpp/core/object.hpp>
+#include <godot_cpp/variant/aabb.hpp>
+#include <godot_cpp/variant/node_path.hpp>
+#include <godot_cpp/variant/rect2.hpp>
+#include <godot_cpp/variant/rect2i.hpp>
 #include <godot_cpp/variant/rid.hpp>
 #include <godot_cpp/variant/rid.hpp>
+#include <godot_cpp/variant/string.hpp>
+#include <godot_cpp/variant/string_name.hpp>
+#include <godot_cpp/variant/variant.hpp>
+#include <godot_cpp/variant/vector2.hpp>
+#include <godot_cpp/variant/vector2i.hpp>
+#include <godot_cpp/variant/vector3.hpp>
+#include <godot_cpp/variant/vector3i.hpp>
 
 
 /**
 /**
  * Hashing functions
  * Hashing functions
@@ -152,9 +164,14 @@ static inline uint64_t make_uint64_t(T p_in) {
 	return _u._u64;
 	return _u._u64;
 }
 }
 
 
+template <class T>
+class Ref;
+
 struct HashMapHasherDefault {
 struct HashMapHasherDefault {
+	static _FORCE_INLINE_ uint32_t hash(const String &p_string) { return p_string.hash(); }
 	static _FORCE_INLINE_ uint32_t hash(const char *p_cstr) { return hash_djb2(p_cstr); }
 	static _FORCE_INLINE_ uint32_t hash(const char *p_cstr) { return hash_djb2(p_cstr); }
 	static _FORCE_INLINE_ uint32_t hash(const uint64_t p_int) { return hash_one_uint64(p_int); }
 	static _FORCE_INLINE_ uint32_t hash(const uint64_t p_int) { return hash_one_uint64(p_int); }
+	static _FORCE_INLINE_ uint32_t hash(const ObjectID &p_id) { return hash_one_uint64(p_id); }
 
 
 	static _FORCE_INLINE_ uint32_t hash(const int64_t p_int) { return hash(uint64_t(p_int)); }
 	static _FORCE_INLINE_ uint32_t hash(const int64_t p_int) { return hash(uint64_t(p_int)); }
 	static _FORCE_INLINE_ uint32_t hash(const float p_float) { return hash_djb2_one_float(p_float); }
 	static _FORCE_INLINE_ uint32_t hash(const float p_float) { return hash_djb2_one_float(p_float); }
@@ -169,6 +186,60 @@ struct HashMapHasherDefault {
 	static _FORCE_INLINE_ uint32_t hash(const char16_t p_uchar) { return (uint32_t)p_uchar; }
 	static _FORCE_INLINE_ uint32_t hash(const char16_t p_uchar) { return (uint32_t)p_uchar; }
 	static _FORCE_INLINE_ uint32_t hash(const char32_t p_uchar) { return (uint32_t)p_uchar; }
 	static _FORCE_INLINE_ uint32_t hash(const char32_t p_uchar) { return (uint32_t)p_uchar; }
 	static _FORCE_INLINE_ uint32_t hash(const RID &p_rid) { return hash_one_uint64(p_rid.get_id()); }
 	static _FORCE_INLINE_ uint32_t hash(const RID &p_rid) { return hash_one_uint64(p_rid.get_id()); }
+
+	static _FORCE_INLINE_ uint32_t hash(const StringName &p_string_name) { return p_string_name.hash(); }
+	static _FORCE_INLINE_ uint32_t hash(const NodePath &p_path) { return p_path.hash(); }
+
+	template <class T>
+	static _FORCE_INLINE_ uint32_t hash(const T *p_pointer) { return hash_one_uint64((uint64_t)p_pointer); }
+
+	template <class T>
+	static _FORCE_INLINE_ uint32_t hash(const Ref<T> &p_ref) { return hash_one_uint64((uint64_t)p_ref.operator->()); }
+
+	static _FORCE_INLINE_ uint32_t hash(const Vector2i &p_vec) {
+		uint32_t h = hash_djb2_one_32(p_vec.x);
+		return hash_djb2_one_32(p_vec.y, h);
+	}
+	static _FORCE_INLINE_ uint32_t hash(const Vector3i &p_vec) {
+		uint32_t h = hash_djb2_one_32(p_vec.x);
+		h = hash_djb2_one_32(p_vec.y, h);
+		return hash_djb2_one_32(p_vec.z, h);
+	}
+
+	static _FORCE_INLINE_ uint32_t hash(const Vector2 &p_vec) {
+		uint32_t h = hash_djb2_one_float(p_vec.x);
+		return hash_djb2_one_float(p_vec.y, h);
+	}
+	static _FORCE_INLINE_ uint32_t hash(const Vector3 &p_vec) {
+		uint32_t h = hash_djb2_one_float(p_vec.x);
+		h = hash_djb2_one_float(p_vec.y, h);
+		return hash_djb2_one_float(p_vec.z, h);
+	}
+
+	static _FORCE_INLINE_ uint32_t hash(const Rect2i &p_rect) {
+		uint32_t h = hash_djb2_one_32(p_rect.position.x);
+		h = hash_djb2_one_32(p_rect.position.y, h);
+		h = hash_djb2_one_32(p_rect.size.x, h);
+		return hash_djb2_one_32(p_rect.size.y, h);
+	}
+
+	static _FORCE_INLINE_ uint32_t hash(const Rect2 &p_rect) {
+		uint32_t h = hash_djb2_one_float(p_rect.position.x);
+		h = hash_djb2_one_float(p_rect.position.y, h);
+		h = hash_djb2_one_float(p_rect.size.x, h);
+		return hash_djb2_one_float(p_rect.size.y, h);
+	}
+
+	static _FORCE_INLINE_ uint32_t hash(const AABB &p_aabb) {
+		uint32_t h = hash_djb2_one_float(p_aabb.position.x);
+		h = hash_djb2_one_float(p_aabb.position.y, h);
+		h = hash_djb2_one_float(p_aabb.position.z, h);
+		h = hash_djb2_one_float(p_aabb.size.x, h);
+		h = hash_djb2_one_float(p_aabb.size.y, h);
+		return hash_djb2_one_float(p_aabb.size.z, h);
+	}
+
+	// static _FORCE_INLINE_ uint32_t hash(const void* p_ptr)  { return uint32_t(uint64_t(p_ptr))*(0x9e3779b1L); }
 };
 };
 
 
 template <typename T>
 template <typename T>
@@ -176,16 +247,70 @@ struct HashMapComparatorDefault {
 	static bool compare(const T &p_lhs, const T &p_rhs) {
 	static bool compare(const T &p_lhs, const T &p_rhs) {
 		return p_lhs == p_rhs;
 		return p_lhs == p_rhs;
 	}
 	}
+};
 
 
-	bool compare(const float &p_lhs, const float &p_rhs) {
+template <>
+struct HashMapComparatorDefault<float> {
+	static bool compare(const float &p_lhs, const float &p_rhs) {
 		return (p_lhs == p_rhs) || (std::isnan(p_lhs) && std::isnan(p_rhs));
 		return (p_lhs == p_rhs) || (std::isnan(p_lhs) && std::isnan(p_rhs));
 	}
 	}
+};
 
 
-	bool compare(const double &p_lhs, const double &p_rhs) {
+template <>
+struct HashMapComparatorDefault<double> {
+	static bool compare(const double &p_lhs, const double &p_rhs) {
 		return (p_lhs == p_rhs) || (std::isnan(p_lhs) && std::isnan(p_rhs));
 		return (p_lhs == p_rhs) || (std::isnan(p_lhs) && std::isnan(p_rhs));
 	}
 	}
 };
 };
 
 
+template <>
+struct HashMapComparatorDefault<Vector2> {
+	static bool compare(const Vector2 &p_lhs, const Vector2 &p_rhs) {
+		return ((p_lhs.x == p_rhs.x) || (std::isnan(p_lhs.x) && std::isnan(p_rhs.x))) && ((p_lhs.y == p_rhs.y) || (std::isnan(p_lhs.y) && std::isnan(p_rhs.y)));
+	}
+};
+
+template <>
+struct HashMapComparatorDefault<Vector3> {
+	static bool compare(const Vector3 &p_lhs, const Vector3 &p_rhs) {
+		return ((p_lhs.x == p_rhs.x) || (std::isnan(p_lhs.x) && std::isnan(p_rhs.x))) && ((p_lhs.y == p_rhs.y) || (std::isnan(p_lhs.y) && std::isnan(p_rhs.y))) && ((p_lhs.z == p_rhs.z) || (std::isnan(p_lhs.z) && std::isnan(p_rhs.z)));
+	}
+};
+
+constexpr uint32_t HASH_TABLE_SIZE_MAX = 29;
+
+const uint32_t hash_table_size_primes[HASH_TABLE_SIZE_MAX] = {
+	5,
+	13,
+	23,
+	47,
+	97,
+	193,
+	389,
+	769,
+	1543,
+	3079,
+	6151,
+	12289,
+	24593,
+	49157,
+	98317,
+	196613,
+	393241,
+	786433,
+	1572869,
+	3145739,
+	6291469,
+	12582917,
+	25165843,
+	50331653,
+	100663319,
+	201326611,
+	402653189,
+	805306457,
+	1610612741,
+};
+
 } // namespace godot
 } // namespace godot
 
 
-#endif // ! HASHFUNCS_HPP
+#endif // HASHFUNCS_HPP

+ 26 - 18
include/godot_cpp/templates/map.hpp → include/godot_cpp/templates/rb_map.hpp

@@ -1,5 +1,5 @@
 /*************************************************************************/
 /*************************************************************************/
-/*  map.hpp                                                              */
+/*  rb_map.hpp                                                           */
 /*************************************************************************/
 /*************************************************************************/
 /*                       This file is part of:                           */
 /*                       This file is part of:                           */
 /*                           GODOT ENGINE                                */
 /*                           GODOT ENGINE                                */
@@ -28,8 +28,8 @@
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 /*************************************************************************/
 /*************************************************************************/
 
 
-#ifndef MAP_HPP
-#define MAP_HPP
+#ifndef RB_MAP_HPP
+#define RB_MAP_HPP
 
 
 #include <godot_cpp/core/error_macros.hpp>
 #include <godot_cpp/core/error_macros.hpp>
 #include <godot_cpp/core/memory.hpp>
 #include <godot_cpp/core/memory.hpp>
@@ -41,7 +41,7 @@ namespace godot {
 // https://web.archive.org/web/20120507164830/https://web.mit.edu/~emin/www/source_code/red_black_tree/index.html
 // https://web.archive.org/web/20120507164830/https://web.mit.edu/~emin/www/source_code/red_black_tree/index.html
 
 
 template <class K, class V, class C = Comparator<K>, class A = DefaultAllocator>
 template <class K, class V, class C = Comparator<K>, class A = DefaultAllocator>
-class Map {
+class RBMap {
 	enum Color {
 	enum Color {
 		RED,
 		RED,
 		BLACK
 		BLACK
@@ -51,7 +51,7 @@ class Map {
 public:
 public:
 	class Element {
 	class Element {
 	private:
 	private:
-		friend class Map<K, V, C, A>;
+		friend class RBMap<K, V, C, A>;
 		int color = RED;
 		int color = RED;
 		Element *right = nullptr;
 		Element *right = nullptr;
 		Element *left = nullptr;
 		Element *left = nullptr;
@@ -113,7 +113,9 @@ public:
 
 
 		_FORCE_INLINE_ bool operator==(const Iterator &b) const { return E == b.E; }
 		_FORCE_INLINE_ bool operator==(const Iterator &b) const { return E == b.E; }
 		_FORCE_INLINE_ bool operator!=(const Iterator &b) const { return E != b.E; }
 		_FORCE_INLINE_ bool operator!=(const Iterator &b) const { return E != b.E; }
-
+		explicit operator bool() const {
+			return E != nullptr;
+		}
 		Iterator(Element *p_E) { E = p_E; }
 		Iterator(Element *p_E) { E = p_E; }
 		Iterator() {}
 		Iterator() {}
 		Iterator(const Iterator &p_it) { E = p_it.E; }
 		Iterator(const Iterator &p_it) { E = p_it.E; }
@@ -138,7 +140,9 @@ public:
 
 
 		_FORCE_INLINE_ bool operator==(const ConstIterator &b) const { return E == b.E; }
 		_FORCE_INLINE_ bool operator==(const ConstIterator &b) const { return E == b.E; }
 		_FORCE_INLINE_ bool operator!=(const ConstIterator &b) const { return E != b.E; }
 		_FORCE_INLINE_ bool operator!=(const ConstIterator &b) const { return E != b.E; }
-
+		explicit operator bool() const {
+			return E != nullptr;
+		}
 		ConstIterator(const Element *p_E) { E = p_E; }
 		ConstIterator(const Element *p_E) { E = p_E; }
 		ConstIterator() {}
 		ConstIterator() {}
 		ConstIterator(const ConstIterator &p_it) { E = p_it.E; }
 		ConstIterator(const ConstIterator &p_it) { E = p_it.E; }
@@ -180,7 +184,7 @@ public:
 private:
 private:
 	struct _Data {
 	struct _Data {
 		Element *_root = nullptr;
 		Element *_root = nullptr;
-		Element *_nil;
+		Element *_nil = nullptr;
 		int size_cache = 0;
 		int size_cache = 0;
 
 
 		_FORCE_INLINE_ _Data() {
 		_FORCE_INLINE_ _Data() {
@@ -346,7 +350,7 @@ private:
 	void _insert_rb_fix(Element *p_new_node) {
 	void _insert_rb_fix(Element *p_new_node) {
 		Element *node = p_new_node;
 		Element *node = p_new_node;
 		Element *nparent = node->parent;
 		Element *nparent = node->parent;
-		Element *ngrand_parent;
+		Element *ngrand_parent = nullptr;
 
 
 		while (nparent->color == RED) {
 		while (nparent->color == RED) {
 			ngrand_parent = nparent->parent;
 			ngrand_parent = nparent->parent;
@@ -502,7 +506,7 @@ private:
 		Element *rp = ((p_node->left == _data._nil) || (p_node->right == _data._nil)) ? p_node : p_node->_next;
 		Element *rp = ((p_node->left == _data._nil) || (p_node->right == _data._nil)) ? p_node : p_node->_next;
 		Element *node = (rp->left == _data._nil) ? rp->right : rp->left;
 		Element *node = (rp->left == _data._nil) ? rp->right : rp->left;
 
 
-		Element *sibling;
+		Element *sibling = nullptr;
 		if (rp == rp->parent->left) {
 		if (rp == rp->parent->left) {
 			rp->parent->left = node;
 			rp->parent->left = node;
 			sibling = rp->parent->right;
 			sibling = rp->parent->right;
@@ -574,7 +578,7 @@ private:
 		memdelete_allocator<Element, A>(p_element);
 		memdelete_allocator<Element, A>(p_element);
 	}
 	}
 
 
-	void _copy_from(const Map &p_map) {
+	void _copy_from(const RBMap &p_map) {
 		clear();
 		clear();
 		// not the fastest way, but safeset to write.
 		// not the fastest way, but safeset to write.
 		for (Element *I = p_map.front(); I; I = I->next()) {
 		for (Element *I = p_map.front(); I; I = I->next()) {
@@ -712,8 +716,12 @@ public:
 		return e;
 		return e;
 	}
 	}
 
 
-	inline bool is_empty() const { return _data.size_cache == 0; }
-	inline int size() const { return _data.size_cache; }
+	inline bool is_empty() const {
+		return _data.size_cache == 0;
+	}
+	inline int size() const {
+		return _data.size_cache;
+	}
 
 
 	int calculate_depth() const {
 	int calculate_depth() const {
 		// used for debug mostly
 		// used for debug mostly
@@ -737,21 +745,21 @@ public:
 		_data._free_root();
 		_data._free_root();
 	}
 	}
 
 
-	void operator=(const Map &p_map) {
+	void operator=(const RBMap &p_map) {
 		_copy_from(p_map);
 		_copy_from(p_map);
 	}
 	}
 
 
-	Map(const Map &p_map) {
+	RBMap(const RBMap &p_map) {
 		_copy_from(p_map);
 		_copy_from(p_map);
 	}
 	}
 
 
-	_FORCE_INLINE_ Map() {}
+	_FORCE_INLINE_ RBMap() {}
 
 
-	~Map() {
+	~RBMap() {
 		clear();
 		clear();
 	}
 	}
 };
 };
 
 
 } // namespace godot
 } // namespace godot
 
 
-#endif // ! MAP_HPP
+#endif // MAP_HPP

+ 22 - 15
include/godot_cpp/templates/set.hpp → include/godot_cpp/templates/rb_set.hpp

@@ -1,5 +1,5 @@
 /*************************************************************************/
 /*************************************************************************/
-/*  set.hpp                                                              */
+/*  rb_set.hpp                                                           */
 /*************************************************************************/
 /*************************************************************************/
 /*                       This file is part of:                           */
 /*                       This file is part of:                           */
 /*                           GODOT ENGINE                                */
 /*                           GODOT ENGINE                                */
@@ -28,8 +28,8 @@
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 /*************************************************************************/
 /*************************************************************************/
 
 
-#ifndef SET_HPP
-#define SET_HPP
+#ifndef RB_SET_HPP
+#define RB_SET_HPP
 
 
 #include <godot_cpp/core/memory.hpp>
 #include <godot_cpp/core/memory.hpp>
 
 
@@ -39,7 +39,7 @@
 namespace godot {
 namespace godot {
 
 
 template <class T, class C = Comparator<T>, class A = DefaultAllocator>
 template <class T, class C = Comparator<T>, class A = DefaultAllocator>
-class Set {
+class RBSet {
 	enum Color {
 	enum Color {
 		RED,
 		RED,
 		BLACK
 		BLACK
@@ -49,7 +49,7 @@ class Set {
 public:
 public:
 	class Element {
 	class Element {
 	private:
 	private:
-		friend class Set<T, C, A>;
+		friend class RBSet<T, C, A>;
 		int color = RED;
 		int color = RED;
 		Element *right = nullptr;
 		Element *right = nullptr;
 		Element *left = nullptr;
 		Element *left = nullptr;
@@ -100,6 +100,7 @@ public:
 		_FORCE_INLINE_ bool operator==(const Iterator &b) const { return E == b.E; }
 		_FORCE_INLINE_ bool operator==(const Iterator &b) const { return E == b.E; }
 		_FORCE_INLINE_ bool operator!=(const Iterator &b) const { return E != b.E; }
 		_FORCE_INLINE_ bool operator!=(const Iterator &b) const { return E != b.E; }
 
 
+		explicit operator bool() const { return E != nullptr; }
 		Iterator(Element *p_E) { E = p_E; }
 		Iterator(Element *p_E) { E = p_E; }
 		Iterator() {}
 		Iterator() {}
 		Iterator(const Iterator &p_it) { E = p_it.E; }
 		Iterator(const Iterator &p_it) { E = p_it.E; }
@@ -129,6 +130,8 @@ public:
 		_FORCE_INLINE_ ConstIterator() {}
 		_FORCE_INLINE_ ConstIterator() {}
 		_FORCE_INLINE_ ConstIterator(const ConstIterator &p_it) { E = p_it.E; }
 		_FORCE_INLINE_ ConstIterator(const ConstIterator &p_it) { E = p_it.E; }
 
 
+		explicit operator bool() const { return E != nullptr; }
+
 	private:
 	private:
 		const Element *E = nullptr;
 		const Element *E = nullptr;
 	};
 	};
@@ -329,7 +332,7 @@ private:
 	void _insert_rb_fix(Element *p_new_node) {
 	void _insert_rb_fix(Element *p_new_node) {
 		Element *node = p_new_node;
 		Element *node = p_new_node;
 		Element *nparent = node->parent;
 		Element *nparent = node->parent;
-		Element *ngrand_parent;
+		Element *ngrand_parent = nullptr;
 
 
 		while (nparent->color == RED) {
 		while (nparent->color == RED) {
 			ngrand_parent = nparent->parent;
 			ngrand_parent = nparent->parent;
@@ -483,7 +486,7 @@ private:
 		Element *rp = ((p_node->left == _data._nil) || (p_node->right == _data._nil)) ? p_node : p_node->_next;
 		Element *rp = ((p_node->left == _data._nil) || (p_node->right == _data._nil)) ? p_node : p_node->_next;
 		Element *node = (rp->left == _data._nil) ? rp->right : rp->left;
 		Element *node = (rp->left == _data._nil) ? rp->right : rp->left;
 
 
-		Element *sibling;
+		Element *sibling = nullptr;
 		if (rp == rp->parent->left) {
 		if (rp == rp->parent->left) {
 			rp->parent->left = node;
 			rp->parent->left = node;
 			sibling = rp->parent->right;
 			sibling = rp->parent->right;
@@ -555,7 +558,7 @@ private:
 		memdelete_allocator<Element, A>(p_element);
 		memdelete_allocator<Element, A>(p_element);
 	}
 	}
 
 
-	void _copy_from(const Set &p_set) {
+	void _copy_from(const RBSet &p_set) {
 		clear();
 		clear();
 		// not the fastest way, but safeset to write.
 		// not the fastest way, but safeset to write.
 		for (Element *I = p_set.front(); I; I = I->next()) {
 		for (Element *I = p_set.front(); I; I = I->next()) {
@@ -662,8 +665,12 @@ public:
 		return e;
 		return e;
 	}
 	}
 
 
-	inline bool is_empty() const { return _data.size_cache == 0; }
-	inline int size() const { return _data.size_cache; }
+	inline bool is_empty() const {
+		return _data.size_cache == 0;
+	}
+	inline int size() const {
+		return _data.size_cache;
+	}
 
 
 	int calculate_depth() const {
 	int calculate_depth() const {
 		// used for debug mostly
 		// used for debug mostly
@@ -687,21 +694,21 @@ public:
 		_data._free_root();
 		_data._free_root();
 	}
 	}
 
 
-	void operator=(const Set &p_set) {
+	void operator=(const RBSet &p_set) {
 		_copy_from(p_set);
 		_copy_from(p_set);
 	}
 	}
 
 
-	Set(const Set &p_set) {
+	RBSet(const RBSet &p_set) {
 		_copy_from(p_set);
 		_copy_from(p_set);
 	}
 	}
 
 
-	_FORCE_INLINE_ Set() {}
+	_FORCE_INLINE_ RBSet() {}
 
 
-	~Set() {
+	~RBSet() {
 		clear();
 		clear();
 	}
 	}
 };
 };
 
 
 } // namespace godot
 } // namespace godot
 
 
-#endif // ! SET_HPP
+#endif // SET_HPP

+ 15 - 5
include/godot_cpp/variant/variant.hpp

@@ -102,14 +102,14 @@ public:
 	};
 	};
 
 
 	enum Operator {
 	enum Operator {
-		//comparison
+		// comparison
 		OP_EQUAL,
 		OP_EQUAL,
 		OP_NOT_EQUAL,
 		OP_NOT_EQUAL,
 		OP_LESS,
 		OP_LESS,
 		OP_LESS_EQUAL,
 		OP_LESS_EQUAL,
 		OP_GREATER,
 		OP_GREATER,
 		OP_GREATER_EQUAL,
 		OP_GREATER_EQUAL,
-		//mathematic
+		// mathematic
 		OP_ADD,
 		OP_ADD,
 		OP_SUBTRACT,
 		OP_SUBTRACT,
 		OP_MULTIPLY,
 		OP_MULTIPLY,
@@ -117,19 +117,19 @@ public:
 		OP_NEGATE,
 		OP_NEGATE,
 		OP_POSITIVE,
 		OP_POSITIVE,
 		OP_MODULE,
 		OP_MODULE,
-		//bitwise
+		// bitwise
 		OP_SHIFT_LEFT,
 		OP_SHIFT_LEFT,
 		OP_SHIFT_RIGHT,
 		OP_SHIFT_RIGHT,
 		OP_BIT_AND,
 		OP_BIT_AND,
 		OP_BIT_OR,
 		OP_BIT_OR,
 		OP_BIT_XOR,
 		OP_BIT_XOR,
 		OP_BIT_NEGATE,
 		OP_BIT_NEGATE,
-		//logic
+		// logic
 		OP_AND,
 		OP_AND,
 		OP_OR,
 		OP_OR,
 		OP_XOR,
 		OP_XOR,
 		OP_NOT,
 		OP_NOT,
-		//containment
+		// containment
 		OP_IN,
 		OP_IN,
 		OP_MAX
 		OP_MAX
 	};
 	};
@@ -285,6 +285,8 @@ public:
 	bool has_key(const Variant &key, bool *r_valid = nullptr) const;
 	bool has_key(const Variant &key, bool *r_valid = nullptr) const;
 	static bool has_member(Variant::Type type, const StringName &member);
 	static bool has_member(Variant::Type type, const StringName &member);
 
 
+	uint32_t hash() const;
+	uint32_t recursive_hash(int recursion_count) const;
 	bool hash_compare(const Variant &variant) const;
 	bool hash_compare(const Variant &variant) const;
 	bool booleanize() const;
 	bool booleanize() const;
 	String stringify() const;
 	String stringify() const;
@@ -299,6 +301,14 @@ public:
 	void clear();
 	void clear();
 };
 };
 
 
+struct VariantHasher {
+	static _FORCE_INLINE_ uint32_t hash(const Variant &p_variant) { return p_variant.hash(); }
+};
+
+struct VariantComparator {
+	static _FORCE_INLINE_ bool compare(const Variant &p_lhs, const Variant &p_rhs) { return p_lhs.hash_compare(p_rhs); }
+};
+
 } // namespace godot
 } // namespace godot
 
 
 #endif // ! GODOT_CPP_VARIANT_HPP
 #endif // ! GODOT_CPP_VARIANT_HPP

+ 45 - 35
src/variant/variant.cpp

@@ -629,6 +629,16 @@ bool Variant::has_member(Variant::Type type, const StringName &member) {
 	return PtrToArg<bool>::convert(&has);
 	return PtrToArg<bool>::convert(&has);
 }
 }
 
 
+uint32_t Variant::hash() const {
+	GDNativeInt hash = internal::gdn_interface->variant_hash(_native_ptr());
+	return PtrToArg<uint32_t>::convert(&hash);
+}
+
+uint32_t Variant::recursive_hash(int recursion_count) const {
+	GDNativeInt hash = internal::gdn_interface->variant_recursive_hash(_native_ptr(), recursion_count);
+	return PtrToArg<uint32_t>::convert(&hash);
+}
+
 bool Variant::hash_compare(const Variant &variant) const {
 bool Variant::hash_compare(const Variant &variant) const {
 	GDNativeBool compare = internal::gdn_interface->variant_hash_compare(_native_ptr(), variant._native_ptr());
 	GDNativeBool compare = internal::gdn_interface->variant_hash_compare(_native_ptr(), variant._native_ptr());
 	return PtrToArg<bool>::convert(&compare);
 	return PtrToArg<bool>::convert(&compare);
@@ -681,45 +691,45 @@ bool Variant::can_convert_strict(Variant::Type from, Variant::Type to) {
 
 
 void Variant::clear() {
 void Variant::clear() {
 	static const bool needs_deinit[Variant::VARIANT_MAX] = {
 	static const bool needs_deinit[Variant::VARIANT_MAX] = {
-		false, //NIL,
-		false, //BOOL,
-		false, //INT,
-		false, //FLOAT,
-		true, //STRING,
-		false, //VECTOR2,
-		false, //VECTOR2I,
-		false, //RECT2,
-		false, //RECT2I,
-		false, //VECTOR3,
-		false, //VECTOR3I,
-		true, //TRANSFORM2D,
-		false, //PLANE,
-		false, //QUATERNION,
-		true, //AABB,
-		true, //BASIS,
-		true, //TRANSFORM,
+		false, // NIL,
+		false, // BOOL,
+		false, // INT,
+		false, // FLOAT,
+		true, // STRING,
+		false, // VECTOR2,
+		false, // VECTOR2I,
+		false, // RECT2,
+		false, // RECT2I,
+		false, // VECTOR3,
+		false, // VECTOR3I,
+		true, // TRANSFORM2D,
+		false, // PLANE,
+		false, // QUATERNION,
+		true, // AABB,
+		true, // BASIS,
+		true, // TRANSFORM,
 
 
 		// misc types
 		// misc types
-		false, //COLOR,
-		true, //STRING_NAME,
-		true, //NODE_PATH,
-		false, //RID,
-		true, //OBJECT,
-		true, //CALLABLE,
-		true, //SIGNAL,
-		true, //DICTIONARY,
-		true, //ARRAY,
+		false, // COLOR,
+		true, // STRING_NAME,
+		true, // NODE_PATH,
+		false, // RID,
+		true, // OBJECT,
+		true, // CALLABLE,
+		true, // SIGNAL,
+		true, // DICTIONARY,
+		true, // ARRAY,
 
 
 		// typed arrays
 		// typed arrays
-		true, //PACKED_BYTE_ARRAY,
-		true, //PACKED_INT32_ARRAY,
-		true, //PACKED_INT64_ARRAY,
-		true, //PACKED_FLOAT32_ARRAY,
-		true, //PACKED_FLOAT64_ARRAY,
-		true, //PACKED_STRING_ARRAY,
-		true, //PACKED_VECTOR2_ARRAY,
-		true, //PACKED_VECTOR3_ARRAY,
-		true, //PACKED_COLOR_ARRAY,
+		true, // PACKED_BYTE_ARRAY,
+		true, // PACKED_INT32_ARRAY,
+		true, // PACKED_INT64_ARRAY,
+		true, // PACKED_FLOAT32_ARRAY,
+		true, // PACKED_FLOAT64_ARRAY,
+		true, // PACKED_STRING_ARRAY,
+		true, // PACKED_VECTOR2_ARRAY,
+		true, // PACKED_VECTOR3_ARRAY,
+		true, // PACKED_COLOR_ARRAY,
 	};
 	};
 
 
 	if (unlikely(needs_deinit[get_type()])) { // Make it fast for types that don't need deinit.
 	if (unlikely(needs_deinit[get_type()])) { // Make it fast for types that don't need deinit.

Some files were not shown because too many files changed in this diff