Allow indexing of String values in scripting languages

This commit is contained in:
George Marques 2021-09-17 16:02:21 -03:00
parent 424ddcba37
commit 262d9397fb
No known key found for this signature in database
GPG key ID: 046BD46A3201E43D
3 changed files with 165 additions and 0 deletions

View file

@ -661,6 +661,91 @@ struct VariantIndexedSetGet_Array {
static uint64_t get_indexed_size(const Variant *base) { return 0; }
};
struct VariantIndexedSetGet_String {
static void get(const Variant *base, int64_t index, Variant *value, bool *oob) {
int64_t length = VariantGetInternalPtr<String>::get_ptr(base)->length();
if (index < 0) {
index += length;
}
if (index < 0 || index >= length) {
*oob = true;
return;
}
char32_t result = (*VariantGetInternalPtr<String>::get_ptr(base))[index];
*value = String(&result, 1);
*oob = false;
}
static void ptr_get(const void *base, int64_t index, void *member) {
/* avoid ptrconvert for performance*/
const String &v = *reinterpret_cast<const String *>(base);
if (index < 0) {
index += v.length();
}
OOB_TEST(index, v.length());
char32_t c = v[index];
PtrToArg<String>::encode(String(&c, 1), member);
}
static void set(Variant *base, int64_t index, const Variant *value, bool *valid, bool *oob) {
if (value->get_type() != Variant::STRING) {
*oob = false;
*valid = false;
return;
}
int64_t length = VariantGetInternalPtr<String>::get_ptr(base)->length();
if (index < 0) {
index += length;
}
if (index < 0 || index >= length) {
*oob = true;
*valid = false;
return;
}
String *b = VariantGetInternalPtr<String>::get_ptr(base);
const String *v = VariantInternal::get_string(value);
if (v->length() == 0) {
b->remove(index);
} else {
b->set(index, v->get(0));
}
*oob = false;
*valid = true;
}
static void validated_set(Variant *base, int64_t index, const Variant *value, bool *oob) {
int64_t length = VariantGetInternalPtr<String>::get_ptr(base)->length();
if (index < 0) {
index += length;
}
if (index < 0 || index >= length) {
*oob = true;
return;
}
String *b = VariantGetInternalPtr<String>::get_ptr(base);
const String *v = VariantInternal::get_string(value);
if (v->length() == 0) {
b->remove(index);
} else {
b->set(index, v->get(0));
}
*oob = false;
}
static void ptr_set(void *base, int64_t index, const void *member) {
/* avoid ptrconvert for performance*/
String &v = *reinterpret_cast<String *>(base);
if (index < 0) {
index += v.length();
}
OOB_TEST(index, v.length());
const String &m = *reinterpret_cast<const String *>(member);
if (unlikely(m.length() == 0)) {
v.remove(index);
} else {
v.set(index, m.unicode_at(0));
}
}
static Variant::Type get_index_type() { return Variant::STRING; }
static uint64_t get_indexed_size(const Variant *base) { return VariantInternal::get_string(base)->length(); }
};
#define INDEXED_SETGET_STRUCT_DICT(m_base_type) \
struct VariantIndexedSetGet_##m_base_type { \
static void get(const Variant *base, int64_t index, Variant *value, bool *oob) { \
@ -758,6 +843,7 @@ static void register_indexed_member(Variant::Type p_type) {
void register_indexed_setters_getters() {
#define REGISTER_INDEXED_MEMBER(m_base_type) register_indexed_member<VariantIndexedSetGet_##m_base_type>(GetTypeInfo<m_base_type>::VARIANT_TYPE)
REGISTER_INDEXED_MEMBER(String);
REGISTER_INDEXED_MEMBER(Vector2);
REGISTER_INDEXED_MEMBER(Vector2i);
REGISTER_INDEXED_MEMBER(Vector3);

View file

@ -513,6 +513,12 @@
<description>
</description>
</method>
<method name="operator []" qualifiers="operator">
<return type="String" />
<argument index="0" name="index" type="int" />
<description>
</description>
</method>
<method name="pad_decimals" qualifiers="const">
<return type="String" />
<argument index="0" name="digits" type="int" />

View file

@ -39,6 +39,7 @@
#include "core/os/main_loop.h"
#include "core/os/os.h"
#include "core/string/ustring.h"
#include "core/variant/variant.h"
#include "tests/test_macros.h"
@ -1380,6 +1381,78 @@ TEST_CASE("[String] validate_node_name") {
String name_with_invalid_chars = "Name with invalid characters :.@removed!";
CHECK(name_with_invalid_chars.validate_node_name() == "Name with invalid characters removed!");
}
TEST_CASE("[String] Variant indexed get") {
Variant s = String("abcd");
bool valid = false;
bool oob = true;
String r = s.get_indexed(1, valid, oob);
CHECK(valid);
CHECK_FALSE(oob);
CHECK_EQ(r, String("b"));
}
TEST_CASE("[String] Variant validated indexed get") {
Variant s = String("abcd");
Variant::ValidatedIndexedGetter getter = Variant::get_member_validated_indexed_getter(Variant::STRING);
Variant r;
bool oob = true;
getter(&s, 1, &r, &oob);
CHECK_FALSE(oob);
CHECK_EQ(r, String("b"));
}
TEST_CASE("[String] Variant ptr indexed get") {
String s("abcd");
Variant::PTRIndexedGetter getter = Variant::get_member_ptr_indexed_getter(Variant::STRING);
String r;
getter(&s, 1, &r);
CHECK_EQ(r, String("b"));
}
TEST_CASE("[String] Variant indexed set") {
Variant s = String("abcd");
bool valid = false;
bool oob = true;
s.set_indexed(1, String("z"), valid, oob);
CHECK(valid);
CHECK_FALSE(oob);
CHECK_EQ(s, String("azcd"));
}
TEST_CASE("[String] Variant validated indexed set") {
Variant s = String("abcd");
Variant::ValidatedIndexedSetter setter = Variant::get_member_validated_indexed_setter(Variant::STRING);
Variant v = String("z");
bool oob = true;
setter(&s, 1, &v, &oob);
CHECK_FALSE(oob);
CHECK_EQ(s, String("azcd"));
}
TEST_CASE("[String] Variant ptr indexed set") {
String s("abcd");
Variant::PTRIndexedSetter setter = Variant::get_member_ptr_indexed_setter(Variant::STRING);
String v("z");
setter(&s, 1, &v);
CHECK_EQ(s, String("azcd"));
}
} // namespace TestString
#endif // TEST_STRING_H