Merge pull request #61087 from reduz/readonly-dictionary

Implement read-only dictionaries.
This commit is contained in:
Rémi Verschelde 2022-05-17 15:11:48 +02:00 committed by GitHub
commit 35004aea48
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 71 additions and 5 deletions

View file

@ -41,6 +41,7 @@
struct DictionaryPrivate { struct DictionaryPrivate {
SafeRefCount refcount; 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; HashMap<Variant, Variant, VariantHasher, VariantComparator> variant_map;
}; };
@ -79,12 +80,23 @@ Variant Dictionary::get_value_at_index(int p_index) const {
} }
Variant &Dictionary::operator[](const Variant &p_key) { Variant &Dictionary::operator[](const Variant &p_key) {
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 {
if (p_key.get_type() == Variant::STRING_NAME) { if (p_key.get_type() == Variant::STRING_NAME) {
const StringName *sn = VariantInternal::get_string_name(&p_key); const StringName *sn = VariantInternal::get_string_name(&p_key);
return _p->variant_map[sn->operator String()]; return _p->variant_map[sn->operator String()];
} else { } else {
return _p->variant_map[p_key]; return _p->variant_map[p_key];
} }
}
} }
const Variant &Dictionary::operator[](const Variant &p_key) const { const Variant &Dictionary::operator[](const Variant &p_key) const {
@ -124,7 +136,12 @@ Variant *Dictionary::getptr(const Variant &p_key) {
if (!E) { if (!E) {
return nullptr; return nullptr;
} }
if (unlikely(_p->read_only != nullptr)) {
*_p->read_only = E->value;
return _p->read_only;
} else {
return &E->value; return &E->value;
}
} }
Variant Dictionary::get_valid(const Variant &p_key) const { 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) { 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) { if (p_key.get_type() == Variant::STRING_NAME) {
const StringName *sn = VariantInternal::get_string_name(&p_key); const StringName *sn = VariantInternal::get_string_name(&p_key);
return _p->variant_map.erase(sn->operator String()); 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 { 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) //make a copy first (thread safe)
if (!p_from._p->refcount.ref()) { if (!p_from._p->refcount.ref()) {
return; // couldn't copy return; // couldn't copy
@ -237,12 +265,16 @@ void Dictionary::_ref(const Dictionary &p_from) const {
} }
void Dictionary::clear() { void Dictionary::clear() {
ERR_FAIL_COND_MSG(_p->read_only, "Dictionary is in read-only state.");
_p->variant_map.clear(); _p->variant_map.clear();
} }
void Dictionary::_unref() const { void Dictionary::_unref() const {
ERR_FAIL_COND(!_p); ERR_FAIL_COND(!_p);
if (_p->refcount.unref()) { if (_p->refcount.unref()) {
if (_p->read_only) {
memdelete(_p->read_only);
}
memdelete(_p); memdelete(_p);
} }
_p = nullptr; _p = nullptr;
@ -330,6 +362,21 @@ Dictionary Dictionary::duplicate(bool p_deep) const {
return recursive_duplicate(p_deep, 0); 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 Dictionary::recursive_duplicate(bool p_deep, int recursion_count) const {
Dictionary n; Dictionary n;
@ -353,6 +400,9 @@ Dictionary Dictionary::recursive_duplicate(bool p_deep, int recursion_count) con
} }
void Dictionary::operator=(const Dictionary &p_dictionary) { void Dictionary::operator=(const Dictionary &p_dictionary) {
if (this == &p_dictionary) {
return;
}
_ref(p_dictionary); _ref(p_dictionary);
} }

View file

@ -84,6 +84,9 @@ public:
Dictionary duplicate(bool p_deep = false) const; Dictionary duplicate(bool p_deep = false) const;
Dictionary recursive_duplicate(bool p_deep, int recursion_count) 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; const void *id() const;
Dictionary(const Dictionary &p_from); Dictionary(const Dictionary &p_from);

View file

@ -766,11 +766,20 @@ struct VariantIndexedSetGet_String {
PtrToArg<Variant>::encode(*ptr, member); \ PtrToArg<Variant>::encode(*ptr, member); \
} \ } \
static void set(Variant *base, int64_t index, const Variant *value, bool *valid, bool *oob) { \ 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; \ (*VariantGetInternalPtr<m_base_type>::get_ptr(base))[index] = *value; \
*oob = false; \ *oob = false; \
*valid = true; \ *valid = true; \
} \ } \
static void validated_set(Variant *base, int64_t index, const Variant *value, bool *oob) { \ 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; \ (*VariantGetInternalPtr<m_base_type>::get_ptr(base))[index] = *value; \
*oob = false; \ *oob = false; \
} \ } \
@ -946,6 +955,10 @@ struct VariantKeyedSetGetDictionary {
PtrToArg<Variant>::encode(*ptr, value); PtrToArg<Variant>::encode(*ptr, value);
} }
static void set(Variant *base, const Variant *key, const Variant *value, bool *r_valid) { 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; (*VariantGetInternalPtr<Dictionary>::get_ptr(base))[*key] = *value;
*r_valid = true; *r_valid = true;
} }