[Core] Add iteration support to Array

This commit is contained in:
A Thousand Ships 2023-12-24 13:44:21 +01:00
parent 1f0f81049f
commit 64146cb7f3
No known key found for this signature in database
GPG key ID: 2033189A662F8BD7
15 changed files with 251 additions and 73 deletions

View file

@ -1330,8 +1330,8 @@ void ProjectSettings::load_scene_groups_cache() {
for (const String &E : scene_paths) {
Array scene_groups = cf->get_value(E, "groups", Array());
HashSet<StringName> cache;
for (int i = 0; i < scene_groups.size(); ++i) {
cache.insert(scene_groups[i]);
for (const Variant &scene_group : scene_groups) {
cache.insert(scene_group);
}
add_scene_groups_cache(E, cache);
}

View file

@ -1313,9 +1313,7 @@ static bool compare_value(const String &p_path, const String &p_field, const Var
} else if (p_old_value.get_type() == Variant::DICTIONARY && p_new_value.get_type() == Variant::DICTIONARY) {
Dictionary old_dict = p_old_value;
Dictionary new_dict = p_new_value;
Array old_keys = old_dict.keys();
for (int i = 0; i < old_keys.size(); i++) {
Variant key = old_keys[i];
for (const Variant &key : old_dict.keys()) {
if (!new_dict.has(key)) {
failed = true;
print_error(vformat("Validate extension JSON: Error: Field '%s': %s was removed.", p_path, key));
@ -1328,9 +1326,7 @@ static bool compare_value(const String &p_path, const String &p_field, const Var
failed = true;
}
}
Array new_keys = old_dict.keys();
for (int i = 0; i < new_keys.size(); i++) {
Variant key = new_keys[i];
for (const Variant &key : old_dict.keys()) {
if (!old_dict.has(key)) {
failed = true;
print_error(vformat("Validate extension JSON: Error: Field '%s': %s was added with value %s.", p_path, key, new_dict[key]));
@ -1356,8 +1352,8 @@ static bool compare_dict_array(const Dictionary &p_old_api, const Dictionary &p_
Array new_api = p_new_api[p_base_array];
HashMap<String, Dictionary> new_api_assoc;
for (int i = 0; i < new_api.size(); i++) {
Dictionary elem = new_api[i];
for (const Variant &var : new_api) {
Dictionary elem = var;
ERR_FAIL_COND_V_MSG(!elem.has(p_name_field), false, vformat("Validate extension JSON: Element of base_array '%s' is missing field '%s'. This is a bug.", base_array, p_name_field));
String name = elem[p_name_field];
if (p_compare_operators && elem.has("right_type")) {
@ -1367,8 +1363,8 @@ static bool compare_dict_array(const Dictionary &p_old_api, const Dictionary &p_
}
Array old_api = p_old_api[p_base_array];
for (int i = 0; i < old_api.size(); i++) {
Dictionary old_elem = old_api[i];
for (const Variant &var : old_api) {
Dictionary old_elem = var;
if (!old_elem.has(p_name_field)) {
failed = true;
print_error(vformat("Validate extension JSON: JSON file: element of base array '%s' is missing the field: '%s'.", base_array, p_name_field));
@ -1508,16 +1504,16 @@ static bool compare_sub_dict_array(HashSet<String> &r_removed_classes_registered
Array new_api = p_new_api[p_outer];
HashMap<String, Dictionary> new_api_assoc;
for (int i = 0; i < new_api.size(); i++) {
Dictionary elem = new_api[i];
for (const Variant &var : new_api) {
Dictionary elem = var;
ERR_FAIL_COND_V_MSG(!elem.has(p_outer_name), false, vformat("Validate extension JSON: Element of base_array '%s' is missing field '%s'. This is a bug.", p_outer, p_outer_name));
new_api_assoc.insert(elem[p_outer_name], elem);
}
Array old_api = p_old_api[p_outer];
for (int i = 0; i < old_api.size(); i++) {
Dictionary old_elem = old_api[i];
for (const Variant &var : old_api) {
Dictionary old_elem = var;
if (!old_elem.has(p_outer_name)) {
failed = true;
print_error(vformat("Validate extension JSON: JSON file: element of base array '%s' is missing the field: '%s'.", p_outer, p_outer_name));

View file

@ -86,7 +86,7 @@ String JSON::_stringify(const Variant &p_var, const String &p_indent, int p_cur_
case Variant::PACKED_STRING_ARRAY:
case Variant::ARRAY: {
Array a = p_var;
if (a.size() == 0) {
if (a.is_empty()) {
return "[]";
}
String s = "[";
@ -95,12 +95,15 @@ String JSON::_stringify(const Variant &p_var, const String &p_indent, int p_cur_
ERR_FAIL_COND_V_MSG(p_markers.has(a.id()), "\"[...]\"", "Converting circular structure to JSON.");
p_markers.insert(a.id());
for (int i = 0; i < a.size(); i++) {
if (i > 0) {
bool first = true;
for (const Variant &var : a) {
if (first) {
first = false;
} else {
s += ",";
s += end_statement;
}
s += _make_indent(p_indent, p_cur_indent + 1) + _stringify(a[i], p_indent, p_cur_indent + 1, p_sort_keys, p_markers);
s += _make_indent(p_indent, p_cur_indent + 1) + _stringify(var, p_indent, p_cur_indent + 1, p_sort_keys, p_markers);
}
s += end_statement + _make_indent(p_indent, p_cur_indent) + "]";
p_markers.erase(a.id());

View file

@ -1729,9 +1729,9 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
}
r_len += 4;
for (int i = 0; i < array.size(); i++) {
for (const Variant &var : array) {
int len;
Error err = encode_variant(array.get(i), buf, len, p_full_objects, p_depth + 1);
Error err = encode_variant(var, buf, len, p_full_objects, p_depth + 1);
ERR_FAIL_COND_V(err, err);
ERR_FAIL_COND_V(len % 4, ERR_BUG);
if (buf) {

View file

@ -1844,8 +1844,8 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
f->store_32(VARIANT_ARRAY);
Array a = p_property;
f->store_32(uint32_t(a.size()));
for (int i = 0; i < a.size(); i++) {
write_variant(f, a[i], resource_map, external_resources, string_map);
for (const Variant &var : a) {
write_variant(f, var, resource_map, external_resources, string_map);
}
} break;
@ -2017,9 +2017,7 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant
case Variant::ARRAY: {
Array varray = p_variant;
_find_resources(varray.get_typed_script());
int len = varray.size();
for (int i = 0; i < len; i++) {
const Variant &v = varray.get(i);
for (const Variant &v : varray) {
_find_resources(v);
}

View file

@ -1035,8 +1035,9 @@ void ResourceLoader::load_translation_remaps() {
Array langs = remaps[E];
Vector<String> lang_remaps;
lang_remaps.resize(langs.size());
for (int i = 0; i < langs.size(); i++) {
lang_remaps.write[i] = langs[i];
String *lang_remaps_ptrw = lang_remaps.ptrw();
for (const Variant &lang : langs) {
*lang_remaps_ptrw++ = lang;
}
translation_remaps[String(E)] = lang_remaps;

View file

@ -142,16 +142,16 @@ MethodInfo MethodInfo::from_dict(const Dictionary &p_dict) {
args = p_dict["args"];
}
for (int i = 0; i < args.size(); i++) {
Dictionary d = args[i];
for (const Variant &arg : args) {
Dictionary d = arg;
mi.arguments.push_back(PropertyInfo::from_dict(d));
}
Array defargs;
if (p_dict.has("default_args")) {
defargs = p_dict["default_args"];
}
for (int i = 0; i < defargs.size(); i++) {
mi.default_arguments.push_back(defargs[i]);
for (const Variant &defarg : defargs) {
mi.default_arguments.push_back(defarg);
}
if (p_dict.has("return")) {
@ -1233,8 +1233,8 @@ void Object::_add_user_signal(const String &p_name, const Array &p_args) {
MethodInfo mi;
mi.name = p_name;
for (int i = 0; i < p_args.size(); i++) {
Dictionary d = p_args[i];
for (const Variant &arg : p_args) {
Dictionary d = arg;
PropertyInfo param;
if (d.has("name")) {
@ -1585,8 +1585,8 @@ void Object::_clear_internal_resource_paths(const Variant &p_var) {
} break;
case Variant::ARRAY: {
Array a = p_var;
for (int i = 0; i < a.size(); i++) {
_clear_internal_resource_paths(a[i]);
for (const Variant &var : a) {
_clear_internal_resource_paths(var);
}
} break;

View file

@ -251,8 +251,8 @@ void ScriptServer::init_languages() {
if (ProjectSettings::get_singleton()->has_setting("_global_script_classes")) {
Array script_classes = GLOBAL_GET("_global_script_classes");
for (int i = 0; i < script_classes.size(); i++) {
Dictionary c = script_classes[i];
for (const Variant &script_class : script_classes) {
Dictionary c = script_class;
if (!c.has("class") || !c.has("language") || !c.has("path") || !c.has("base")) {
continue;
}
@ -263,8 +263,8 @@ void ScriptServer::init_languages() {
#endif
Array script_classes = ProjectSettings::get_singleton()->get_global_class_list();
for (int i = 0; i < script_classes.size(); i++) {
Dictionary c = script_classes[i];
for (const Variant &script_class : script_classes) {
Dictionary c = script_class;
if (!c.has("class") || !c.has("language") || !c.has("path") || !c.has("base")) {
continue;
}
@ -463,8 +463,8 @@ void ScriptServer::save_global_classes() {
Dictionary class_icons;
Array script_classes = ProjectSettings::get_singleton()->get_global_class_list();
for (int i = 0; i < script_classes.size(); i++) {
Dictionary d = script_classes[i];
for (const Variant &script_class : script_classes) {
Dictionary d = script_class;
if (!d.has("name") || !d.has("icon")) {
continue;
}

View file

@ -319,8 +319,8 @@ public:
}
if (r_errors != nullptr && ret.has("errors")) {
Array errors = ret["errors"];
for (int i = 0; i < errors.size(); i++) {
Dictionary err = errors[i];
for (const Variant &error : errors) {
Dictionary err = error;
ERR_CONTINUE(!err.has("line"));
ERR_CONTINUE(!err.has("column"));
ERR_CONTINUE(!err.has("message"));
@ -339,8 +339,8 @@ public:
if (r_warnings != nullptr && ret.has("warnings")) {
ERR_FAIL_COND_V(!ret.has("warnings"), false);
Array warnings = ret["warnings"];
for (int i = 0; i < warnings.size(); i++) {
Dictionary warn = warnings[i];
for (const Variant &warning : warnings) {
Dictionary warn = warning;
ERR_CONTINUE(!warn.has("start_line"));
ERR_CONTINUE(!warn.has("end_line"));
ERR_CONTINUE(!warn.has("leftmost_column"));
@ -402,8 +402,8 @@ public:
if (r_options != nullptr && ret.has("options")) {
Array options = ret["options"];
for (int i = 0; i < options.size(); i++) {
Dictionary op = options[i];
for (const Variant &var : options) {
Dictionary op = var;
CodeCompletionOption option;
ERR_CONTINUE(!op.has("kind"));
option.kind = CodeCompletionKind(int(op["kind"]));
@ -502,8 +502,8 @@ public:
}
if (p_values != nullptr && ret.has("values")) {
Array values = ret["values"];
for (int i = 0; i < values.size(); i++) {
p_values->push_back(values[i]);
for (const Variant &value : values) {
p_values->push_back(value);
}
}
}
@ -522,8 +522,8 @@ public:
}
if (p_values != nullptr && ret.has("values")) {
Array values = ret["values"];
for (int i = 0; i < values.size(); i++) {
p_values->push_back(values[i]);
for (const Variant &value : values) {
p_values->push_back(value);
}
}
}
@ -549,8 +549,8 @@ public:
}
if (p_values != nullptr && ret.has("values")) {
Array values = ret["values"];
for (int i = 0; i < values.size(); i++) {
p_values->push_back(values[i]);
for (const Variant &value : values) {
p_values->push_back(value);
}
}
}
@ -562,9 +562,9 @@ public:
TypedArray<Dictionary> ret;
GDVIRTUAL_REQUIRED_CALL(_debug_get_current_stack_info, ret);
Vector<StackInfo> sret;
for (int i = 0; i < ret.size(); i++) {
for (const Variant &var : ret) {
StackInfo si;
Dictionary d = ret[i];
Dictionary d = var;
ERR_CONTINUE(!d.has("file"));
ERR_CONTINUE(!d.has("func"));
ERR_CONTINUE(!d.has("line"));
@ -595,8 +595,8 @@ public:
virtual void get_public_functions(List<MethodInfo> *p_functions) const override {
TypedArray<Dictionary> ret;
GDVIRTUAL_REQUIRED_CALL(_get_public_functions, ret);
for (int i = 0; i < ret.size(); i++) {
MethodInfo mi = MethodInfo::from_dict(ret[i]);
for (const Variant &var : ret) {
MethodInfo mi = MethodInfo::from_dict(var);
p_functions->push_back(mi);
}
}
@ -615,8 +615,8 @@ public:
virtual void get_public_annotations(List<MethodInfo> *p_annotations) const override {
TypedArray<Dictionary> ret;
GDVIRTUAL_REQUIRED_CALL(_get_public_annotations, ret);
for (int i = 0; i < ret.size(); i++) {
MethodInfo mi = MethodInfo::from_dict(ret[i]);
for (const Variant &var : ret) {
MethodInfo mi = MethodInfo::from_dict(var);
p_annotations->push_back(mi);
}
}

View file

@ -81,6 +81,22 @@ void Array::_unref() const {
_p = nullptr;
}
Array::Iterator Array::begin() {
return Iterator(_p->array.ptrw(), _p->read_only);
}
Array::Iterator Array::end() {
return Iterator(_p->array.ptrw() + _p->array.size(), _p->read_only);
}
Array::ConstIterator Array::begin() const {
return ConstIterator(_p->array.ptr(), _p->read_only);
}
Array::ConstIterator Array::end() const {
return ConstIterator(_p->array.ptr() + _p->array.size(), _p->read_only);
}
Variant &Array::operator[](int p_idx) {
if (unlikely(_p->read_only)) {
*_p->read_only = _p->array[p_idx];

View file

@ -46,6 +46,70 @@ class Array {
void _unref() const;
public:
struct ConstIterator {
_FORCE_INLINE_ const Variant &operator*() const;
_FORCE_INLINE_ const Variant *operator->() const;
_FORCE_INLINE_ ConstIterator &operator++();
_FORCE_INLINE_ ConstIterator &operator--();
_FORCE_INLINE_ bool operator==(const ConstIterator &p_other) const { return element_ptr == p_other.element_ptr; }
_FORCE_INLINE_ bool operator!=(const ConstIterator &p_other) const { return element_ptr == p_other.element_ptr; }
_FORCE_INLINE_ ConstIterator(const Variant *p_element_ptr, Variant *p_read_only = nullptr) :
element_ptr(p_element_ptr), read_only(p_read_only) {}
_FORCE_INLINE_ ConstIterator() {}
_FORCE_INLINE_ ConstIterator(const ConstIterator &p_other) :
element_ptr(p_other.element_ptr), read_only(p_other.read_only) {}
_FORCE_INLINE_ ConstIterator &operator=(const ConstIterator &p_other) {
element_ptr = p_other.element_ptr;
read_only = p_other.read_only;
return *this;
}
private:
const Variant *element_ptr = nullptr;
Variant *read_only = nullptr;
};
struct Iterator {
_FORCE_INLINE_ Variant &operator*() const;
_FORCE_INLINE_ Variant *operator->() const;
_FORCE_INLINE_ Iterator &operator++();
_FORCE_INLINE_ Iterator &operator--();
_FORCE_INLINE_ bool operator==(const Iterator &p_other) const { return element_ptr == p_other.element_ptr; }
_FORCE_INLINE_ bool operator!=(const Iterator &p_other) const { return element_ptr != p_other.element_ptr; }
_FORCE_INLINE_ Iterator(Variant *p_element_ptr, Variant *p_read_only = nullptr) :
element_ptr(p_element_ptr), read_only(p_read_only) {}
_FORCE_INLINE_ Iterator() {}
_FORCE_INLINE_ Iterator(const Iterator &p_other) :
element_ptr(p_other.element_ptr), read_only(p_other.read_only) {}
_FORCE_INLINE_ Iterator &operator=(const Iterator &p_other) {
element_ptr = p_other.element_ptr;
read_only = p_other.read_only;
return *this;
}
operator ConstIterator() const {
return ConstIterator(element_ptr, read_only);
}
private:
Variant *element_ptr = nullptr;
Variant *read_only = nullptr;
};
Iterator begin();
Iterator end();
ConstIterator begin() const;
ConstIterator end() const;
void _ref(const Array &p_from) const;
Variant &operator[](int p_idx);

View file

@ -865,4 +865,56 @@ Callable Callable::bind(VarArgs... p_args) const {
return bindp(sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
}
Variant &Array::Iterator::operator*() const {
if (unlikely(read_only)) {
*read_only = *element_ptr;
return *read_only;
}
return *element_ptr;
}
Variant *Array::Iterator::operator->() const {
if (unlikely(read_only)) {
*read_only = *element_ptr;
return read_only;
}
return element_ptr;
}
Array::Iterator &Array::Iterator::operator++() {
element_ptr++;
return *this;
}
Array::Iterator &Array::Iterator::operator--() {
element_ptr--;
return *this;
}
const Variant &Array::ConstIterator::operator*() const {
if (unlikely(read_only)) {
*read_only = *element_ptr;
return *read_only;
}
return *element_ptr;
}
const Variant *Array::ConstIterator::operator->() const {
if (unlikely(read_only)) {
*read_only = *element_ptr;
return read_only;
}
return element_ptr;
}
Array::ConstIterator &Array::ConstIterator::operator++() {
element_ptr++;
return *this;
}
Array::ConstIterator &Array::ConstIterator::operator--() {
element_ptr--;
return *this;
}
#endif // VARIANT_H

View file

@ -2012,12 +2012,14 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
p_recursion_count++;
p_store_string_func(p_store_string_ud, "[");
int len = array.size();
for (int i = 0; i < len; i++) {
if (i > 0) {
bool first = true;
for (const Variant &var : array) {
if (first) {
first = false;
} else {
p_store_string_func(p_store_string_ud, ", ");
}
write(array[i], p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count);
write(var, p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count);
}
p_store_string_func(p_store_string_ud, "]");

View file

@ -273,8 +273,8 @@ Ref<PackedScene> ResourceLoaderText::_parse_node_tag(VariantParser::ResourcePars
if (next_tag.fields.has("groups")) {
Array groups = next_tag.fields["groups"];
for (int i = 0; i < groups.size(); i++) {
packed_scene->get_state()->add_node_group(node_id, packed_scene->get_state()->add_name(groups[i]));
for (const Variant &group : groups) {
packed_scene->get_state()->add_node_group(node_id, packed_scene->get_state()->add_name(group));
}
}
@ -352,8 +352,8 @@ Ref<PackedScene> ResourceLoaderText::_parse_node_tag(VariantParser::ResourcePars
}
Vector<int> bind_ints;
for (int i = 0; i < binds.size(); i++) {
bind_ints.push_back(packed_scene->get_state()->add_value(binds[i]));
for (const Variant &bind : binds) {
bind_ints.push_back(packed_scene->get_state()->add_value(bind));
}
packed_scene->get_state()->add_connection(
@ -1952,10 +1952,8 @@ void ResourceFormatSaverTextInstance::_find_resources(const Variant &p_variant,
case Variant::ARRAY: {
Array varray = p_variant;
_find_resources(varray.get_typed_script());
int len = varray.size();
for (int i = 0; i < len; i++) {
const Variant &v = varray.get(i);
_find_resources(v);
for (const Variant &var : varray) {
_find_resources(var);
}
} break;

View file

@ -545,6 +545,54 @@ TEST_CASE("[Array] Recursive self comparison") {
a2.clear();
}
TEST_CASE("[Array] Iteration") {
Array a1 = build_array(1, 2, 3);
Array a2 = build_array(1, 2, 3);
int idx = 0;
for (Variant &E : a1) {
CHECK_EQ(int(a2[idx]), int(E));
idx++;
}
idx = 0;
for (const Variant &E : (const Array &)a1) {
CHECK_EQ(int(a2[idx]), int(E));
idx++;
}
a1.clear();
}
TEST_CASE("[Array] Iteration and modification") {
Array a1 = build_array(1, 2, 3);
Array a2 = build_array(2, 3, 4);
Array a3 = build_array(1, 2, 3);
Array a4 = build_array(1, 2, 3);
a3.make_read_only();
int idx = 0;
for (Variant &E : a1) {
E = a2[idx];
idx++;
}
CHECK_EQ(a1, a2);
// Ensure read-only is respected.
idx = 0;
for (Variant &E : a3) {
E = a2[idx];
}
CHECK_EQ(a3, a4);
a1.clear();
a2.clear();
a4.clear();
}
} // namespace TestArray
#endif // TEST_ARRAY_H