/* * call-seq: * MessageClass.decode(data) => message * * Decodes the given data (as a string containing bytes in protocol buffers wire * format) under the interpretration given by this message class's definition * and returns a message object with the corresponding field values. */ VALUE Message_decode(VALUE klass, VALUE data) { VALUE descriptor = rb_ivar_get(klass, descriptor_instancevar_interned); Descriptor* desc = ruby_to_Descriptor(descriptor); VALUE msgklass = Descriptor_msgclass(descriptor); VALUE msg_rb; MessageHeader* msg; if (TYPE(data) != T_STRING) { rb_raise(rb_eArgError, "Expected string for binary protobuf data."); } msg_rb = rb_class_new_instance(0, NULL, msgklass); TypedData_Get_Struct(msg_rb, MessageHeader, &Message_type, msg); { const upb_pbdecodermethod* method = msgdef_decodermethod(desc); const upb_handlers* h = upb_pbdecodermethod_desthandlers(method); stackenv se; upb_sink sink; upb_pbdecoder* decoder; stackenv_init(&se, "Error occurred during parsing: %s"); upb_sink_reset(&sink, h, msg); decoder = upb_pbdecoder_create(&se.env, method, &sink); upb_bufsrc_putbuf(RSTRING_PTR(data), RSTRING_LEN(data), upb_pbdecoder_input(decoder)); stackenv_uninit(&se); } return msg_rb; }
/* * call-seq: * MessageClass.decode_json(data) => message * * Decodes the given data (as a string containing bytes in protocol buffers wire * format) under the interpretration given by this message class's definition * and returns a message object with the corresponding field values. */ VALUE Message_decode_json(VALUE klass, VALUE data) { VALUE descriptor = rb_ivar_get(klass, descriptor_instancevar_interned); Descriptor* desc = ruby_to_Descriptor(descriptor); VALUE msgklass = Descriptor_msgclass(descriptor); VALUE msg_rb; MessageHeader* msg; if (TYPE(data) != T_STRING) { rb_raise(rb_eArgError, "Expected string for JSON data."); } // TODO(cfallin): Check and respect string encoding. If not UTF-8, we need to // convert, because string handlers pass data directly to message string // fields. msg_rb = rb_class_new_instance(0, NULL, msgklass); TypedData_Get_Struct(msg_rb, MessageHeader, &Message_type, msg); { stackenv se; upb_sink sink; upb_json_parser* parser; stackenv_init(&se, "Error occurred during parsing: %s"); upb_sink_reset(&sink, get_fill_handlers(desc), msg); parser = upb_json_parser_create(&se.env, &sink); upb_bufsrc_putbuf(RSTRING_PTR(data), RSTRING_LEN(data), upb_json_parser_input(parser)); stackenv_uninit(&se); } return msg_rb; }
/* * call-seq: * MessageClass.encode_json(msg) => json_string * * Encodes the given message object into its serialized JSON representation. */ VALUE Message_encode_json(VALUE klass, VALUE msg_rb) { VALUE descriptor = rb_ivar_get(klass, descriptor_instancevar_interned); Descriptor* desc = ruby_to_Descriptor(descriptor); stringsink sink; stringsink_init(&sink); { const upb_handlers* serialize_handlers = msgdef_json_serialize_handlers(desc); upb_json_printer* printer; stackenv se; VALUE ret; stackenv_init(&se, "Error occurred during encoding: %s"); printer = upb_json_printer_create(&se.env, serialize_handlers, &sink.sink); putmsg(msg_rb, desc, upb_json_printer_input(printer), 0); ret = rb_str_new(sink.ptr, sink.len); stackenv_uninit(&se); stringsink_uninit(&sink); return ret; } }
/* * call-seq: * Builder.finalize_to_pool(pool) * * Adds all accumulated message and enum descriptors created in this builder * context to the given pool. The operation occurs atomically, and all * descriptors can refer to each other (including in cycles). This is the only * way to build (co)recursive message definitions. * * This method is usually called automatically by DescriptorPool#build after it * invokes the given user block in the context of the builder. The user should * not normally need to call this manually because a Builder is not normally * created manually. */ VALUE Builder_finalize_to_pool(VALUE _self, VALUE pool_rb) { DEFINE_SELF(Builder, self, _self); DescriptorPool* pool = ruby_to_DescriptorPool(pool_rb); REALLOC_N(self->defs, upb_def*, RARRAY_LEN(self->pending_list)); for (int i = 0; i < RARRAY_LEN(self->pending_list); i++) { VALUE def_rb = rb_ary_entry(self->pending_list, i); if (CLASS_OF(def_rb) == cDescriptor) { self->defs[i] = (upb_def*)ruby_to_Descriptor(def_rb)->msgdef; validate_msgdef((const upb_msgdef*)self->defs[i]); } else if (CLASS_OF(def_rb) == cEnumDescriptor) { self->defs[i] = (upb_def*)ruby_to_EnumDescriptor(def_rb)->enumdef; validate_enumdef((const upb_enumdef*)self->defs[i]); } } CHECK_UPB(upb_symtab_add(pool->symtab, (upb_def**)self->defs, RARRAY_LEN(self->pending_list), NULL, &status), "Unable to add defs to DescriptorPool"); for (int i = 0; i < RARRAY_LEN(self->pending_list); i++) { VALUE def_rb = rb_ary_entry(self->pending_list, i); add_def_obj(self->defs[i], def_rb); } self->pending_list = rb_ary_new(); return Qnil; }
static void putsubmsg(VALUE submsg, const upb_fielddef *f, upb_sink *sink, int depth) { if (submsg == Qnil) return; upb_sink subsink; VALUE descriptor = rb_ivar_get(submsg, descriptor_instancevar_interned); Descriptor* subdesc = ruby_to_Descriptor(descriptor); upb_sink_startsubmsg(sink, getsel(f, UPB_HANDLER_STARTSUBMSG), &subsink); putmsg(submsg, subdesc, &subsink, depth + 1); upb_sink_endsubmsg(sink, getsel(f, UPB_HANDLER_ENDSUBMSG)); }
/* * call-seq: * DescriptorPool.add(descriptor) * * Adds the given Descriptor or EnumDescriptor to this pool. All references to * other types in a Descriptor's fields must be resolvable within this pool or * an exception will be raised. */ VALUE DescriptorPool_add(VALUE _self, VALUE def) { DEFINE_SELF(DescriptorPool, self, _self); VALUE def_klass = rb_obj_class(def); if (def_klass == cDescriptor) { add_descriptor_to_pool(self, ruby_to_Descriptor(def)); } else if (def_klass == cEnumDescriptor) { add_enumdesc_to_pool(self, ruby_to_EnumDescriptor(def)); } else { rb_raise(rb_eArgError, "Second argument must be a Descriptor or EnumDescriptor."); } return Qnil; }
VALUE Message_alloc(VALUE klass) { VALUE descriptor = rb_iv_get(klass, kDescriptorInstanceVar); Descriptor* desc = ruby_to_Descriptor(descriptor); MessageHeader* msg = (MessageHeader*)ALLOC_N( uint8_t, sizeof(MessageHeader) + desc->layout->size); memset(Message_data(msg), 0, desc->layout->size); // We wrap first so that everything in the message object is GC-rooted in case // a collection happens during object creation in layout_init(). VALUE ret = TypedData_Wrap_Struct(klass, &Message_type, msg); msg->descriptor = desc; rb_iv_set(ret, kDescriptorInstanceVar, descriptor); layout_init(desc->layout, Message_data(msg)); return ret; }
/* * call-seq: * MessageClass.encode_json(msg) => json_string * * Encodes the given message object into its serialized JSON representation. */ VALUE Message_encode_json(int argc, VALUE* argv, VALUE klass) { VALUE descriptor = rb_ivar_get(klass, descriptor_instancevar_interned); Descriptor* desc = ruby_to_Descriptor(descriptor); VALUE msg_rb; VALUE preserve_proto_fieldnames = Qfalse; stringsink sink; if (argc < 1 || argc > 2) { rb_raise(rb_eArgError, "Expected 1 or 2 arguments."); } msg_rb = argv[0]; if (argc == 2) { VALUE hash_args = argv[1]; if (TYPE(hash_args) != T_HASH) { rb_raise(rb_eArgError, "Expected hash arguments."); } preserve_proto_fieldnames = rb_hash_lookup2( hash_args, ID2SYM(rb_intern("preserve_proto_fieldnames")), Qfalse); } stringsink_init(&sink); { const upb_handlers* serialize_handlers = msgdef_json_serialize_handlers(desc, RTEST(preserve_proto_fieldnames)); upb_json_printer* printer; stackenv se; VALUE ret; stackenv_init(&se, "Error occurred during encoding: %s"); printer = upb_json_printer_create(&se.env, serialize_handlers, &sink.sink); putmsg(msg_rb, desc, upb_json_printer_input(printer), 0); ret = rb_enc_str_new(sink.ptr, sink.len, rb_utf8_encoding()); stackenv_uninit(&se); stringsink_uninit(&sink); return ret; } }
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); } } }
/* * call-seq: * MessageBuilderContext.map(name, key_type, value_type, number, * value_type_class = nil) * * Defines a new map field on this message type with the given key and value * types, tag number, and type class (for message and enum value types). The key * type must be :int32/:uint32/:int64/:uint64, :bool, or :string. The value type * type must be a Ruby symbol (as accepted by FieldDescriptor#type=) and the * type_class must be a string, if present (as accepted by * FieldDescriptor#submsg_name=). */ VALUE MessageBuilderContext_map(int argc, VALUE* argv, VALUE _self) { DEFINE_SELF(MessageBuilderContext, self, _self); if (argc < 4) { rb_raise(rb_eArgError, "Expected at least 4 arguments."); } VALUE name = argv[0]; VALUE key_type = argv[1]; VALUE value_type = argv[2]; VALUE number = argv[3]; VALUE type_class = (argc > 4) ? argv[4] : Qnil; // Validate the key type. We can't accept enums, messages, or floats/doubles // as map keys. (We exclude these explicitly, and the field-descriptor setter // below then ensures that the type is one of the remaining valid options.) if (SYM2ID(key_type) == rb_intern("float") || SYM2ID(key_type) == rb_intern("double") || SYM2ID(key_type) == rb_intern("enum") || SYM2ID(key_type) == rb_intern("message")) { rb_raise(rb_eArgError, "Cannot add a map field with a float, double, enum, or message " "type."); } // Create a new message descriptor for the map entry message, and create a // repeated submessage field here with that type. VALUE mapentry_desc = rb_class_new_instance(0, NULL, cDescriptor); VALUE mapentry_desc_name = rb_funcall(self->descriptor, rb_intern("name"), 0); mapentry_desc_name = rb_str_cat2(mapentry_desc_name, "_MapEntry_"); mapentry_desc_name = rb_str_cat2(mapentry_desc_name, rb_id2name(SYM2ID(name))); Descriptor_name_set(mapentry_desc, mapentry_desc_name); // The 'mapentry' attribute has no Ruby setter because we do not want the user // attempting to DIY the setup below; we want to ensure that the fields are // correct. So we reach into the msgdef here to set the bit manually. Descriptor* mapentry_desc_self = ruby_to_Descriptor(mapentry_desc); upb_msgdef_setmapentry((upb_msgdef*)mapentry_desc_self->msgdef, true); // optional <type> key = 1; VALUE key_field = rb_class_new_instance(0, NULL, cFieldDescriptor); FieldDescriptor_name_set(key_field, rb_str_new2("key")); FieldDescriptor_label_set(key_field, ID2SYM(rb_intern("optional"))); FieldDescriptor_number_set(key_field, INT2NUM(1)); FieldDescriptor_type_set(key_field, key_type); Descriptor_add_field(mapentry_desc, key_field); // optional <type> value = 2; VALUE value_field = rb_class_new_instance(0, NULL, cFieldDescriptor); FieldDescriptor_name_set(value_field, rb_str_new2("value")); FieldDescriptor_label_set(value_field, ID2SYM(rb_intern("optional"))); FieldDescriptor_number_set(value_field, INT2NUM(2)); FieldDescriptor_type_set(value_field, value_type); if (type_class != Qnil) { VALUE submsg_name = rb_str_new2("."); // prepend '.' to make name absolute. submsg_name = rb_str_append(submsg_name, type_class); FieldDescriptor_submsg_name_set(value_field, submsg_name); } Descriptor_add_field(mapentry_desc, value_field); // Add the map-entry message type to the current builder, and use the type to // create the map field itself. Builder* builder_self = ruby_to_Builder(self->builder); rb_ary_push(builder_self->pending_list, mapentry_desc); VALUE map_field = rb_class_new_instance(0, NULL, cFieldDescriptor); VALUE name_str = rb_str_new2(rb_id2name(SYM2ID(name))); FieldDescriptor_name_set(map_field, name_str); FieldDescriptor_number_set(map_field, number); FieldDescriptor_label_set(map_field, ID2SYM(rb_intern("repeated"))); FieldDescriptor_type_set(map_field, ID2SYM(rb_intern("message"))); VALUE submsg_name = rb_str_new2("."); // prepend '.' to make name absolute. submsg_name = rb_str_append(submsg_name, mapentry_desc_name); FieldDescriptor_submsg_name_set(map_field, submsg_name); Descriptor_add_field(self->descriptor, map_field); return Qnil; }