diff --git a/core/templates/hash_map.h b/core/templates/hash_map.h index 4da73f1cfb76..e1745110d7fe 100644 --- a/core/templates/hash_map.h +++ b/core/templates/hash_map.h @@ -353,6 +353,40 @@ class HashMap { return true; } + // Replace the key of an entry in-place, without invalidating iterators or changing the entries position during iteration. + // p_old_key must exist in the map and p_new_key must not, unless it is equal to p_old_key. + bool replace_key(const TKey &p_old_key, const TKey &p_new_key) { + if (p_old_key == p_new_key) { + return true; + } + uint32_t pos = 0; + ERR_FAIL_COND_V(_lookup_pos(p_new_key, pos), false); + ERR_FAIL_COND_V(!_lookup_pos(p_old_key, pos), false); + HashMapElement *element = elements[pos]; + + // Delete the old entries in hashes and elements. + const uint32_t capacity = hash_table_size_primes[capacity_index]; + const uint64_t capacity_inv = hash_table_size_primes_inv[capacity_index]; + uint32_t next_pos = fastmod((pos + 1), capacity_inv, capacity); + while (hashes[next_pos] != EMPTY_HASH && _get_probe_length(next_pos, hashes[next_pos], capacity, capacity_inv) != 0) { + SWAP(hashes[next_pos], hashes[pos]); + SWAP(elements[next_pos], elements[pos]); + pos = next_pos; + next_pos = fastmod((pos + 1), capacity_inv, capacity); + } + hashes[pos] = EMPTY_HASH; + elements[pos] = nullptr; + // _insert_with_hash will increment this again. + num_elements--; + + // Update the HashMapElement with the new key and reinsert it. + const_cast(element->data.key) = p_new_key; + uint32_t hash = _hash(p_new_key); + _insert_with_hash(hash, element); + + 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) { diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 515bd60ccaa6..7239216ba954 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -1137,7 +1137,6 @@ void Node::_set_name_nocheck(const StringName &p_name) { void Node::set_name(const String &p_name) { ERR_FAIL_COND_MSG(data.inside_tree && !Thread::is_main_thread(), "Changing the name to nodes inside the SceneTree is only allowed from the main thread. Use `set_name.call_deferred(new_name)`."); - ERR_FAIL_COND_MSG(data.parent && data.parent->data.blocked > 0, "Parent node is busy setting up children, `set_name(new_name)` failed. Consider using `set_name.call_deferred(new_name)` instead."); String name = p_name.validate_node_name(); ERR_FAIL_COND(name.is_empty()); @@ -1149,9 +1148,9 @@ void Node::set_name(const String &p_name) { data.name = name; if (data.parent) { - data.parent->data.children.erase(old_name); data.parent->_validate_child_name(this, true); - data.parent->data.children.insert(data.name, this); + bool success = data.parent->data.children.replace_key(old_name, data.name); + ERR_FAIL_COND_MSG(!success, "Renaming child in hashtable failed, this is a bug."); } if (data.unique_name_in_owner && data.owner) {