summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/config/project_settings.cpp8
-rw-r--r--core/config/project_settings.h6
-rw-r--r--core/string/ustring.cpp35
-rw-r--r--core/string/ustring.h1
-rw-r--r--core/variant/array.cpp60
-rw-r--r--core/variant/array.h4
-rw-r--r--core/variant/dictionary.cpp60
-rw-r--r--core/variant/dictionary.h3
-rw-r--r--core/variant/variant_setget.cpp22
9 files changed, 177 insertions, 22 deletions
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index 3ef9a69d06..4638d70e59 100644
--- a/core/config/project_settings.cpp
+++ b/core/config/project_settings.cpp
@@ -764,7 +764,7 @@ Error ProjectSettings::save() {
return error;
}
-Error ProjectSettings::_save_settings_binary(const String &p_file, const HashMap<String, List<String>> &props, const CustomMap &p_custom, const String &p_custom_features) {
+Error ProjectSettings::_save_settings_binary(const String &p_file, const RBMap<String, List<String>> &props, const CustomMap &p_custom, const String &p_custom_features) {
Error err;
Ref<FileAccess> file = FileAccess::open(p_file, FileAccess::WRITE, &err);
ERR_FAIL_COND_V_MSG(err != OK, err, "Couldn't save project.binary at " + p_file + ".");
@@ -832,7 +832,7 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const HashMap
return OK;
}
-Error ProjectSettings::_save_settings_text(const String &p_file, const HashMap<String, List<String>> &props, const CustomMap &p_custom, const String &p_custom_features) {
+Error ProjectSettings::_save_settings_text(const String &p_file, const RBMap<String, List<String>> &props, const CustomMap &p_custom, const String &p_custom_features) {
Error err;
Ref<FileAccess> file = FileAccess::open(p_file, FileAccess::WRITE, &err);
@@ -947,7 +947,7 @@ Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_cust
for (const KeyValue<String, Variant> &E : p_custom) {
// Lookup global prop to store in the same order
- HashMap<StringName, VariantContainer>::Iterator global_prop = props.find(E.key);
+ RBMap<StringName, VariantContainer>::Iterator global_prop = props.find(E.key);
_VCSort vc;
vc.name = E.key;
@@ -957,7 +957,7 @@ Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_cust
vclist.insert(vc);
}
- HashMap<String, List<String>> props;
+ RBMap<String, List<String>> props;
for (RBSet<_VCSort>::Element *E = vclist.front(); E; E = E->next()) {
String category = E->get().name;
diff --git a/core/config/project_settings.h b/core/config/project_settings.h
index f8dc618cb8..bacc8adc54 100644
--- a/core/config/project_settings.h
+++ b/core/config/project_settings.h
@@ -84,7 +84,7 @@ protected:
int last_builtin_order = 0;
uint64_t last_save_time = 0;
- HashMap<StringName, VariantContainer> props;
+ RBMap<StringName, VariantContainer> props; // NOTE: Key order is used e.g. in the save_custom method.
String resource_path;
HashMap<StringName, PropertyInfo> custom_prop_info;
bool disable_feature_overrides = false;
@@ -108,8 +108,8 @@ protected:
Error _load_settings_binary(const String &p_path);
Error _load_settings_text_or_binary(const String &p_text_path, const String &p_bin_path);
- Error _save_settings_text(const String &p_file, const HashMap<String, List<String>> &props, const CustomMap &p_custom = CustomMap(), const String &p_custom_features = String());
- Error _save_settings_binary(const String &p_file, const HashMap<String, List<String>> &props, const CustomMap &p_custom = CustomMap(), const String &p_custom_features = String());
+ Error _save_settings_text(const String &p_file, const RBMap<String, List<String>> &props, const CustomMap &p_custom = CustomMap(), const String &p_custom_features = String());
+ Error _save_settings_binary(const String &p_file, const RBMap<String, List<String>> &props, const CustomMap &p_custom = CustomMap(), const String &p_custom_features = String());
Error _save_custom_bnd(const String &p_file);
diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp
index 015dfbc651..f6ad0fe3b0 100644
--- a/core/string/ustring.cpp
+++ b/core/string/ustring.cpp
@@ -3655,6 +3655,31 @@ bool String::is_absolute_path() const {
}
}
+static _FORCE_INLINE_ bool _is_valid_identifier_bit(int p_index, char32_t p_char) {
+ if (p_index == 0 && is_digit(p_char)) {
+ return false; // No start with number plz.
+ }
+ return is_ascii_identifier_char(p_char);
+}
+
+String String::validate_identifier() const {
+ if (is_empty()) {
+ return "_"; // Empty string is not a valid identifier;
+ }
+
+ String result = *this;
+ int len = result.length();
+ char32_t *buffer = result.ptrw();
+
+ for (int i = 0; i < len; i++) {
+ if (!_is_valid_identifier_bit(i, buffer[i])) {
+ buffer[i] = '_';
+ }
+ }
+
+ return result;
+}
+
bool String::is_valid_identifier() const {
int len = length();
@@ -3665,15 +3690,7 @@ bool String::is_valid_identifier() const {
const char32_t *str = &operator[](0);
for (int i = 0; i < len; i++) {
- if (i == 0) {
- if (is_digit(str[0])) {
- return false; // no start with number plz
- }
- }
-
- bool valid_char = is_ascii_identifier_char(str[i]);
-
- if (!valid_char) {
+ if (!_is_valid_identifier_bit(i, str[i])) {
return false;
}
}
diff --git a/core/string/ustring.h b/core/string/ustring.h
index 48f2e45105..0d10d4cf10 100644
--- a/core/string/ustring.h
+++ b/core/string/ustring.h
@@ -427,6 +427,7 @@ public:
// node functions
static const String invalid_node_name_characters;
String validate_node_name() const;
+ String validate_identifier() const;
bool is_valid_identifier() const;
bool is_valid_int() const;
diff --git a/core/variant/array.cpp b/core/variant/array.cpp
index afc4acadf9..7551350c95 100644
--- a/core/variant/array.cpp
+++ b/core/variant/array.cpp
@@ -43,7 +43,7 @@ class ArrayPrivate {
public:
SafeRefCount refcount;
Vector<Variant> array;
-
+ Variant *read_only = nullptr; // If enabled, a pointer is used to a temporary value that is used to return read-only values.
ContainerTypeValidate typed;
};
@@ -52,6 +52,16 @@ void Array::_ref(const Array &p_from) const {
ERR_FAIL_COND(!_fp); // should NOT happen.
+ if (unlikely(_fp->read_only != nullptr)) {
+ // If p_from is a read-only array, just copy the contents to avoid further modification.
+ _unref();
+ _p = memnew(ArrayPrivate);
+ _p->refcount.init();
+ _p->array = _fp->array;
+ _p->typed = _fp->typed;
+ return;
+ }
+
if (_fp == _p) {
return; // whatever it is, nothing to do here move along
}
@@ -71,16 +81,27 @@ void Array::_unref() const {
}
if (_p->refcount.unref()) {
+ if (_p->read_only) {
+ memdelete(_p->read_only);
+ }
memdelete(_p);
}
_p = nullptr;
}
Variant &Array::operator[](int p_idx) {
+ if (unlikely(_p->read_only)) {
+ *_p->read_only = _p->array[p_idx];
+ return *_p->read_only;
+ }
return _p->array.write[p_idx];
}
const Variant &Array::operator[](int p_idx) const {
+ if (unlikely(_p->read_only)) {
+ *_p->read_only = _p->array[p_idx];
+ return *_p->read_only;
+ }
return _p->array[p_idx];
}
@@ -93,6 +114,7 @@ bool Array::is_empty() const {
}
void Array::clear() {
+ ERR_FAIL_COND_MSG(_p->read_only, "Array is in read-only state.");
_p->array.clear();
}
@@ -224,34 +246,43 @@ bool Array::_assign(const Array &p_array) {
}
void Array::operator=(const Array &p_array) {
+ if (this == &p_array) {
+ return;
+ }
_ref(p_array);
}
void Array::push_back(const Variant &p_value) {
+ ERR_FAIL_COND_MSG(_p->read_only, "Array is in read-only state.");
ERR_FAIL_COND(!_p->typed.validate(p_value, "push_back"));
_p->array.push_back(p_value);
}
void Array::append_array(const Array &p_array) {
+ ERR_FAIL_COND_MSG(_p->read_only, "Array is in read-only state.");
ERR_FAIL_COND(!_p->typed.validate(p_array, "append_array"));
_p->array.append_array(p_array._p->array);
}
Error Array::resize(int p_new_size) {
+ ERR_FAIL_COND_V_MSG(_p->read_only, ERR_LOCKED, "Array is in read-only state.");
return _p->array.resize(p_new_size);
}
Error Array::insert(int p_pos, const Variant &p_value) {
+ ERR_FAIL_COND_V_MSG(_p->read_only, ERR_LOCKED, "Array is in read-only state.");
ERR_FAIL_COND_V(!_p->typed.validate(p_value, "insert"), ERR_INVALID_PARAMETER);
return _p->array.insert(p_pos, p_value);
}
void Array::fill(const Variant &p_value) {
+ ERR_FAIL_COND_MSG(_p->read_only, "Array is in read-only state.");
ERR_FAIL_COND(!_p->typed.validate(p_value, "fill"));
_p->array.fill(p_value);
}
void Array::erase(const Variant &p_value) {
+ ERR_FAIL_COND_MSG(_p->read_only, "Array is in read-only state.");
ERR_FAIL_COND(!_p->typed.validate(p_value, "erase"));
_p->array.erase(p_value);
}
@@ -323,10 +354,12 @@ bool Array::has(const Variant &p_value) const {
}
void Array::remove_at(int p_pos) {
+ ERR_FAIL_COND_MSG(_p->read_only, "Array is in read-only state.");
_p->array.remove_at(p_pos);
}
void Array::set(int p_idx, const Variant &p_value) {
+ ERR_FAIL_COND_MSG(_p->read_only, "Array is in read-only state.");
ERR_FAIL_COND(!_p->typed.validate(p_value, "set"));
operator[](p_idx) = p_value;
@@ -481,14 +514,17 @@ struct _ArrayVariantSort {
};
void Array::sort() {
+ ERR_FAIL_COND_MSG(_p->read_only, "Array is in read-only state.");
_p->array.sort_custom<_ArrayVariantSort>();
}
void Array::sort_custom(const Callable &p_callable) {
+ ERR_FAIL_COND_MSG(_p->read_only, "Array is in read-only state.");
_p->array.sort_custom<CallableComparator, true>(p_callable);
}
void Array::shuffle() {
+ ERR_FAIL_COND_MSG(_p->read_only, "Array is in read-only state.");
const int n = _p->array.size();
if (n < 2) {
return;
@@ -515,15 +551,18 @@ int Array::bsearch_custom(const Variant &p_value, const Callable &p_callable, bo
}
void Array::reverse() {
+ ERR_FAIL_COND_MSG(_p->read_only, "Array is in read-only state.");
_p->array.reverse();
}
void Array::push_front(const Variant &p_value) {
+ ERR_FAIL_COND_MSG(_p->read_only, "Array is in read-only state.");
ERR_FAIL_COND(!_p->typed.validate(p_value, "push_front"));
_p->array.insert(0, p_value);
}
Variant Array::pop_back() {
+ ERR_FAIL_COND_V_MSG(_p->read_only, Variant(), "Array is in read-only state.");
if (!_p->array.is_empty()) {
const int n = _p->array.size() - 1;
const Variant ret = _p->array.get(n);
@@ -534,6 +573,7 @@ Variant Array::pop_back() {
}
Variant Array::pop_front() {
+ ERR_FAIL_COND_V_MSG(_p->read_only, Variant(), "Array is in read-only state.");
if (!_p->array.is_empty()) {
const Variant ret = _p->array.get(0);
_p->array.remove_at(0);
@@ -543,6 +583,7 @@ Variant Array::pop_front() {
}
Variant Array::pop_at(int p_pos) {
+ ERR_FAIL_COND_V_MSG(_p->read_only, Variant(), "Array is in read-only state.");
if (_p->array.is_empty()) {
// Return `null` without printing an error to mimic `pop_back()` and `pop_front()` behavior.
return Variant();
@@ -627,6 +668,7 @@ bool Array::typed_assign(const Array &p_other) {
}
void Array::set_typed(uint32_t p_type, const StringName &p_class_name, const Variant &p_script) {
+ ERR_FAIL_COND_MSG(_p->read_only, "Array is in read-only state.");
ERR_FAIL_COND_MSG(_p->array.size() > 0, "Type can only be set when array is empty.");
ERR_FAIL_COND_MSG(_p->refcount.get() > 1, "Type can only be set when array has no more than one user.");
ERR_FAIL_COND_MSG(_p->typed.type != Variant::NIL, "Type can only be set once.");
@@ -656,6 +698,22 @@ Variant Array::get_typed_script() const {
return _p->typed.script;
}
+void Array::set_read_only(bool p_enable) {
+ if (p_enable == bool(_p->read_only != nullptr)) {
+ return;
+ }
+ if (p_enable) {
+ _p->read_only = memnew(Variant);
+ } else {
+ memdelete(_p->read_only);
+ _p->read_only = nullptr;
+ }
+}
+
+bool Array::is_read_only() const {
+ return _p->read_only != nullptr;
+}
+
Array::Array(const Array &p_from) {
_p = nullptr;
_ref(p_from);
diff --git a/core/variant/array.h b/core/variant/array.h
index ab5f7cd50f..f537700f99 100644
--- a/core/variant/array.h
+++ b/core/variant/array.h
@@ -125,6 +125,10 @@ public:
uint32_t get_typed_builtin() const;
StringName get_typed_class_name() const;
Variant get_typed_script() const;
+
+ void set_read_only(bool p_enable);
+ bool is_read_only() const;
+
Array(const Array &p_from);
Array();
~Array();
diff --git a/core/variant/dictionary.cpp b/core/variant/dictionary.cpp
index 46543da2c2..bda8c93a79 100644
--- a/core/variant/dictionary.cpp
+++ b/core/variant/dictionary.cpp
@@ -41,6 +41,7 @@
struct DictionaryPrivate {
SafeRefCount refcount;
+ Variant *read_only = nullptr; // If enabled, a pointer is used to a temporary value that is used to return read-only values.
HashMap<Variant, Variant, VariantHasher, VariantComparator> variant_map;
};
@@ -79,11 +80,22 @@ Variant Dictionary::get_value_at_index(int p_index) const {
}
Variant &Dictionary::operator[](const Variant &p_key) {
- if (p_key.get_type() == Variant::STRING_NAME) {
- const StringName *sn = VariantInternal::get_string_name(&p_key);
- return _p->variant_map[sn->operator String()];
+ if (unlikely(_p->read_only)) {
+ if (p_key.get_type() == Variant::STRING_NAME) {
+ const StringName *sn = VariantInternal::get_string_name(&p_key);
+ *_p->read_only = _p->variant_map[sn->operator String()];
+ } else {
+ *_p->read_only = _p->variant_map[p_key];
+ }
+
+ return *_p->read_only;
} else {
- return _p->variant_map[p_key];
+ if (p_key.get_type() == Variant::STRING_NAME) {
+ const StringName *sn = VariantInternal::get_string_name(&p_key);
+ return _p->variant_map[sn->operator String()];
+ } else {
+ return _p->variant_map[p_key];
+ }
}
}
@@ -124,7 +136,12 @@ Variant *Dictionary::getptr(const Variant &p_key) {
if (!E) {
return nullptr;
}
- return &E->value;
+ if (unlikely(_p->read_only != nullptr)) {
+ *_p->read_only = E->value;
+ return _p->read_only;
+ } else {
+ return &E->value;
+ }
}
Variant Dictionary::get_valid(const Variant &p_key) const {
@@ -179,6 +196,7 @@ bool Dictionary::has_all(const Array &p_keys) const {
}
bool Dictionary::erase(const Variant &p_key) {
+ ERR_FAIL_COND_V_MSG(_p->read_only, false, "Dictionary is in read-only state.");
if (p_key.get_type() == Variant::STRING_NAME) {
const StringName *sn = VariantInternal::get_string_name(&p_key);
return _p->variant_map.erase(sn->operator String());
@@ -220,6 +238,16 @@ bool Dictionary::recursive_equal(const Dictionary &p_dictionary, int recursion_c
}
void Dictionary::_ref(const Dictionary &p_from) const {
+ if (unlikely(p_from._p->read_only != nullptr)) {
+ // If p_from is a read-only dictionary, just copy the contents to avoid further modification.
+ if (_p) {
+ _unref();
+ }
+ _p = memnew(DictionaryPrivate);
+ _p->refcount.init();
+ _p->variant_map = p_from._p->variant_map;
+ return;
+ }
//make a copy first (thread safe)
if (!p_from._p->refcount.ref()) {
return; // couldn't copy
@@ -237,12 +265,16 @@ void Dictionary::_ref(const Dictionary &p_from) const {
}
void Dictionary::clear() {
+ ERR_FAIL_COND_MSG(_p->read_only, "Dictionary is in read-only state.");
_p->variant_map.clear();
}
void Dictionary::_unref() const {
ERR_FAIL_COND(!_p);
if (_p->refcount.unref()) {
+ if (_p->read_only) {
+ memdelete(_p->read_only);
+ }
memdelete(_p);
}
_p = nullptr;
@@ -330,6 +362,21 @@ Dictionary Dictionary::duplicate(bool p_deep) const {
return recursive_duplicate(p_deep, 0);
}
+void Dictionary::set_read_only(bool p_enable) {
+ if (p_enable == bool(_p->read_only != nullptr)) {
+ return;
+ }
+ if (p_enable) {
+ _p->read_only = memnew(Variant);
+ } else {
+ memdelete(_p->read_only);
+ _p->read_only = nullptr;
+ }
+}
+bool Dictionary::is_read_only() const {
+ return _p->read_only != nullptr;
+}
+
Dictionary Dictionary::recursive_duplicate(bool p_deep, int recursion_count) const {
Dictionary n;
@@ -353,6 +400,9 @@ Dictionary Dictionary::recursive_duplicate(bool p_deep, int recursion_count) con
}
void Dictionary::operator=(const Dictionary &p_dictionary) {
+ if (this == &p_dictionary) {
+ return;
+ }
_ref(p_dictionary);
}
diff --git a/core/variant/dictionary.h b/core/variant/dictionary.h
index 16cf0c2bf8..1224a4ff6f 100644
--- a/core/variant/dictionary.h
+++ b/core/variant/dictionary.h
@@ -84,6 +84,9 @@ public:
Dictionary duplicate(bool p_deep = false) const;
Dictionary recursive_duplicate(bool p_deep, int recursion_count) const;
+ void set_read_only(bool p_enable);
+ bool is_read_only() const;
+
const void *id() const;
Dictionary(const Dictionary &p_from);
diff --git a/core/variant/variant_setget.cpp b/core/variant/variant_setget.cpp
index 6023e4d129..3839da495f 100644
--- a/core/variant/variant_setget.cpp
+++ b/core/variant/variant_setget.cpp
@@ -624,6 +624,11 @@ struct VariantIndexedSetGet_Array {
PtrToArg<Variant>::encode(v[index], member);
}
static void set(Variant *base, int64_t index, const Variant *value, bool *valid, bool *oob) {
+ if (VariantGetInternalPtr<Array>::get_ptr(base)->is_read_only()) {
+ *valid = false;
+ *oob = true;
+ return;
+ }
int64_t size = VariantGetInternalPtr<Array>::get_ptr(base)->size();
if (index < 0) {
index += size;
@@ -638,6 +643,10 @@ struct VariantIndexedSetGet_Array {
*valid = true;
}
static void validated_set(Variant *base, int64_t index, const Variant *value, bool *oob) {
+ if (VariantGetInternalPtr<Array>::get_ptr(base)->is_read_only()) {
+ *oob = true;
+ return;
+ }
int64_t size = VariantGetInternalPtr<Array>::get_ptr(base)->size();
if (index < 0) {
index += size;
@@ -766,11 +775,20 @@ struct VariantIndexedSetGet_String {
PtrToArg<Variant>::encode(*ptr, member); \
} \
static void set(Variant *base, int64_t index, const Variant *value, bool *valid, bool *oob) { \
+ if (VariantGetInternalPtr<m_base_type>::get_ptr(base)->is_read_only()) { \
+ *valid = false; \
+ *oob = true; \
+ return; \
+ } \
(*VariantGetInternalPtr<m_base_type>::get_ptr(base))[index] = *value; \
*oob = false; \
*valid = true; \
} \
static void validated_set(Variant *base, int64_t index, const Variant *value, bool *oob) { \
+ if (VariantGetInternalPtr<m_base_type>::get_ptr(base)->is_read_only()) { \
+ *oob = true; \
+ return; \
+ } \
(*VariantGetInternalPtr<m_base_type>::get_ptr(base))[index] = *value; \
*oob = false; \
} \
@@ -946,6 +964,10 @@ struct VariantKeyedSetGetDictionary {
PtrToArg<Variant>::encode(*ptr, value);
}
static void set(Variant *base, const Variant *key, const Variant *value, bool *r_valid) {
+ if (VariantGetInternalPtr<Dictionary>::get_ptr(base)->is_read_only()) {
+ *r_valid = false;
+ return;
+ }
(*VariantGetInternalPtr<Dictionary>::get_ptr(base))[*key] = *value;
*r_valid = true;
}