Browse Source

Core: Add typed array support for binary serialization

Danil Alexeev 2 years ago
parent
commit
c30bec7fca
2 changed files with 238 additions and 52 deletions
  1. 169 43
      core/io/marshalls.cpp
  2. 69 9
      tests/core/io/test_marshalls.h

+ 169 - 43
core/io/marshalls.cpp

@@ -30,7 +30,10 @@
 
 
 #include "marshalls.h"
 #include "marshalls.h"
 
 
+#include "core/core_string_names.h"
+#include "core/io/resource_loader.h"
 #include "core/object/ref_counted.h"
 #include "core/object/ref_counted.h"
+#include "core/object/script_language.h"
 #include "core/os/keyboard.h"
 #include "core/os/keyboard.h"
 #include "core/string/print_string.h"
 #include "core/string/print_string.h"
 
 
@@ -55,9 +58,22 @@ ObjectID EncodedObjectAsID::get_object_id() const {
 #define ERR_FAIL_ADD_OF(a, b, err) ERR_FAIL_COND_V(((int32_t)(b)) < 0 || ((int32_t)(a)) < 0 || ((int32_t)(a)) > INT_MAX - ((int32_t)(b)), err)
 #define ERR_FAIL_ADD_OF(a, b, err) ERR_FAIL_COND_V(((int32_t)(b)) < 0 || ((int32_t)(a)) < 0 || ((int32_t)(a)) > INT_MAX - ((int32_t)(b)), err)
 #define ERR_FAIL_MUL_OF(a, b, err) ERR_FAIL_COND_V(((int32_t)(a)) < 0 || ((int32_t)(b)) <= 0 || ((int32_t)(a)) > INT_MAX / ((int32_t)(b)), err)
 #define ERR_FAIL_MUL_OF(a, b, err) ERR_FAIL_COND_V(((int32_t)(a)) < 0 || ((int32_t)(b)) <= 0 || ((int32_t)(a)) > INT_MAX / ((int32_t)(b)), err)
 
 
-#define ENCODE_MASK 0xFF
-#define ENCODE_FLAG_64 1 << 16
-#define ENCODE_FLAG_OBJECT_AS_ID 1 << 16
+// Byte 0: `Variant::Type`, byte 1: unused, bytes 2 and 3: additional data.
+#define HEADER_TYPE_MASK 0xFF
+
+// For `Variant::INT`, `Variant::FLOAT` and other math types.
+#define HEADER_DATA_FLAG_64 (1 << 16)
+
+// For `Variant::OBJECT`.
+#define HEADER_DATA_FLAG_OBJECT_AS_ID (1 << 16)
+
+// For `Variant::ARRAY`.
+// Occupies bits 16 and 17.
+#define HEADER_DATA_FIELD_TYPED_ARRAY_MASK (0b11 << 16)
+#define HEADER_DATA_FIELD_TYPED_ARRAY_NONE (0b00 << 16)
+#define HEADER_DATA_FIELD_TYPED_ARRAY_BUILTIN (0b01 << 16)
+#define HEADER_DATA_FIELD_TYPED_ARRAY_CLASS_NAME (0b10 << 16)
+#define HEADER_DATA_FIELD_TYPED_ARRAY_SCRIPT (0b11 << 16)
 
 
 static Error _decode_string(const uint8_t *&buf, int &len, int *r_len, String &r_string) {
 static Error _decode_string(const uint8_t *&buf, int &len, int *r_len, String &r_string) {
 	ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
 	ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
@@ -101,9 +117,9 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
 
 
 	ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
 	ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
 
 
-	uint32_t type = decode_uint32(buf);
+	uint32_t header = decode_uint32(buf);
 
 
-	ERR_FAIL_COND_V((type & ENCODE_MASK) >= Variant::VARIANT_MAX, ERR_INVALID_DATA);
+	ERR_FAIL_COND_V((header & HEADER_TYPE_MASK) >= Variant::VARIANT_MAX, ERR_INVALID_DATA);
 
 
 	buf += 4;
 	buf += 4;
 	len -= 4;
 	len -= 4;
@@ -114,7 +130,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
 	// Note: We cannot use sizeof(real_t) for decoding, in case a different size is encoded.
 	// Note: We cannot use sizeof(real_t) for decoding, in case a different size is encoded.
 	// Decoding math types always checks for the encoded size, while encoding always uses compilation setting.
 	// Decoding math types always checks for the encoded size, while encoding always uses compilation setting.
 	// This does lead to some code duplication for decoding, but compatibility is the priority.
 	// This does lead to some code duplication for decoding, but compatibility is the priority.
-	switch (type & ENCODE_MASK) {
+	switch (header & HEADER_TYPE_MASK) {
 		case Variant::NIL: {
 		case Variant::NIL: {
 			r_variant = Variant();
 			r_variant = Variant();
 		} break;
 		} break;
@@ -127,7 +143,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
 			}
 			}
 		} break;
 		} break;
 		case Variant::INT: {
 		case Variant::INT: {
-			if (type & ENCODE_FLAG_64) {
+			if (header & HEADER_DATA_FLAG_64) {
 				ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA);
 				ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA);
 				int64_t val = decode_uint64(buf);
 				int64_t val = decode_uint64(buf);
 				r_variant = val;
 				r_variant = val;
@@ -146,7 +162,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
 
 
 		} break;
 		} break;
 		case Variant::FLOAT: {
 		case Variant::FLOAT: {
-			if (type & ENCODE_FLAG_64) {
+			if (header & HEADER_DATA_FLAG_64) {
 				ERR_FAIL_COND_V((size_t)len < sizeof(double), ERR_INVALID_DATA);
 				ERR_FAIL_COND_V((size_t)len < sizeof(double), ERR_INVALID_DATA);
 				double val = decode_double(buf);
 				double val = decode_double(buf);
 				r_variant = val;
 				r_variant = val;
@@ -176,7 +192,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
 		// math types
 		// math types
 		case Variant::VECTOR2: {
 		case Variant::VECTOR2: {
 			Vector2 val;
 			Vector2 val;
-			if (type & ENCODE_FLAG_64) {
+			if (header & HEADER_DATA_FLAG_64) {
 				ERR_FAIL_COND_V((size_t)len < sizeof(double) * 2, ERR_INVALID_DATA);
 				ERR_FAIL_COND_V((size_t)len < sizeof(double) * 2, ERR_INVALID_DATA);
 				val.x = decode_double(&buf[0]);
 				val.x = decode_double(&buf[0]);
 				val.y = decode_double(&buf[sizeof(double)]);
 				val.y = decode_double(&buf[sizeof(double)]);
@@ -210,7 +226,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
 		} break;
 		} break;
 		case Variant::RECT2: {
 		case Variant::RECT2: {
 			Rect2 val;
 			Rect2 val;
-			if (type & ENCODE_FLAG_64) {
+			if (header & HEADER_DATA_FLAG_64) {
 				ERR_FAIL_COND_V((size_t)len < sizeof(double) * 4, ERR_INVALID_DATA);
 				ERR_FAIL_COND_V((size_t)len < sizeof(double) * 4, ERR_INVALID_DATA);
 				val.position.x = decode_double(&buf[0]);
 				val.position.x = decode_double(&buf[0]);
 				val.position.y = decode_double(&buf[sizeof(double)]);
 				val.position.y = decode_double(&buf[sizeof(double)]);
@@ -250,7 +266,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
 		} break;
 		} break;
 		case Variant::VECTOR3: {
 		case Variant::VECTOR3: {
 			Vector3 val;
 			Vector3 val;
-			if (type & ENCODE_FLAG_64) {
+			if (header & HEADER_DATA_FLAG_64) {
 				ERR_FAIL_COND_V((size_t)len < sizeof(double) * 3, ERR_INVALID_DATA);
 				ERR_FAIL_COND_V((size_t)len < sizeof(double) * 3, ERR_INVALID_DATA);
 				val.x = decode_double(&buf[0]);
 				val.x = decode_double(&buf[0]);
 				val.y = decode_double(&buf[sizeof(double)]);
 				val.y = decode_double(&buf[sizeof(double)]);
@@ -287,7 +303,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
 		} break;
 		} break;
 		case Variant::VECTOR4: {
 		case Variant::VECTOR4: {
 			Vector4 val;
 			Vector4 val;
-			if (type & ENCODE_FLAG_64) {
+			if (header & HEADER_DATA_FLAG_64) {
 				ERR_FAIL_COND_V((size_t)len < sizeof(double) * 4, ERR_INVALID_DATA);
 				ERR_FAIL_COND_V((size_t)len < sizeof(double) * 4, ERR_INVALID_DATA);
 				val.x = decode_double(&buf[0]);
 				val.x = decode_double(&buf[0]);
 				val.y = decode_double(&buf[sizeof(double)]);
 				val.y = decode_double(&buf[sizeof(double)]);
@@ -327,7 +343,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
 		} break;
 		} break;
 		case Variant::TRANSFORM2D: {
 		case Variant::TRANSFORM2D: {
 			Transform2D val;
 			Transform2D val;
-			if (type & ENCODE_FLAG_64) {
+			if (header & HEADER_DATA_FLAG_64) {
 				ERR_FAIL_COND_V((size_t)len < sizeof(double) * 6, ERR_INVALID_DATA);
 				ERR_FAIL_COND_V((size_t)len < sizeof(double) * 6, ERR_INVALID_DATA);
 				for (int i = 0; i < 3; i++) {
 				for (int i = 0; i < 3; i++) {
 					for (int j = 0; j < 2; j++) {
 					for (int j = 0; j < 2; j++) {
@@ -355,7 +371,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
 		} break;
 		} break;
 		case Variant::PLANE: {
 		case Variant::PLANE: {
 			Plane val;
 			Plane val;
-			if (type & ENCODE_FLAG_64) {
+			if (header & HEADER_DATA_FLAG_64) {
 				ERR_FAIL_COND_V((size_t)len < sizeof(double) * 4, ERR_INVALID_DATA);
 				ERR_FAIL_COND_V((size_t)len < sizeof(double) * 4, ERR_INVALID_DATA);
 				val.normal.x = decode_double(&buf[0]);
 				val.normal.x = decode_double(&buf[0]);
 				val.normal.y = decode_double(&buf[sizeof(double)]);
 				val.normal.y = decode_double(&buf[sizeof(double)]);
@@ -381,7 +397,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
 		} break;
 		} break;
 		case Variant::QUATERNION: {
 		case Variant::QUATERNION: {
 			Quaternion val;
 			Quaternion val;
-			if (type & ENCODE_FLAG_64) {
+			if (header & HEADER_DATA_FLAG_64) {
 				ERR_FAIL_COND_V((size_t)len < sizeof(double) * 4, ERR_INVALID_DATA);
 				ERR_FAIL_COND_V((size_t)len < sizeof(double) * 4, ERR_INVALID_DATA);
 				val.x = decode_double(&buf[0]);
 				val.x = decode_double(&buf[0]);
 				val.y = decode_double(&buf[sizeof(double)]);
 				val.y = decode_double(&buf[sizeof(double)]);
@@ -407,7 +423,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
 		} break;
 		} break;
 		case Variant::AABB: {
 		case Variant::AABB: {
 			AABB val;
 			AABB val;
-			if (type & ENCODE_FLAG_64) {
+			if (header & HEADER_DATA_FLAG_64) {
 				ERR_FAIL_COND_V((size_t)len < sizeof(double) * 6, ERR_INVALID_DATA);
 				ERR_FAIL_COND_V((size_t)len < sizeof(double) * 6, ERR_INVALID_DATA);
 				val.position.x = decode_double(&buf[0]);
 				val.position.x = decode_double(&buf[0]);
 				val.position.y = decode_double(&buf[sizeof(double)]);
 				val.position.y = decode_double(&buf[sizeof(double)]);
@@ -437,7 +453,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
 		} break;
 		} break;
 		case Variant::BASIS: {
 		case Variant::BASIS: {
 			Basis val;
 			Basis val;
-			if (type & ENCODE_FLAG_64) {
+			if (header & HEADER_DATA_FLAG_64) {
 				ERR_FAIL_COND_V((size_t)len < sizeof(double) * 9, ERR_INVALID_DATA);
 				ERR_FAIL_COND_V((size_t)len < sizeof(double) * 9, ERR_INVALID_DATA);
 				for (int i = 0; i < 3; i++) {
 				for (int i = 0; i < 3; i++) {
 					for (int j = 0; j < 3; j++) {
 					for (int j = 0; j < 3; j++) {
@@ -465,7 +481,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
 		} break;
 		} break;
 		case Variant::TRANSFORM3D: {
 		case Variant::TRANSFORM3D: {
 			Transform3D val;
 			Transform3D val;
-			if (type & ENCODE_FLAG_64) {
+			if (header & HEADER_DATA_FLAG_64) {
 				ERR_FAIL_COND_V((size_t)len < sizeof(double) * 12, ERR_INVALID_DATA);
 				ERR_FAIL_COND_V((size_t)len < sizeof(double) * 12, ERR_INVALID_DATA);
 				for (int i = 0; i < 3; i++) {
 				for (int i = 0; i < 3; i++) {
 					for (int j = 0; j < 3; j++) {
 					for (int j = 0; j < 3; j++) {
@@ -499,7 +515,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
 		} break;
 		} break;
 		case Variant::PROJECTION: {
 		case Variant::PROJECTION: {
 			Projection val;
 			Projection val;
-			if (type & ENCODE_FLAG_64) {
+			if (header & HEADER_DATA_FLAG_64) {
 				ERR_FAIL_COND_V((size_t)len < sizeof(double) * 16, ERR_INVALID_DATA);
 				ERR_FAIL_COND_V((size_t)len < sizeof(double) * 16, ERR_INVALID_DATA);
 				for (int i = 0; i < 4; i++) {
 				for (int i = 0; i < 4; i++) {
 					for (int j = 0; j < 4; j++) {
 					for (int j = 0; j < 4; j++) {
@@ -560,12 +576,12 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
 
 
 				uint32_t namecount = strlen &= 0x7FFFFFFF;
 				uint32_t namecount = strlen &= 0x7FFFFFFF;
 				uint32_t subnamecount = decode_uint32(buf + 4);
 				uint32_t subnamecount = decode_uint32(buf + 4);
-				uint32_t flags = decode_uint32(buf + 8);
+				uint32_t np_flags = decode_uint32(buf + 8);
 
 
 				len -= 12;
 				len -= 12;
 				buf += 12;
 				buf += 12;
 
 
-				if (flags & 2) { // Obsolete format with property separate from subpath
+				if (np_flags & 2) { // Obsolete format with property separate from subpath.
 					subnamecount++;
 					subnamecount++;
 				}
 				}
 
 
@@ -589,7 +605,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
 					}
 					}
 				}
 				}
 
 
-				r_variant = NodePath(names, subnames, flags & 1);
+				r_variant = NodePath(names, subnames, np_flags & 1);
 
 
 			} else {
 			} else {
 				//old format, just a string
 				//old format, just a string
@@ -608,8 +624,8 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
 			r_variant = RID::from_uint64(id);
 			r_variant = RID::from_uint64(id);
 		} break;
 		} break;
 		case Variant::OBJECT: {
 		case Variant::OBJECT: {
-			if (type & ENCODE_FLAG_OBJECT_AS_ID) {
-				//this _is_ allowed
+			if (header & HEADER_DATA_FLAG_OBJECT_AS_ID) {
+				// This _is_ allowed.
 				ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA);
 				ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA);
 				ObjectID val = ObjectID(decode_uint64(buf));
 				ObjectID val = ObjectID(decode_uint64(buf));
 				if (r_len) {
 				if (r_len) {
@@ -625,7 +641,6 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
 
 
 					r_variant = obj_as_id;
 					r_variant = obj_as_id;
 				}
 				}
-
 			} else {
 			} else {
 				ERR_FAIL_COND_V(!p_allow_objects, ERR_UNAUTHORIZED);
 				ERR_FAIL_COND_V(!p_allow_objects, ERR_UNAUTHORIZED);
 
 
@@ -672,7 +687,16 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
 							(*r_len) += used;
 							(*r_len) += used;
 						}
 						}
 
 
-						obj->set(str, value);
+						if (str == "script") {
+							ERR_FAIL_COND_V_MSG(value.get_type() != Variant::STRING, ERR_INVALID_DATA, "Invalid value for \"script\" property, expected script path as String.");
+							String path = value;
+							ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://") || !ResourceLoader::exists(path, "Script"), ERR_INVALID_DATA, "Invalid script path: '" + path + "'.");
+							Ref<Script> script = ResourceLoader::load(path, "Script");
+							ERR_FAIL_COND_V_MSG(script.is_null(), ERR_INVALID_DATA, "Can't load script at path: '" + path + "'.");
+							obj->set_script(script);
+						} else {
+							obj->set(str, value);
+						}
 					}
 					}
 
 
 					if (Object::cast_to<RefCounted>(obj)) {
 					if (Object::cast_to<RefCounted>(obj)) {
@@ -747,7 +771,60 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
 
 
 		} break;
 		} break;
 		case Variant::ARRAY: {
 		case Variant::ARRAY: {
+			Variant::Type builtin_type = Variant::VARIANT_MAX;
+			StringName class_name;
+			Ref<Script> script;
+
+			switch (header & HEADER_DATA_FIELD_TYPED_ARRAY_MASK) {
+				case HEADER_DATA_FIELD_TYPED_ARRAY_NONE:
+					break; // Untyped array.
+				case HEADER_DATA_FIELD_TYPED_ARRAY_BUILTIN: {
+					ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
+
+					int32_t bt = decode_uint32(buf);
+					buf += 4;
+					len -= 4;
+					if (r_len) {
+						(*r_len) += 4;
+					}
+
+					ERR_FAIL_INDEX_V(bt, Variant::VARIANT_MAX, ERR_INVALID_DATA);
+					builtin_type = (Variant::Type)bt;
+					ERR_FAIL_COND_V(!p_allow_objects && builtin_type == Variant::OBJECT, ERR_UNAUTHORIZED);
+				} break;
+				case HEADER_DATA_FIELD_TYPED_ARRAY_CLASS_NAME: {
+					ERR_FAIL_COND_V(!p_allow_objects, ERR_UNAUTHORIZED);
+
+					String str;
+					Error err = _decode_string(buf, len, r_len, str);
+					if (err) {
+						return err;
+					}
+
+					builtin_type = Variant::OBJECT;
+					class_name = str;
+				} break;
+				case HEADER_DATA_FIELD_TYPED_ARRAY_SCRIPT: {
+					ERR_FAIL_COND_V(!p_allow_objects, ERR_UNAUTHORIZED);
+
+					String path;
+					Error err = _decode_string(buf, len, r_len, path);
+					if (err) {
+						return err;
+					}
+					ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://") || !ResourceLoader::exists(path, "Script"), ERR_INVALID_DATA, "Invalid script path: '" + path + "'.");
+					script = ResourceLoader::load(path, "Script");
+					ERR_FAIL_COND_V_MSG(script.is_null(), ERR_INVALID_DATA, "Can't load script at path: '" + path + "'.");
+
+					builtin_type = Variant::OBJECT;
+					class_name = script->get_instance_base_type();
+				} break;
+				default:
+					ERR_FAIL_V(ERR_INVALID_DATA); // Future proofing.
+			}
+
 			ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
 			ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
+
 			int32_t count = decode_uint32(buf);
 			int32_t count = decode_uint32(buf);
 			//  bool shared = count&0x80000000;
 			//  bool shared = count&0x80000000;
 			count &= 0x7FFFFFFF;
 			count &= 0x7FFFFFFF;
@@ -760,6 +837,9 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
 			}
 			}
 
 
 			Array varr;
 			Array varr;
+			if (builtin_type != Variant::VARIANT_MAX) {
+				varr.set_typed(builtin_type, class_name, script);
+			}
 
 
 			for (int i = 0; i < count; i++) {
 			for (int i = 0; i < count; i++) {
 				int used = 0;
 				int used = 0;
@@ -936,7 +1016,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
 
 
 			Vector<Vector2> varray;
 			Vector<Vector2> varray;
 
 
-			if (type & ENCODE_FLAG_64) {
+			if (header & HEADER_DATA_FLAG_64) {
 				ERR_FAIL_MUL_OF(count, sizeof(double) * 2, ERR_INVALID_DATA);
 				ERR_FAIL_MUL_OF(count, sizeof(double) * 2, ERR_INVALID_DATA);
 				ERR_FAIL_COND_V(count < 0 || count * sizeof(double) * 2 > (size_t)len, ERR_INVALID_DATA);
 				ERR_FAIL_COND_V(count < 0 || count * sizeof(double) * 2 > (size_t)len, ERR_INVALID_DATA);
 
 
@@ -996,7 +1076,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
 
 
 			Vector<Vector3> varray;
 			Vector<Vector3> varray;
 
 
-			if (type & ENCODE_FLAG_64) {
+			if (header & HEADER_DATA_FLAG_64) {
 				ERR_FAIL_MUL_OF(count, sizeof(double) * 3, ERR_INVALID_DATA);
 				ERR_FAIL_MUL_OF(count, sizeof(double) * 3, ERR_INVALID_DATA);
 				ERR_FAIL_COND_V(count < 0 || count * sizeof(double) * 3 > (size_t)len, ERR_INVALID_DATA);
 				ERR_FAIL_COND_V(count < 0 || count * sizeof(double) * 3 > (size_t)len, ERR_INVALID_DATA);
 
 
@@ -1122,20 +1202,20 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
 
 
 	r_len = 0;
 	r_len = 0;
 
 
-	uint32_t flags = 0;
+	uint32_t header = p_variant.get_type();
 
 
 	switch (p_variant.get_type()) {
 	switch (p_variant.get_type()) {
 		case Variant::INT: {
 		case Variant::INT: {
 			int64_t val = p_variant;
 			int64_t val = p_variant;
 			if (val > (int64_t)INT_MAX || val < (int64_t)INT_MIN) {
 			if (val > (int64_t)INT_MAX || val < (int64_t)INT_MIN) {
-				flags |= ENCODE_FLAG_64;
+				header |= HEADER_DATA_FLAG_64;
 			}
 			}
 		} break;
 		} break;
 		case Variant::FLOAT: {
 		case Variant::FLOAT: {
 			double d = p_variant;
 			double d = p_variant;
 			float f = d;
 			float f = d;
 			if (double(f) != d) {
 			if (double(f) != d) {
-				flags |= ENCODE_FLAG_64;
+				header |= HEADER_DATA_FLAG_64;
 			}
 			}
 		} break;
 		} break;
 		case Variant::OBJECT: {
 		case Variant::OBJECT: {
@@ -1151,7 +1231,23 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
 			}
 			}
 
 
 			if (!p_full_objects) {
 			if (!p_full_objects) {
-				flags |= ENCODE_FLAG_OBJECT_AS_ID;
+				header |= HEADER_DATA_FLAG_OBJECT_AS_ID;
+			}
+		} break;
+		case Variant::ARRAY: {
+			Array array = p_variant;
+			if (array.is_typed()) {
+				Ref<Script> script = array.get_typed_script();
+				if (script.is_valid()) {
+					ERR_FAIL_COND_V(!p_full_objects, ERR_UNAVAILABLE);
+					header |= HEADER_DATA_FIELD_TYPED_ARRAY_SCRIPT;
+				} else if (array.get_typed_class_name() != StringName()) {
+					ERR_FAIL_COND_V(!p_full_objects, ERR_UNAVAILABLE);
+					header |= HEADER_DATA_FIELD_TYPED_ARRAY_CLASS_NAME;
+				} else {
+					ERR_FAIL_COND_V(!p_full_objects && array.get_typed_builtin() == Variant::OBJECT, ERR_UNAVAILABLE);
+					header |= HEADER_DATA_FIELD_TYPED_ARRAY_BUILTIN;
+				}
 			}
 			}
 		} break;
 		} break;
 #ifdef REAL_T_IS_DOUBLE
 #ifdef REAL_T_IS_DOUBLE
@@ -1168,7 +1264,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
 		case Variant::BASIS:
 		case Variant::BASIS:
 		case Variant::RECT2:
 		case Variant::RECT2:
 		case Variant::AABB: {
 		case Variant::AABB: {
-			flags |= ENCODE_FLAG_64;
+			header |= HEADER_DATA_FLAG_64;
 		} break;
 		} break;
 #endif // REAL_T_IS_DOUBLE
 #endif // REAL_T_IS_DOUBLE
 		default: {
 		default: {
@@ -1176,7 +1272,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
 	}
 	}
 
 
 	if (buf) {
 	if (buf) {
-		encode_uint32(p_variant.get_type() | flags, buf);
+		encode_uint32(header, buf);
 		buf += 4;
 		buf += 4;
 	}
 	}
 	r_len += 4;
 	r_len += 4;
@@ -1194,7 +1290,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
 
 
 		} break;
 		} break;
 		case Variant::INT: {
 		case Variant::INT: {
-			if (flags & ENCODE_FLAG_64) {
+			if (header & HEADER_DATA_FLAG_64) {
 				//64 bits
 				//64 bits
 				if (buf) {
 				if (buf) {
 					encode_uint64(p_variant.operator int64_t(), buf);
 					encode_uint64(p_variant.operator int64_t(), buf);
@@ -1210,7 +1306,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
 			}
 			}
 		} break;
 		} break;
 		case Variant::FLOAT: {
 		case Variant::FLOAT: {
-			if (flags & ENCODE_FLAG_64) {
+			if (header & HEADER_DATA_FLAG_64) {
 				if (buf) {
 				if (buf) {
 					encode_double(p_variant.operator double(), buf);
 					encode_double(p_variant.operator double(), buf);
 				}
 				}
@@ -1523,8 +1619,21 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
 
 
 						_encode_string(E.name, buf, r_len);
 						_encode_string(E.name, buf, r_len);
 
 
+						Variant value;
+
+						if (E.name == CoreStringNames::get_singleton()->_script) {
+							Ref<Script> script = obj->get_script();
+							if (script.is_valid()) {
+								String path = script->get_path();
+								ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://"), ERR_UNAVAILABLE, "Failed to encode a path to a custom script.");
+								value = path;
+							}
+						} else {
+							value = obj->get(E.name);
+						}
+
 						int len;
 						int len;
-						Error err = encode_variant(obj->get(E.name), buf, len, p_full_objects, p_depth + 1);
+						Error err = encode_variant(value, buf, len, p_full_objects, p_depth + 1);
 						ERR_FAIL_COND_V(err, err);
 						ERR_FAIL_COND_V(err, err);
 						ERR_FAIL_COND_V(len % 4, ERR_BUG);
 						ERR_FAIL_COND_V(len % 4, ERR_BUG);
 						r_len += len;
 						r_len += len;
@@ -1594,24 +1703,41 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
 
 
 		} break;
 		} break;
 		case Variant::ARRAY: {
 		case Variant::ARRAY: {
-			Array v = p_variant;
+			Array array = p_variant;
+
+			if (array.is_typed()) {
+				Variant variant = array.get_typed_script();
+				Ref<Script> script = variant;
+				if (script.is_valid()) {
+					String path = script->get_path();
+					ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://"), ERR_UNAVAILABLE, "Failed to encode a path to a custom script for an array type.");
+					_encode_string(path, buf, r_len);
+				} else if (array.get_typed_class_name() != StringName()) {
+					_encode_string(array.get_typed_class_name(), buf, r_len);
+				} else {
+					if (buf) {
+						encode_uint32(array.get_typed_builtin(), buf);
+						buf += 4;
+					}
+					r_len += 4;
+				}
+			}
 
 
 			if (buf) {
 			if (buf) {
-				encode_uint32(uint32_t(v.size()), buf);
+				encode_uint32(uint32_t(array.size()), buf);
 				buf += 4;
 				buf += 4;
 			}
 			}
-
 			r_len += 4;
 			r_len += 4;
 
 
-			for (int i = 0; i < v.size(); i++) {
+			for (int i = 0; i < array.size(); i++) {
 				int len;
 				int len;
-				Error err = encode_variant(v.get(i), buf, len, p_full_objects, p_depth + 1);
+				Error err = encode_variant(array.get(i), buf, len, p_full_objects, p_depth + 1);
 				ERR_FAIL_COND_V(err, err);
 				ERR_FAIL_COND_V(err, err);
 				ERR_FAIL_COND_V(len % 4, ERR_BUG);
 				ERR_FAIL_COND_V(len % 4, ERR_BUG);
-				r_len += len;
 				if (buf) {
 				if (buf) {
 					buf += len;
 					buf += len;
 				}
 				}
+				r_len += len;
 			}
 			}
 
 
 		} break;
 		} break;

+ 69 - 9
tests/core/io/test_marshalls.h

@@ -160,7 +160,7 @@ TEST_CASE("[Marshalls] NIL Variant encoding") {
 	uint8_t buffer[4];
 	uint8_t buffer[4];
 
 
 	CHECK(encode_variant(variant, buffer, r_len) == OK);
 	CHECK(encode_variant(variant, buffer, r_len) == OK);
-	CHECK_MESSAGE(r_len == 4, "Length == 4 bytes for Variant::Type");
+	CHECK_MESSAGE(r_len == 4, "Length == 4 bytes for header");
 	CHECK_MESSAGE(buffer[0] == 0x00, "Variant::NIL");
 	CHECK_MESSAGE(buffer[0] == 0x00, "Variant::NIL");
 	CHECK(buffer[1] == 0x00);
 	CHECK(buffer[1] == 0x00);
 	CHECK(buffer[2] == 0x00);
 	CHECK(buffer[2] == 0x00);
@@ -174,7 +174,7 @@ TEST_CASE("[Marshalls] INT 32 bit Variant encoding") {
 	uint8_t buffer[8];
 	uint8_t buffer[8];
 
 
 	CHECK(encode_variant(variant, buffer, r_len) == OK);
 	CHECK(encode_variant(variant, buffer, r_len) == OK);
-	CHECK_MESSAGE(r_len == 8, "Length == 4 bytes for Variant::Type + 4 bytes for int32_t");
+	CHECK_MESSAGE(r_len == 8, "Length == 4 bytes for header + 4 bytes for int32_t");
 	CHECK_MESSAGE(buffer[0] == 0x02, "Variant::INT");
 	CHECK_MESSAGE(buffer[0] == 0x02, "Variant::INT");
 	CHECK(buffer[1] == 0x00);
 	CHECK(buffer[1] == 0x00);
 	CHECK(buffer[2] == 0x00);
 	CHECK(buffer[2] == 0x00);
@@ -192,10 +192,10 @@ TEST_CASE("[Marshalls] INT 64 bit Variant encoding") {
 	uint8_t buffer[12];
 	uint8_t buffer[12];
 
 
 	CHECK(encode_variant(variant, buffer, r_len) == OK);
 	CHECK(encode_variant(variant, buffer, r_len) == OK);
-	CHECK_MESSAGE(r_len == 12, "Length == 4 bytes for Variant::Type + 8 bytes for int64_t");
+	CHECK_MESSAGE(r_len == 12, "Length == 4 bytes for header + 8 bytes for int64_t");
 	CHECK_MESSAGE(buffer[0] == 0x02, "Variant::INT");
 	CHECK_MESSAGE(buffer[0] == 0x02, "Variant::INT");
 	CHECK(buffer[1] == 0x00);
 	CHECK(buffer[1] == 0x00);
-	CHECK_MESSAGE(buffer[2] == 0x01, "ENCODE_FLAG_64");
+	CHECK_MESSAGE(buffer[2] == 0x01, "HEADER_DATA_FLAG_64");
 	CHECK(buffer[3] == 0x00);
 	CHECK(buffer[3] == 0x00);
 	// Check value
 	// Check value
 	CHECK(buffer[4] == 0xef);
 	CHECK(buffer[4] == 0xef);
@@ -214,7 +214,7 @@ TEST_CASE("[Marshalls] FLOAT single precision Variant encoding") {
 	uint8_t buffer[8];
 	uint8_t buffer[8];
 
 
 	CHECK(encode_variant(variant, buffer, r_len) == OK);
 	CHECK(encode_variant(variant, buffer, r_len) == OK);
-	CHECK_MESSAGE(r_len == 8, "Length == 4 bytes for Variant::Type + 4 bytes for float");
+	CHECK_MESSAGE(r_len == 8, "Length == 4 bytes for header + 4 bytes for float");
 	CHECK_MESSAGE(buffer[0] == 0x03, "Variant::FLOAT");
 	CHECK_MESSAGE(buffer[0] == 0x03, "Variant::FLOAT");
 	CHECK(buffer[1] == 0x00);
 	CHECK(buffer[1] == 0x00);
 	CHECK(buffer[2] == 0x00);
 	CHECK(buffer[2] == 0x00);
@@ -232,10 +232,10 @@ TEST_CASE("[Marshalls] FLOAT double precision Variant encoding") {
 	uint8_t buffer[12];
 	uint8_t buffer[12];
 
 
 	CHECK(encode_variant(variant, buffer, r_len) == OK);
 	CHECK(encode_variant(variant, buffer, r_len) == OK);
-	CHECK_MESSAGE(r_len == 12, "Length == 4 bytes for Variant::Type + 8 bytes for double");
+	CHECK_MESSAGE(r_len == 12, "Length == 4 bytes for header + 8 bytes for double");
 	CHECK_MESSAGE(buffer[0] == 0x03, "Variant::FLOAT");
 	CHECK_MESSAGE(buffer[0] == 0x03, "Variant::FLOAT");
 	CHECK(buffer[1] == 0x00);
 	CHECK(buffer[1] == 0x00);
-	CHECK_MESSAGE(buffer[2] == 0x01, "ENCODE_FLAG_64");
+	CHECK_MESSAGE(buffer[2] == 0x01, "HEADER_DATA_FLAG_64");
 	CHECK(buffer[3] == 0x00);
 	CHECK(buffer[3] == 0x00);
 	// Check value
 	// Check value
 	CHECK(buffer[4] == 0x55);
 	CHECK(buffer[4] == 0x55);
@@ -292,7 +292,7 @@ TEST_CASE("[Marshalls] INT 64 bit Variant decoding") {
 	Variant variant;
 	Variant variant;
 	int r_len;
 	int r_len;
 	uint8_t buffer[] = {
 	uint8_t buffer[] = {
-		0x02, 0x00, 0x01, 0x00, // Variant::INT & ENCODE_FLAG_64
+		0x02, 0x00, 0x01, 0x00, // Variant::INT, HEADER_DATA_FLAG_64
 		0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0xf1 // value
 		0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0xf1 // value
 	};
 	};
 
 
@@ -318,7 +318,7 @@ TEST_CASE("[Marshalls] FLOAT double precision Variant decoding") {
 	Variant variant;
 	Variant variant;
 	int r_len;
 	int r_len;
 	uint8_t buffer[] = {
 	uint8_t buffer[] = {
-		0x03, 0x00, 0x01, 0x00, // Variant::FLOAT & ENCODE_FLAG_64
+		0x03, 0x00, 0x01, 0x00, // Variant::FLOAT, HEADER_DATA_FLAG_64
 		0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xd5, 0x3f // value
 		0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xd5, 0x3f // value
 	};
 	};
 
 
@@ -326,6 +326,66 @@ TEST_CASE("[Marshalls] FLOAT double precision Variant decoding") {
 	CHECK(r_len == 12);
 	CHECK(r_len == 12);
 	CHECK(variant == Variant(0.33333333333333333));
 	CHECK(variant == Variant(0.33333333333333333));
 }
 }
+
+TEST_CASE("[Marshalls] Typed array encoding") {
+	int r_len;
+	Array array;
+	array.set_typed(Variant::INT, StringName(), Ref<Script>());
+	array.push_back(Variant(uint64_t(0x0f123456789abcdef)));
+	uint8_t buffer[24];
+
+	CHECK(encode_variant(array, buffer, r_len) == OK);
+	CHECK_MESSAGE(r_len == 24, "Length == 4 bytes for header + 4 bytes for array type + 4 bytes for array size + 12 bytes for element");
+	CHECK_MESSAGE(buffer[0] == 0x1c, "Variant::ARRAY");
+	CHECK(buffer[1] == 0x00);
+	CHECK_MESSAGE(buffer[2] == 0x01, "HEADER_DATA_FIELD_TYPED_ARRAY_BUILTIN");
+	CHECK(buffer[3] == 0x00);
+	// Check array type.
+	CHECK_MESSAGE(buffer[4] == 0x02, "Variant::INT");
+	CHECK(buffer[5] == 0x00);
+	CHECK(buffer[6] == 0x00);
+	CHECK(buffer[7] == 0x00);
+	// Check array size.
+	CHECK(buffer[8] == 0x01);
+	CHECK(buffer[9] == 0x00);
+	CHECK(buffer[10] == 0x00);
+	CHECK(buffer[11] == 0x00);
+	// Check element type.
+	CHECK_MESSAGE(buffer[12] == 0x02, "Variant::INT");
+	CHECK(buffer[13] == 0x00);
+	CHECK_MESSAGE(buffer[14] == 0x01, "HEADER_DATA_FLAG_64");
+	CHECK(buffer[15] == 0x00);
+	// Check element value.
+	CHECK(buffer[16] == 0xef);
+	CHECK(buffer[17] == 0xcd);
+	CHECK(buffer[18] == 0xab);
+	CHECK(buffer[19] == 0x89);
+	CHECK(buffer[20] == 0x67);
+	CHECK(buffer[21] == 0x45);
+	CHECK(buffer[22] == 0x23);
+	CHECK(buffer[23] == 0xf1);
+}
+
+TEST_CASE("[Marshalls] Typed array decoding") {
+	Variant variant;
+	int r_len;
+	uint8_t buffer[] = {
+		0x1c, 0x00, 0x01, 0x00, // Variant::ARRAY, HEADER_DATA_FIELD_TYPED_ARRAY_BUILTIN
+		0x02, 0x00, 0x00, 0x00, // Array type (Variant::INT).
+		0x01, 0x00, 0x00, 0x00, // Array size.
+		0x02, 0x00, 0x01, 0x00, // Element type (Variant::INT, HEADER_DATA_FLAG_64).
+		0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0xf1, // Element value.
+	};
+
+	CHECK(decode_variant(variant, buffer, 24, &r_len) == OK);
+	CHECK(r_len == 24);
+	CHECK(variant.get_type() == Variant::ARRAY);
+	Array array = variant;
+	CHECK(array.get_typed_builtin() == Variant::INT);
+	CHECK(array.size() == 1);
+	CHECK(array[0] == Variant(uint64_t(0x0f123456789abcdef)));
+}
+
 } // namespace TestMarshalls
 } // namespace TestMarshalls
 
 
 #endif // TEST_MARSHALLS_H
 #endif // TEST_MARSHALLS_H