Browse Source

Allow indexing of String values in scripting languages

George Marques 4 years ago
parent
commit
262d9397fb
3 changed files with 165 additions and 0 deletions
  1. 86 0
      core/variant/variant_setget.cpp
  2. 6 0
      doc/classes/String.xml
  3. 73 0
      tests/test_string.h

+ 86 - 0
core/variant/variant_setget.cpp

@@ -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);

+ 6 - 0
doc/classes/String.xml

@@ -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" />

+ 73 - 0
tests/test_string.h

@@ -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