Skip to content

Commit

Permalink
Core: Add typed array support for binary serialization
Browse files Browse the repository at this point in the history
  • Loading branch information
dalexeev committed Aug 22, 2023
1 parent 6758a7f commit 86c2ddf
Showing 1 changed file with 140 additions and 10 deletions.
150 changes: 140 additions & 10 deletions core/io/marshalls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -56,8 +59,20 @@ ObjectID EncodedObjectAsID::get_object_id() const {
#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

// For `Variant::INT`, `Variant::FLOAT` and other math types.
#define ENCODE_FLAG_64 (1 << 16)

// For `Variant::OBJECT`.
#define ENCODE_FLAG_OBJECT_AS_ID (1 << 16)

// For `Variant::ARRAY`.
// Occupies bits 16 and 17. If we need more flags/fields, we can place them after.
#define ENCODE_FIELD_TYPED_ARRAY_MASK (0b11 << 16)
#define ENCODE_FIELD_TYPED_ARRAY_NONE (0b00 << 16)
#define ENCODE_FIELD_TYPED_ARRAY_BUILTIN (0b01 << 16)
#define ENCODE_FIELD_TYPED_ARRAY_CLASS_NAME (0b10 << 16)
#define ENCODE_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);
Expand Down Expand Up @@ -609,7 +624,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
} break;
case Variant::OBJECT: {
if (type & ENCODE_FLAG_OBJECT_AS_ID) {
//this _is_ allowed
// This _is_ allowed.
ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA);
ObjectID val = ObjectID(decode_uint64(buf));
if (r_len) {
Expand All @@ -625,7 +640,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);

Expand Down Expand Up @@ -657,6 +671,21 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
return err;
}

if (str == "script") {
String path;
err = _decode_string(buf, len, r_len, path);
if (err) {
return err;
}
if (!path.is_empty()) {
ERR_FAIL_COND_V_MSG(!path.is_resource_file(), ERR_INVALID_DATA, "Invalid script path: '" + path + "'.");
Ref<Script> script = ResourceLoader::load(path);
ERR_FAIL_COND_V_MSG(script.is_null(), ERR_INVALID_DATA, "Can't load script at path: '" + path + "'.");
obj->set_script(script);
}
continue;
}

Variant value;
int used;
err = decode_variant(value, buf, len, &used, p_allow_objects, p_depth + 1);
Expand Down Expand Up @@ -745,7 +774,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 (type & ENCODE_FIELD_TYPED_ARRAY_MASK) {
case ENCODE_FIELD_TYPED_ARRAY_NONE:
break; // Untyped array.
case ENCODE_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 ENCODE_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 ENCODE_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.is_resource_file(), ERR_INVALID_DATA, "Invalid script path: '" + path + "'.");
script = ResourceLoader::load(path);
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;
Expand All @@ -758,6 +840,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;
Expand Down Expand Up @@ -1152,6 +1237,22 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
flags |= ENCODE_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);
flags |= ENCODE_FIELD_TYPED_ARRAY_SCRIPT;
} else if (array.get_typed_class_name() != StringName()) {
ERR_FAIL_COND_V(!p_full_objects, ERR_UNAVAILABLE);
flags |= ENCODE_FIELD_TYPED_ARRAY_CLASS_NAME;
} else {
ERR_FAIL_COND_V(!p_full_objects && array.get_typed_builtin() == Variant::OBJECT, ERR_UNAVAILABLE);
flags |= ENCODE_FIELD_TYPED_ARRAY_BUILTIN;
}
}
} break;
#ifdef REAL_T_IS_DOUBLE
case Variant::VECTOR2:
case Variant::VECTOR3:
Expand Down Expand Up @@ -1517,6 +1618,18 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo

_encode_string(E.name, buf, r_len);

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.is_resource_file(), ERR_UNAVAILABLE, "Failed to encode a path to a custom script.");
_encode_string(path, buf, r_len);
} else {
_encode_string("", buf, r_len);
}
continue;
}

int len;
Error err = encode_variant(obj->get(E.name), buf, len, p_full_objects, p_depth + 1);
ERR_FAIL_COND_V(err, err);
Expand Down Expand Up @@ -1588,24 +1701,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 (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.is_resource_file(), 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(v.size()), 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;
Expand Down

0 comments on commit 86c2ddf

Please sign in to comment.