diff options
Diffstat (limited to 'ruby/ext/google/protobuf_c/storage.c')
-rw-r--r-- | ruby/ext/google/protobuf_c/storage.c | 281 |
1 files changed, 194 insertions, 87 deletions
diff --git a/ruby/ext/google/protobuf_c/storage.c b/ruby/ext/google/protobuf_c/storage.c index 163b2f81..6cf4158b 100644 --- a/ruby/ext/google/protobuf_c/storage.c +++ b/ruby/ext/google/protobuf_c/storage.c @@ -38,6 +38,8 @@ // Ruby <-> native slot management. // ----------------------------------------------------------------------------- +#define CHARPTR_AT(msg, ofs) ((char*)msg + ofs) +#define DEREF_OFFSET(msg, ofs, type) *(type*)CHARPTR_AT(msg, ofs) #define DEREF(memory, type) *(type*)(memory) size_t native_slot_size(upb_fieldtype_t type) { @@ -57,37 +59,6 @@ size_t native_slot_size(upb_fieldtype_t type) { } } -static VALUE value_from_default(const upb_fielddef *field) { - switch (upb_fielddef_type(field)) { - case UPB_TYPE_FLOAT: return DBL2NUM(upb_fielddef_defaultfloat(field)); - case UPB_TYPE_DOUBLE: return DBL2NUM(upb_fielddef_defaultdouble(field)); - case UPB_TYPE_BOOL: - return upb_fielddef_defaultbool(field) ? Qtrue : Qfalse; - case UPB_TYPE_MESSAGE: return Qnil; - case UPB_TYPE_ENUM: { - const upb_enumdef *enumdef = upb_fielddef_enumsubdef(field); - int32_t num = upb_fielddef_defaultint32(field); - const char *label = upb_enumdef_iton(enumdef, num); - if (label) { - return ID2SYM(rb_intern(label)); - } else { - return INT2NUM(num); - } - } - case UPB_TYPE_INT32: return INT2NUM(upb_fielddef_defaultint32(field)); - case UPB_TYPE_INT64: return LL2NUM(upb_fielddef_defaultint64(field));; - case UPB_TYPE_UINT32: return UINT2NUM(upb_fielddef_defaultuint32(field)); - case UPB_TYPE_UINT64: return ULL2NUM(upb_fielddef_defaultuint64(field)); - case UPB_TYPE_STRING: - case UPB_TYPE_BYTES: { - size_t size; - const char *str = upb_fielddef_defaultstr(field, &size); - return rb_str_new(str, size); - } - default: return Qnil; - } -} - static bool is_ruby_num(VALUE value) { return (TYPE(value) == T_FLOAT || TYPE(value) == T_FIXNUM || @@ -404,7 +375,12 @@ const upb_msgdef *map_entry_msgdef(const upb_fielddef* field) { } bool is_map_field(const upb_fielddef *field) { - return tryget_map_entry_msgdef(field) != NULL; + const upb_msgdef* subdef = tryget_map_entry_msgdef(field); + if (subdef == NULL) return false; + + // Map fields are a proto3 feature. + // If we're using proto2 syntax we need to fallback to the repeated field. + return upb_msgdef_syntax(subdef) == UPB_SYNTAX_PROTO3; } const upb_fielddef* map_field_key(const upb_fielddef* field) { @@ -433,6 +409,12 @@ const upb_fielddef* map_entry_value(const upb_msgdef* msgdef) { // Memory layout management. // ----------------------------------------------------------------------------- +bool field_contains_hasbit(MessageLayout* layout, + const upb_fielddef* field) { + return layout->fields[upb_fielddef_index(field)].hasbit != + MESSAGE_FIELD_NO_HASBIT; +} + static size_t align_up_to(size_t offset, size_t granularity) { // Granularity must be a power of two. return (offset + granularity - 1) & ~(granularity - 1); @@ -447,6 +429,23 @@ MessageLayout* create_layout(const upb_msgdef* msgdef) { layout->fields = ALLOC_N(MessageField, nfields); + size_t hasbit = 0; + for (upb_msg_field_begin(&it, msgdef); + !upb_msg_field_done(&it); + upb_msg_field_next(&it)) { + const upb_fielddef* field = upb_msg_iter_field(&it); + if (upb_fielddef_haspresence(field)) { + layout->fields[upb_fielddef_index(field)].hasbit = hasbit++; + } else { + layout->fields[upb_fielddef_index(field)].hasbit = + MESSAGE_FIELD_NO_HASBIT; + } + } + + if (hasbit != 0) { + off += (hasbit + 8 - 1) / 8; + } + for (upb_msg_field_begin(&it, msgdef); !upb_msg_field_done(&it); upb_msg_field_next(&it)) { @@ -569,6 +568,136 @@ static uint32_t* slot_oneof_case(MessageLayout* layout, layout->fields[upb_fielddef_index(field)].case_offset); } +static void slot_set_hasbit(MessageLayout* layout, + const void* storage, + const upb_fielddef* field) { + size_t hasbit = layout->fields[upb_fielddef_index(field)].hasbit; + assert(hasbit != MESSAGE_FIELD_NO_HASBIT); + + ((uint8_t*)storage)[hasbit / 8] |= 1 << (hasbit % 8); +} + +static void slot_clear_hasbit(MessageLayout* layout, + const void* storage, + const upb_fielddef* field) { + size_t hasbit = layout->fields[upb_fielddef_index(field)].hasbit; + assert(hasbit != MESSAGE_FIELD_NO_HASBIT); + ((uint8_t*)storage)[hasbit / 8] &= ~(1 << (hasbit % 8)); +} + +static bool slot_is_hasbit_set(MessageLayout* layout, + const void* storage, + const upb_fielddef* field) { + size_t hasbit = layout->fields[upb_fielddef_index(field)].hasbit; + if (hasbit == MESSAGE_FIELD_NO_HASBIT) { + return false; + } + + return DEREF_OFFSET( + (uint8_t*)storage, hasbit / 8, char) & (1 << (hasbit % 8)); +} + +VALUE layout_has(MessageLayout* layout, + const void* storage, + const upb_fielddef* field) { + assert(field_contains_hasbit(layout, field)); + return slot_is_hasbit_set(layout, storage, field) ? Qtrue : Qfalse; +} + +void layout_clear(MessageLayout* layout, + const void* storage, + const upb_fielddef* field) { + void* memory = slot_memory(layout, storage, field); + uint32_t* oneof_case = slot_oneof_case(layout, storage, field); + + if (field_contains_hasbit(layout, field)) { + slot_clear_hasbit(layout, storage, field); + } + + if (upb_fielddef_containingoneof(field)) { + memset(memory, 0, NATIVE_SLOT_MAX_SIZE); + *oneof_case = ONEOF_CASE_NONE; + } else if (is_map_field(field)) { + VALUE map = Qnil; + + const upb_fielddef* key_field = map_field_key(field); + const upb_fielddef* value_field = map_field_value(field); + VALUE type_class = field_type_class(value_field); + + if (type_class != Qnil) { + VALUE args[3] = { + fieldtype_to_ruby(upb_fielddef_type(key_field)), + fieldtype_to_ruby(upb_fielddef_type(value_field)), + type_class, + }; + map = rb_class_new_instance(3, args, cMap); + } else { + VALUE args[2] = { + fieldtype_to_ruby(upb_fielddef_type(key_field)), + fieldtype_to_ruby(upb_fielddef_type(value_field)), + }; + map = rb_class_new_instance(2, args, cMap); + } + + DEREF(memory, VALUE) = map; + } else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { + VALUE ary = Qnil; + + VALUE type_class = field_type_class(field); + + if (type_class != Qnil) { + VALUE args[2] = { + fieldtype_to_ruby(upb_fielddef_type(field)), + type_class, + }; + ary = rb_class_new_instance(2, args, cRepeatedField); + } else { + VALUE args[1] = { fieldtype_to_ruby(upb_fielddef_type(field)) }; + ary = rb_class_new_instance(1, args, cRepeatedField); + } + + DEREF(memory, VALUE) = ary; + } else { + native_slot_set(upb_fielddef_type(field), field_type_class(field), + memory, layout_get_default(field)); + } +} + +VALUE layout_get_default(const upb_fielddef *field) { + switch (upb_fielddef_type(field)) { + case UPB_TYPE_FLOAT: return DBL2NUM(upb_fielddef_defaultfloat(field)); + case UPB_TYPE_DOUBLE: return DBL2NUM(upb_fielddef_defaultdouble(field)); + case UPB_TYPE_BOOL: + return upb_fielddef_defaultbool(field) ? Qtrue : Qfalse; + case UPB_TYPE_MESSAGE: return Qnil; + case UPB_TYPE_ENUM: { + const upb_enumdef *enumdef = upb_fielddef_enumsubdef(field); + int32_t num = upb_fielddef_defaultint32(field); + const char *label = upb_enumdef_iton(enumdef, num); + if (label) { + return ID2SYM(rb_intern(label)); + } else { + return INT2NUM(num); + } + } + case UPB_TYPE_INT32: return INT2NUM(upb_fielddef_defaultint32(field)); + case UPB_TYPE_INT64: return LL2NUM(upb_fielddef_defaultint64(field));; + case UPB_TYPE_UINT32: return UINT2NUM(upb_fielddef_defaultuint32(field)); + case UPB_TYPE_UINT64: return ULL2NUM(upb_fielddef_defaultuint64(field)); + case UPB_TYPE_STRING: + case UPB_TYPE_BYTES: { + size_t size; + const char *str = upb_fielddef_defaultstr(field, &size); + VALUE str_rb = rb_str_new(str, size); + + rb_enc_associate(str_rb, (upb_fielddef_type(field) == UPB_TYPE_BYTES) ? + kRubyString8bitEncoding : kRubyStringUtf8Encoding); + rb_obj_freeze(str_rb); + return str_rb; + } + default: return Qnil; + } +} VALUE layout_get(MessageLayout* layout, const void* storage, @@ -576,15 +705,24 @@ VALUE layout_get(MessageLayout* layout, void* memory = slot_memory(layout, storage, field); uint32_t* oneof_case = slot_oneof_case(layout, storage, field); + bool field_set; + if (field_contains_hasbit(layout, field)) { + field_set = slot_is_hasbit_set(layout, storage, field); + } else { + field_set = true; + } + if (upb_fielddef_containingoneof(field)) { if (*oneof_case != upb_fielddef_number(field)) { - return value_from_default(field); + return layout_get_default(field); } return native_slot_get(upb_fielddef_type(field), field_type_class(field), memory); } else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { return *((VALUE *)memory); + } else if (!field_set) { + return layout_get_default(field); } else { return native_slot_get(upb_fielddef_type(field), field_type_class(field), @@ -689,67 +827,24 @@ void layout_set(MessageLayout* layout, check_repeated_field_type(val, field); DEREF(memory, VALUE) = val; } else { - native_slot_set(upb_fielddef_type(field), field_type_class(field), - memory, val); + native_slot_set(upb_fielddef_type(field), field_type_class(field), memory, + val); + } + + if (layout->fields[upb_fielddef_index(field)].hasbit != + MESSAGE_FIELD_NO_HASBIT) { + slot_set_hasbit(layout, storage, field); } } void layout_init(MessageLayout* layout, void* storage) { + upb_msg_field_iter it; for (upb_msg_field_begin(&it, layout->msgdef); !upb_msg_field_done(&it); upb_msg_field_next(&it)) { - const upb_fielddef* field = upb_msg_iter_field(&it); - void* memory = slot_memory(layout, storage, field); - uint32_t* oneof_case = slot_oneof_case(layout, storage, field); - - if (upb_fielddef_containingoneof(field)) { - memset(memory, 0, NATIVE_SLOT_MAX_SIZE); - *oneof_case = ONEOF_CASE_NONE; - } else if (is_map_field(field)) { - VALUE map = Qnil; - - const upb_fielddef* key_field = map_field_key(field); - const upb_fielddef* value_field = map_field_value(field); - VALUE type_class = field_type_class(value_field); - - if (type_class != Qnil) { - VALUE args[3] = { - fieldtype_to_ruby(upb_fielddef_type(key_field)), - fieldtype_to_ruby(upb_fielddef_type(value_field)), - type_class, - }; - map = rb_class_new_instance(3, args, cMap); - } else { - VALUE args[2] = { - fieldtype_to_ruby(upb_fielddef_type(key_field)), - fieldtype_to_ruby(upb_fielddef_type(value_field)), - }; - map = rb_class_new_instance(2, args, cMap); - } - - DEREF(memory, VALUE) = map; - } else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { - VALUE ary = Qnil; - - VALUE type_class = field_type_class(field); - - if (type_class != Qnil) { - VALUE args[2] = { - fieldtype_to_ruby(upb_fielddef_type(field)), - type_class, - }; - ary = rb_class_new_instance(2, args, cRepeatedField); - } else { - VALUE args[1] = { fieldtype_to_ruby(upb_fielddef_type(field)) }; - ary = rb_class_new_instance(1, args, cRepeatedField); - } - - DEREF(memory, VALUE) = ary; - } else { - native_slot_init(upb_fielddef_type(field), memory); - } + layout_clear(layout, storage, upb_msg_iter_field(&it)); } } @@ -796,6 +891,11 @@ void layout_dup(MessageLayout* layout, void* to, void* from) { } else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { DEREF(to_memory, VALUE) = RepeatedField_dup(DEREF(from_memory, VALUE)); } else { + if (field_contains_hasbit(layout, field)) { + if (!slot_is_hasbit_set(layout, from, field)) continue; + slot_set_hasbit(layout, to, field); + } + native_slot_dup(upb_fielddef_type(field), to_memory, from_memory); } } @@ -825,6 +925,11 @@ void layout_deep_copy(MessageLayout* layout, void* to, void* from) { DEREF(to_memory, VALUE) = RepeatedField_deep_copy(DEREF(from_memory, VALUE)); } else { + if (field_contains_hasbit(layout, field)) { + if (!slot_is_hasbit_set(layout, from, field)) continue; + slot_set_hasbit(layout, to, field); + } + native_slot_deep_copy(upb_fielddef_type(field), to_memory, from_memory); } } @@ -861,8 +966,10 @@ VALUE layout_eq(MessageLayout* layout, void* msg1, void* msg2) { return Qfalse; } } else { - if (!native_slot_eq(upb_fielddef_type(field), - msg1_memory, msg2_memory)) { + if (slot_is_hasbit_set(layout, msg1, field) != + slot_is_hasbit_set(layout, msg2, field) || + !native_slot_eq(upb_fielddef_type(field), + msg1_memory, msg2_memory)) { return Qfalse; } } |