diff options
Diffstat (limited to 'core')
43 files changed, 702 insertions, 84 deletions
diff --git a/core/config/project_settings.h b/core/config/project_settings.h index bacc8adc54..d9b1a9b81b 100644 --- a/core/config/project_settings.h +++ b/core/config/project_settings.h @@ -91,7 +91,7 @@ protected: bool using_datapack = false; List<String> input_presets; - RBSet<String> custom_features; + HashSet<String> custom_features; HashMap<StringName, StringName> feature_overrides; HashMap<StringName, AutoloadInfo> autoloads; diff --git a/core/core_constants.cpp b/core/core_constants.cpp index a53929a3af..ea1304f5ba 100644 --- a/core/core_constants.cpp +++ b/core/core_constants.cpp @@ -645,7 +645,7 @@ void register_global_constants() { // rpc BIND_CORE_ENUM_CONSTANT_CUSTOM("RPC_MODE_DISABLED", Multiplayer::RPC_MODE_DISABLED); BIND_CORE_ENUM_CONSTANT_CUSTOM("RPC_MODE_ANY_PEER", Multiplayer::RPC_MODE_ANY_PEER); - BIND_CORE_ENUM_CONSTANT_CUSTOM("RPC_MODE_AUTH", Multiplayer::RPC_MODE_AUTHORITY); + BIND_CORE_ENUM_CONSTANT_CUSTOM("RPC_MODE_AUTHORITY", Multiplayer::RPC_MODE_AUTHORITY); BIND_CORE_ENUM_CONSTANT_CUSTOM("TRANSFER_MODE_UNRELIABLE", Multiplayer::TRANSFER_MODE_UNRELIABLE); BIND_CORE_ENUM_CONSTANT_CUSTOM("TRANSFER_MODE_UNRELIABLE_ORDERED", Multiplayer::TRANSFER_MODE_UNRELIABLE_ORDERED); diff --git a/core/debugger/local_debugger.cpp b/core/debugger/local_debugger.cpp index f378ba94c3..06e08081e9 100644 --- a/core/debugger/local_debugger.cpp +++ b/core/debugger/local_debugger.cpp @@ -241,15 +241,15 @@ void LocalDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { } else if (line.begins_with("br") || line.begins_with("break")) { if (line.get_slice_count(" ") <= 1) { - const HashMap<int, RBSet<StringName>> &breakpoints = script_debugger->get_breakpoints(); + const HashMap<int, HashSet<StringName>> &breakpoints = script_debugger->get_breakpoints(); if (breakpoints.size() == 0) { print_line("No Breakpoints."); continue; } print_line("Breakpoint(s): " + itos(breakpoints.size())); - for (const KeyValue<int, RBSet<StringName>> &E : breakpoints) { - print_line("\t" + String(E.value.front()->get()) + ":" + itos(E.key)); + for (const KeyValue<int, HashSet<StringName>> &E : breakpoints) { + print_line("\t" + String(*E.value.begin()) + ":" + itos(E.key)); } } else { diff --git a/core/debugger/script_debugger.cpp b/core/debugger/script_debugger.cpp index 1efa7f7690..e30f3e7886 100644 --- a/core/debugger/script_debugger.cpp +++ b/core/debugger/script_debugger.cpp @@ -50,7 +50,7 @@ int ScriptDebugger::get_depth() const { void ScriptDebugger::insert_breakpoint(int p_line, const StringName &p_source) { if (!breakpoints.has(p_line)) { - breakpoints[p_line] = RBSet<StringName>(); + breakpoints[p_line] = HashSet<StringName>(); } breakpoints[p_line].insert(p_source); } diff --git a/core/debugger/script_debugger.h b/core/debugger/script_debugger.h index a5a72d7c54..5124b357a5 100644 --- a/core/debugger/script_debugger.h +++ b/core/debugger/script_debugger.h @@ -33,8 +33,8 @@ #include "core/object/script_language.h" #include "core/string/string_name.h" +#include "core/templates/hash_set.h" #include "core/templates/rb_map.h" -#include "core/templates/rb_set.h" #include "core/templates/vector.h" class ScriptDebugger { @@ -44,7 +44,7 @@ class ScriptDebugger { int depth = -1; bool skip_breakpoints = false; - HashMap<int, RBSet<StringName>> breakpoints; + HashMap<int, HashSet<StringName>> breakpoints; ScriptLanguage *break_lang = nullptr; Vector<StackInfo> error_stack_info; @@ -66,7 +66,7 @@ public: bool is_breakpoint(int p_line, const StringName &p_source) const; bool is_breakpoint_line(int p_line) const; void clear_breakpoints(); - const HashMap<int, RBSet<StringName>> &get_breakpoints() const { return breakpoints; } + const HashMap<int, HashSet<StringName>> &get_breakpoints() const { return breakpoints; } void debug(ScriptLanguage *p_lang, bool p_can_continue = true, bool p_is_error_breakpoint = false); ScriptLanguage *get_break_language() const; diff --git a/core/extension/gdnative_interface.cpp b/core/extension/gdnative_interface.cpp index a1d54f9c6d..58103e3af3 100644 --- a/core/extension/gdnative_interface.cpp +++ b/core/extension/gdnative_interface.cpp @@ -230,6 +230,16 @@ static void gdnative_variant_iter_get(const GDNativeVariantPtr p_self, GDNativeV } /// Variant functions. +static GDNativeInt gdnative_variant_hash(const GDNativeVariantPtr p_self) { + const Variant *self = (const Variant *)p_self; + return self->hash(); +} + +static GDNativeInt gdnative_variant_recursive_hash(const GDNativeVariantPtr p_self, GDNativeInt p_recursion_count) { + const Variant *self = (const Variant *)p_self; + return self->recursive_hash(p_recursion_count); +} + static GDNativeBool gdnative_variant_hash_compare(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_other) { const Variant *self = (const Variant *)p_self; const Variant *other = (const Variant *)p_other; @@ -944,6 +954,8 @@ void gdnative_setup_interface(GDNativeInterface *p_interface) { gdni.variant_iter_init = gdnative_variant_iter_init; gdni.variant_iter_next = gdnative_variant_iter_next; gdni.variant_iter_get = gdnative_variant_iter_get; + gdni.variant_hash = gdnative_variant_hash; + gdni.variant_recursive_hash = gdnative_variant_recursive_hash; gdni.variant_hash_compare = gdnative_variant_hash_compare; gdni.variant_booleanize = gdnative_variant_booleanize; gdni.variant_sub = gdnative_variant_sub; diff --git a/core/extension/gdnative_interface.h b/core/extension/gdnative_interface.h index 98976b29f6..095c7983ee 100644 --- a/core/extension/gdnative_interface.h +++ b/core/extension/gdnative_interface.h @@ -413,6 +413,8 @@ typedef struct { 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); 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_booleanize)(const GDNativeVariantPtr p_self); void (*variant_sub)(const GDNativeVariantPtr p_a, const GDNativeVariantPtr p_b, GDNativeVariantPtr r_dst); diff --git a/core/input/input.cpp b/core/input/input.cpp index 4befdfac58..b3a68bb98c 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -134,8 +134,12 @@ void Input::_bind_methods() { ClassDB::bind_method(D_METHOD("set_custom_mouse_cursor", "image", "shape", "hotspot"), &Input::set_custom_mouse_cursor, DEFVAL(CURSOR_ARROW), DEFVAL(Vector2())); ClassDB::bind_method(D_METHOD("parse_input_event", "event"), &Input::parse_input_event); ClassDB::bind_method(D_METHOD("set_use_accumulated_input", "enable"), &Input::set_use_accumulated_input); + ClassDB::bind_method(D_METHOD("is_using_accumulated_input"), &Input::is_using_accumulated_input); ClassDB::bind_method(D_METHOD("flush_buffered_events"), &Input::flush_buffered_events); + ADD_PROPERTY(PropertyInfo(Variant::INT, "mouse_mode"), "set_mouse_mode", "get_mouse_mode"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_accumulated_input"), "set_use_accumulated_input", "is_using_accumulated_input"); + BIND_ENUM_CONSTANT(MOUSE_MODE_VISIBLE); BIND_ENUM_CONSTANT(MOUSE_MODE_HIDDEN); BIND_ENUM_CONSTANT(MOUSE_MODE_CAPTURED); @@ -897,6 +901,10 @@ void Input::set_use_accumulated_input(bool p_enable) { use_accumulated_input = p_enable; } +bool Input::is_using_accumulated_input() { + return use_accumulated_input; +} + void Input::release_pressed_events() { flush_buffered_events(); // this is needed to release actions strengths @@ -1401,8 +1409,8 @@ String Input::get_joy_guid(int p_device) const { return joy_names[p_device].uid; } -Array Input::get_connected_joypads() { - Array ret; +TypedArray<int> Input::get_connected_joypads() { + TypedArray<int> ret; HashMap<int, Joypad>::Iterator elem = joy_names.begin(); while (elem) { if (elem->value.connected) { diff --git a/core/input/input.h b/core/input/input.h index 7bb7889a43..f02f2abae5 100644 --- a/core/input/input.h +++ b/core/input/input.h @@ -35,6 +35,8 @@ #include "core/object/object.h" #include "core/os/keyboard.h" #include "core/os/thread_safe.h" +#include "core/templates/rb_set.h" +#include "core/variant/typed_array.h" class Input : public Object { GDCLASS(Input, Object); @@ -258,7 +260,7 @@ public: float get_joy_axis(int p_device, JoyAxis p_axis) const; String get_joy_name(int p_idx); - Array get_connected_joypads(); + TypedArray<int> get_connected_joypads(); Vector2 get_joy_vibration_strength(int p_device); float get_joy_vibration_duration(int p_device); uint64_t get_joy_vibration_timestamp(int p_device); @@ -325,6 +327,7 @@ public: bool is_using_input_buffering(); void set_use_input_buffering(bool p_enable); void set_use_accumulated_input(bool p_enable); + bool is_using_accumulated_input(); void release_pressed_events(); diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h index 404ad38c96..19a0cce796 100644 --- a/core/io/file_access_pack.h +++ b/core/io/file_access_pack.h @@ -34,9 +34,9 @@ #include "core/io/dir_access.h" #include "core/io/file_access.h" #include "core/string/print_string.h" +#include "core/templates/hash_set.h" #include "core/templates/list.h" #include "core/templates/rb_map.h" -#include "core/templates/rb_set.h" // Godot's packed file magic header ("GDPC" in ASCII). #define PACK_HEADER_MAGIC 0x43504447 @@ -73,7 +73,7 @@ private: PackedDir *parent = nullptr; String name; HashMap<String, PackedDir *> subdirs; - RBSet<String> files; + HashSet<String> files; }; struct PathMD5 { diff --git a/core/io/json.cpp b/core/io/json.cpp index b3a9762e75..4c4d91f851 100644 --- a/core/io/json.cpp +++ b/core/io/json.cpp @@ -55,7 +55,7 @@ String JSON::_make_indent(const String &p_indent, int p_size) { return indent_text; } -String JSON::_stringify(const Variant &p_var, const String &p_indent, int p_cur_indent, bool p_sort_keys, RBSet<const void *> &p_markers, bool p_full_precision) { +String JSON::_stringify(const Variant &p_var, const String &p_indent, int p_cur_indent, bool p_sort_keys, HashSet<const void *> &p_markers, bool p_full_precision) { String colon = ":"; String end_statement = ""; @@ -529,7 +529,7 @@ Error JSON::_parse_string(const String &p_json, Variant &r_ret, String &r_err_st } String JSON::stringify(const Variant &p_var, const String &p_indent, bool p_sort_keys, bool p_full_precision) { - RBSet<const void *> markers; + HashSet<const void *> markers; return _stringify(p_var, p_indent, 0, p_sort_keys, markers, p_full_precision); } diff --git a/core/io/json.h b/core/io/json.h index f883d3963a..6ba0a8c76b 100644 --- a/core/io/json.h +++ b/core/io/json.h @@ -70,7 +70,7 @@ class JSON : public RefCounted { static const char *tk_name[]; static String _make_indent(const String &p_indent, int p_size); - static String _stringify(const Variant &p_var, const String &p_indent, int p_cur_indent, bool p_sort_keys, RBSet<const void *> &p_markers, bool p_full_precision = false); + static String _stringify(const Variant &p_var, const String &p_indent, int p_cur_indent, bool p_sort_keys, HashSet<const void *> &p_markers, bool p_full_precision = false); static Error _get_token(const char32_t *p_str, int &index, int p_len, Token &r_token, int &line, String &r_err_str); static Error _parse_value(Variant &value, Token &token, const char32_t *p_str, int &index, int p_len, int &line, String &r_err_str); static Error _parse_array(Array &array, const char32_t *p_str, int &index, int p_len, int &line, String &r_err_str); diff --git a/core/io/logger.cpp b/core/io/logger.cpp index 925bfdbd02..5820ec0c09 100644 --- a/core/io/logger.cpp +++ b/core/io/logger.cpp @@ -128,7 +128,7 @@ void RotatedFileLogger::clear_old_backups() { da->list_dir_begin(); String f = da->get_next(); - RBSet<String> backups; + HashSet<String> backups; while (!f.is_empty()) { if (!da->current_is_dir() && f.begins_with(basename) && f.get_extension() == extension && f != base_path.get_file()) { backups.insert(f); @@ -137,12 +137,12 @@ void RotatedFileLogger::clear_old_backups() { } da->list_dir_end(); - if (backups.size() > max_backups) { + if (backups.size() > (uint32_t)max_backups) { // since backups are appended with timestamp and Set iterates them in sorted order, // first backups are the oldest int to_delete = backups.size() - max_backups; - for (RBSet<String>::Element *E = backups.front(); E && to_delete > 0; E = E->next(), --to_delete) { - da->remove(E->get()); + for (HashSet<String>::Iterator E = backups.begin(); E && to_delete > 0; ++E, --to_delete) { + da->remove(*E); } } } @@ -172,7 +172,7 @@ void RotatedFileLogger::rotate_file() { } file = FileAccess::open(base_path, FileAccess::WRITE); - file->detach_from_objectdb(); // Note: This FileAccess instance will exist longer than ObjectDB, therefor can't be registered in ObjectDB. + file->detach_from_objectdb(); // Note: This FileAccess instance will exist longer than ObjectDB, therefore can't be registered in ObjectDB. } RotatedFileLogger::RotatedFileLogger(const String &p_base_path, int p_max_files) : diff --git a/core/io/resource.h b/core/io/resource.h index 53c828f9cd..a45bc6e1e4 100644 --- a/core/io/resource.h +++ b/core/io/resource.h @@ -54,7 +54,7 @@ public: virtual String get_base_extension() const { return "res"; } private: - RBSet<ObjectID> owners; + HashSet<ObjectID> owners; friend class ResBase; friend class ResourceCache; diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp index cf87869a32..24458f20b4 100644 --- a/core/io/resource_format_binary.cpp +++ b/core/io/resource_format_binary.cpp @@ -1976,7 +1976,7 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const Ref<Re } if (p.pi.type == Variant::OBJECT && missing_resource_properties.has(F.name)) { - // Was this missing resource overriden? If so do not save the old value. + // Was this missing resource overridden? If so do not save the old value. Ref<Resource> res = p.value; if (res.is_null()) { p.value = missing_resource_properties[F.name]; @@ -2022,7 +2022,7 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const Ref<Re // save internal resource table f->store_32(saved_resources.size()); //amount of internal resources Vector<uint64_t> ofs_pos; - RBSet<String> used_unique_ids; + HashSet<String> used_unique_ids; for (Ref<Resource> &r : saved_resources) { if (r->is_built_in()) { diff --git a/core/io/resource_format_binary.h b/core/io/resource_format_binary.h index db29909dd5..5da880ddb8 100644 --- a/core/io/resource_format_binary.h +++ b/core/io/resource_format_binary.h @@ -127,7 +127,7 @@ class ResourceFormatSaverBinaryInstance { bool big_endian; bool takeover_paths; String magic; - RBSet<Ref<Resource>> resource_set; + HashSet<Ref<Resource>> resource_set; struct NonPersistentKey { //for resource properties generated on the fly Ref<Resource> base; diff --git a/core/io/resource_importer.cpp b/core/io/resource_importer.cpp index 5deee9721b..934cb780e6 100644 --- a/core/io/resource_importer.cpp +++ b/core/io/resource_importer.cpp @@ -139,7 +139,7 @@ Ref<Resource> ResourceFormatImporter::load(const String &p_path, const String &p } void ResourceFormatImporter::get_recognized_extensions(List<String> *p_extensions) const { - RBSet<String> found; + HashSet<String> found; for (int i = 0; i < importers.size(); i++) { List<String> local_exts; @@ -159,7 +159,7 @@ void ResourceFormatImporter::get_recognized_extensions_for_type(const String &p_ return; } - RBSet<String> found; + HashSet<String> found; for (int i = 0; i < importers.size(); i++) { String res_type = importers[i]->get_resource_type(); diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h index e189ad1dff..815dd1dd72 100644 --- a/core/io/resource_loader.h +++ b/core/io/resource_loader.h @@ -145,7 +145,7 @@ private: bool start_next = true; int requests = 0; int poll_requests = 0; - RBSet<String> sub_tasks; + HashSet<String> sub_tasks; }; static void _thread_load_function(void *p_userdata); diff --git a/core/io/stream_peer_ssl.cpp b/core/io/stream_peer_ssl.cpp index ea8435e587..5b90fb52a6 100644 --- a/core/io/stream_peer_ssl.cpp +++ b/core/io/stream_peer_ssl.cpp @@ -60,6 +60,7 @@ void StreamPeerSSL::_bind_methods() { ClassDB::bind_method(D_METHOD("accept_stream", "stream", "private_key", "certificate", "chain"), &StreamPeerSSL::accept_stream, DEFVAL(Ref<X509Certificate>())); ClassDB::bind_method(D_METHOD("connect_to_stream", "stream", "validate_certs", "for_hostname", "valid_certificate"), &StreamPeerSSL::connect_to_stream, DEFVAL(false), DEFVAL(String()), DEFVAL(Ref<X509Certificate>())); ClassDB::bind_method(D_METHOD("get_status"), &StreamPeerSSL::get_status); + ClassDB::bind_method(D_METHOD("get_stream"), &StreamPeerSSL::get_stream); ClassDB::bind_method(D_METHOD("disconnect_from_stream"), &StreamPeerSSL::disconnect_from_stream); ClassDB::bind_method(D_METHOD("set_blocking_handshake_enabled", "enabled"), &StreamPeerSSL::set_blocking_handshake_enabled); ClassDB::bind_method(D_METHOD("is_blocking_handshake_enabled"), &StreamPeerSSL::is_blocking_handshake_enabled); diff --git a/core/io/stream_peer_ssl.h b/core/io/stream_peer_ssl.h index 15f646d897..fe68667adc 100644 --- a/core/io/stream_peer_ssl.h +++ b/core/io/stream_peer_ssl.h @@ -61,6 +61,7 @@ public: virtual Error accept_stream(Ref<StreamPeer> p_base, Ref<CryptoKey> p_key, Ref<X509Certificate> p_cert, Ref<X509Certificate> p_ca_chain = Ref<X509Certificate>()) = 0; virtual Error connect_to_stream(Ref<StreamPeer> p_base, bool p_validate_certs = false, const String &p_for_hostname = String(), Ref<X509Certificate> p_valid_cert = Ref<X509Certificate>()) = 0; virtual Status get_status() const = 0; + virtual Ref<StreamPeer> get_stream() const = 0; virtual void disconnect_from_stream() = 0; diff --git a/core/math/a_star.cpp b/core/math/a_star.cpp index 29aa959e54..efa970c681 100644 --- a/core/math/a_star.cpp +++ b/core/math/a_star.cpp @@ -151,15 +151,15 @@ void AStar3D::connect_points(int p_id, int p_with_id, bool bidirectional) { s.direction = Segment::BIDIRECTIONAL; } - RBSet<Segment>::Element *element = segments.find(s); - if (element != nullptr) { - s.direction |= element->get().direction; + HashSet<Segment, Segment>::Iterator element = segments.find(s); + if (element) { + s.direction |= element->direction; if (s.direction == Segment::BIDIRECTIONAL) { // Both are neighbours of each other now a->unlinked_neighbours.remove(b->id); b->unlinked_neighbours.remove(a->id); } - segments.erase(element); + segments.remove(element); } segments.insert(s); @@ -177,16 +177,16 @@ void AStar3D::disconnect_points(int p_id, int p_with_id, bool bidirectional) { Segment s(p_id, p_with_id); int remove_direction = bidirectional ? (int)Segment::BIDIRECTIONAL : s.direction; - RBSet<Segment>::Element *element = segments.find(s); - if (element != nullptr) { + HashSet<Segment, Segment>::Iterator element = segments.find(s); + if (element) { // s is the new segment // Erase the directions to be removed - s.direction = (element->get().direction & ~remove_direction); + s.direction = (element->direction & ~remove_direction); a->neighbours.remove(b->id); if (bidirectional) { b->neighbours.remove(a->id); - if (element->get().direction != Segment::BIDIRECTIONAL) { + if (element->direction != Segment::BIDIRECTIONAL) { a->unlinked_neighbours.remove(b->id); b->unlinked_neighbours.remove(a->id); } @@ -198,7 +198,7 @@ void AStar3D::disconnect_points(int p_id, int p_with_id, bool bidirectional) { } } - segments.erase(element); + segments.remove(element); if (s.direction != Segment::NONE) { segments.insert(s); } @@ -235,10 +235,10 @@ Vector<int> AStar3D::get_point_connections(int p_id) { bool AStar3D::are_points_connected(int p_id, int p_with_id, bool bidirectional) const { Segment s(p_id, p_with_id); - const RBSet<Segment>::Element *element = segments.find(s); + const HashSet<Segment, Segment>::Iterator element = segments.find(s); - return element != nullptr && - (bidirectional || (element->get().direction & s.direction) == s.direction); + return element && + (bidirectional || (element->direction & s.direction) == s.direction); } void AStar3D::clear() { diff --git a/core/math/a_star.h b/core/math/a_star.h index 086be839b5..e2f75ad18c 100644 --- a/core/math/a_star.h +++ b/core/math/a_star.h @@ -92,7 +92,10 @@ class AStar3D : public RefCounted { }; unsigned char direction = NONE; - bool operator<(const Segment &p_s) const { return key < p_s.key; } + static uint32_t hash(const Segment &p_seg) { + return hash_one_uint64(p_seg.key); + } + bool operator==(const Segment &p_s) const { return key == p_s.key; } Segment() {} Segment(int p_from, int p_to) { @@ -112,7 +115,7 @@ class AStar3D : public RefCounted { uint64_t pass = 1; OAHashMap<int, Point *> points; - RBSet<Segment> segments; + HashSet<Segment, Segment> segments; bool _solve(Point *begin_point, Point *end_point); diff --git a/core/math/quick_hull.cpp b/core/math/quick_hull.cpp index 43744deeb0..c7727a44a1 100644 --- a/core/math/quick_hull.cpp +++ b/core/math/quick_hull.cpp @@ -52,7 +52,7 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry3D::MeshData &r_ Vector<bool> valid_points; valid_points.resize(p_points.size()); - RBSet<Vector3> valid_cache; + HashSet<Vector3> valid_cache; for (int i = 0; i < p_points.size(); i++) { Vector3 sp = p_points[i].snapped(Vector3(0.0001, 0.0001, 0.0001)); diff --git a/core/math/quick_hull.h b/core/math/quick_hull.h index 1c354880b4..6783743fc2 100644 --- a/core/math/quick_hull.h +++ b/core/math/quick_hull.h @@ -33,8 +33,8 @@ #include "core/math/aabb.h" #include "core/math/geometry_3d.h" +#include "core/templates/hash_set.h" #include "core/templates/list.h" -#include "core/templates/rb_set.h" class QuickHull { public: diff --git a/core/math/static_raycaster.h b/core/math/static_raycaster.h index adc81906d7..bc6511c073 100644 --- a/core/math/static_raycaster.h +++ b/core/math/static_raycaster.h @@ -102,7 +102,7 @@ public: virtual void add_mesh(const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices, unsigned int p_id) = 0; virtual void commit() = 0; - virtual void set_mesh_filter(const RBSet<int> &p_mesh_ids) = 0; + virtual void set_mesh_filter(const HashSet<int> &p_mesh_ids) = 0; virtual void clear_mesh_filter() = 0; static Ref<StaticRaycaster> create(); diff --git a/core/multiplayer/multiplayer.h b/core/multiplayer/multiplayer.h index 5eb968171a..f4c965b0f8 100644 --- a/core/multiplayer/multiplayer.h +++ b/core/multiplayer/multiplayer.h @@ -46,7 +46,7 @@ enum TransferMode { enum RPCMode { RPC_MODE_DISABLED, // No rpc for this method, calls to this will be blocked (default) RPC_MODE_ANY_PEER, // Any peer can call this RPC - RPC_MODE_AUTHORITY, // / Only the node's multiplayer authority (server by default) can call this RPC + RPC_MODE_AUTHORITY, // Only the node's multiplayer authority (server by default) can call this RPC }; struct RPCConfig { diff --git a/core/multiplayer/multiplayer_api.h b/core/multiplayer/multiplayer_api.h index b93f2acbd3..cc7743ccf8 100644 --- a/core/multiplayer/multiplayer_api.h +++ b/core/multiplayer/multiplayer_api.h @@ -113,7 +113,7 @@ public: private: Ref<MultiplayerPeer> multiplayer_peer; - RBSet<int> connected_peers; + HashSet<int> connected_peers; int remote_sender_id = 0; int remote_sender_override = 0; @@ -172,7 +172,7 @@ public: bool has_multiplayer_peer() const { return multiplayer_peer.is_valid(); } Vector<int> get_peer_ids() const; - const RBSet<int> get_connected_peers() const { return connected_peers; } + const HashSet<int> get_connected_peers() const { return connected_peers; } int get_remote_sender_id() const { return remote_sender_override ? remote_sender_override : remote_sender_id; } void set_remote_sender_override(int p_id) { remote_sender_override = p_id; } int get_unique_id() const; diff --git a/core/object/callable_method_pointer.h b/core/object/callable_method_pointer.h index 577d4b9fbd..f2a440b49a 100644 --- a/core/object/callable_method_pointer.h +++ b/core/object/callable_method_pointer.h @@ -245,4 +245,86 @@ Callable create_custom_callable_function_pointer(T *p_instance, #define callable_mp(I, M) create_custom_callable_function_pointer(I, M) #endif +// STATIC VERSIONS + +template <class... P> +class CallableCustomStaticMethodPointer : public CallableCustomMethodPointerBase { + struct Data { + void (*method)(P...); + } data; + +public: + virtual ObjectID get_object() const { + return ObjectID(); + } + + virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const { + call_with_variant_args_static_ret(data.method, p_arguments, p_argcount, r_return_value, r_call_error); + r_return_value = Variant(); + } + + CallableCustomStaticMethodPointer(void (*p_method)(P...)) { + memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes. + data.method = p_method; + _setup((uint32_t *)&data, sizeof(Data)); + } +}; + +template <class T, class... P> +Callable create_custom_callable_static_function_pointer( +#ifdef DEBUG_METHODS_ENABLED + const char *p_func_text, +#endif + void (*p_method)(P...)) { + typedef CallableCustomStaticMethodPointer<P...> CCMP; // Messes with memnew otherwise. + CCMP *ccmp = memnew(CCMP(p_method)); +#ifdef DEBUG_METHODS_ENABLED + ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand. +#endif + return Callable(ccmp); +} + +template <class R, class... P> +class CallableCustomStaticMethodPointerRet : public CallableCustomMethodPointerBase { + struct Data { + R(*method) + (P...); + } data; + +public: + virtual ObjectID get_object() const { + return ObjectID(); + } + + virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const { + call_with_variant_args_static_ret(data.method, p_arguments, p_argcount, r_return_value, r_call_error); + } + + CallableCustomStaticMethodPointerRet(R (*p_method)(P...)) { + memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes. + data.method = p_method; + _setup((uint32_t *)&data, sizeof(Data)); + } +}; + +template <class R, class... P> +Callable create_custom_callable_static_function_pointer( +#ifdef DEBUG_METHODS_ENABLED + const char *p_func_text, +#endif + R (*p_method)(P...)) { + typedef CallableCustomStaticMethodPointerRet<R, P...> CCMP; // Messes with memnew otherwise. + CCMP *ccmp = memnew(CCMP(p_method)); +#ifdef DEBUG_METHODS_ENABLED + ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand. +#endif + return Callable(ccmp); +} + +#ifdef DEBUG_METHODS_ENABLED +#define callable_mp_static(M) create_custom_callable_static_function_pointer(#M, M) +#else +#define callable_mp_static(M) create_custom_callable_static_function_pointer(M) +#endif + #endif // CALLABLE_METHOD_POINTER_H diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp index d19cbf2642..f61bd24efd 100644 --- a/core/object/class_db.cpp +++ b/core/object/class_db.cpp @@ -1390,7 +1390,7 @@ void ClassDB::get_extensions_for_type(const StringName &p_class, List<String> *p } HashMap<StringName, HashMap<StringName, Variant>> ClassDB::default_values; -RBSet<StringName> ClassDB::default_values_cached; +HashSet<StringName> ClassDB::default_values_cached; Variant ClassDB::class_get_default_property_value(const StringName &p_class, const StringName &p_property, bool *r_valid) { if (!default_values_cached.has(p_class)) { diff --git a/core/object/class_db.h b/core/object/class_db.h index 67b71ab058..2448a86e33 100644 --- a/core/object/class_db.h +++ b/core/object/class_db.h @@ -38,6 +38,7 @@ // Makes callable_mp readily available in all classes connecting signals. // Needs to come after method_bind and object have been included. #include "core/object/callable_method_pointer.h" +#include "core/templates/hash_set.h" #define DEFVAL(m_defval) (m_defval) @@ -110,7 +111,7 @@ public: #ifdef DEBUG_METHODS_ENABLED List<StringName> constant_order; List<StringName> method_order; - RBSet<StringName> methods_in_properties; + HashSet<StringName> methods_in_properties; List<MethodInfo> virtual_methods; HashMap<StringName, MethodInfo> virtual_methods_map; HashMap<StringName, Vector<Error>> method_error_values; @@ -149,7 +150,7 @@ public: static void _add_class2(const StringName &p_class, const StringName &p_inherits); static HashMap<StringName, HashMap<StringName, Variant>> default_values; - static RBSet<StringName> default_values_cached; + static HashSet<StringName> default_values_cached; // Native structs, used by binder struct NativeStruct { diff --git a/core/object/object.h b/core/object/object.h index ca7b9965f1..988d261d77 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -37,9 +37,9 @@ #include "core/os/rw_lock.h" #include "core/os/spin_lock.h" #include "core/templates/hash_map.h" +#include "core/templates/hash_set.h" #include "core/templates/list.h" #include "core/templates/rb_map.h" -#include "core/templates/rb_set.h" #include "core/templates/safe_refcount.h" #include "core/templates/vmap.h" #include "core/variant/callable_bind.h" @@ -510,7 +510,7 @@ private: #ifdef TOOLS_ENABLED bool _edited = false; uint32_t _edited_version = 0; - RBSet<String> editor_section_folding; + HashSet<String> editor_section_folding; #endif ScriptInstance *script_instance = nullptr; Variant script; // Reference does not exist yet, store it in a Variant. @@ -815,7 +815,7 @@ public: #ifdef TOOLS_ENABLED void editor_set_section_unfold(const String &p_section, bool p_unfolded); bool editor_is_section_unfolded(const String &p_section); - const RBSet<String> &editor_get_section_folding() const { return editor_section_folding; } + const HashSet<String> &editor_get_section_folding() const { return editor_section_folding; } void editor_clear_section_folding() { editor_section_folding.clear(); } #endif diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp index 1546d52fd2..66c9a80193 100644 --- a/core/object/script_language.cpp +++ b/core/object/script_language.cpp @@ -475,7 +475,7 @@ bool PlaceHolderScriptInstance::has_method(const StringName &p_method) const { } void PlaceHolderScriptInstance::update(const List<PropertyInfo> &p_properties, const HashMap<StringName, Variant> &p_values) { - RBSet<StringName> new_values; + HashSet<StringName> new_values; for (const PropertyInfo &E : p_properties) { StringName n = E.name; new_values.insert(n); diff --git a/core/object/script_language.h b/core/object/script_language.h index b1481a372e..0a8e79a24e 100644 --- a/core/object/script_language.h +++ b/core/object/script_language.h @@ -155,7 +155,7 @@ public: virtual int get_member_line(const StringName &p_member) const { return -1; } virtual void get_constants(HashMap<StringName, Variant> *p_constants) {} - virtual void get_members(RBSet<StringName> *p_constants) {} + virtual void get_members(HashSet<StringName> *p_constants) {} virtual bool is_placeholder_fallback_enabled() const { return false; } @@ -283,7 +283,7 @@ public: virtual Ref<Script> make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const { return Ref<Script>(); } virtual Vector<ScriptTemplate> get_built_in_templates(StringName p_object) { return Vector<ScriptTemplate>(); } virtual bool is_using_templates() { return false; } - virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptError> *r_errors = nullptr, List<Warning> *r_warnings = nullptr, RBSet<int> *r_safe_lines = nullptr) const = 0; + virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptError> *r_errors = nullptr, List<Warning> *r_warnings = nullptr, HashSet<int> *r_safe_lines = nullptr) const = 0; virtual String validate_path(const String &p_path) const { return ""; } virtual Script *create_script() const = 0; virtual bool has_named_classes() const = 0; diff --git a/core/object/script_language_extension.h b/core/object/script_language_extension.h index 5ffa6c5a70..406a431a11 100644 --- a/core/object/script_language_extension.h +++ b/core/object/script_language_extension.h @@ -163,7 +163,7 @@ public: } } GDVIRTUAL0RC(TypedArray<StringName>, _get_members) - virtual void get_members(RBSet<StringName> *p_members) override { + virtual void get_members(HashSet<StringName> *p_members) override { TypedArray<StringName> members; GDVIRTUAL_REQUIRED_CALL(_get_members, members); for (int i = 0; i < members.size(); i++) { @@ -282,7 +282,7 @@ public: EXBIND0R(bool, is_using_templates) GDVIRTUAL6RC(Dictionary, _validate, const String &, const String &, bool, bool, bool, bool) - virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptError> *r_errors = nullptr, List<Warning> *r_warnings = nullptr, RBSet<int> *r_safe_lines = nullptr) const override { + virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptError> *r_errors = nullptr, List<Warning> *r_warnings = nullptr, HashSet<int> *r_safe_lines = nullptr) const override { Dictionary ret; GDVIRTUAL_REQUIRED_CALL(_validate, p_script, p_path, r_functions != nullptr, r_errors != nullptr, r_warnings != nullptr, r_safe_lines != nullptr, ret); if (!ret.has("valid")) { diff --git a/core/os/os.cpp b/core/os/os.cpp index 4f7095b0fc..327f1c95f2 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -413,19 +413,29 @@ bool OS::has_feature(const String &p_feature) { if (sizeof(void *) == 4 && p_feature == "32") { return true; } -#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) +#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) || defined(__i386) || defined(__i386__) || defined(_M_IX86) || defined(_M_X64) +#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) || defined(_M_X64) if (p_feature == "x86_64") { return true; } -#elif (defined(__i386) || defined(__i386__)) +#elif defined(__i386) || defined(__i386__) || defined(_M_IX86) + if (p_feature == "x86_32") { + return true; + } +#endif if (p_feature == "x86") { return true; } -#elif defined(__aarch64__) +#elif defined(__arm__) || defined(__aarch64__) || defined(_M_ARM) || defined(_M_ARM64) +#if defined(__aarch64__) || defined(_M_ARM64) if (p_feature == "arm64") { return true; } -#elif defined(__arm__) +#elif defined(__arm__) || defined(_M_ARM) + if (p_feature == "arm32") { + return true; + } +#endif #if defined(__ARM_ARCH_7A__) if (p_feature == "armv7a" || p_feature == "armv7") { return true; @@ -457,6 +467,19 @@ bool OS::has_feature(const String &p_feature) { if (p_feature == "ppc") { return true; } +#elif defined(__wasm__) +#if defined(__wasm64__) + if (p_feature == "wasm64") { + return true; + } +#elif defined(__wasm32__) + if (p_feature == "wasm32") { + return true; + } +#endif + if (p_feature == "wasm") { + return true; + } #endif if (_check_internal_feature_support(p_feature)) { diff --git a/core/string/translation.h b/core/string/translation.h index f58f6f91a2..20c6ebd5a5 100644 --- a/core/string/translation.h +++ b/core/string/translation.h @@ -74,7 +74,7 @@ class TranslationServer : public Object { String locale = "en"; String fallback; - RBSet<Ref<Translation>> translations; + HashSet<Ref<Translation>> translations; Ref<Translation> tool_translation; Ref<Translation> doc_translation; @@ -111,7 +111,7 @@ class TranslationServer : public Object { String name; String script; String default_country; - RBSet<String> supported_countries; + HashSet<String> supported_countries; }; static Vector<LocaleScriptInfo> locale_script_info; diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index f6ad0fe3b0..d189f3224b 100644 --- a/core/string/ustring.cpp +++ b/core/string/ustring.cpp @@ -2039,7 +2039,7 @@ int64_t String::hex_to_int() const { } // Check for overflow/underflow, with special case to ensure INT64_MIN does not result in error bool overflow = ((hex > INT64_MAX / 16) && (sign == 1 || (sign == -1 && hex != (INT64_MAX >> 4) + 1))) || (sign == -1 && hex == (INT64_MAX >> 4) + 1 && c > '0'); - ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as 64-bit integer, provided value is " + (sign == 1 ? "too big." : "too small.")); + ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as a 64-bit signed integer, since the value is " + (sign == 1 ? "too large." : "too small.")); hex *= 16; hex += n; s++; @@ -2078,7 +2078,7 @@ int64_t String::bin_to_int() const { } // Check for overflow/underflow, with special case to ensure INT64_MIN does not result in error bool overflow = ((binary > INT64_MAX / 2) && (sign == 1 || (sign == -1 && binary != (INT64_MAX >> 1) + 1))) || (sign == -1 && binary == (INT64_MAX >> 1) + 1 && c > '0'); - ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as 64-bit integer, provided value is " + (sign == 1 ? "too big." : "too small.")); + ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as a 64-bit signed integer, since the value is " + (sign == 1 ? "too large." : "too small.")); binary *= 2; binary += n; s++; @@ -2101,7 +2101,7 @@ int64_t String::to_int() const { char32_t c = operator[](i); if (is_digit(c)) { bool overflow = (integer > INT64_MAX / 10) || (integer == INT64_MAX / 10 && ((sign == 1 && c > '7') || (sign == -1 && c > '8'))); - ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as 64-bit integer, provided value is " + (sign == 1 ? "too big." : "too small.")); + ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + *this + " as a 64-bit signed integer, since the value is " + (sign == 1 ? "too large." : "too small.")); integer *= 10; integer += c - '0'; @@ -2130,7 +2130,7 @@ int64_t String::to_int(const char *p_str, int p_len) { char c = p_str[i]; if (is_digit(c)) { bool overflow = (integer > INT64_MAX / 10) || (integer == INT64_MAX / 10 && ((sign == 1 && c > '7') || (sign == -1 && c > '8'))); - ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + String(p_str).substr(0, to) + " as integer, provided value is " + (sign == 1 ? "too big." : "too small.")); + ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + String(p_str).substr(0, to) + " as a 64-bit signed integer, since the value is " + (sign == 1 ? "too large." : "too small.")); integer *= 10; integer += c - '0'; @@ -2161,7 +2161,7 @@ int64_t String::to_int(const wchar_t *p_str, int p_len) { wchar_t c = p_str[i]; if (is_digit(c)) { bool overflow = (integer > INT64_MAX / 10) || (integer == INT64_MAX / 10 && ((sign == 1 && c > '7') || (sign == -1 && c > '8'))); - ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + String(p_str).substr(0, to) + " as integer, provided value is " + (sign == 1 ? "too big." : "too small.")); + ERR_FAIL_COND_V_MSG(overflow, sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + String(p_str).substr(0, to) + " as a 64-bit signed integer, since the value is " + (sign == 1 ? "too large." : "too small.")); integer *= 10; integer += c - '0'; @@ -2483,7 +2483,7 @@ int64_t String::to_int(const char32_t *p_str, int p_len, bool p_clamp) { return INT64_MIN; } } else { - ERR_FAIL_V_MSG(sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + number + " as integer, provided value is " + (sign == 1 ? "too big." : "too small.")); + ERR_FAIL_V_MSG(sign == 1 ? INT64_MAX : INT64_MIN, "Cannot represent " + number + " as a 64-bit signed integer, since the value is " + (sign == 1 ? "too large." : "too small.")); } } integer *= 10; @@ -3712,18 +3712,15 @@ String String::uri_encode() const { const CharString temp = utf8(); String res; for (int i = 0; i < temp.length(); ++i) { - char ord = temp[i]; + uint8_t ord = temp[i]; if (ord == '.' || ord == '-' || ord == '~' || is_ascii_identifier_char(ord)) { res += ord; } else { - char h_Val[3]; -#if defined(__GNUC__) || defined(_MSC_VER) - snprintf(h_Val, 3, "%02hhX", ord); -#else - sprintf(h_Val, "%02hhX", ord); -#endif - res += "%"; - res += h_Val; + char p[4] = { '%', 0, 0, 0 }; + static const char hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + p[1] = hex[ord >> 4]; + p[2] = hex[ord & 0xF]; + res += p; } } return res; diff --git a/core/templates/bin_sorted_array.h b/core/templates/bin_sorted_array.h index 59ac4cdaa1..d928bd7a82 100644 --- a/core/templates/bin_sorted_array.h +++ b/core/templates/bin_sorted_array.h @@ -61,7 +61,7 @@ public: } uint64_t move(uint64_t p_idx, uint64_t p_bin) { - ERR_FAIL_COND_V(p_idx >= array.size(), -1); + ERR_FAIL_UNSIGNED_INDEX_V(p_idx, array.size(), -1); uint64_t current_bin = bin_limits.size() - 1; while (p_idx > bin_limits[current_bin]) { @@ -113,7 +113,7 @@ public: } void remove_at(uint64_t p_idx) { - ERR_FAIL_COND(p_idx >= array.size()); + ERR_FAIL_UNSIGNED_INDEX(p_idx, array.size()); uint64_t new_idx = move(p_idx, 0); uint64_t swap_idx = array.size() - 1; diff --git a/core/templates/hash_set.h b/core/templates/hash_set.h new file mode 100644 index 0000000000..2318067dcc --- /dev/null +++ b/core/templates/hash_set.h @@ -0,0 +1,473 @@ +/*************************************************************************/ +/* hash_set.h */ +/*************************************************************************/ +/* 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_H +#define HASH_SET_H + +#include "core/math/math_funcs.h" +#include "core/os/memory.h" +#include "core/templates/hash_map.h" +#include "core/templates/hashfuncs.h" +#include "core/templates/paged_allocator.h" + +/** + * 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); + } + } +}; + +#endif // HASH_SET_H diff --git a/core/templates/rb_set.h b/core/templates/rb_set.h index 2de816769c..226ea979c9 100644 --- a/core/templates/rb_set.h +++ b/core/templates/rb_set.h @@ -99,6 +99,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; } + explicit operator bool() const { return E != nullptr; } Iterator(Element *p_E) { E = p_E; } Iterator() {} Iterator(const Iterator &p_it) { E = p_it.E; } @@ -128,6 +129,8 @@ public: _FORCE_INLINE_ ConstIterator() {} _FORCE_INLINE_ ConstIterator(const ConstIterator &p_it) { E = p_it.E; } + explicit operator bool() const { return E != nullptr; } + private: const Element *E = nullptr; }; diff --git a/core/templates/rid_owner.h b/core/templates/rid_owner.h index d26977380e..9c74b41818 100644 --- a/core/templates/rid_owner.h +++ b/core/templates/rid_owner.h @@ -34,9 +34,9 @@ #include "core/os/memory.h" #include "core/os/spin_lock.h" #include "core/string/print_string.h" +#include "core/templates/hash_set.h" #include "core/templates/list.h" #include "core/templates/oa_hash_map.h" -#include "core/templates/rb_set.h" #include "core/templates/rid.h" #include "core/templates/safe_refcount.h" diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp index e69bd88413..2b4e777865 100644 --- a/core/variant/variant.cpp +++ b/core/variant/variant.cpp @@ -3102,6 +3102,10 @@ bool Variant::hash_compare(const Variant &p_variant, int recursion_count) const return *reinterpret_cast<const String *>(_data._mem) == *reinterpret_cast<const String *>(p_variant._data._mem); } break; + case STRING_NAME: { + return *reinterpret_cast<const StringName *>(_data._mem) == *reinterpret_cast<const StringName *>(p_variant._data._mem); + } break; + case VECTOR2: { const Vector2 *l = reinterpret_cast<const Vector2 *>(_data._mem); const Vector2 *r = reinterpret_cast<const Vector2 *>(p_variant._data._mem); diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index 2e77a4b2f8..3fe5ead347 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -1472,6 +1472,10 @@ static void _register_variant_builtin_methods() { bind_static_method(String, chr, sarray("char"), varray()); bind_static_method(String, humanize_size, sarray("size"), varray()); + /* StringName */ + + bind_method(StringName, hash, sarray(), varray()); + /* Vector2 */ bind_method(Vector2, angle, sarray(), varray()); @@ -1684,6 +1688,7 @@ static void _register_variant_builtin_methods() { bind_method(NodePath, get_name_count, sarray(), varray()); bind_method(NodePath, get_name, sarray("idx"), varray()); bind_method(NodePath, get_subname_count, sarray(), varray()); + bind_method(NodePath, hash, sarray(), varray()); bind_method(NodePath, get_subname, sarray("idx"), varray()); bind_method(NodePath, get_concatenated_subnames, sarray(), varray()); bind_method(NodePath, get_as_property_path, sarray(), varray()); |