static void check_map_field_type(VALUE val, const upb_fielddef* field) { assert(is_map_field(field)); const upb_fielddef* key_field = map_field_key(field); const upb_fielddef* value_field = map_field_value(field); if (!RB_TYPE_P(val, T_DATA) || !RTYPEDDATA_P(val) || RTYPEDDATA_TYPE(val) != &Map_type) { rb_raise(rb_eTypeError, "Expected Map instance"); } Map* self = ruby_to_Map(val); if (self->key_type != upb_fielddef_type(key_field)) { rb_raise(rb_eTypeError, "Map key type does not match field's key type"); } if (self->value_type != upb_fielddef_type(value_field)) { rb_raise(rb_eTypeError, "Map value type does not match field's value type"); } if (upb_fielddef_type(value_field) == UPB_TYPE_MESSAGE || upb_fielddef_type(value_field) == UPB_TYPE_ENUM) { if (self->value_type_class != get_def_obj(upb_fielddef_subdef(value_field))) { rb_raise(rb_eTypeError, "Map value type has wrong message/enum class"); } } }
void layout_deep_copy(MessageLayout* layout, void* to, void* from) { 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* to_memory = slot_memory(layout, to, field); uint32_t* to_oneof_case = slot_oneof_case(layout, to, field); void* from_memory = slot_memory(layout, from, field); uint32_t* from_oneof_case = slot_oneof_case(layout, from, field); if (upb_fielddef_containingoneof(field)) { if (*from_oneof_case == upb_fielddef_number(field)) { *to_oneof_case = *from_oneof_case; native_slot_deep_copy(upb_fielddef_type(field), to_memory, from_memory); } } else if (is_map_field(field)) { DEREF(to_memory, VALUE) = Map_deep_copy(DEREF(from_memory, VALUE)); } else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { 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); } } }
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_name(field), upb_fielddef_type(field), field_type_class(field), memory, layout_get_default(field)); } }
int Message_initialize_kwarg(VALUE key, VALUE val, VALUE _self) { MessageHeader* self; char *name; const upb_fielddef* f; TypedData_Get_Struct(_self, MessageHeader, &Message_type, self); if (TYPE(key) == T_STRING) { name = RSTRING_PTR(key); } else if (TYPE(key) == T_SYMBOL) { name = RSTRING_PTR(rb_id2str(SYM2ID(key))); } else { rb_raise(rb_eArgError, "Expected string or symbols as hash keys when initializing proto from hash."); } f = upb_msgdef_ntofz(self->descriptor->msgdef, name); if (f == NULL) { rb_raise(rb_eArgError, "Unknown field name '%s' in initialization map entry.", name); } if (TYPE(val) == T_NIL) { return 0; } if (is_map_field(f)) { VALUE map; if (TYPE(val) != T_HASH) { rb_raise(rb_eArgError, "Expected Hash object as initializer value for map field '%s'.", name); } map = layout_get(self->descriptor->layout, Message_data(self), f); Map_merge_into_self(map, val); } else if (upb_fielddef_label(f) == UPB_LABEL_REPEATED) { VALUE ary; if (TYPE(val) != T_ARRAY) { rb_raise(rb_eArgError, "Expected array as initializer value for repeated field '%s'.", name); } ary = layout_get(self->descriptor->layout, Message_data(self), f); for (int i = 0; i < RARRAY_LEN(val); i++) { VALUE entry = rb_ary_entry(val, i); if (TYPE(entry) == T_HASH && upb_fielddef_issubmsg(f)) { entry = create_submsg_from_hash(f, entry); } RepeatedField_push(ary, entry); } } else { if (TYPE(val) == T_HASH && upb_fielddef_issubmsg(f)) { val = create_submsg_from_hash(f, val); } layout_set(self->descriptor->layout, Message_data(self), f, val); } return 0; }
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); } } }
void layout_set(MessageLayout* layout, void* storage, const upb_fielddef* field, VALUE val) { void* memory = slot_memory(layout, storage, field); uint32_t* oneof_case = slot_oneof_case(layout, storage, field); if (upb_fielddef_containingoneof(field)) { if (val == Qnil) { // Assigning nil to a oneof field clears the oneof completely. *oneof_case = ONEOF_CASE_NONE; memset(memory, 0, NATIVE_SLOT_MAX_SIZE); } else { // The transition between field types for a single oneof (union) slot is // somewhat complex because we need to ensure that a GC triggered at any // point by a call into the Ruby VM sees a valid state for this field and // does not either go off into the weeds (following what it thinks is a // VALUE but is actually a different field type) or miss an object (seeing // what it thinks is a primitive field but is actually a VALUE for the new // field type). // // In order for the transition to be safe, the oneof case slot must be in // sync with the value slot whenever the Ruby VM has been called. Thus, we // use native_slot_set_value_and_case(), which ensures that both the value // and case number are altered atomically (w.r.t. the Ruby VM). native_slot_set_value_and_case( upb_fielddef_name(field), upb_fielddef_type(field), field_type_class(field), memory, val, oneof_case, upb_fielddef_number(field)); } } else if (is_map_field(field)) { check_map_field_type(val, field); DEREF(memory, VALUE) = val; } else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { check_repeated_field_type(val, field); DEREF(memory, VALUE) = val; } else { native_slot_set(upb_fielddef_name(field), 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); } }
/* * call-seq: * Message.to_h => {} * * Returns the message as a Ruby Hash object, with keys as symbols. */ VALUE Message_to_h(VALUE _self) { MessageHeader* self; VALUE hash; upb_msg_field_iter it; TypedData_Get_Struct(_self, MessageHeader, &Message_type, self); hash = rb_hash_new(); for (upb_msg_field_begin(&it, self->descriptor->msgdef); !upb_msg_field_done(&it); upb_msg_field_next(&it)) { const upb_fielddef* field = upb_msg_iter_field(&it); // For proto2, do not include fields which are not set. if (upb_msgdef_syntax(self->descriptor->msgdef) == UPB_SYNTAX_PROTO2 && field_contains_hasbit(self->descriptor->layout, field) && !layout_has(self->descriptor->layout, Message_data(self), field)) { continue; } VALUE msg_value = layout_get(self->descriptor->layout, Message_data(self), field); VALUE msg_key = ID2SYM(rb_intern(upb_fielddef_name(field))); if (is_map_field(field)) { msg_value = Map_to_h(msg_value); } else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { msg_value = RepeatedField_to_ary(msg_value); if (upb_msgdef_syntax(self->descriptor->msgdef) == UPB_SYNTAX_PROTO2 && RARRAY_LEN(msg_value) == 0) { continue; } if (upb_fielddef_type(field) == UPB_TYPE_MESSAGE) { for (int i = 0; i < RARRAY_LEN(msg_value); i++) { VALUE elem = rb_ary_entry(msg_value, i); rb_ary_store(msg_value, i, Message_to_h(elem)); } } } else if (msg_value != Qnil && upb_fielddef_type(field) == UPB_TYPE_MESSAGE) { msg_value = Message_to_h(msg_value); } rb_hash_aset(hash, msg_key, msg_value); } return hash; }
int Message_initialize_kwarg(VALUE key, VALUE val, VALUE _self) { MessageHeader* self; VALUE method_str; char* name; const upb_fielddef* f; TypedData_Get_Struct(_self, MessageHeader, &Message_type, self); if (!SYMBOL_P(key)) { rb_raise(rb_eArgError, "Expected symbols as hash keys in initialization map."); } method_str = rb_id2str(SYM2ID(key)); name = RSTRING_PTR(method_str); f = upb_msgdef_ntofz(self->descriptor->msgdef, name); if (f == NULL) { rb_raise(rb_eArgError, "Unknown field name '%s' in initialization map entry.", name); } if (is_map_field(f)) { VALUE map; if (TYPE(val) != T_HASH) { rb_raise(rb_eArgError, "Expected Hash object as initializer value for map field '%s'.", name); } map = layout_get(self->descriptor->layout, Message_data(self), f); Map_merge_into_self(map, val); } else if (upb_fielddef_label(f) == UPB_LABEL_REPEATED) { VALUE ary; if (TYPE(val) != T_ARRAY) { rb_raise(rb_eArgError, "Expected array as initializer value for repeated field '%s'.", name); } ary = layout_get(self->descriptor->layout, Message_data(self), f); for (int i = 0; i < RARRAY_LEN(val); i++) { RepeatedField_push(ary, rb_ary_entry(val, i)); } } else { layout_set(self->descriptor->layout, Message_data(self), f, val); } return 0; }
VALUE layout_eq(MessageLayout* layout, void* msg1, void* msg2) { 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* msg1_memory = slot_memory(layout, msg1, field); uint32_t* msg1_oneof_case = slot_oneof_case(layout, msg1, field); void* msg2_memory = slot_memory(layout, msg2, field); uint32_t* msg2_oneof_case = slot_oneof_case(layout, msg2, field); if (upb_fielddef_containingoneof(field)) { if (*msg1_oneof_case != *msg2_oneof_case || (*msg1_oneof_case == upb_fielddef_number(field) && !native_slot_eq(upb_fielddef_type(field), msg1_memory, msg2_memory))) { return Qfalse; } } else if (is_map_field(field)) { if (!Map_eq(DEREF(msg1_memory, VALUE), DEREF(msg2_memory, VALUE))) { return Qfalse; } } else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { if (!RepeatedField_eq(DEREF(msg1_memory, VALUE), DEREF(msg2_memory, VALUE))) { return Qfalse; } } else { 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; } } } return Qtrue; }
static void add_handlers_for_message(const void *closure, upb_handlers *h) { const upb_msgdef* msgdef = upb_handlers_msgdef(h); Descriptor* desc = ruby_to_Descriptor(get_def_obj((void*)msgdef)); upb_msg_field_iter i; // If this is a mapentry message type, set up a special set of handlers and // bail out of the normal (user-defined) message type handling. if (upb_msgdef_mapentry(msgdef)) { add_handlers_for_mapentry(msgdef, h, desc); return; } // Ensure layout exists. We may be invoked to create handlers for a given // message if we are included as a submsg of another message type before our // class is actually built, so to work around this, we just create the layout // (and handlers, in the class-building function) on-demand. if (desc->layout == NULL) { desc->layout = create_layout(desc->msgdef); } for (upb_msg_field_begin(&i, desc->msgdef); !upb_msg_field_done(&i); upb_msg_field_next(&i)) { const upb_fielddef *f = upb_msg_iter_field(&i); size_t offset = desc->layout->fields[upb_fielddef_index(f)].offset + sizeof(MessageHeader); if (upb_fielddef_containingoneof(f)) { size_t oneof_case_offset = desc->layout->fields[upb_fielddef_index(f)].case_offset + sizeof(MessageHeader); add_handlers_for_oneof_field(h, f, offset, oneof_case_offset); } else if (is_map_field(f)) { add_handlers_for_mapfield(h, f, offset, desc); } else if (upb_fielddef_isseq(f)) { add_handlers_for_repeated_field(h, f, offset); } else { add_handlers_for_singular_field(h, f, offset); } } }
static void putmsg(VALUE msg_rb, const Descriptor* desc, upb_sink *sink, int depth) { MessageHeader* msg; upb_msg_field_iter i; upb_status status; upb_sink_startmsg(sink); // Protect against cycles (possible because users may freely reassign message // and repeated fields) by imposing a maximum recursion depth. if (depth > ENCODE_MAX_NESTING) { rb_raise(rb_eRuntimeError, "Maximum recursion depth exceeded during encoding."); } TypedData_Get_Struct(msg_rb, MessageHeader, &Message_type, msg); for (upb_msg_field_begin(&i, desc->msgdef); !upb_msg_field_done(&i); upb_msg_field_next(&i)) { upb_fielddef *f = upb_msg_iter_field(&i); uint32_t offset = desc->layout->fields[upb_fielddef_index(f)].offset + sizeof(MessageHeader); if (upb_fielddef_containingoneof(f)) { uint32_t oneof_case_offset = desc->layout->fields[upb_fielddef_index(f)].case_offset + sizeof(MessageHeader); // For a oneof, check that this field is actually present -- skip all the // below if not. if (DEREF(msg, oneof_case_offset, uint32_t) != upb_fielddef_number(f)) { continue; } // Otherwise, fall through to the appropriate singular-field handler // below. } if (is_map_field(f)) { VALUE map = DEREF(msg, offset, VALUE); if (map != Qnil) { putmap(map, f, sink, depth); } } else if (upb_fielddef_isseq(f)) { VALUE ary = DEREF(msg, offset, VALUE); if (ary != Qnil) { putary(ary, f, sink, depth); } } else if (upb_fielddef_isstring(f)) { VALUE str = DEREF(msg, offset, VALUE); if (RSTRING_LEN(str) > 0) { putstr(str, f, sink); } } else if (upb_fielddef_issubmsg(f)) { putsubmsg(DEREF(msg, offset, VALUE), f, sink, depth); } else { upb_selector_t sel = getsel(f, upb_handlers_getprimitivehandlertype(f)); #define T(upbtypeconst, upbtype, ctype, default_value) \ case upbtypeconst: { \ ctype value = DEREF(msg, offset, ctype); \ if (value != default_value) { \ upb_sink_put##upbtype(sink, sel, value); \ } \ } \ break; switch (upb_fielddef_type(f)) { T(UPB_TYPE_FLOAT, float, float, 0.0) T(UPB_TYPE_DOUBLE, double, double, 0.0) T(UPB_TYPE_BOOL, bool, uint8_t, 0) case UPB_TYPE_ENUM: T(UPB_TYPE_INT32, int32, int32_t, 0) T(UPB_TYPE_UINT32, uint32, uint32_t, 0) T(UPB_TYPE_INT64, int64, int64_t, 0) T(UPB_TYPE_UINT64, uint64, uint64_t, 0) case UPB_TYPE_STRING: case UPB_TYPE_BYTES: case UPB_TYPE_MESSAGE: rb_raise(rb_eRuntimeError, "Internal error."); } #undef T } } upb_sink_endmsg(sink, &status); }
const upb_fielddef* map_field_value(const upb_fielddef* field) { assert(is_map_field(field)); const upb_msgdef* subdef = upb_fielddef_msgsubdef(field); return map_entry_value(subdef); }