aboutsummaryrefslogtreecommitdiff
path: root/ruby/ext/google/protobuf_c/message.c
diff options
context:
space:
mode:
Diffstat (limited to 'ruby/ext/google/protobuf_c/message.c')
-rw-r--r--ruby/ext/google/protobuf_c/message.c67
1 files changed, 56 insertions, 11 deletions
diff --git a/ruby/ext/google/protobuf_c/message.c b/ruby/ext/google/protobuf_c/message.c
index 837a974b..63a61baf 100644
--- a/ruby/ext/google/protobuf_c/message.c
+++ b/ruby/ext/google/protobuf_c/message.c
@@ -44,6 +44,11 @@ void Message_mark(void* _self) {
}
void Message_free(void* self) {
+ stringsink* unknown = ((MessageHeader *)self)->unknown_fields;
+ if (unknown != NULL) {
+ stringsink_uninit(unknown);
+ free(unknown);
+ }
xfree(self);
}
@@ -67,6 +72,8 @@ VALUE Message_alloc(VALUE klass) {
msg->descriptor = desc;
rb_ivar_set(ret, descriptor_instancevar_interned, descriptor);
+ msg->unknown_fields = NULL;
+
layout_init(desc->layout, Message_data(msg));
return ret;
@@ -217,20 +224,32 @@ VALUE Message_respond_to_missing(int argc, VALUE* argv, VALUE _self) {
return Qtrue;
}
+VALUE create_submsg_from_hash(const upb_fielddef *f, VALUE hash) {
+ const upb_def *d = upb_fielddef_subdef(f);
+ assert(d != NULL);
+
+ VALUE descriptor = get_def_obj(d);
+ VALUE msgclass = rb_funcall(descriptor, rb_intern("msgclass"), 0, NULL);
+
+ VALUE args[1] = { hash };
+ return rb_class_new_instance(1, args, msgclass);
+}
+
int Message_initialize_kwarg(VALUE key, VALUE val, VALUE _self) {
MessageHeader* self;
- VALUE method_str;
- char* name;
+ char *name;
const upb_fielddef* f;
TypedData_Get_Struct(_self, MessageHeader, &Message_type, self);
- if (!SYMBOL_P(key)) {
+ 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 symbols as hash keys in initialization map.");
+ "Expected string or symbols as hash keys when initializing proto from hash.");
}
- 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,
@@ -255,9 +274,18 @@ int Message_initialize_kwarg(VALUE key, VALUE val, VALUE _self) {
}
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));
+ 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;
@@ -394,7 +422,12 @@ VALUE Message_inspect(VALUE _self) {
return str;
}
-
+/*
+ * 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;
@@ -410,8 +443,20 @@ VALUE Message_to_h(VALUE _self) {
VALUE msg_value = layout_get(self->descriptor->layout, Message_data(self),
field);
VALUE msg_key = ID2SYM(rb_intern(upb_fielddef_name(field)));
- if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) {
+ if (upb_fielddef_ismap(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_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);
}
@@ -495,9 +540,9 @@ VALUE build_class_from_descriptor(Descriptor* desc) {
get_def_obj(desc->msgdef));
rb_define_alloc_func(klass, Message_alloc);
rb_require("google/protobuf/message_exts");
- rb_include_module(klass, rb_eval_string("Google::Protobuf::MessageExts"));
+ rb_include_module(klass, rb_eval_string("::Google::Protobuf::MessageExts"));
rb_extend_object(
- klass, rb_eval_string("Google::Protobuf::MessageExts::ClassMethods"));
+ klass, rb_eval_string("::Google::Protobuf::MessageExts::ClassMethods"));
rb_define_method(klass, "method_missing",
Message_method_missing, -1);