diff options
Diffstat (limited to 'ruby/ext')
-rw-r--r-- | ruby/ext/google/protobuf_c/defs.c | 615 | ||||
-rw-r--r-- | ruby/ext/google/protobuf_c/encode_decode.c | 115 | ||||
-rw-r--r-- | ruby/ext/google/protobuf_c/message.c | 191 | ||||
-rw-r--r-- | ruby/ext/google/protobuf_c/protobuf.c | 2 | ||||
-rw-r--r-- | ruby/ext/google/protobuf_c/protobuf.h | 59 | ||||
-rw-r--r-- | ruby/ext/google/protobuf_c/storage.c | 281 |
6 files changed, 1012 insertions, 251 deletions
diff --git a/ruby/ext/google/protobuf_c/defs.c b/ruby/ext/google/protobuf_c/defs.c index 9fe04503..dbbff88c 100644 --- a/ruby/ext/google/protobuf_c/defs.c +++ b/ruby/ext/google/protobuf_c/defs.c @@ -122,7 +122,7 @@ void DescriptorPool_register(VALUE module) { module, "DescriptorPool", rb_cObject); rb_define_alloc_func(klass, DescriptorPool_alloc); rb_define_method(klass, "add", DescriptorPool_add, 1); - rb_define_method(klass, "build", DescriptorPool_build, 0); + rb_define_method(klass, "build", DescriptorPool_build, -1); rb_define_method(klass, "lookup", DescriptorPool_lookup, 1); rb_define_singleton_method(klass, "generated_pool", DescriptorPool_generated_pool, 0); @@ -181,7 +181,7 @@ VALUE DescriptorPool_add(VALUE _self, VALUE def) { * Builder#add_enum within the block as appropriate. This is the recommended, * idiomatic way to define new message and enum types. */ -VALUE DescriptorPool_build(VALUE _self) { +VALUE DescriptorPool_build(int argc, VALUE* argv, VALUE _self) { VALUE ctx = rb_class_new_instance(0, NULL, cBuilder); VALUE block = rb_block_proc(); rb_funcall_with_block(ctx, rb_intern("instance_eval"), 0, NULL, block); @@ -289,6 +289,7 @@ void Descriptor_register(VALUE module) { VALUE klass = rb_define_class_under( module, "Descriptor", rb_cObject); rb_define_alloc_func(klass, Descriptor_alloc); + rb_define_method(klass, "initialize", Descriptor_initialize, 1); rb_define_method(klass, "each", Descriptor_each, 0); rb_define_method(klass, "lookup", Descriptor_lookup, 1); rb_define_method(klass, "add_field", Descriptor_add_field, 1); @@ -298,6 +299,7 @@ void Descriptor_register(VALUE module) { rb_define_method(klass, "msgclass", Descriptor_msgclass, 0); rb_define_method(klass, "name", Descriptor_name, 0); rb_define_method(klass, "name=", Descriptor_name_set, 1); + rb_define_method(klass, "file_descriptor", Descriptor_file_descriptor, 0); rb_include_module(klass, rb_mEnumerable); rb_gc_register_address(&cDescriptor); cDescriptor = klass; @@ -305,6 +307,36 @@ void Descriptor_register(VALUE module) { /* * call-seq: + * Descriptor.new(file_descriptor) + * + * Initializes a new descriptor and assigns a file descriptor to it. + */ +VALUE Descriptor_initialize(VALUE _self, VALUE file_descriptor_rb) { + DEFINE_SELF(Descriptor, self, _self); + + FileDescriptor* file_descriptor = ruby_to_FileDescriptor(file_descriptor_rb); + + CHECK_UPB( + upb_filedef_addmsg(file_descriptor->filedef, self->msgdef, NULL, &status), + "Failed to associate message to file descriptor."); + add_def_obj(file_descriptor->filedef, file_descriptor_rb); + + return Qnil; +} + +/* + * call-seq: + * Descriptor.file_descriptor + * + * Returns the FileDescriptor object this message belongs to. + */ +VALUE Descriptor_file_descriptor(VALUE _self) { + DEFINE_SELF(Descriptor, self, _self); + return get_def_obj(upb_def_file(self->msgdef)); +} + +/* + * call-seq: * Descriptor.name => name * * Returns the name of this message type as a fully-qualfied string (e.g., @@ -471,6 +503,142 @@ VALUE Descriptor_msgclass(VALUE _self) { } // ----------------------------------------------------------------------------- +// FileDescriptor. +// ----------------------------------------------------------------------------- + +DEFINE_CLASS(FileDescriptor, "Google::Protobuf::FileDescriptor"); + +void FileDescriptor_mark(void* _self) { +} + +void FileDescriptor_free(void* _self) { + FileDescriptor* self = _self; + upb_filedef_unref(self->filedef, &self->filedef); + xfree(self); +} + +/* + * call-seq: + * FileDescriptor.new => file + * + * Returns a new file descriptor. The syntax must be set before it's passed + * to a builder. + */ +VALUE FileDescriptor_alloc(VALUE klass) { + FileDescriptor* self = ALLOC(FileDescriptor); + VALUE ret = TypedData_Wrap_Struct(klass, &_FileDescriptor_type, self); + upb_filedef* filedef = upb_filedef_new(&self->filedef); + self->filedef = filedef; + return ret; +} + +void FileDescriptor_register(VALUE module) { + VALUE klass = rb_define_class_under( + module, "FileDescriptor", rb_cObject); + rb_define_alloc_func(klass, FileDescriptor_alloc); + rb_define_method(klass, "initialize", FileDescriptor_initialize, -1); + rb_define_method(klass, "name", FileDescriptor_name, 0); + rb_define_method(klass, "syntax", FileDescriptor_syntax, 0); + rb_define_method(klass, "syntax=", FileDescriptor_syntax_set, 1); + cFileDescriptor = klass; + rb_gc_register_address(&cFileDescriptor); +} + +/* + * call-seq: + * FileDescriptor.new(name, options = nil) => file + * + * Initializes a new file descriptor with the given file name. + * Also accepts an optional "options" hash, specifying other optional + * metadata about the file. The options hash currently accepts the following + * * "syntax": :proto2 or :proto3 (default: :proto3) + */ +VALUE FileDescriptor_initialize(int argc, VALUE* argv, VALUE _self) { + DEFINE_SELF(FileDescriptor, self, _self); + + VALUE name_rb; + VALUE options = Qnil; + rb_scan_args(argc, argv, "11", &name_rb, &options); + + if (name_rb != Qnil) { + Check_Type(name_rb, T_STRING); + const char* name = get_str(name_rb); + CHECK_UPB(upb_filedef_setname(self->filedef, name, &status), + "Error setting file name"); + } + + // Default syntax is proto3. + VALUE syntax = ID2SYM(rb_intern("proto3")); + if (options != Qnil) { + Check_Type(options, T_HASH); + + if (rb_funcall(options, rb_intern("key?"), 1, + ID2SYM(rb_intern("syntax"))) == Qtrue) { + syntax = rb_hash_lookup(options, ID2SYM(rb_intern("syntax"))); + } + } + FileDescriptor_syntax_set(_self, syntax); + + return Qnil; +} + +/* + * call-seq: + * FileDescriptor.name => name + * + * Returns the name of the file. + */ +VALUE FileDescriptor_name(VALUE _self) { + DEFINE_SELF(FileDescriptor, self, _self); + const char* name = upb_filedef_name(self->filedef); + return name == NULL ? Qnil : rb_str_new2(name); +} + +/* + * call-seq: + * FileDescriptor.syntax => syntax + * + * Returns this file descriptors syntax. + * + * Valid syntax versions are: + * :proto2 or :proto3. + */ +VALUE FileDescriptor_syntax(VALUE _self) { + DEFINE_SELF(FileDescriptor, self, _self); + + switch (upb_filedef_syntax(self->filedef)) { + case UPB_SYNTAX_PROTO3: return ID2SYM(rb_intern("proto3")); + case UPB_SYNTAX_PROTO2: return ID2SYM(rb_intern("proto2")); + default: return Qnil; + } +} + +/* + * call-seq: + * FileDescriptor.syntax = version + * + * Sets this file descriptor's syntax, can be :proto3 or :proto2. + */ +VALUE FileDescriptor_syntax_set(VALUE _self, VALUE syntax_rb) { + DEFINE_SELF(FileDescriptor, self, _self); + Check_Type(syntax_rb, T_SYMBOL); + + upb_syntax_t syntax; + if (SYM2ID(syntax_rb) == rb_intern("proto3")) { + syntax = UPB_SYNTAX_PROTO3; + } else if (SYM2ID(syntax_rb) == rb_intern("proto2")) { + syntax = UPB_SYNTAX_PROTO2; + } else { + rb_raise(rb_eArgError, "Expected :proto3 or :proto3, received '%s'", + rb_id2name(SYM2ID(syntax_rb))); + } + + CHECK_UPB(upb_filedef_setsyntax(self->filedef, syntax, &status), + "Error setting file syntax for proto"); + return Qnil; +} + +// ----------------------------------------------------------------------------- // FieldDescriptor. // ----------------------------------------------------------------------------- @@ -509,6 +677,8 @@ void FieldDescriptor_register(VALUE module) { rb_define_method(klass, "name=", FieldDescriptor_name_set, 1); rb_define_method(klass, "type", FieldDescriptor_type, 0); rb_define_method(klass, "type=", FieldDescriptor_type_set, 1); + rb_define_method(klass, "default", FieldDescriptor_default, 0); + rb_define_method(klass, "default=", FieldDescriptor_default_set, 1); rb_define_method(klass, "label", FieldDescriptor_label, 0); rb_define_method(klass, "label=", FieldDescriptor_label_set, 1); rb_define_method(klass, "number", FieldDescriptor_number, 0); @@ -516,6 +686,8 @@ void FieldDescriptor_register(VALUE module) { rb_define_method(klass, "submsg_name", FieldDescriptor_submsg_name, 0); rb_define_method(klass, "submsg_name=", FieldDescriptor_submsg_name_set, 1); rb_define_method(klass, "subtype", FieldDescriptor_subtype, 0); + rb_define_method(klass, "has?", FieldDescriptor_has, 1); + rb_define_method(klass, "clear", FieldDescriptor_clear, 1); rb_define_method(klass, "get", FieldDescriptor_get, 1); rb_define_method(klass, "set", FieldDescriptor_set, 2); rb_gc_register_address(&cFieldDescriptor); @@ -693,6 +865,71 @@ VALUE FieldDescriptor_type_set(VALUE _self, VALUE type) { /* * call-seq: + * FieldDescriptor.default => default + * + * Returns this field's default, as a Ruby object, or nil if not yet set. + */ +VALUE FieldDescriptor_default(VALUE _self) { + DEFINE_SELF(FieldDescriptor, self, _self); + return layout_get_default(self->fielddef); +} + +/* + * call-seq: + * FieldDescriptor.default = default + * + * Sets this field's default value. Raises an exception when calling with + * proto syntax 3. + */ +VALUE FieldDescriptor_default_set(VALUE _self, VALUE default_value) { + DEFINE_SELF(FieldDescriptor, self, _self); + upb_fielddef* mut_def = check_field_notfrozen(self->fielddef); + + switch (upb_fielddef_type(mut_def)) { + case UPB_TYPE_FLOAT: + upb_fielddef_setdefaultfloat(mut_def, NUM2DBL(default_value)); + break; + case UPB_TYPE_DOUBLE: + upb_fielddef_setdefaultdouble(mut_def, NUM2DBL(default_value)); + break; + case UPB_TYPE_BOOL: + if (!RB_TYPE_P(default_value, T_TRUE) && + !RB_TYPE_P(default_value, T_FALSE) && + !RB_TYPE_P(default_value, T_NIL)) { + rb_raise(cTypeError, "Expected boolean for default value."); + } + + upb_fielddef_setdefaultbool(mut_def, RTEST(default_value)); + break; + case UPB_TYPE_ENUM: + case UPB_TYPE_INT32: + upb_fielddef_setdefaultint32(mut_def, NUM2INT(default_value)); + break; + case UPB_TYPE_INT64: + upb_fielddef_setdefaultint64(mut_def, NUM2INT(default_value)); + break; + case UPB_TYPE_UINT32: + upb_fielddef_setdefaultuint32(mut_def, NUM2UINT(default_value)); + break; + case UPB_TYPE_UINT64: + upb_fielddef_setdefaultuint64(mut_def, NUM2UINT(default_value)); + break; + case UPB_TYPE_STRING: + case UPB_TYPE_BYTES: + CHECK_UPB(upb_fielddef_setdefaultcstr(mut_def, StringValuePtr(default_value), + &status), + "Error setting default string"); + break; + default: + rb_raise(rb_eArgError, "Defaults not supported on field %s.%s", + upb_fielddef_fullname(mut_def), upb_fielddef_name(mut_def)); + } + + return Qnil; +} + +/* + * call-seq: * FieldDescriptor.label => label * * Returns this field's label (i.e., plurality), as a Ruby symbol. @@ -861,6 +1098,44 @@ VALUE FieldDescriptor_get(VALUE _self, VALUE msg_rb) { /* * call-seq: + * FieldDescriptor.has?(message) => boolean + * + * Returns whether the value is set on the given message. Raises an + * exception when calling with proto syntax 3. + */ +VALUE FieldDescriptor_has(VALUE _self, VALUE msg_rb) { + DEFINE_SELF(FieldDescriptor, self, _self); + MessageHeader* msg; + TypedData_Get_Struct(msg_rb, MessageHeader, &Message_type, msg); + if (msg->descriptor->msgdef != upb_fielddef_containingtype(self->fielddef)) { + rb_raise(cTypeError, "has method called on wrong message type"); + } else if (!upb_fielddef_haspresence(self->fielddef)) { + rb_raise(rb_eArgError, "does not track presence"); + } + + return layout_has(msg->descriptor->layout, Message_data(msg), self->fielddef); +} + +/* + * call-seq: + * FieldDescriptor.clear(message) + * + * Clears the field from the message if it's set. + */ +VALUE FieldDescriptor_clear(VALUE _self, VALUE msg_rb) { + DEFINE_SELF(FieldDescriptor, self, _self); + MessageHeader* msg; + TypedData_Get_Struct(msg_rb, MessageHeader, &Message_type, msg); + if (msg->descriptor->msgdef != upb_fielddef_containingtype(self->fielddef)) { + rb_raise(cTypeError, "has method called on wrong message type"); + } + + layout_clear(msg->descriptor->layout, Message_data(msg), self->fielddef); + return Qnil; +} + +/* + * call-seq: * FieldDescriptor.set(message, value) * * Sets the value corresponding to this field to the given value on the given @@ -1029,6 +1304,7 @@ void EnumDescriptor_register(VALUE module) { VALUE klass = rb_define_class_under( module, "EnumDescriptor", rb_cObject); rb_define_alloc_func(klass, EnumDescriptor_alloc); + rb_define_method(klass, "initialize", EnumDescriptor_initialize, 1); rb_define_method(klass, "name", EnumDescriptor_name, 0); rb_define_method(klass, "name=", EnumDescriptor_name_set, 1); rb_define_method(klass, "add_value", EnumDescriptor_add_value, 2); @@ -1036,6 +1312,7 @@ void EnumDescriptor_register(VALUE module) { rb_define_method(klass, "lookup_value", EnumDescriptor_lookup_value, 1); rb_define_method(klass, "each", EnumDescriptor_each, 0); rb_define_method(klass, "enummodule", EnumDescriptor_enummodule, 0); + rb_define_method(klass, "file_descriptor", EnumDescriptor_file_descriptor, 0); rb_include_module(klass, rb_mEnumerable); rb_gc_register_address(&cEnumDescriptor); cEnumDescriptor = klass; @@ -1043,6 +1320,35 @@ void EnumDescriptor_register(VALUE module) { /* * call-seq: + * Descriptor.new(file_descriptor) + * + * Initializes a new descriptor and assigns a file descriptor to it. + */ +VALUE EnumDescriptor_initialize(VALUE _self, VALUE file_descriptor_rb) { + DEFINE_SELF(EnumDescriptor, self, _self); + FileDescriptor* file_descriptor = ruby_to_FileDescriptor(file_descriptor_rb); + CHECK_UPB( + upb_filedef_addenum(file_descriptor->filedef, self->enumdef, + NULL, &status), + "Failed to associate enum to file descriptor."); + add_def_obj(file_descriptor->filedef, file_descriptor_rb); + + return Qnil; +} + +/* + * call-seq: + * Descriptor.file_descriptor + * + * Returns the FileDescriptor object this enum belongs to. + */ +VALUE EnumDescriptor_file_descriptor(VALUE _self) { + DEFINE_SELF(EnumDescriptor, self, _self); + return get_def_obj(upb_def_file(self->enumdef)); +} + +/* + * call-seq: * EnumDescriptor.name => name * * Returns the name of this enum type. @@ -1223,34 +1529,56 @@ VALUE MessageBuilderContext_initialize(VALUE _self, return Qnil; } -static VALUE msgdef_add_field(VALUE msgdef, +static VALUE msgdef_add_field(VALUE msgdef_rb, const char* label, VALUE name, VALUE type, VALUE number, - VALUE type_class) { - VALUE fielddef = rb_class_new_instance(0, NULL, cFieldDescriptor); + VALUE type_class, + VALUE options) { + VALUE fielddef_rb = rb_class_new_instance(0, NULL, cFieldDescriptor); VALUE name_str = rb_str_new2(rb_id2name(SYM2ID(name))); - rb_funcall(fielddef, rb_intern("label="), 1, ID2SYM(rb_intern(label))); - rb_funcall(fielddef, rb_intern("name="), 1, name_str); - rb_funcall(fielddef, rb_intern("type="), 1, type); - rb_funcall(fielddef, rb_intern("number="), 1, number); + rb_funcall(fielddef_rb, rb_intern("label="), 1, ID2SYM(rb_intern(label))); + rb_funcall(fielddef_rb, rb_intern("name="), 1, name_str); + rb_funcall(fielddef_rb, rb_intern("type="), 1, type); + rb_funcall(fielddef_rb, rb_intern("number="), 1, number); if (type_class != Qnil) { - if (TYPE(type_class) != T_STRING) { - rb_raise(rb_eArgError, "Expected string for type class"); - } + Check_Type(type_class, T_STRING); + // Make it an absolute type name by prepending a dot. type_class = rb_str_append(rb_str_new2("."), type_class); - rb_funcall(fielddef, rb_intern("submsg_name="), 1, type_class); + rb_funcall(fielddef_rb, rb_intern("submsg_name="), 1, type_class); } - rb_funcall(msgdef, rb_intern("add_field"), 1, fielddef); - return fielddef; + if (options != Qnil) { + Check_Type(options, T_HASH); + + if (rb_funcall(options, rb_intern("key?"), 1, + ID2SYM(rb_intern("default"))) == Qtrue) { + Descriptor* msgdef = ruby_to_Descriptor(msgdef_rb); + if (upb_msgdef_syntax((upb_msgdef*)msgdef->msgdef) == UPB_SYNTAX_PROTO3) { + rb_raise(rb_eArgError, "Cannot set :default when using proto3 syntax."); + } + + FieldDescriptor* fielddef = ruby_to_FieldDescriptor(fielddef_rb); + if (!upb_fielddef_haspresence((upb_fielddef*)fielddef->fielddef) || + upb_fielddef_issubmsg((upb_fielddef*)fielddef->fielddef)) { + rb_raise(rb_eArgError, "Cannot set :default on this kind of field."); + } + + rb_funcall(fielddef_rb, rb_intern("default="), 1, + rb_hash_lookup(options, ID2SYM(rb_intern("default")))); + } + } + + rb_funcall(msgdef_rb, rb_intern("add_field"), 1, fielddef_rb); + return fielddef_rb; } /* * call-seq: - * MessageBuilderContext.optional(name, type, number, type_class = nil) + * MessageBuilderContext.optional(name, type, number, type_class = nil, + * options = nil) * * Defines a new optional field on this message type with the given type, tag * number, and type class (for message and enum fields). The type must be a Ruby @@ -1259,23 +1587,26 @@ static VALUE msgdef_add_field(VALUE msgdef, */ VALUE MessageBuilderContext_optional(int argc, VALUE* argv, VALUE _self) { DEFINE_SELF(MessageBuilderContext, self, _self); - VALUE name, type, number, type_class; + VALUE name, type, number; + VALUE type_class, options = Qnil; - if (argc < 3) { - rb_raise(rb_eArgError, "Expected at least 3 arguments."); + rb_scan_args(argc, argv, "32", &name, &type, &number, &type_class, &options); + + // Allow passing (name, type, number, options) or + // (name, type, number, type_class, options) + if (argc == 4 && RB_TYPE_P(type_class, T_HASH)) { + options = type_class; + type_class = Qnil; } - name = argv[0]; - type = argv[1]; - number = argv[2]; - type_class = (argc > 3) ? argv[3] : Qnil; return msgdef_add_field(self->descriptor, "optional", - name, type, number, type_class); + name, type, number, type_class, options); } /* * call-seq: - * MessageBuilderContext.required(name, type, number, type_class = nil) + * MessageBuilderContext.required(name, type, number, type_class = nil, + * options = nil) * * Defines a new required field on this message type with the given type, tag * number, and type class (for message and enum fields). The type must be a Ruby @@ -1288,18 +1619,20 @@ VALUE MessageBuilderContext_optional(int argc, VALUE* argv, VALUE _self) { */ VALUE MessageBuilderContext_required(int argc, VALUE* argv, VALUE _self) { DEFINE_SELF(MessageBuilderContext, self, _self); - VALUE name, type, number, type_class; + VALUE name, type, number; + VALUE type_class, options = Qnil; - if (argc < 3) { - rb_raise(rb_eArgError, "Expected at least 3 arguments."); + rb_scan_args(argc, argv, "32", &name, &type, &number, &type_class, &options); + + // Allow passing (name, type, number, options) or + // (name, type, number, type_class, options) + if (argc == 4 && RB_TYPE_P(type_class, T_HASH)) { + options = type_class; + type_class = Qnil; } - name = argv[0]; - type = argv[1]; - number = argv[2]; - type_class = (argc > 3) ? argv[3] : Qnil; return msgdef_add_field(self->descriptor, "required", - name, type, number, type_class); + name, type, number, type_class, options); } /* @@ -1324,7 +1657,7 @@ VALUE MessageBuilderContext_repeated(int argc, VALUE* argv, VALUE _self) { type_class = (argc > 3) ? argv[3] : Qnil; return msgdef_add_field(self->descriptor, "repeated", - name, type, number, type_class); + name, type, number, type_class, Qnil); } /* @@ -1365,9 +1698,17 @@ VALUE MessageBuilderContext_map(int argc, VALUE* argv, VALUE _self) { "type."); } + Descriptor* descriptor = ruby_to_Descriptor(self->descriptor); + if (upb_msgdef_syntax(descriptor->msgdef) == UPB_SYNTAX_PROTO2) { + rb_raise(rb_eArgError, + "Cannot add a native map field using proto2 syntax."); + } + // Create a new message descriptor for the map entry message, and create a // repeated submessage field here with that type. - mapentry_desc = rb_class_new_instance(0, NULL, cDescriptor); + VALUE file_descriptor_rb = + rb_funcall(self->descriptor, rb_intern("file_descriptor"), 0); + mapentry_desc = rb_class_new_instance(1, &file_descriptor_rb, cDescriptor); 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, @@ -1410,8 +1751,8 @@ VALUE MessageBuilderContext_map(int argc, VALUE* argv, VALUE _self) { { // 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); + Builder* builder = ruby_to_Builder(self->builder); + rb_ary_push(builder->pending_list, mapentry_desc); } { @@ -1514,7 +1855,8 @@ VALUE OneofBuilderContext_initialize(VALUE _self, /* * call-seq: - * OneofBuilderContext.optional(name, type, number, type_class = nil) + * OneofBuilderContext.optional(name, type, number, type_class = nil, + * default_value = nil) * * Defines a new optional field in this oneof with the given type, tag number, * and type class (for message and enum fields). The type must be a Ruby symbol @@ -1523,18 +1865,13 @@ VALUE OneofBuilderContext_initialize(VALUE _self, */ VALUE OneofBuilderContext_optional(int argc, VALUE* argv, VALUE _self) { DEFINE_SELF(OneofBuilderContext, self, _self); - VALUE name, type, number, type_class; + VALUE name, type, number; + VALUE type_class, options = Qnil; - if (argc < 3) { - rb_raise(rb_eArgError, "Expected at least 3 arguments."); - } - name = argv[0]; - type = argv[1]; - number = argv[2]; - type_class = (argc > 3) ? argv[3] : Qnil; + rb_scan_args(argc, argv, "32", &name, &type, &number, &type_class, &options); return msgdef_add_field(self->descriptor, "optional", - name, type, number, type_class); + name, type, number, type_class, options); } // ----------------------------------------------------------------------------- @@ -1604,6 +1941,112 @@ VALUE EnumBuilderContext_value(VALUE _self, VALUE name, VALUE number) { return enumdef_add_value(self->enumdesc, name, number); } + +// ----------------------------------------------------------------------------- +// FileBuilderContext. +// ----------------------------------------------------------------------------- + +DEFINE_CLASS(FileBuilderContext, + "Google::Protobuf::Internal::FileBuilderContext"); + +void FileBuilderContext_mark(void* _self) { + FileBuilderContext* self = _self; + rb_gc_mark(self->pending_list); + rb_gc_mark(self->file_descriptor); + rb_gc_mark(self->builder); +} + +void FileBuilderContext_free(void* _self) { + FileBuilderContext* self = _self; + xfree(self); +} + +VALUE FileBuilderContext_alloc(VALUE klass) { + FileBuilderContext* self = ALLOC(FileBuilderContext); + VALUE ret = TypedData_Wrap_Struct(klass, &_FileBuilderContext_type, self); + self->pending_list = Qnil; + self->file_descriptor = Qnil; + self->builder = Qnil; + return ret; +} + +void FileBuilderContext_register(VALUE module) { + VALUE klass = rb_define_class_under(module, "FileBuilderContext", rb_cObject); + rb_define_alloc_func(klass, FileBuilderContext_alloc); + rb_define_method(klass, "initialize", FileBuilderContext_initialize, 2); + rb_define_method(klass, "add_message", FileBuilderContext_add_message, 1); + rb_define_method(klass, "add_enum", FileBuilderContext_add_enum, 1); + rb_gc_register_address(&cFileBuilderContext); + cFileBuilderContext = klass; +} + +/* + * call-seq: + * FileBuilderContext.new(file_descriptor, builder) => context + * + * Create a new file builder context for the given file descriptor and + * builder context. This class is intended to serve as a DSL context to be used + * with #instance_eval. + */ +VALUE FileBuilderContext_initialize(VALUE _self, VALUE file_descriptor, + VALUE builder) { + DEFINE_SELF(FileBuilderContext, self, _self); + self->pending_list = rb_ary_new(); + self->file_descriptor = file_descriptor; + self->builder = builder; + return Qnil; +} + +/* + * call-seq: + * FileBuilderContext.add_message(name, &block) + * + * Creates a new, empty descriptor with the given name, and invokes the block in + * the context of a MessageBuilderContext on that descriptor. The block can then + * call, e.g., MessageBuilderContext#optional and MessageBuilderContext#repeated + * methods to define the message fields. + * + * This is the recommended, idiomatic way to build message definitions. + */ +VALUE FileBuilderContext_add_message(VALUE _self, VALUE name) { + DEFINE_SELF(FileBuilderContext, self, _self); + VALUE msgdef = rb_class_new_instance(1, &self->file_descriptor, cDescriptor); + VALUE args[2] = { msgdef, self->builder }; + VALUE ctx = rb_class_new_instance(2, args, cMessageBuilderContext); + VALUE block = rb_block_proc(); + rb_funcall(msgdef, rb_intern("name="), 1, name); + rb_funcall_with_block(ctx, rb_intern("instance_eval"), 0, NULL, block); + rb_ary_push(self->pending_list, msgdef); + return Qnil; +} + +/* + * call-seq: + * FileBuilderContext.add_enum(name, &block) + * + * Creates a new, empty enum descriptor with the given name, and invokes the + * block in the context of an EnumBuilderContext on that descriptor. The block + * can then call EnumBuilderContext#add_value to define the enum values. + * + * This is the recommended, idiomatic way to build enum definitions. + */ +VALUE FileBuilderContext_add_enum(VALUE _self, VALUE name) { + DEFINE_SELF(FileBuilderContext, self, _self); + VALUE enumdef = + rb_class_new_instance(1, &self->file_descriptor, cEnumDescriptor); + VALUE ctx = rb_class_new_instance(1, &enumdef, cEnumBuilderContext); + VALUE block = rb_block_proc(); + rb_funcall(enumdef, rb_intern("name="), 1, name); + rb_funcall_with_block(ctx, rb_intern("instance_eval"), 0, NULL, block); + rb_ary_push(self->pending_list, enumdef); + return Qnil; +} + +VALUE FileBuilderContext_pending_descriptors(VALUE _self) { + DEFINE_SELF(FileBuilderContext, self, _self); + return self->pending_list; +} + // ----------------------------------------------------------------------------- // Builder. // ----------------------------------------------------------------------------- @@ -1613,6 +2056,7 @@ DEFINE_CLASS(Builder, "Google::Protobuf::Internal::Builder"); void Builder_mark(void* _self) { Builder* self = _self; rb_gc_mark(self->pending_list); + rb_gc_mark(self->default_file_descriptor); } void Builder_free(void* _self) { @@ -1635,15 +2079,17 @@ VALUE Builder_alloc(VALUE klass) { klass, &_Builder_type, self); self->pending_list = Qnil; self->defs = NULL; + self->default_file_descriptor = Qnil; return ret; } void Builder_register(VALUE module) { VALUE klass = rb_define_class_under(module, "Builder", rb_cObject); - rb_define_alloc_func(klass, Builder_alloc); + rb_define_alloc_func(klass, Builder_alloc); + rb_define_method(klass, "initialize", Builder_initialize, 0); + rb_define_method(klass, "add_file", Builder_add_file, -1); rb_define_method(klass, "add_message", Builder_add_message, 1); rb_define_method(klass, "add_enum", Builder_add_enum, 1); - rb_define_method(klass, "initialize", Builder_initialize, 0); rb_define_method(klass, "finalize_to_pool", Builder_finalize_to_pool, 1); rb_gc_register_address(&cBuilder); cBuilder = klass; @@ -1651,13 +2097,40 @@ void Builder_register(VALUE module) { /* * call-seq: - * Builder.new(d) => builder + * Builder.new * - * Create a new message builder. + * Initializes a new builder. */ VALUE Builder_initialize(VALUE _self) { DEFINE_SELF(Builder, self, _self); self->pending_list = rb_ary_new(); + VALUE file_name = Qnil; + self->default_file_descriptor = + rb_class_new_instance(1, &file_name, cFileDescriptor); + return Qnil; +} + +/* + * call-seq: + * Builder.add_file(name, options = nil, &block) + * + * Creates a new, file descriptor with the given name and options and invokes + * the block in the context of a FileBuilderContext on that descriptor. The + * block can then call FileBuilderContext#add_message or + * FileBuilderContext#add_enum to define new messages or enums, respectively. + * + * This is the recommended, idiomatic way to build file descriptors. + */ +VALUE Builder_add_file(int argc, VALUE* argv, VALUE _self) { + DEFINE_SELF(Builder, self, _self); + VALUE file_descriptor = rb_class_new_instance(argc, argv, cFileDescriptor); + VALUE args[2] = { file_descriptor, _self }; + VALUE ctx = rb_class_new_instance(2, args, cFileBuilderContext); + VALUE block = rb_block_proc(); + rb_funcall_with_block(ctx, rb_intern("instance_eval"), 0, NULL, block); + + rb_ary_concat(self->pending_list, + FileBuilderContext_pending_descriptors(ctx)); return Qnil; } @@ -1665,16 +2138,17 @@ VALUE Builder_initialize(VALUE _self) { * call-seq: * Builder.add_message(name, &block) * - * Creates a new, empty descriptor with the given name, and invokes the block in - * the context of a MessageBuilderContext on that descriptor. The block can then - * call, e.g., MessageBuilderContext#optional and MessageBuilderContext#repeated - * methods to define the message fields. + * Old and deprecated way to create a new descriptor. + * See FileBuilderContext.add_message for the recommended way. * - * This is the recommended, idiomatic way to build message definitions. + * Exists for backwards compatibility to allow building descriptor pool for + * files generated by protoc which don't add messages within "add_file" block. + * Descriptors created this way get assigned to a default empty FileDescriptor. */ VALUE Builder_add_message(VALUE _self, VALUE name) { DEFINE_SELF(Builder, self, _self); - VALUE msgdef = rb_class_new_instance(0, NULL, cDescriptor); + VALUE msgdef = + rb_class_new_instance(1, &self->default_file_descriptor, cDescriptor); VALUE args[2] = { msgdef, _self }; VALUE ctx = rb_class_new_instance(2, args, cMessageBuilderContext); VALUE block = rb_block_proc(); @@ -1688,15 +2162,18 @@ VALUE Builder_add_message(VALUE _self, VALUE name) { * call-seq: * Builder.add_enum(name, &block) * - * Creates a new, empty enum descriptor with the given name, and invokes the - * block in the context of an EnumBuilderContext on that descriptor. The block - * can then call EnumBuilderContext#add_value to define the enum values. + * Old and deprecated way to create a new enum descriptor. + * See FileBuilderContext.add_enum for the recommended way. * - * This is the recommended, idiomatic way to build enum definitions. + * Exists for backwards compatibility to allow building descriptor pool for + * files generated by protoc which don't add enums within "add_file" block. + * Enum descriptors created this way get assigned to a default empty + * FileDescriptor. */ VALUE Builder_add_enum(VALUE _self, VALUE name) { DEFINE_SELF(Builder, self, _self); - VALUE enumdef = rb_class_new_instance(0, NULL, cEnumDescriptor); + VALUE enumdef = + rb_class_new_instance(1, &self->default_file_descriptor, cEnumDescriptor); VALUE ctx = rb_class_new_instance(1, &enumdef, cEnumBuilderContext); VALUE block = rb_block_proc(); rb_funcall(enumdef, rb_intern("name="), 1, name); @@ -1705,7 +2182,7 @@ VALUE Builder_add_enum(VALUE _self, VALUE name) { return Qnil; } -static void validate_msgdef(const upb_msgdef* msgdef) { +static void proto3_validate_msgdef(const upb_msgdef* msgdef) { // Verify that no required fields exist. proto3 does not support these. upb_msg_field_iter it; for (upb_msg_field_begin(&it, msgdef); @@ -1718,7 +2195,7 @@ static void validate_msgdef(const upb_msgdef* msgdef) { } } -static void validate_enumdef(const upb_enumdef* enumdef) { +static void proto3_validate_enumdef(const upb_enumdef* enumdef) { // Verify that an entry exists with integer value 0. (This is the default // value.) const char* lookup = upb_enumdef_iton(enumdef, 0); @@ -1753,10 +2230,16 @@ VALUE Builder_finalize_to_pool(VALUE _self, VALUE pool_rb) { 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]); + + if (upb_filedef_syntax(upb_def_file(self->defs[i])) == UPB_SYNTAX_PROTO3) { + proto3_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]); + + if (upb_filedef_syntax(upb_def_file(self->defs[i])) == UPB_SYNTAX_PROTO3) { + proto3_validate_enumdef((const upb_enumdef*)self->defs[i]); + } } } diff --git a/ruby/ext/google/protobuf_c/encode_decode.c b/ruby/ext/google/protobuf_c/encode_decode.c index 8c6ded64..c2c40ddd 100644 --- a/ruby/ext/google/protobuf_c/encode_decode.c +++ b/ruby/ext/google/protobuf_c/encode_decode.c @@ -100,24 +100,34 @@ void stringsink_uninit(stringsink *sink) { #define DEREF(msg, ofs, type) *(type*)(((uint8_t *)msg) + ofs) -// Creates a handlerdata that simply contains the offset for this field. -static const void* newhandlerdata(upb_handlers* h, uint32_t ofs) { - size_t* hd_ofs = ALLOC(size_t); - *hd_ofs = ofs; - upb_handlers_addcleanup(h, hd_ofs, xfree); - return hd_ofs; +typedef struct { + size_t ofs; + int32_t hasbit; +} field_handlerdata_t; + +// Creates a handlerdata that contains the offset and the hasbit for the field +static const void* newhandlerdata(upb_handlers* h, uint32_t ofs, int32_t hasbit) { + field_handlerdata_t *hd = ALLOC(field_handlerdata_t); + hd->ofs = ofs; + hd->hasbit = hasbit; + upb_handlers_addcleanup(h, hd, xfree); + return hd; } typedef struct { size_t ofs; + int32_t hasbit; const upb_msgdef *md; } submsg_handlerdata_t; // Creates a handlerdata that contains offset and submessage type information. -static const void *newsubmsghandlerdata(upb_handlers* h, uint32_t ofs, +static const void *newsubmsghandlerdata(upb_handlers* h, + uint32_t ofs, + int32_t hasbit, const upb_fielddef* f) { submsg_handlerdata_t *hd = ALLOC(submsg_handlerdata_t); hd->ofs = ofs; + hd->hasbit = hasbit; hd->md = upb_fielddef_msgsubdef(f); upb_handlers_addcleanup(h, hd, xfree); return hd; @@ -189,6 +199,13 @@ static void* appendstr_handler(void *closure, return (void*)str; } +static void set_hasbit(void *closure, int32_t hasbit) { + if (hasbit > 0) { + uint8_t* storage = closure; + storage[hasbit/8] |= 1 << (hasbit % 8); + } +} + // Appends a 'bytes' string to a repeated field. static void* appendbytes_handler(void *closure, const void *hd, @@ -205,10 +222,12 @@ static void* str_handler(void *closure, const void *hd, size_t size_hint) { MessageHeader* msg = closure; - const size_t *ofs = hd; + const field_handlerdata_t *fieldhandler = hd; + VALUE str = rb_str_new2(""); rb_enc_associate(str, kRubyStringUtf8Encoding); - DEREF(msg, *ofs, VALUE) = str; + DEREF(msg, fieldhandler->ofs, VALUE) = str; + set_hasbit(closure, fieldhandler->hasbit); return (void*)str; } @@ -217,10 +236,12 @@ static void* bytes_handler(void *closure, const void *hd, size_t size_hint) { MessageHeader* msg = closure; - const size_t *ofs = hd; + const field_handlerdata_t *fieldhandler = hd; + VALUE str = rb_str_new2(""); rb_enc_associate(str, kRubyString8bitEncoding); - DEREF(msg, *ofs, VALUE) = str; + DEREF(msg, fieldhandler->ofs, VALUE) = str; + set_hasbit(closure, fieldhandler->hasbit); return (void*)str; } @@ -280,8 +301,11 @@ static void *submsg_handler(void *closure, const void *hd) { rb_class_new_instance(0, NULL, subklass); } + set_hasbit(closure, submsgdata->hasbit); + submsg_rb = DEREF(msg, submsgdata->ofs, VALUE); TypedData_Get_Struct(submsg_rb, MessageHeader, &Message_type, submsg); + return submsg; } @@ -500,7 +524,7 @@ static void add_handlers_for_repeated_field(upb_handlers *h, const upb_fielddef *f, size_t offset) { upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER; - upb_handlerattr_sethandlerdata(&attr, newhandlerdata(h, offset)); + upb_handlerattr_sethandlerdata(&attr, newhandlerdata(h, offset, -1)); upb_handlers_setstartseq(h, f, startseq_handler, &attr); upb_handlerattr_uninit(&attr); @@ -534,7 +558,7 @@ static void add_handlers_for_repeated_field(upb_handlers *h, } case UPB_TYPE_MESSAGE: { upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER; - upb_handlerattr_sethandlerdata(&attr, newsubmsghandlerdata(h, 0, f)); + upb_handlerattr_sethandlerdata(&attr, newsubmsghandlerdata(h, 0, -1, f)); upb_handlers_setstartsubmsg(h, f, appendsubmsg_handler, &attr); upb_handlerattr_uninit(&attr); break; @@ -545,7 +569,15 @@ static void add_handlers_for_repeated_field(upb_handlers *h, // Set up handlers for a singular field. static void add_handlers_for_singular_field(upb_handlers *h, const upb_fielddef *f, - size_t offset) { + size_t offset, + size_t hasbit_off) { + // The offset we pass to UPB points to the start of the Message, + // rather than the start of where our data is stored. + int32_t hasbit = -1; + if (hasbit_off != MESSAGE_FIELD_NO_HASBIT) { + hasbit = hasbit_off + sizeof(MessageHeader) * 8; + } + switch (upb_fielddef_type(f)) { case UPB_TYPE_BOOL: case UPB_TYPE_INT32: @@ -555,13 +587,13 @@ static void add_handlers_for_singular_field(upb_handlers *h, case UPB_TYPE_INT64: case UPB_TYPE_UINT64: case UPB_TYPE_DOUBLE: - upb_msg_setscalarhandler(h, f, offset, -1); + upb_msg_setscalarhandler(h, f, offset, hasbit); break; case UPB_TYPE_STRING: case UPB_TYPE_BYTES: { bool is_bytes = upb_fielddef_type(f) == UPB_TYPE_BYTES; upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER; - upb_handlerattr_sethandlerdata(&attr, newhandlerdata(h, offset)); + upb_handlerattr_sethandlerdata(&attr, newhandlerdata(h, offset, hasbit)); upb_handlers_setstartstr(h, f, is_bytes ? bytes_handler : str_handler, &attr); @@ -572,7 +604,9 @@ static void add_handlers_for_singular_field(upb_handlers *h, } case UPB_TYPE_MESSAGE: { upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER; - upb_handlerattr_sethandlerdata(&attr, newsubmsghandlerdata(h, offset, f)); + upb_handlerattr_sethandlerdata(&attr, + newsubmsghandlerdata(h, offset, + hasbit, f)); upb_handlers_setstartsubmsg(h, f, submsg_handler, &attr); upb_handlerattr_uninit(&attr); break; @@ -610,10 +644,12 @@ static void add_handlers_for_mapentry(const upb_msgdef* msgdef, add_handlers_for_singular_field( h, key_field, - offsetof(map_parse_frame_t, key_storage)); + offsetof(map_parse_frame_t, key_storage), + MESSAGE_FIELD_NO_HASBIT); add_handlers_for_singular_field( h, value_field, - offsetof(map_parse_frame_t, value_storage)); + offsetof(map_parse_frame_t, value_storage), + MESSAGE_FIELD_NO_HASBIT); } // Set up handlers for a oneof field. @@ -718,7 +754,8 @@ static void add_handlers_for_message(const void *closure, upb_handlers *h) { } else if (upb_fielddef_isseq(f)) { add_handlers_for_repeated_field(h, f, offset); } else { - add_handlers_for_singular_field(h, f, offset); + add_handlers_for_singular_field( + h, f, offset, desc->layout->fields[upb_fielddef_index(f)].hasbit); } } } @@ -901,11 +938,6 @@ VALUE Message_decode_json(VALUE klass, VALUE data) { /* msgvisitor *****************************************************************/ -// TODO: If/when we support proto2 semantics in addition to the current proto3 -// semantics, which means that we have true field presence, we will want to -// modify msgvisitor so that it emits all present fields rather than all -// non-default-value fields. - static void putmsg(VALUE msg, const Descriptor* desc, upb_sink *sink, int depth, bool emit_defaults); @@ -962,6 +994,7 @@ static void putary(VALUE ary, const upb_fielddef *f, upb_sink *sink, int size; if (ary == Qnil) return; + if (!emit_defaults && NUM2INT(RepeatedField_length(ary)) == 0) return; size = NUM2INT(RepeatedField_length(ary)); if (size == 0 && !emit_defaults) return; @@ -1062,6 +1095,8 @@ static void putmap(VALUE map, const upb_fielddef *f, upb_sink *sink, Map_iter it; if (map == Qnil) return; + if (!emit_defaults && Map_length(map) == 0) return; + self = ruby_to_Map(map); upb_sink_startseq(sink, getsel(f, UPB_HANDLER_STARTSEQ), &subsink); @@ -1144,7 +1179,15 @@ static void putmsg(VALUE msg_rb, const Descriptor* desc, } } else if (upb_fielddef_isstring(f)) { VALUE str = DEREF(msg, offset, VALUE); - if (is_matching_oneof || emit_defaults || RSTRING_LEN(str) > 0) { + bool is_default = false; + + if (upb_msgdef_syntax(desc->msgdef) == UPB_SYNTAX_PROTO2) { + is_default = layout_has(desc->layout, Message_data(msg), f) == Qfalse; + } else if (upb_msgdef_syntax(desc->msgdef) == UPB_SYNTAX_PROTO3) { + is_default = RSTRING_LEN(str) == 0; + } + + if (is_matching_oneof || emit_defaults || !is_default) { putstr(str, f, sink); } } else if (upb_fielddef_issubmsg(f)) { @@ -1152,13 +1195,19 @@ static void putmsg(VALUE msg_rb, const Descriptor* desc, } 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 (is_matching_oneof || emit_defaults || value != default_value) { \ - upb_sink_put##upbtype(sink, sel, value); \ - } \ - } \ +#define T(upbtypeconst, upbtype, ctype, default_value) \ + case upbtypeconst: { \ + ctype value = DEREF(msg, offset, ctype); \ + bool is_default = false; \ + if (upb_fielddef_haspresence(f)) { \ + is_default = layout_has(desc->layout, Message_data(msg), f) == Qfalse; \ + } else if (upb_msgdef_syntax(desc->msgdef) == UPB_SYNTAX_PROTO3) { \ + is_default = default_value == value; \ + } \ + if (is_matching_oneof || emit_defaults || !is_default) { \ + upb_sink_put##upbtype(sink, sel, value); \ + } \ + } \ break; switch (upb_fielddef_type(f)) { diff --git a/ruby/ext/google/protobuf_c/message.c b/ruby/ext/google/protobuf_c/message.c index 721c1112..81a78276 100644 --- a/ruby/ext/google/protobuf_c/message.c +++ b/ruby/ext/google/protobuf_c/message.c @@ -79,7 +79,7 @@ VALUE Message_alloc(VALUE klass) { return ret; } -static VALUE which_oneof_field(MessageHeader* self, const upb_oneofdef* o) { +static const upb_fielddef* which_oneof_field(MessageHeader* self, const upb_oneofdef* o) { upb_oneof_iter it; size_t case_ofs; uint32_t oneof_case; @@ -88,7 +88,7 @@ static VALUE which_oneof_field(MessageHeader* self, const upb_oneofdef* o) { // If no fields in the oneof, always nil. if (upb_oneofdef_numfields(o) == 0) { - return Qnil; + return NULL; } // Grab the first field in the oneof so we can get its layout info to find the // oneof_case field. @@ -103,22 +103,83 @@ static VALUE which_oneof_field(MessageHeader* self, const upb_oneofdef* o) { oneof_case = *((uint32_t*)((char*)Message_data(self) + case_ofs)); if (oneof_case == ONEOF_CASE_NONE) { - return Qnil; + return NULL; } // oneof_case is a field index, so find that field. f = upb_oneofdef_itof(o, oneof_case); assert(f != NULL); - return ID2SYM(rb_intern(upb_fielddef_name(f))); + return f; +} + +enum { + METHOD_UNKNOWN = 0, + METHOD_GETTER = 1, + METHOD_SETTER = 2, + METHOD_CLEAR = 3, + METHOD_PRESENCE = 4 +}; + +static int extract_method_call(VALUE method_name, MessageHeader* self, + const upb_fielddef **f, const upb_oneofdef **o) { + Check_Type(method_name, T_SYMBOL); + + VALUE method_str = rb_id2str(SYM2ID(method_name)); + char* name = RSTRING_PTR(method_str); + size_t name_len = RSTRING_LEN(method_str); + int accessor_type; + const upb_oneofdef* test_o; + const upb_fielddef* test_f; + + if (name[name_len - 1] == '=') { + accessor_type = METHOD_SETTER; + name_len--; + // We want to ensure if the proto has something named clear_foo or has_foo?, + // we don't strip the prefix. + } else if (strncmp("clear_", name, 6) == 0 && + !upb_msgdef_lookupname(self->descriptor->msgdef, name, name_len, + &test_f, &test_o)) { + accessor_type = METHOD_CLEAR; + name = name + 6; + name_len = name_len - 6; + } else if (strncmp("has_", name, 4) == 0 && name[name_len - 1] == '?' && + !upb_msgdef_lookupname(self->descriptor->msgdef, name, name_len, + &test_f, &test_o)) { + accessor_type = METHOD_PRESENCE; + name = name + 4; + name_len = name_len - 5; + } else { + accessor_type = METHOD_GETTER; + } + + // Verify the name corresponds to a oneof or field in this message. + if (!upb_msgdef_lookupname(self->descriptor->msgdef, name, name_len, + &test_f, &test_o)) { + return METHOD_UNKNOWN; + } + + // Method calls like 'has_foo?' are not allowed if field "foo" does not have + // a hasbit (e.g. repeated fields or non-message type fields for proto3 + // syntax). + if (accessor_type == METHOD_PRESENCE && test_f != NULL && + !upb_fielddef_haspresence(test_f)) { + return METHOD_UNKNOWN; + } + + *o = test_o; + *f = test_f; + return accessor_type; } /* * call-seq: * Message.method_missing(*args) * - * Provides accessors and setters for message fields according to their field - * names. For any field whose name does not conflict with a built-in method, an + * Provides accessors and setters and methods to clear and check for presence of + * message fields according to their field names. + * + * For any field whose name does not conflict with a built-in method, an * accessor is provided with the same name as the field, and a setter is * provided with the name of the field plus the '=' suffix. Thus, given a * message instance 'msg' with field 'foo', the following code is valid: @@ -129,13 +190,17 @@ static VALUE which_oneof_field(MessageHeader* self, const upb_oneofdef* o) { * This method also provides read-only accessors for oneofs. If a oneof exists * with name 'my_oneof', then msg.my_oneof will return a Ruby symbol equal to * the name of the field in that oneof that is currently set, or nil if none. + * + * It also provides methods of the form 'clear_fieldname' to clear the value + * of the field 'fieldname'. For basic data types, this will set the default + * value of the field. + * + * Additionally, it provides methods of the form 'has_fieldname?', which returns + * true if the field 'fieldname' is set in the message object, else false. For + * 'proto3' syntax, calling this for a basic type field will result in an error. */ VALUE Message_method_missing(int argc, VALUE* argv, VALUE _self) { MessageHeader* self; - VALUE method_name, method_str; - char* name; - size_t name_len; - bool setter; const upb_oneofdef* o; const upb_fielddef* f; @@ -143,54 +208,54 @@ VALUE Message_method_missing(int argc, VALUE* argv, VALUE _self) { if (argc < 1) { rb_raise(rb_eArgError, "Expected method name as first argument."); } - method_name = argv[0]; - if (!SYMBOL_P(method_name)) { - rb_raise(rb_eArgError, "Expected symbol as method name."); - } - method_str = rb_id2str(SYM2ID(method_name)); - name = RSTRING_PTR(method_str); - name_len = RSTRING_LEN(method_str); - setter = false; - // Setters have names that end in '='. - if (name[name_len - 1] == '=') { - setter = true; - name_len--; - } - - // See if this name corresponds to either a oneof or field in this message. - if (!upb_msgdef_lookupname(self->descriptor->msgdef, name, name_len, &f, - &o)) { + int accessor_type = extract_method_call(argv[0], self, &f, &o); + if (accessor_type == METHOD_UNKNOWN || (o == NULL && f == NULL) ) { return rb_call_super(argc, argv); + } else if (accessor_type == METHOD_SETTER) { + if (argc != 2) { + rb_raise(rb_eArgError, "Expected 2 arguments, received %d", argc); + } + } else if (argc != 1) { + rb_raise(rb_eArgError, "Expected 1 argument, received %d", argc); } + // Return which of the oneof fields are set if (o != NULL) { - // This is a oneof -- return which field inside the oneof is set. - if (setter) { + if (accessor_type == METHOD_SETTER) { rb_raise(rb_eRuntimeError, "Oneof accessors are read-only."); } - return which_oneof_field(self, o); - } else { - // This is a field -- get or set the field's value. - assert(f); - if (setter) { - if (argc < 2) { - rb_raise(rb_eArgError, "No value provided to setter."); + + const upb_fielddef* oneof_field = which_oneof_field(self, o); + if (accessor_type == METHOD_PRESENCE) { + return oneof_field == NULL ? Qfalse : Qtrue; + } else if (accessor_type == METHOD_CLEAR) { + if (oneof_field != NULL) { + layout_clear(self->descriptor->layout, Message_data(self), oneof_field); } - layout_set(self->descriptor->layout, Message_data(self), f, argv[1]); return Qnil; } else { - return layout_get(self->descriptor->layout, Message_data(self), f); + // METHOD_ACCESSOR + return oneof_field == NULL ? Qnil : + ID2SYM(rb_intern(upb_fielddef_name(oneof_field))); } + // Otherwise we're operating on a single proto field + } else if (accessor_type == METHOD_SETTER) { + layout_set(self->descriptor->layout, Message_data(self), f, argv[1]); + return Qnil; + } else if (accessor_type == METHOD_CLEAR) { + layout_clear(self->descriptor->layout, Message_data(self), f); + return Qnil; + } else if (accessor_type == METHOD_PRESENCE) { + return layout_has(self->descriptor->layout, Message_data(self), f); + } else { + return layout_get(self->descriptor->layout, Message_data(self), f); } } + VALUE Message_respond_to_missing(int argc, VALUE* argv, VALUE _self) { MessageHeader* self; - VALUE method_name, method_str; - char* name; - size_t name_len; - bool setter; const upb_oneofdef* o; const upb_fielddef* f; @@ -198,30 +263,15 @@ VALUE Message_respond_to_missing(int argc, VALUE* argv, VALUE _self) { if (argc < 1) { rb_raise(rb_eArgError, "Expected method name as first argument."); } - method_name = argv[0]; - if (!SYMBOL_P(method_name)) { - rb_raise(rb_eArgError, "Expected symbol as method name."); - } - method_str = rb_id2str(SYM2ID(method_name)); - name = RSTRING_PTR(method_str); - name_len = RSTRING_LEN(method_str); - setter = false; - - // Setters have names that end in '='. - if (name[name_len - 1] == '=') { - setter = true; - name_len--; - } - // See if this name corresponds to either a oneof or field in this message. - if (!upb_msgdef_lookupname(self->descriptor->msgdef, name, name_len, &f, - &o)) { + int accessor_type = extract_method_call(argv[0], self, &f, &o); + if (accessor_type == METHOD_UNKNOWN) { return rb_call_super(argc, argv); + } else if (o != NULL) { + return accessor_type == METHOD_SETTER ? Qfalse : Qtrue; + } else { + return Qtrue; } - if (o != NULL) { - return setter ? Qfalse : Qtrue; - } - return Qtrue; } VALUE create_submsg_from_hash(const upb_fielddef *f, VALUE hash) { @@ -444,13 +494,25 @@ VALUE Message_to_h(VALUE _self) { !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 (upb_fielddef_ismap(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++) { @@ -458,6 +520,7 @@ VALUE Message_to_h(VALUE _self) { 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); diff --git a/ruby/ext/google/protobuf_c/protobuf.c b/ruby/ext/google/protobuf_c/protobuf.c index fe6bb406..fd964c78 100644 --- a/ruby/ext/google/protobuf_c/protobuf.c +++ b/ruby/ext/google/protobuf_c/protobuf.c @@ -91,12 +91,14 @@ void Init_protobuf_c() { descriptor_instancevar_interned = rb_intern(kDescriptorInstanceVar); DescriptorPool_register(protobuf); Descriptor_register(protobuf); + FileDescriptor_register(protobuf); FieldDescriptor_register(protobuf); OneofDescriptor_register(protobuf); EnumDescriptor_register(protobuf); MessageBuilderContext_register(internal); OneofBuilderContext_register(internal); EnumBuilderContext_register(internal); + FileBuilderContext_register(internal); Builder_register(internal); RepeatedField_register(protobuf); Map_register(protobuf); diff --git a/ruby/ext/google/protobuf_c/protobuf.h b/ruby/ext/google/protobuf_c/protobuf.h index 3e5c0520..0aa385a0 100644 --- a/ruby/ext/google/protobuf_c/protobuf.h +++ b/ruby/ext/google/protobuf_c/protobuf.h @@ -40,6 +40,7 @@ // Forward decls. struct DescriptorPool; struct Descriptor; +struct FileDescriptor; struct FieldDescriptor; struct EnumDescriptor; struct MessageLayout; @@ -47,10 +48,12 @@ struct MessageField; struct MessageHeader; struct MessageBuilderContext; struct EnumBuilderContext; +struct FileBuilderContext; struct Builder; typedef struct DescriptorPool DescriptorPool; typedef struct Descriptor Descriptor; +typedef struct FileDescriptor FileDescriptor; typedef struct FieldDescriptor FieldDescriptor; typedef struct OneofDescriptor OneofDescriptor; typedef struct EnumDescriptor EnumDescriptor; @@ -60,6 +63,7 @@ typedef struct MessageHeader MessageHeader; typedef struct MessageBuilderContext MessageBuilderContext; typedef struct OneofBuilderContext OneofBuilderContext; typedef struct EnumBuilderContext EnumBuilderContext; +typedef struct FileBuilderContext FileBuilderContext; typedef struct Builder Builder; /* @@ -118,6 +122,10 @@ struct Descriptor { const upb_handlers* json_serialize_handlers_preserve; }; +struct FileDescriptor { + const upb_filedef* filedef; +}; + struct FieldDescriptor { const upb_fielddef* fielddef; }; @@ -145,18 +153,27 @@ struct EnumBuilderContext { VALUE enumdesc; }; +struct FileBuilderContext { + VALUE pending_list; + VALUE file_descriptor; + VALUE builder; +}; + struct Builder { VALUE pending_list; + VALUE default_file_descriptor; upb_def** defs; // used only while finalizing }; extern VALUE cDescriptorPool; extern VALUE cDescriptor; +extern VALUE cFileDescriptor; extern VALUE cFieldDescriptor; extern VALUE cEnumDescriptor; extern VALUE cMessageBuilderContext; extern VALUE cOneofBuilderContext; extern VALUE cEnumBuilderContext; +extern VALUE cFileBuilderContext; extern VALUE cBuilder; extern VALUE cError; @@ -175,7 +192,7 @@ VALUE DescriptorPool_alloc(VALUE klass); void DescriptorPool_register(VALUE module); DescriptorPool* ruby_to_DescriptorPool(VALUE value); VALUE DescriptorPool_add(VALUE _self, VALUE def); -VALUE DescriptorPool_build(VALUE _self); +VALUE DescriptorPool_build(int argc, VALUE* argv, VALUE _self); VALUE DescriptorPool_lookup(VALUE _self, VALUE name); VALUE DescriptorPool_generated_pool(VALUE _self); @@ -184,6 +201,7 @@ void Descriptor_free(void* _self); VALUE Descriptor_alloc(VALUE klass); void Descriptor_register(VALUE module); Descriptor* ruby_to_Descriptor(VALUE value); +VALUE Descriptor_initialize(VALUE _self, VALUE file_descriptor_rb); VALUE Descriptor_name(VALUE _self); VALUE Descriptor_name_set(VALUE _self, VALUE str); VALUE Descriptor_each(VALUE _self); @@ -193,8 +211,19 @@ VALUE Descriptor_add_oneof(VALUE _self, VALUE obj); VALUE Descriptor_each_oneof(VALUE _self); VALUE Descriptor_lookup_oneof(VALUE _self, VALUE name); VALUE Descriptor_msgclass(VALUE _self); +VALUE Descriptor_file_descriptor(VALUE _self); extern const rb_data_type_t _Descriptor_type; +void FileDescriptor_mark(void* _self); +void FileDescriptor_free(void* _self); +VALUE FileDescriptor_alloc(VALUE klass); +void FileDescriptor_register(VALUE module); +FileDescriptor* ruby_to_FileDescriptor(VALUE value); +VALUE FileDescriptor_initialize(int argc, VALUE* argv, VALUE _self); +VALUE FileDescriptor_name(VALUE _self); +VALUE FileDescriptor_syntax(VALUE _self); +VALUE FileDescriptor_syntax_set(VALUE _self, VALUE syntax); + void FieldDescriptor_mark(void* _self); void FieldDescriptor_free(void* _self); VALUE FieldDescriptor_alloc(VALUE klass); @@ -204,6 +233,8 @@ VALUE FieldDescriptor_name(VALUE _self); VALUE FieldDescriptor_name_set(VALUE _self, VALUE str); VALUE FieldDescriptor_type(VALUE _self); VALUE FieldDescriptor_type_set(VALUE _self, VALUE type); +VALUE FieldDescriptor_default(VALUE _self); +VALUE FieldDescriptor_default_set(VALUE _self, VALUE default_value); VALUE FieldDescriptor_label(VALUE _self); VALUE FieldDescriptor_label_set(VALUE _self, VALUE label); VALUE FieldDescriptor_number(VALUE _self); @@ -211,6 +242,8 @@ VALUE FieldDescriptor_number_set(VALUE _self, VALUE number); VALUE FieldDescriptor_submsg_name(VALUE _self); VALUE FieldDescriptor_submsg_name_set(VALUE _self, VALUE value); VALUE FieldDescriptor_subtype(VALUE _self); +VALUE FieldDescriptor_has(VALUE _self, VALUE msg_rb); +VALUE FieldDescriptor_clear(VALUE _self, VALUE msg_rb); VALUE FieldDescriptor_get(VALUE _self, VALUE msg_rb); VALUE FieldDescriptor_set(VALUE _self, VALUE msg_rb, VALUE value); upb_fieldtype_t ruby_to_fieldtype(VALUE type); @@ -231,6 +264,8 @@ void EnumDescriptor_free(void* _self); VALUE EnumDescriptor_alloc(VALUE klass); void EnumDescriptor_register(VALUE module); EnumDescriptor* ruby_to_EnumDescriptor(VALUE value); +VALUE EnumDescriptor_initialize(VALUE _self, VALUE file_descriptor_rb); +VALUE EnumDescriptor_file_descriptor(VALUE _self); VALUE EnumDescriptor_name(VALUE _self); VALUE EnumDescriptor_name_set(VALUE _self, VALUE str); VALUE EnumDescriptor_add_value(VALUE _self, VALUE name, VALUE number); @@ -272,12 +307,23 @@ EnumBuilderContext* ruby_to_EnumBuilderContext(VALUE value); VALUE EnumBuilderContext_initialize(VALUE _self, VALUE enumdesc); VALUE EnumBuilderContext_value(VALUE _self, VALUE name, VALUE number); +void FileBuilderContext_mark(void* _self); +void FileBuilderContext_free(void* _self); +VALUE FileBuilderContext_alloc(VALUE klass); +void FileBuilderContext_register(VALUE module); +VALUE FileBuilderContext_initialize(VALUE _self, VALUE file_descriptor, + VALUE builder); +VALUE FileBuilderContext_add_message(VALUE _self, VALUE name); +VALUE FileBuilderContext_add_enum(VALUE _self, VALUE name); +VALUE FileBuilderContext_pending_descriptors(VALUE _self); + void Builder_mark(void* _self); void Builder_free(void* _self); VALUE Builder_alloc(VALUE klass); void Builder_register(VALUE module); Builder* ruby_to_Builder(VALUE value); VALUE Builder_initialize(VALUE _self); +VALUE Builder_add_file(int argc, VALUE *argv, VALUE _self); VALUE Builder_add_message(VALUE _self, VALUE name); VALUE Builder_add_enum(VALUE _self, VALUE name); VALUE Builder_finalize_to_pool(VALUE _self, VALUE pool_rb); @@ -443,10 +489,12 @@ VALUE Map_iter_value(Map_iter* iter); // ----------------------------------------------------------------------------- #define MESSAGE_FIELD_NO_CASE ((size_t)-1) +#define MESSAGE_FIELD_NO_HASBIT ((size_t)-1) struct MessageField { size_t offset; size_t case_offset; // for oneofs, a uint32. Else, MESSAGE_FIELD_NO_CASE. + size_t hasbit; }; struct MessageLayout { @@ -457,6 +505,9 @@ struct MessageLayout { MessageLayout* create_layout(const upb_msgdef* msgdef); void free_layout(MessageLayout* layout); +bool field_contains_hasbit(MessageLayout* layout, + const upb_fielddef* field); +VALUE layout_get_default(const upb_fielddef* field); VALUE layout_get(MessageLayout* layout, const void* storage, const upb_fielddef* field); @@ -464,6 +515,12 @@ void layout_set(MessageLayout* layout, void* storage, const upb_fielddef* field, VALUE val); +VALUE layout_has(MessageLayout* layout, + const void* storage, + const upb_fielddef* field); +void layout_clear(MessageLayout* layout, + const void* storage, + const upb_fielddef* field); void layout_init(MessageLayout* layout, void* storage); void layout_mark(MessageLayout* layout, void* storage); void layout_dup(MessageLayout* layout, void* to, void* from); 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; } } |