Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions core/io/json.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1045,6 +1045,7 @@ Variant JSON::_from_native(const Variant &p_variant, bool p_full_objects, int p_
fd["name"] = String(info->get_field_name(i));
fd["type"] = String(StructInfo::type_to_token(info->get_field_type(i)));
fd["typed"] = info->is_field_typed(i);
fd["nullable"] = info->is_field_nullable(i);
if (info->get_field_class_name(i) != StringName()) {
fd["class_name"] = String(info->get_field_class_name(i));
}
Expand Down Expand Up @@ -1573,6 +1574,7 @@ Variant JSON::_to_native(const Variant &p_json, bool p_allow_objects, int p_dept
f.type = StructInfo::type_from_token(String(fd.get("type", "nil")));
ERR_FAIL_COND_V_MSG(f.type == Variant::VARIANT_MAX, Variant(), "Unknown struct field type token.");
f.is_typed = bool(fd.get("typed", false));
f.is_nullable = bool(fd.get("nullable", false));
if (fd.has("class_name")) {
f.class_name = StringName(String(fd["class_name"]));
}
Expand Down
12 changes: 12 additions & 0 deletions core/io/marshalls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1364,6 +1364,13 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
if (r_len) {
(*r_len) += 4;
}
ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
f.is_nullable = decode_uint32(buf) != 0;
buf += 4;
len -= 4;
if (r_len) {
(*r_len) += 4;
}
{
String cn;
Error err = _decode_string(buf, len, r_len, cn);
Expand Down Expand Up @@ -2044,6 +2051,11 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
buf += 4;
}
r_len += 4;
if (buf) {
encode_uint32(info->is_field_nullable(i) ? 1 : 0, buf);
buf += 4;
}
r_len += 4;
_encode_string(String(info->get_field_class_name(i)), buf, r_len);
_encode_string(String(info->get_field_struct_type_id(i)), buf, r_len);

Expand Down
216 changes: 171 additions & 45 deletions core/variant/struct.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,35 +48,165 @@ static_assert(std::is_nothrow_move_constructible_v<Struct>);
static_assert(std::is_nothrow_move_assignable_v<Struct>);
static_assert(std::is_nothrow_destructible_v<Struct>);

template <typename T>
struct StructFieldOps {
static void construct(void *p_slot, const Variant &p_default) {
if (p_default.get_type() == Variant::NIL) {
memnew_placement(p_slot, T());
} else if constexpr (std::is_same_v<T, Array> || std::is_same_v<T, Dictionary>) {
memnew_placement(p_slot, T(p_default.duplicate(true).operator T()));
} else {
memnew_placement(p_slot, T(p_default.operator T()));
}
}
static void copy(void *p_dst, const void *p_src) {
memnew_placement(p_dst, T(*static_cast<const T *>(p_src)));
}
static void destruct(void *p_slot) {
if constexpr (!std::is_trivially_destructible_v<T>) {
static_cast<T *>(p_slot)->~T();
}
}
static Variant get(const void *p_slot) {
return Variant(*static_cast<const T *>(p_slot));
}
static void set(void *p_slot, const Variant &p_value) {
*static_cast<T *>(p_slot) = p_value.operator T();
}
};

struct StructData {
Ref<StructInfo> info;
Variant *values = nullptr;
int field_count = 0;

_FORCE_INLINE_ int field_count() const { return info->get_field_count(); }

StructData() = default;
StructData(const StructData &) = delete;
StructData &operator=(const StructData &) = delete;

static constexpr size_t data_offset() {
return (sizeof(StructData) + alignof(Variant) - 1) & ~(alignof(Variant) - 1);
}

_FORCE_INLINE_ uint8_t *data() {
return reinterpret_cast<uint8_t *>(this) + data_offset();
}
_FORCE_INLINE_ const uint8_t *data() const {
return reinterpret_cast<const uint8_t *>(this) + data_offset();
}
_FORCE_INLINE_ void *slot(int p_index) { return data() + info->get_field_offset(p_index); }
_FORCE_INLINE_ const void *slot(int p_index) const { return data() + info->get_field_offset(p_index); }

void construct_default(int p_index) {
void *s = slot(p_index);
switch (info->get_field_storage(p_index)) {
#define _STRUCT_CONSTRUCT(m_suffix, m_type) \
case StructInfo::STORAGE_##m_suffix: \
StructFieldOps<m_type>::construct(s, info->_get_field_default_raw(p_index)); \
return;
STRUCT_NATIVE_STORAGE_TYPES(_STRUCT_CONSTRUCT)
#undef _STRUCT_CONSTRUCT
default:
memnew_placement(s, Variant(info->instantiate_default(p_index)));
return;
}
}

void copy_construct(int p_index, const StructData &p_from) {
void *d = slot(p_index);
const void *sc = p_from.slot(p_index);
switch (info->get_field_storage(p_index)) {
#define _STRUCT_COPY(m_suffix, m_type) \
case StructInfo::STORAGE_##m_suffix: \
StructFieldOps<m_type>::copy(d, sc); \
return;
STRUCT_NATIVE_STORAGE_TYPES(_STRUCT_COPY)
#undef _STRUCT_COPY
default:
memnew_placement(d, Variant(*static_cast<const Variant *>(sc)));
return;
}
}

void destruct(int p_index) {
void *s = slot(p_index);
switch (info->get_field_storage(p_index)) {
#define _STRUCT_DESTRUCT(m_suffix, m_type) \
case StructInfo::STORAGE_##m_suffix: \
StructFieldOps<m_type>::destruct(s); \
return;
STRUCT_NATIVE_STORAGE_TYPES(_STRUCT_DESTRUCT)
#undef _STRUCT_DESTRUCT
default:
static_cast<Variant *>(s)->~Variant();
return;
}
}

Variant get(int p_index) const {
const void *s = slot(p_index);
switch (info->get_field_storage(p_index)) {
#define _STRUCT_GET(m_suffix, m_type) \
case StructInfo::STORAGE_##m_suffix: \
return StructFieldOps<m_type>::get(s);
STRUCT_NATIVE_STORAGE_TYPES(_STRUCT_GET)
#undef _STRUCT_GET
default:
return *static_cast<const Variant *>(s);
}
}

// p_value must already be normalized to the field's type by the caller.
void set(int p_index, const Variant &p_value) {
void *s = slot(p_index);
switch (info->get_field_storage(p_index)) {
#define _STRUCT_SET(m_suffix, m_type) \
case StructInfo::STORAGE_##m_suffix: \
StructFieldOps<m_type>::set(s, p_value); \
return;
STRUCT_NATIVE_STORAGE_TYPES(_STRUCT_SET)
#undef _STRUCT_SET
default:
*static_cast<Variant *>(s) = p_value;
return;
}
}

static StructData *alloc(const Ref<StructInfo> &p_info) {
const size_t bytes = data_offset() + p_info->get_data_size();
uint8_t *mem = (uint8_t *)memalloc(bytes);
ERR_FAIL_NULL_V(mem, nullptr);
StructData *d = memnew_placement(mem, StructData);
d->info = p_info;
return d;
}

static void free(StructData *p_data) {
if (!p_data) {
return;
}
const int fc = p_data->field_count();
for (int i = 0; i < fc; i++) {
p_data->destruct(i);
}
p_data->~StructData();
memfree(p_data);
}

static StructData *create(const Ref<StructInfo> &p_info) {
ERR_FAIL_COND_V_MSG(p_info.is_null(), nullptr,
"Cannot construct a Struct without a schema.");
ERR_FAIL_COND_V_MSG(!p_info->is_frozen(), nullptr,
"Cannot construct a Struct from an unfinished (unfrozen) schema.");

StructData *d = memnew(StructData);
d->info = p_info;
d->field_count = p_info->get_field_count();
if (d->field_count > 0) {
d->values = memnew_arr(Variant, d->field_count);
for (int i = 0; i < d->field_count; i++) {
d->values[i] = p_info->instantiate_default(i);
}
StructData *d = alloc(p_info);
ERR_FAIL_NULL_V(d, nullptr);
const int fc = d->field_count();
for (int i = 0; i < fc; i++) {
d->construct_default(i);
}
return d;
}

~StructData() {
if (values) {
memdelete_arr(values);
values = nullptr;
}
}
};

StructData *Struct::_copy_data(const StructData *p_from) {
Expand All @@ -85,23 +215,19 @@ StructData *Struct::_copy_data(const StructData *p_from) {
}
DEV_ASSERT(p_from->info.is_valid());
DEV_ASSERT(p_from->info->is_frozen());
DEV_ASSERT(p_from->field_count >= 0);

StructData *d = memnew(StructData);
d->info = p_from->info;
d->field_count = p_from->field_count;
if (d->field_count > 0) {
d->values = memnew_arr(Variant, d->field_count);
for (int i = 0; i < d->field_count; i++) {
d->values[i] = p_from->values[i];
}

StructData *d = StructData::alloc(p_from->info);
ERR_FAIL_NULL_V(d, nullptr);
const int fc = d->field_count();
for (int i = 0; i < fc; i++) {
d->copy_construct(i, *p_from);
}
return d;
}

void Struct::_free_data() noexcept {
if (_p) {
memdelete(_p);
StructData::free(_p);
_p = nullptr;
}
}
Expand Down Expand Up @@ -156,13 +282,13 @@ uint64_t Struct::get_layout_hash() const noexcept {
}

int Struct::get_field_count() const noexcept {
return _p ? _p->field_count : 0;
return _p ? _p->field_count() : 0;
}

Variant Struct::get_member(int p_index) const {
ERR_FAIL_NULL_V(_p, Variant());
ERR_FAIL_INDEX_V(p_index, _p->field_count, Variant());
return _p->values[p_index];
ERR_FAIL_INDEX_V(p_index, _p->field_count(), Variant());
return _p->get(p_index);
}

Variant Struct::_make_serializable(const Variant &p_value) {
Expand All @@ -178,12 +304,12 @@ Variant Struct::get_member_serializable(int p_index) const {

void Struct::set_member(int p_index, const Variant &p_value) {
ERR_FAIL_NULL(_p);
ERR_FAIL_INDEX(p_index, _p->field_count);
ERR_FAIL_INDEX(p_index, _p->field_count());
Variant normalized;
ERR_FAIL_COND_MSG(!_p->info->normalize_value(p_index, p_value, normalized),
vformat(R"(Value of type "%s" is incompatible with struct field %d.)",
Variant::get_type_name(p_value.get_type()), p_index));
_p->values[p_index] = normalized;
_p->set(p_index, normalized);
}

bool Struct::get_named(const StringName &p_name, Variant &r_value) const {
Expand All @@ -196,8 +322,8 @@ bool Struct::get_named(const StringName &p_name, Variant &r_value) const {
if (idx < 0) {
return false;
}
DEV_ASSERT(idx < _p->field_count);
r_value = _p->values[idx];
DEV_ASSERT(idx < _p->field_count());
r_value = _p->get(idx);
return true;
}

Expand All @@ -209,12 +335,12 @@ bool Struct::set_named(const StringName &p_name, const Variant &p_value) {
if (idx < 0) {
return false;
}
DEV_ASSERT(idx < _p->field_count);
DEV_ASSERT(idx < _p->field_count());
Variant normalized;
ERR_FAIL_COND_V_MSG(!_p->info->normalize_value(idx, p_value, normalized), false,
vformat(R"(Value of type "%s" is incompatible with struct field "%s".)",
Variant::get_type_name(p_value.get_type()), p_name));
_p->values[idx] = normalized;
_p->set(idx, normalized);
return true;
}

Expand All @@ -228,8 +354,8 @@ uint32_t Struct::recursive_hash(int recursion_count) const {
h = hash_murmur3_one_32(_p->info->get_logical_type_id().hash(), h);
h = hash_murmur3_one_64(_p->info->get_layout_hash(), h);
recursion_count++;
for (int i = 0; i < _p->field_count; i++) {
h = hash_murmur3_one_32(_p->values[i].recursive_hash(recursion_count), h);
for (int i = 0; i < _p->field_count(); i++) {
h = hash_murmur3_one_32(_p->get(i).recursive_hash(recursion_count), h);
}
}
return hash_fmix32(h);
Expand All @@ -241,10 +367,10 @@ int Struct::index_of(const StringName &p_name) const {

bool Struct::try_set_member(int p_index, const Variant &p_value) {
Variant normalized;
if (!_p || p_index < 0 || p_index >= _p->field_count || !_p->info->normalize_value(p_index, p_value, normalized)) {
if (!_p || p_index < 0 || p_index >= _p->field_count() || !_p->info->normalize_value(p_index, p_value, normalized)) {
return false;
}
_p->values[p_index] = normalized;
_p->set(p_index, normalized);
return true;
}

Expand All @@ -258,8 +384,8 @@ Struct Struct::recursive_duplicate(bool p_deep, ResourceDeepDuplicateMode p_deep
}
recursion_count++;
Struct dup = *this;
for (int i = 0; i < dup._p->field_count; i++) {
dup._p->values[i] = dup._p->values[i].recursive_duplicate(p_deep, p_deep_subresources_mode, recursion_count);
for (int i = 0; i < dup._p->field_count(); i++) {
dup._p->set(i, dup._p->get(i).recursive_duplicate(p_deep, p_deep_subresources_mode, recursion_count));
}
return dup;
}
Expand All @@ -278,8 +404,8 @@ bool Struct::operator==(const Struct &p_other) const {
if (lhs_info != rhs_info && !lhs_info->is_same_layout_as(*rhs_info)) {
return false;
}
for (int i = 0; i < _p->field_count; i++) {
if (!(_p->values[i] == p_other._p->values[i])) {
for (int i = 0; i < _p->field_count(); i++) {
if (!(_p->get(i) == p_other._p->get(i))) {
return false;
}
}
Expand Down
3 changes: 2 additions & 1 deletion core/variant/struct_info.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,12 +205,13 @@ static String _h128_hex(const Vector<uint8_t> &p_bytes) {

Vector<uint8_t> StructInfo::get_layout_descriptor() const {
Vector<uint8_t> buf;
buf.push_back(1);
buf.push_back(2);
_append_u32_le(buf, (uint32_t)fields.size());
for (const Field &f : fields) {
_append_lp(buf, String(f.name));
_append_lp(buf, String(type_to_token(f.type)));
buf.push_back(f.is_typed ? 1 : 0);
buf.push_back(f.is_nullable ? 1 : 0);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
_append_lp(buf, String(f.class_name));
_append_lp(buf, String(f.struct_type_id));
}
Expand Down
Loading
Loading