Core: Add typed array support for binary serialization
This commit is contained in:
parent
fe01776f05
commit
c30bec7fca
|
@ -30,7 +30,10 @@
|
|||
|
||||
#include "marshalls.h"
|
||||
|
||||
#include "core/core_string_names.h"
|
||||
#include "core/io/resource_loader.h"
|
||||
#include "core/object/ref_counted.h"
|
||||
#include "core/object/script_language.h"
|
||||
#include "core/os/keyboard.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_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) {
|
||||
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);
|
||||
|
||||
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;
|
||||
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.
|
||||
// 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.
|
||||
switch (type & ENCODE_MASK) {
|
||||
switch (header & HEADER_TYPE_MASK) {
|
||||
case Variant::NIL: {
|
||||
r_variant = Variant();
|
||||
} break;
|
||||
|
@ -127,7 +143,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
|
|||
}
|
||||
} break;
|
||||
case Variant::INT: {
|
||||
if (type & ENCODE_FLAG_64) {
|
||||
if (header & HEADER_DATA_FLAG_64) {
|
||||
ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA);
|
||||
int64_t val = decode_uint64(buf);
|
||||
r_variant = val;
|
||||
|
@ -146,7 +162,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
|
|||
|
||||
} break;
|
||||
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);
|
||||
double val = decode_double(buf);
|
||||
r_variant = val;
|
||||
|
@ -176,7 +192,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
|
|||
// math types
|
||||
case Variant::VECTOR2: {
|
||||
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);
|
||||
val.x = decode_double(&buf[0]);
|
||||
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;
|
||||
case Variant::RECT2: {
|
||||
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);
|
||||
val.position.x = decode_double(&buf[0]);
|
||||
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;
|
||||
case Variant::VECTOR3: {
|
||||
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);
|
||||
val.x = decode_double(&buf[0]);
|
||||
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;
|
||||
case Variant::VECTOR4: {
|
||||
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);
|
||||
val.x = decode_double(&buf[0]);
|
||||
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;
|
||||
case Variant::TRANSFORM2D: {
|
||||
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);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
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;
|
||||
case Variant::PLANE: {
|
||||
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);
|
||||
val.normal.x = decode_double(&buf[0]);
|
||||
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;
|
||||
case Variant::QUATERNION: {
|
||||
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);
|
||||
val.x = decode_double(&buf[0]);
|
||||
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;
|
||||
case Variant::AABB: {
|
||||
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);
|
||||
val.position.x = decode_double(&buf[0]);
|
||||
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;
|
||||
case Variant::BASIS: {
|
||||
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);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
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;
|
||||
case Variant::TRANSFORM3D: {
|
||||
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);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
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;
|
||||
case Variant::PROJECTION: {
|
||||
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);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
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 subnamecount = decode_uint32(buf + 4);
|
||||
uint32_t flags = decode_uint32(buf + 8);
|
||||
uint32_t np_flags = decode_uint32(buf + 8);
|
||||
|
||||
len -= 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++;
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
//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);
|
||||
} break;
|
||||
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);
|
||||
ObjectID val = ObjectID(decode_uint64(buf));
|
||||
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;
|
||||
}
|
||||
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
|
||||
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)) {
|
||||
|
@ -747,7 +771,60 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
|
|||
|
||||
} break;
|
||||
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);
|
||||
|
||||
int32_t count = decode_uint32(buf);
|
||||
// bool shared = count&0x80000000;
|
||||
count &= 0x7FFFFFFF;
|
||||
|
@ -760,6 +837,9 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
|
|||
}
|
||||
|
||||
Array varr;
|
||||
if (builtin_type != Variant::VARIANT_MAX) {
|
||||
varr.set_typed(builtin_type, class_name, script);
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
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;
|
||||
|
||||
if (type & ENCODE_FLAG_64) {
|
||||
if (header & HEADER_DATA_FLAG_64) {
|
||||
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);
|
||||
|
||||
|
@ -996,7 +1076,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
|
|||
|
||||
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_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;
|
||||
|
||||
uint32_t flags = 0;
|
||||
uint32_t header = p_variant.get_type();
|
||||
|
||||
switch (p_variant.get_type()) {
|
||||
case Variant::INT: {
|
||||
int64_t val = p_variant;
|
||||
if (val > (int64_t)INT_MAX || val < (int64_t)INT_MIN) {
|
||||
flags |= ENCODE_FLAG_64;
|
||||
header |= HEADER_DATA_FLAG_64;
|
||||
}
|
||||
} break;
|
||||
case Variant::FLOAT: {
|
||||
double d = p_variant;
|
||||
float f = d;
|
||||
if (double(f) != d) {
|
||||
flags |= ENCODE_FLAG_64;
|
||||
header |= HEADER_DATA_FLAG_64;
|
||||
}
|
||||
} break;
|
||||
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) {
|
||||
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;
|
||||
#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::RECT2:
|
||||
case Variant::AABB: {
|
||||
flags |= ENCODE_FLAG_64;
|
||||
header |= HEADER_DATA_FLAG_64;
|
||||
} break;
|
||||
#endif // REAL_T_IS_DOUBLE
|
||||
default: {
|
||||
|
@ -1176,7 +1272,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
|
|||
}
|
||||
|
||||
if (buf) {
|
||||
encode_uint32(p_variant.get_type() | flags, buf);
|
||||
encode_uint32(header, buf);
|
||||
buf += 4;
|
||||
}
|
||||
r_len += 4;
|
||||
|
@ -1194,7 +1290,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
|
|||
|
||||
} break;
|
||||
case Variant::INT: {
|
||||
if (flags & ENCODE_FLAG_64) {
|
||||
if (header & HEADER_DATA_FLAG_64) {
|
||||
//64 bits
|
||||
if (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;
|
||||
case Variant::FLOAT: {
|
||||
if (flags & ENCODE_FLAG_64) {
|
||||
if (header & HEADER_DATA_FLAG_64) {
|
||||
if (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);
|
||||
|
||||
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;
|
||||
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(len % 4, ERR_BUG);
|
||||
r_len += len;
|
||||
|
@ -1594,24 +1703,41 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
|
|||
|
||||
} break;
|
||||
case Variant::ARRAY: {
|
||||
Array v = p_variant;
|
||||
Array array = p_variant;
|
||||
|
||||
if (buf) {
|
||||
encode_uint32(uint32_t(v.size()), buf);
|
||||
buf += 4;
|
||||
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) {
|
||||
encode_uint32(uint32_t(array.size()), buf);
|
||||
buf += 4;
|
||||
}
|
||||
r_len += 4;
|
||||
|
||||
for (int i = 0; i < v.size(); i++) {
|
||||
for (int i = 0; i < array.size(); i++) {
|
||||
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(len % 4, ERR_BUG);
|
||||
r_len += len;
|
||||
if (buf) {
|
||||
buf += len;
|
||||
}
|
||||
r_len += len;
|
||||
}
|
||||
|
||||
} break;
|
||||
|
|
|
@ -160,7 +160,7 @@ TEST_CASE("[Marshalls] NIL Variant encoding") {
|
|||
uint8_t buffer[4];
|
||||
|
||||
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(buffer[1] == 0x00);
|
||||
CHECK(buffer[2] == 0x00);
|
||||
|
@ -174,7 +174,7 @@ TEST_CASE("[Marshalls] INT 32 bit Variant encoding") {
|
|||
uint8_t buffer[8];
|
||||
|
||||
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(buffer[1] == 0x00);
|
||||
CHECK(buffer[2] == 0x00);
|
||||
|
@ -192,10 +192,10 @@ TEST_CASE("[Marshalls] INT 64 bit Variant encoding") {
|
|||
uint8_t buffer[12];
|
||||
|
||||
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(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 value
|
||||
CHECK(buffer[4] == 0xef);
|
||||
|
@ -214,7 +214,7 @@ TEST_CASE("[Marshalls] FLOAT single precision Variant encoding") {
|
|||
uint8_t buffer[8];
|
||||
|
||||
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(buffer[1] == 0x00);
|
||||
CHECK(buffer[2] == 0x00);
|
||||
|
@ -232,10 +232,10 @@ TEST_CASE("[Marshalls] FLOAT double precision Variant encoding") {
|
|||
uint8_t buffer[12];
|
||||
|
||||
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(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 value
|
||||
CHECK(buffer[4] == 0x55);
|
||||
|
@ -292,7 +292,7 @@ TEST_CASE("[Marshalls] INT 64 bit Variant decoding") {
|
|||
Variant variant;
|
||||
int r_len;
|
||||
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
|
||||
};
|
||||
|
||||
|
@ -318,7 +318,7 @@ TEST_CASE("[Marshalls] FLOAT double precision Variant decoding") {
|
|||
Variant variant;
|
||||
int r_len;
|
||||
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
|
||||
};
|
||||
|
||||
|
@ -326,6 +326,66 @@ TEST_CASE("[Marshalls] FLOAT double precision Variant decoding") {
|
|||
CHECK(r_len == 12);
|
||||
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
|
||||
|
||||
#endif // TEST_MARSHALLS_H
|
||||
|
|
Loading…
Reference in New Issue