summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorreduz <reduzio@gmail.com>2022-05-16 15:44:43 +0200
committerreduz <reduzio@gmail.com>2022-05-16 23:31:46 +0200
commite6c443a353cb3923b80f4fa112667a08159ee971 (patch)
tree238260992b1c448293d039a3b66b47e5d20cbd34
parentc41f62c3df85fd71539ae09bd97964e5e367d897 (diff)
Implement read-only dictionaries.
* Add ability to set them read only. * If read-only, it can't be modified. This is added in order to optionally make const dictionaries (and eventually arrays) properly read-only in GDScript.
-rw-r--r--core/variant/dictionary.cpp60
-rw-r--r--core/variant/dictionary.h3
-rw-r--r--core/variant/variant_setget.cpp13
3 files changed, 71 insertions, 5 deletions
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..6514922d01 100644
--- a/core/variant/variant_setget.cpp
+++ b/core/variant/variant_setget.cpp
@@ -766,11 +766,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 +955,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;
}