diff options
Diffstat (limited to 'ruby')
-rw-r--r-- | ruby/README.md | 84 | ||||
-rw-r--r-- | ruby/ext/google/protobuf_c/defs.c | 307 | ||||
-rw-r--r-- | ruby/ext/google/protobuf_c/encode_decode.c | 233 | ||||
-rw-r--r-- | ruby/ext/google/protobuf_c/map.c | 2 | ||||
-rw-r--r-- | ruby/ext/google/protobuf_c/message.c | 44 | ||||
-rw-r--r-- | ruby/ext/google/protobuf_c/protobuf.c | 2 | ||||
-rw-r--r-- | ruby/ext/google/protobuf_c/protobuf.h | 63 | ||||
-rw-r--r-- | ruby/ext/google/protobuf_c/repeated_field.c | 59 | ||||
-rw-r--r-- | ruby/ext/google/protobuf_c/storage.c | 296 | ||||
-rw-r--r-- | ruby/ext/google/protobuf_c/upb.c | 1091 | ||||
-rw-r--r-- | ruby/ext/google/protobuf_c/upb.h | 662 | ||||
-rw-r--r-- | ruby/google-protobuf.gemspec | 6 | ||||
-rw-r--r-- | ruby/tests/basic.rb | 116 | ||||
-rw-r--r-- | ruby/tests/generated_code.proto | 67 | ||||
-rw-r--r-- | ruby/tests/generated_code.rb | 74 | ||||
-rw-r--r-- | ruby/tests/generated_code_test.rb | 17 |
16 files changed, 2763 insertions, 360 deletions
diff --git a/ruby/README.md b/ruby/README.md index c966a103..8331d286 100644 --- a/ruby/README.md +++ b/ruby/README.md @@ -7,8 +7,51 @@ we recommend using protoc's Ruby generation support with .proto files. The build process in this directory only installs the extension; you need to install protoc as well to have Ruby code generation functionality. -Installation ------------- +Installation from Gem +--------------------- + +When we release a version of Protocol Buffers, we will upload a Gem to +[RubyGems](https://www.rubygems.org/). To use this pre-packaged gem, simply +install it as you would any other gem: + + $ gem install [--prerelease] google-protobuf + +The `--pre` flag is necessary if we have not yet made a non-alpha/beta release +of the Ruby extension; it allows `gem` to consider these "pre-release" +alpha/beta versions. + +Once the gem is installed, you may or may not need `protoc`. If you write your +message type descriptions directly in the Ruby DSL, you do not need it. +However, if you wish to generate the Ruby DSL from a `.proto` file, you will +also want to install Protocol Buffers itself, as described in this repository's +main `README` file. The version of `protoc` included in the latest release +supports the `--ruby_out` option to generate Ruby code. + +A simple example of using the Ruby extension follows. More extensive +documentation may be found in the RubyDoc comments (`call-seq` tags) in the +source, and we plan to release separate, more detailed, documentation at a +later date. + + require 'google/protobuf' + + # generated from my_proto_types.proto with protoc: + # $ protoc --ruby_out=. my_proto_types.proto + require 'my_proto_types' + + mymessage = MyTestMessage.new(:field1 => 42, :field2 => ["a", "b", "c"]) + mymessage.field1 = 43 + mymessage.field2.push("d") + mymessage.field3 = SubMessage.new(:foo => 100) + + encoded_data = MyTestMessage.encode(mymessage) + decoded = MyTestMessage.decode(encoded_data) + assert decoded == mymessage + + puts "JSON:" + puts MyTestMessage.encode_json(mymessage) + +Installation from Source (Building Gem) +--------------------------------------- To build this Ruby extension, you will need: @@ -16,15 +59,8 @@ To build this Ruby extension, you will need: * Bundler * Ruby development headers * a C compiler -* the upb submodule - -First, ensure that upb/ is checked out: - $ cd .. # top level protobuf directory - $ git submodule init - $ git submodule update - -Then install the required Ruby gems: +First, install the required Ruby gems: $ sudo gem install bundler rake rake-compiler rspec rubygems-tasks @@ -32,3 +68,31 @@ Then build the Gem: $ rake gem $ gem install pkg/protobuf-$VERSION.gem + +This gem includes the upb parsing and serialization library as a single-file +amalgamation. It is up-to-date with upb git commit +`535bc2fe2f2b467f59347ffc9449e11e47791257`. + +Version Number Scheme +--------------------- + +We are using a version number scheme that is a hybrid of Protocol Buffers' +overall version number and some Ruby-specific rules. Gem does not allow +re-uploads of a gem with the same version number, so we add a sequence number +("upload version") to the version. We also format alphabetical tags (alpha, +pre, ...) slightly differently, and we avoid hyphens. In more detail: + +* First, we determine the prefix: a Protocol Buffers version "3.0.0-alpha-2" + becomes "3.0.0.alpha.2". When we release 3.0.0, this prefix will be simply + "3.0.0". +* We then append the upload version: "3.0.0.alpha.2.0" or "3.0.0.0". If we need + to upload a new version of the gem to fix an issue, the version becomes + "3.0.0.alpha.2.1" or "3.0.0.1". +* If we are working on a prerelease version, we append a prerelease tag: + "3.0.0.alpha.3.0.pre". The prerelease tag comes at the end so that when + version numbers are sorted, any prerelease builds are ordered between the + prior version and current version. + +These rules are designed to work with the sorting rules for +[Gem::Version](http://ruby-doc.org/stdlib-2.0/libdoc/rubygems/rdoc/Gem/Version.html): +release numbers should sort in actual release order. diff --git a/ruby/ext/google/protobuf_c/defs.c b/ruby/ext/google/protobuf_c/defs.c index a18aaac4..b39c27f4 100644 --- a/ruby/ext/google/protobuf_c/defs.c +++ b/ruby/ext/google/protobuf_c/defs.c @@ -58,11 +58,15 @@ static upb_def* check_notfrozen(const upb_def* def) { } static upb_msgdef* check_msg_notfrozen(const upb_msgdef* def) { - return (upb_msgdef*)check_notfrozen((const upb_def*)def); + return upb_downcast_msgdef_mutable(check_notfrozen((const upb_def*)def)); } static upb_fielddef* check_field_notfrozen(const upb_fielddef* def) { - return (upb_fielddef*)check_notfrozen((const upb_def*)def); + return upb_downcast_fielddef_mutable(check_notfrozen((const upb_def*)def)); +} + +static upb_oneofdef* check_oneof_notfrozen(const upb_oneofdef* def) { + return (upb_oneofdef*)check_notfrozen((const upb_def*)def); } static upb_enumdef* check_enum_notfrozen(const upb_enumdef* def) { @@ -282,6 +286,9 @@ void Descriptor_register(VALUE module) { 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); + rb_define_method(klass, "add_oneof", Descriptor_add_oneof, 1); + rb_define_method(klass, "each_oneof", Descriptor_each_oneof, 0); + rb_define_method(klass, "lookup_oneof", Descriptor_lookup_oneof, 1); 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); @@ -328,10 +335,10 @@ VALUE Descriptor_name_set(VALUE _self, VALUE str) { VALUE Descriptor_each(VALUE _self) { DEFINE_SELF(Descriptor, self, _self); - upb_msg_iter it; - for (upb_msg_begin(&it, self->msgdef); - !upb_msg_done(&it); - upb_msg_next(&it)) { + upb_msg_field_iter it; + for (upb_msg_field_begin(&it, self->msgdef); + !upb_msg_field_done(&it); + upb_msg_field_next(&it)) { const upb_fielddef* field = upb_msg_iter_field(&it); VALUE obj = get_def_obj(field); rb_yield(obj); @@ -360,7 +367,7 @@ VALUE Descriptor_lookup(VALUE _self, VALUE name) { * call-seq: * Descriptor.add_field(field) => nil * - * Adds the given FieldDescriptor to this message type. The descriptor must not + * Adds the given FieldDescriptor to this message type. This descriptor must not * have been added to a pool yet. Raises an exception if a field with the same * name or number already exists. Sub-type references (e.g. for fields of type * message) are not resolved at this point. @@ -379,6 +386,67 @@ VALUE Descriptor_add_field(VALUE _self, VALUE obj) { /* * call-seq: + * Descriptor.add_oneof(oneof) => nil + * + * Adds the given OneofDescriptor to this message type. This descriptor must not + * have been added to a pool yet. Raises an exception if a oneof with the same + * name already exists, or if any of the oneof's fields' names or numbers + * conflict with an existing field in this message type. All fields in the oneof + * are added to the message descriptor. Sub-type references (e.g. for fields of + * type message) are not resolved at this point. + */ +VALUE Descriptor_add_oneof(VALUE _self, VALUE obj) { + DEFINE_SELF(Descriptor, self, _self); + upb_msgdef* mut_def = check_msg_notfrozen(self->msgdef); + OneofDescriptor* def = ruby_to_OneofDescriptor(obj); + upb_oneofdef* mut_oneof_def = check_oneof_notfrozen(def->oneofdef); + CHECK_UPB( + upb_msgdef_addoneof(mut_def, mut_oneof_def, NULL, &status), + "Adding oneof to Descriptor failed"); + add_def_obj(def->oneofdef, obj); + return Qnil; +} + +/* + * call-seq: + * Descriptor.each_oneof(&block) => nil + * + * Invokes the given block for each oneof in this message type, passing the + * corresponding OneofDescriptor. + */ +VALUE Descriptor_each_oneof(VALUE _self) { + DEFINE_SELF(Descriptor, self, _self); + + upb_msg_oneof_iter it; + for (upb_msg_oneof_begin(&it, self->msgdef); + !upb_msg_oneof_done(&it); + upb_msg_oneof_next(&it)) { + const upb_oneofdef* oneof = upb_msg_iter_oneof(&it); + VALUE obj = get_def_obj(oneof); + rb_yield(obj); + } + return Qnil; +} + +/* + * call-seq: + * Descriptor.lookup_oneof(name) => OneofDescriptor + * + * Returns the oneof descriptor for the oneof with the given name, if present, + * or nil if none. + */ +VALUE Descriptor_lookup_oneof(VALUE _self, VALUE name) { + DEFINE_SELF(Descriptor, self, _self); + const char* s = get_str(name); + const upb_oneofdef* oneof = upb_msgdef_ntooz(self->msgdef, s); + if (oneof == NULL) { + return Qnil; + } + return get_def_obj(oneof); +} + +/* + * call-seq: * Descriptor.msgclass => message_klass * * Returns the Ruby class created for this message type. Valid only once the @@ -744,6 +812,120 @@ VALUE FieldDescriptor_set(VALUE _self, VALUE msg_rb, VALUE value) { } // ----------------------------------------------------------------------------- +// OneofDescriptor. +// ----------------------------------------------------------------------------- + +DEFINE_CLASS(OneofDescriptor, "Google::Protobuf::OneofDescriptor"); + +void OneofDescriptor_mark(void* _self) { +} + +void OneofDescriptor_free(void* _self) { + OneofDescriptor* self = _self; + upb_oneofdef_unref(self->oneofdef, &self->oneofdef); + xfree(self); +} + +/* + * call-seq: + * OneofDescriptor.new => oneof_descriptor + * + * Creates a new, empty, oneof descriptor. The oneof may only be modified prior + * to being added to a message descriptor which is subsequently added to a pool. + */ +VALUE OneofDescriptor_alloc(VALUE klass) { + OneofDescriptor* self = ALLOC(OneofDescriptor); + VALUE ret = TypedData_Wrap_Struct(klass, &_OneofDescriptor_type, self); + self->oneofdef = upb_oneofdef_new(&self->oneofdef); + return ret; +} + +void OneofDescriptor_register(VALUE module) { + VALUE klass = rb_define_class_under( + module, "OneofDescriptor", rb_cObject); + rb_define_alloc_func(klass, OneofDescriptor_alloc); + rb_define_method(klass, "name", OneofDescriptor_name, 0); + rb_define_method(klass, "name=", OneofDescriptor_name_set, 1); + rb_define_method(klass, "add_field", OneofDescriptor_add_field, 1); + rb_define_method(klass, "each", OneofDescriptor_each, 0); + rb_include_module(klass, rb_mEnumerable); + cOneofDescriptor = klass; + rb_gc_register_address(&cOneofDescriptor); +} + +/* + * call-seq: + * OneofDescriptor.name => name + * + * Returns the name of this oneof. + */ +VALUE OneofDescriptor_name(VALUE _self) { + DEFINE_SELF(OneofDescriptor, self, _self); + return rb_str_maybe_null(upb_oneofdef_name(self->oneofdef)); +} + +/* + * call-seq: + * OneofDescriptor.name = name + * + * Sets a new name for this oneof. The oneof must not have been added to a + * message descriptor yet. + */ +VALUE OneofDescriptor_name_set(VALUE _self, VALUE value) { + DEFINE_SELF(OneofDescriptor, self, _self); + upb_oneofdef* mut_def = check_oneof_notfrozen(self->oneofdef); + const char* str = get_str(value); + CHECK_UPB(upb_oneofdef_setname(mut_def, str, &status), + "Error setting oneof name"); + return Qnil; +} + +/* + * call-seq: + * OneofDescriptor.add_field(field) => nil + * + * Adds a field to this oneof. The field may have been added to this oneof in + * the past, or the message to which this oneof belongs (if any), but may not + * have already been added to any other oneof or message. Otherwise, an + * exception is raised. + * + * All fields added to the oneof via this method will be automatically added to + * the message to which this oneof belongs, if it belongs to one currently, or + * else will be added to any message to which the oneof is later added at the + * time that it is added. + */ +VALUE OneofDescriptor_add_field(VALUE _self, VALUE obj) { + DEFINE_SELF(OneofDescriptor, self, _self); + upb_oneofdef* mut_def = check_oneof_notfrozen(self->oneofdef); + FieldDescriptor* def = ruby_to_FieldDescriptor(obj); + upb_fielddef* mut_field_def = check_field_notfrozen(def->fielddef); + CHECK_UPB( + upb_oneofdef_addfield(mut_def, mut_field_def, NULL, &status), + "Adding field to OneofDescriptor failed"); + add_def_obj(def->fielddef, obj); + return Qnil; +} + +/* + * call-seq: + * OneofDescriptor.each(&block) => nil + * + * Iterates through fields in this oneof, yielding to the block on each one. + */ +VALUE OneofDescriptor_each(VALUE _self, VALUE field) { + DEFINE_SELF(OneofDescriptor, self, _self); + upb_oneof_iter it; + for (upb_oneof_begin(&it, self->oneofdef); + !upb_oneof_done(&it); + upb_oneof_next(&it)) { + const upb_fielddef* f = upb_oneof_iter_field(&it); + VALUE obj = get_def_obj(f); + rb_yield(obj); + } + return Qnil; +} + +// ----------------------------------------------------------------------------- // EnumDescriptor. // ----------------------------------------------------------------------------- @@ -952,6 +1134,7 @@ void MessageBuilderContext_register(VALUE module) { rb_define_method(klass, "required", MessageBuilderContext_required, -1); rb_define_method(klass, "repeated", MessageBuilderContext_repeated, -1); rb_define_method(klass, "map", MessageBuilderContext_map, -1); + rb_define_method(klass, "oneof", MessageBuilderContext_oneof, 1); cMessageBuilderContext = klass; rb_gc_register_address(&cMessageBuilderContext); } @@ -1165,6 +1348,110 @@ VALUE MessageBuilderContext_map(int argc, VALUE* argv, VALUE _self) { return Qnil; } +/* + * call-seq: + * MessageBuilderContext.oneof(name, &block) => nil + * + * Creates a new OneofDescriptor with the given name, creates a + * OneofBuilderContext attached to that OneofDescriptor, evaluates the given + * block in the context of that OneofBuilderContext with #instance_eval, and + * then adds the oneof to the message. + * + * This is the recommended, idiomatic way to build oneof definitions. + */ +VALUE MessageBuilderContext_oneof(VALUE _self, VALUE name) { + DEFINE_SELF(MessageBuilderContext, self, _self); + VALUE oneofdef = rb_class_new_instance(0, NULL, cOneofDescriptor); + VALUE args[2] = { oneofdef, self->builder }; + VALUE ctx = rb_class_new_instance(2, args, cOneofBuilderContext); + VALUE block = rb_block_proc(); + VALUE name_str = rb_str_new2(rb_id2name(SYM2ID(name))); + rb_funcall(oneofdef, rb_intern("name="), 1, name_str); + rb_funcall_with_block(ctx, rb_intern("instance_eval"), 0, NULL, block); + Descriptor_add_oneof(self->descriptor, oneofdef); + + return Qnil; +} + +// ----------------------------------------------------------------------------- +// OneofBuilderContext. +// ----------------------------------------------------------------------------- + +DEFINE_CLASS(OneofBuilderContext, + "Google::Protobuf::Internal::OneofBuilderContext"); + +void OneofBuilderContext_mark(void* _self) { + OneofBuilderContext* self = _self; + rb_gc_mark(self->descriptor); + rb_gc_mark(self->builder); +} + +void OneofBuilderContext_free(void* _self) { + OneofBuilderContext* self = _self; + xfree(self); +} + +VALUE OneofBuilderContext_alloc(VALUE klass) { + OneofBuilderContext* self = ALLOC(OneofBuilderContext); + VALUE ret = TypedData_Wrap_Struct( + klass, &_OneofBuilderContext_type, self); + self->descriptor = Qnil; + self->builder = Qnil; + return ret; +} + +void OneofBuilderContext_register(VALUE module) { + VALUE klass = rb_define_class_under( + module, "OneofBuilderContext", rb_cObject); + rb_define_alloc_func(klass, OneofBuilderContext_alloc); + rb_define_method(klass, "initialize", + OneofBuilderContext_initialize, 2); + rb_define_method(klass, "optional", OneofBuilderContext_optional, -1); + cOneofBuilderContext = klass; + rb_gc_register_address(&cOneofBuilderContext); +} + +/* + * call-seq: + * OneofBuilderContext.new(desc, builder) => context + * + * Create a new oneof builder context around the given oneof descriptor and + * builder context. This class is intended to serve as a DSL context to be used + * with #instance_eval. + */ +VALUE OneofBuilderContext_initialize(VALUE _self, + VALUE oneofdef, + VALUE builder) { + DEFINE_SELF(OneofBuilderContext, self, _self); + self->descriptor = oneofdef; + self->builder = builder; + return Qnil; +} + +/* + * call-seq: + * OneofBuilderContext.optional(name, type, number, type_class = 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 + * (as accepted by FieldDescriptor#type=) and the type_class must be a string, + * if present (as accepted by FieldDescriptor#submsg_name=). + */ +VALUE OneofBuilderContext_optional(int argc, VALUE* argv, VALUE _self) { + DEFINE_SELF(OneofBuilderContext, self, _self); + + if (argc < 3) { + rb_raise(rb_eArgError, "Expected at least 3 arguments."); + } + VALUE name = argv[0]; + VALUE type = argv[1]; + VALUE number = argv[2]; + VALUE type_class = (argc > 3) ? argv[3] : Qnil; + + return msgdef_add_field(self->descriptor, "optional", + name, type, number, type_class); +} + // ----------------------------------------------------------------------------- // EnumBuilderContext. // ----------------------------------------------------------------------------- @@ -1322,8 +1609,10 @@ VALUE Builder_add_enum(VALUE _self, VALUE name) { static void validate_msgdef(const upb_msgdef* msgdef) { // Verify that no required fields exist. proto3 does not support these. - upb_msg_iter it; - for (upb_msg_begin(&it, msgdef); !upb_msg_done(&it); upb_msg_next(&it)) { + upb_msg_field_iter it; + 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_label(field) == UPB_LABEL_REQUIRED) { rb_raise(rb_eTypeError, "Required fields are unsupported in proto3."); diff --git a/ruby/ext/google/protobuf_c/encode_decode.c b/ruby/ext/google/protobuf_c/encode_decode.c index e5e1514b..5730504d 100644 --- a/ruby/ext/google/protobuf_c/encode_decode.c +++ b/ruby/ext/google/protobuf_c/encode_decode.c @@ -59,6 +59,36 @@ static const void *newsubmsghandlerdata(upb_handlers* h, uint32_t ofs, return hd; } +typedef struct { + size_t ofs; // union data slot + size_t case_ofs; // oneof_case field + uint32_t oneof_case_num; // oneof-case number to place in oneof_case field + const upb_msgdef *md; // msgdef, for oneof submessage handler +} oneof_handlerdata_t; + +static const void *newoneofhandlerdata(upb_handlers *h, + uint32_t ofs, + uint32_t case_ofs, + const upb_fielddef *f) { + oneof_handlerdata_t *hd = ALLOC(oneof_handlerdata_t); + hd->ofs = ofs; + hd->case_ofs = case_ofs; + // We reuse the field tag number as a oneof union discriminant tag. Note that + // we don't expose these numbers to the user, so the only requirement is that + // we have some unique ID for each union case/possibility. The field tag + // numbers are already present and are easy to use so there's no reason to + // create a separate ID space. In addition, using the field tag number here + // lets us easily look up the field in the oneof accessor. + hd->oneof_case_num = upb_fielddef_number(f); + if (upb_fielddef_type(f) == UPB_TYPE_MESSAGE) { + hd->md = upb_fielddef_msgsubdef(f); + } else { + hd->md = NULL; + } + upb_handlers_addcleanup(h, hd, free); + return hd; +} + // A handler that starts a repeated field. Gets the Repeated*Field instance for // this field (such an instance always exists even in an empty message). static void *startseq_handler(void* closure, const void* hd) { @@ -67,8 +97,7 @@ static void *startseq_handler(void* closure, const void* hd) { return (void*)DEREF(msg, *ofs, VALUE); } -// Handlers that append primitive values to a repeated field (a regular Ruby -// array for now). +// Handlers that append primitive values to a repeated field. #define DEFINE_APPEND_HANDLER(type, ctype) \ static bool append##type##_handler(void *closure, const void *hd, \ ctype val) { \ @@ -85,7 +114,7 @@ DEFINE_APPEND_HANDLER(int64, int64_t) DEFINE_APPEND_HANDLER(uint64, uint64_t) DEFINE_APPEND_HANDLER(double, double) -// Appends a string to a repeated field (a regular Ruby array for now). +// Appends a string to a repeated field. static void* appendstr_handler(void *closure, const void *hd, size_t size_hint) { @@ -96,7 +125,7 @@ static void* appendstr_handler(void *closure, return (void*)str; } -// Appends a 'bytes' string to a repeated field (a regular Ruby array for now). +// Appends a 'bytes' string to a repeated field. static void* appendbytes_handler(void *closure, const void *hd, size_t size_hint) { @@ -179,7 +208,11 @@ typedef struct { size_t ofs; upb_fieldtype_t key_field_type; upb_fieldtype_t value_field_type; - VALUE value_field_typeclass; + + // We know that we can hold this reference because the handlerdata has the + // same lifetime as the upb_handlers struct, and the upb_handlers struct holds + // a reference to the upb_msgdef, which in turn has references to its subdefs. + const upb_def* value_field_subdef; } map_handlerdata_t; // Temporary frame for map parsing: at the beginning of a map entry message, a @@ -219,8 +252,15 @@ static bool endmap_handler(void *closure, const void *hd, upb_status* s) { VALUE key = native_slot_get( mapdata->key_field_type, Qnil, &frame->key_storage); + + VALUE value_field_typeclass = Qnil; + if (mapdata->value_field_type == UPB_TYPE_MESSAGE || + mapdata->value_field_type == UPB_TYPE_ENUM) { + value_field_typeclass = get_def_obj(mapdata->value_field_subdef); + } + VALUE value = native_slot_get( - mapdata->value_field_type, mapdata->value_field_typeclass, + mapdata->value_field_type, value_field_typeclass, &frame->value_storage); Map_index_set(frame->map, key, value); @@ -251,21 +291,90 @@ static map_handlerdata_t* new_map_handlerdata( MAP_VALUE_FIELD); assert(value_field != NULL); hd->value_field_type = upb_fielddef_type(value_field); - hd->value_field_typeclass = field_type_class(value_field); - - // Ensure that value_field_typeclass is properly GC-rooted. We must do this - // because we hold a reference to the Ruby class in the handlerdata, which is - // owned by the handlers. The handlers are owned by *this* message's Ruby - // object, but each Ruby object is rooted independently at the def -> Ruby - // object map. So we have to ensure that the Ruby objects we depend on will - // stick around as long as we're around. - if (hd->value_field_typeclass != Qnil) { - rb_ary_push(desc->typeclass_references, hd->value_field_typeclass); - } + hd->value_field_subdef = upb_fielddef_subdef(value_field); return hd; } +// Handlers that set primitive values in oneofs. +#define DEFINE_ONEOF_HANDLER(type, ctype) \ + static bool oneof##type##_handler(void *closure, const void *hd, \ + ctype val) { \ + const oneof_handlerdata_t *oneofdata = hd; \ + DEREF(closure, oneofdata->case_ofs, uint32_t) = \ + oneofdata->oneof_case_num; \ + DEREF(closure, oneofdata->ofs, ctype) = val; \ + return true; \ + } + +DEFINE_ONEOF_HANDLER(bool, bool) +DEFINE_ONEOF_HANDLER(int32, int32_t) +DEFINE_ONEOF_HANDLER(uint32, uint32_t) +DEFINE_ONEOF_HANDLER(float, float) +DEFINE_ONEOF_HANDLER(int64, int64_t) +DEFINE_ONEOF_HANDLER(uint64, uint64_t) +DEFINE_ONEOF_HANDLER(double, double) + +#undef DEFINE_ONEOF_HANDLER + +// Handlers for strings in a oneof. +static void *oneofstr_handler(void *closure, + const void *hd, + size_t size_hint) { + MessageHeader* msg = closure; + const oneof_handlerdata_t *oneofdata = hd; + VALUE str = rb_str_new2(""); + rb_enc_associate(str, kRubyStringUtf8Encoding); + DEREF(msg, oneofdata->case_ofs, uint32_t) = + oneofdata->oneof_case_num; + DEREF(msg, oneofdata->ofs, VALUE) = str; + return (void*)str; +} + +static void *oneofbytes_handler(void *closure, + const void *hd, + size_t size_hint) { + MessageHeader* msg = closure; + const oneof_handlerdata_t *oneofdata = hd; + VALUE str = rb_str_new2(""); + rb_enc_associate(str, kRubyString8bitEncoding); + DEREF(msg, oneofdata->case_ofs, uint32_t) = + oneofdata->oneof_case_num; + DEREF(msg, oneofdata->ofs, VALUE) = str; + return (void*)str; +} + +// Handler for a submessage field in a oneof. +static void *oneofsubmsg_handler(void *closure, + const void *hd) { + MessageHeader* msg = closure; + const oneof_handlerdata_t *oneofdata = hd; + uint32_t oldcase = DEREF(msg, oneofdata->case_ofs, uint32_t); + + VALUE subdesc = + get_def_obj((void*)oneofdata->md); + VALUE subklass = Descriptor_msgclass(subdesc); + + if (oldcase != oneofdata->oneof_case_num || + DEREF(msg, oneofdata->ofs, VALUE) == Qnil) { + DEREF(msg, oneofdata->ofs, VALUE) = + rb_class_new_instance(0, NULL, subklass); + } + // Set the oneof case *after* allocating the new class instance -- otherwise, + // if the Ruby GC is invoked as part of a call into the VM, it might invoke + // our mark routines, and our mark routines might see the case value + // indicating a VALUE is present and expect a valid VALUE. See comment in + // layout_set() for more detail: basically, the change to the value and the + // case must be atomic w.r.t. the Ruby VM. + DEREF(msg, oneofdata->case_ofs, uint32_t) = + oneofdata->oneof_case_num; + + VALUE submsg_rb = DEREF(msg, oneofdata->ofs, VALUE); + MessageHeader* submsg; + TypedData_Get_Struct(submsg_rb, MessageHeader, &Message_type, submsg); + return submsg; +} + // Set up handlers for a repeated field. static void add_handlers_for_repeated_field(upb_handlers *h, const upb_fielddef *f, @@ -383,6 +492,53 @@ static void add_handlers_for_mapentry(const upb_msgdef* msgdef, offsetof(map_parse_frame_t, value_storage)); } +// Set up handlers for a oneof field. +static void add_handlers_for_oneof_field(upb_handlers *h, + const upb_fielddef *f, + size_t offset, + size_t oneof_case_offset) { + + upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER; + upb_handlerattr_sethandlerdata( + &attr, newoneofhandlerdata(h, offset, oneof_case_offset, f)); + + switch (upb_fielddef_type(f)) { + +#define SET_HANDLER(utype, ltype) \ + case utype: \ + upb_handlers_set##ltype(h, f, oneof##ltype##_handler, &attr); \ + break; + + SET_HANDLER(UPB_TYPE_BOOL, bool); + SET_HANDLER(UPB_TYPE_INT32, int32); + SET_HANDLER(UPB_TYPE_UINT32, uint32); + SET_HANDLER(UPB_TYPE_ENUM, int32); + SET_HANDLER(UPB_TYPE_FLOAT, float); + SET_HANDLER(UPB_TYPE_INT64, int64); + SET_HANDLER(UPB_TYPE_UINT64, uint64); + SET_HANDLER(UPB_TYPE_DOUBLE, double); + +#undef SET_HANDLER + + case UPB_TYPE_STRING: + case UPB_TYPE_BYTES: { + bool is_bytes = upb_fielddef_type(f) == UPB_TYPE_BYTES; + upb_handlers_setstartstr(h, f, is_bytes ? + oneofbytes_handler : oneofstr_handler, + &attr); + upb_handlers_setstring(h, f, stringdata_handler, NULL); + break; + } + case UPB_TYPE_MESSAGE: { + upb_handlers_setstartsubmsg(h, f, oneofsubmsg_handler, &attr); + break; + } + } + + upb_handlerattr_uninit(&attr); +} + + 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)); @@ -402,16 +558,20 @@ static void add_handlers_for_message(const void *closure, upb_handlers *h) { desc->layout = create_layout(desc->msgdef); } - upb_msg_iter i; - - for (upb_msg_begin(&i, desc->msgdef); - !upb_msg_done(&i); - upb_msg_next(&i)) { + upb_msg_field_iter i; + 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->offsets[upb_fielddef_index(f)] + + size_t offset = desc->layout->fields[upb_fielddef_index(f)].offset + sizeof(MessageHeader); - if (is_map_field(f)) { + 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); @@ -804,13 +964,28 @@ static void putmsg(VALUE msg_rb, const Descriptor* desc, MessageHeader* msg; TypedData_Get_Struct(msg_rb, MessageHeader, &Message_type, msg); - upb_msg_iter i; - for (upb_msg_begin(&i, desc->msgdef); - !upb_msg_done(&i); - upb_msg_next(&i)) { + upb_msg_field_iter i; + 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->offsets[upb_fielddef_index(f)] + sizeof(MessageHeader); + 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); diff --git a/ruby/ext/google/protobuf_c/map.c b/ruby/ext/google/protobuf_c/map.c index 4ee71d18..12e7a9d9 100644 --- a/ruby/ext/google/protobuf_c/map.c +++ b/ruby/ext/google/protobuf_c/map.c @@ -683,7 +683,7 @@ VALUE Map_inspect(VALUE _self) { first = false; } str = rb_str_append(str, rb_funcall(key, inspect_sym, 0)); - str = rb_str_cat2(str, " => "); + str = rb_str_cat2(str, "=>"); str = rb_str_append(str, rb_funcall(value, inspect_sym, 0)); } diff --git a/ruby/ext/google/protobuf_c/message.c b/ruby/ext/google/protobuf_c/message.c index ee8881d4..7e58a617 100644 --- a/ruby/ext/google/protobuf_c/message.c +++ b/ruby/ext/google/protobuf_c/message.c @@ -70,6 +70,35 @@ VALUE Message_alloc(VALUE klass) { return ret; } +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; + } + // Grab the first field in the oneof so we can get its layout info to find the + // oneof_case field. + upb_oneof_iter it; + upb_oneof_begin(&it, o); + assert(!upb_oneof_done(&it)); + const upb_fielddef* first_field = upb_oneof_iter_field(&it); + assert(upb_fielddef_containingoneof(first_field) != NULL); + + size_t case_ofs = + self->descriptor->layout-> + fields[upb_fielddef_index(first_field)].case_offset; + uint32_t oneof_case = *((uint32_t*)(Message_data(self) + case_ofs)); + + if (oneof_case == ONEOF_CASE_NONE) { + return Qnil; + } + + // oneof_case is a field index, so find that field. + const upb_fielddef* f = upb_oneofdef_itof(o, oneof_case); + assert(f != NULL); + + return ID2SYM(rb_intern(upb_fielddef_name(f))); +} + /* * call-seq: * Message.method_missing(*args) @@ -82,6 +111,10 @@ VALUE Message_alloc(VALUE klass) { * * msg.foo = 42 * puts msg.foo + * + * 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. */ VALUE Message_method_missing(int argc, VALUE* argv, VALUE _self) { MessageHeader* self; @@ -104,6 +137,17 @@ VALUE Message_method_missing(int argc, VALUE* argv, VALUE _self) { name_len--; } + // Check for a oneof name first. + const upb_oneofdef* o = upb_msgdef_ntoo(self->descriptor->msgdef, + name, name_len); + if (o != NULL) { + if (setter) { + rb_raise(rb_eRuntimeError, "Oneof accessors are read-only."); + } + return which_oneof_field(self, o); + } + + // Otherwise, check for a field with that name. const upb_fielddef* f = upb_msgdef_ntof(self->descriptor->msgdef, name, name_len); diff --git a/ruby/ext/google/protobuf_c/protobuf.c b/ruby/ext/google/protobuf_c/protobuf.c index 30552705..d2d35033 100644 --- a/ruby/ext/google/protobuf_c/protobuf.c +++ b/ruby/ext/google/protobuf_c/protobuf.c @@ -77,8 +77,10 @@ void Init_protobuf_c() { DescriptorPool_register(protobuf); Descriptor_register(protobuf); FieldDescriptor_register(protobuf); + OneofDescriptor_register(protobuf); EnumDescriptor_register(protobuf); MessageBuilderContext_register(internal); + OneofBuilderContext_register(internal); EnumBuilderContext_register(internal); Builder_register(internal); RepeatedField_register(protobuf); diff --git a/ruby/ext/google/protobuf_c/protobuf.h b/ruby/ext/google/protobuf_c/protobuf.h index 88ae62e4..d8a327aa 100644 --- a/ruby/ext/google/protobuf_c/protobuf.h +++ b/ruby/ext/google/protobuf_c/protobuf.h @@ -43,6 +43,7 @@ struct Descriptor; struct FieldDescriptor; struct EnumDescriptor; struct MessageLayout; +struct MessageField; struct MessageHeader; struct MessageBuilderContext; struct EnumBuilderContext; @@ -51,10 +52,13 @@ struct Builder; typedef struct DescriptorPool DescriptorPool; typedef struct Descriptor Descriptor; typedef struct FieldDescriptor FieldDescriptor; +typedef struct OneofDescriptor OneofDescriptor; typedef struct EnumDescriptor EnumDescriptor; typedef struct MessageLayout MessageLayout; +typedef struct MessageField MessageField; typedef struct MessageHeader MessageHeader; typedef struct MessageBuilderContext MessageBuilderContext; +typedef struct OneofBuilderContext OneofBuilderContext; typedef struct EnumBuilderContext EnumBuilderContext; typedef struct Builder Builder; @@ -120,6 +124,10 @@ struct FieldDescriptor { const upb_fielddef* fielddef; }; +struct OneofDescriptor { + const upb_oneofdef* oneofdef; +}; + struct EnumDescriptor { const upb_enumdef* enumdef; VALUE module; // begins as nil @@ -130,6 +138,11 @@ struct MessageBuilderContext { VALUE builder; }; +struct OneofBuilderContext { + VALUE descriptor; + VALUE builder; +}; + struct EnumBuilderContext { VALUE enumdesc; }; @@ -144,6 +157,7 @@ extern VALUE cDescriptor; extern VALUE cFieldDescriptor; extern VALUE cEnumDescriptor; extern VALUE cMessageBuilderContext; +extern VALUE cOneofBuilderContext; extern VALUE cEnumBuilderContext; extern VALUE cBuilder; @@ -175,6 +189,9 @@ VALUE Descriptor_name_set(VALUE _self, VALUE str); VALUE Descriptor_each(VALUE _self); VALUE Descriptor_lookup(VALUE _self, VALUE name); VALUE Descriptor_add_field(VALUE _self, VALUE obj); +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); extern const rb_data_type_t _Descriptor_type; @@ -199,6 +216,16 @@ VALUE FieldDescriptor_set(VALUE _self, VALUE msg_rb, VALUE value); upb_fieldtype_t ruby_to_fieldtype(VALUE type); VALUE fieldtype_to_ruby(upb_fieldtype_t type); +void OneofDescriptor_mark(void* _self); +void OneofDescriptor_free(void* _self); +VALUE OneofDescriptor_alloc(VALUE klass); +void OneofDescriptor_register(VALUE module); +OneofDescriptor* ruby_to_OneofDescriptor(VALUE value); +VALUE OneofDescriptor_name(VALUE _self); +VALUE OneofDescriptor_name_set(VALUE _self, VALUE value); +VALUE OneofDescriptor_add_field(VALUE _self, VALUE field); +VALUE OneofDescriptor_each(VALUE _self, VALUE field); + void EnumDescriptor_mark(void* _self); void EnumDescriptor_free(void* _self); VALUE EnumDescriptor_alloc(VALUE klass); @@ -225,6 +252,17 @@ VALUE MessageBuilderContext_optional(int argc, VALUE* argv, VALUE _self); VALUE MessageBuilderContext_required(int argc, VALUE* argv, VALUE _self); VALUE MessageBuilderContext_repeated(int argc, VALUE* argv, VALUE _self); VALUE MessageBuilderContext_map(int argc, VALUE* argv, VALUE _self); +VALUE MessageBuilderContext_oneof(VALUE _self, VALUE name); + +void OneofBuilderContext_mark(void* _self); +void OneofBuilderContext_free(void* _self); +VALUE OneofBuilderContext_alloc(VALUE klass); +void OneofBuilderContext_register(VALUE module); +OneofBuilderContext* ruby_to_OneofBuilderContext(VALUE value); +VALUE OneofBuilderContext_initialize(VALUE _self, + VALUE descriptor, + VALUE builder); +VALUE OneofBuilderContext_optional(int argc, VALUE* argv, VALUE _self); void EnumBuilderContext_mark(void* _self); void EnumBuilderContext_free(void* _self); @@ -247,13 +285,22 @@ VALUE Builder_finalize_to_pool(VALUE _self, VALUE pool_rb); // Native slot storage abstraction. // ----------------------------------------------------------------------------- -#define NATIVE_SLOT_MAX_SIZE sizeof(void*) +#define NATIVE_SLOT_MAX_SIZE sizeof(uint64_t) size_t native_slot_size(upb_fieldtype_t type); void native_slot_set(upb_fieldtype_t type, VALUE type_class, void* memory, VALUE value); +// Atomically (with respect to Ruby VM calls) either update the value and set a +// oneof case, or do neither. If |case_memory| is null, then no case value is +// set. +void native_slot_set_value_and_case(upb_fieldtype_t type, + VALUE type_class, + void* memory, + VALUE value, + uint32_t* case_memory, + uint32_t case_number); VALUE native_slot_get(upb_fieldtype_t type, VALUE type_class, const void* memory); @@ -275,6 +322,11 @@ VALUE field_type_class(const upb_fielddef* field); #define MAP_KEY_FIELD 1 #define MAP_VALUE_FIELD 2 +// Oneof case slot value to indicate that no oneof case is set. The value `0` is +// safe because field numbers are used as case identifiers, and no field can +// have a number of 0. +#define ONEOF_CASE_NONE 0 + // These operate on a map field (i.e., a repeated field of submessages whose // submessage type is a map-entry msgdef). bool is_map_field(const upb_fielddef* field); @@ -384,9 +436,16 @@ VALUE Map_iter_value(Map_iter* iter); // Message layout / storage. // ----------------------------------------------------------------------------- +#define MESSAGE_FIELD_NO_CASE ((size_t)-1) + +struct MessageField { + size_t offset; + size_t case_offset; // for oneofs, a uint32. Else, MESSAGE_FIELD_NO_CASE. +}; + struct MessageLayout { const upb_msgdef* msgdef; - size_t* offsets; + MessageField* fields; size_t size; }; diff --git a/ruby/ext/google/protobuf_c/repeated_field.c b/ruby/ext/google/protobuf_c/repeated_field.c index 6e3f0bc7..8cf2e29b 100644 --- a/ruby/ext/google/protobuf_c/repeated_field.c +++ b/ruby/ext/google/protobuf_c/repeated_field.c @@ -318,6 +318,29 @@ VALUE RepeatedField_deep_copy(VALUE _self) { /* * call-seq: + * RepeatedField.to_ary => array + * + * Used when converted implicitly into array, e.g. compared to an Array. + * Also called as a fallback of Object#to_a + */ +VALUE RepeatedField_to_ary(VALUE _self) { + RepeatedField* self = ruby_to_RepeatedField(_self); + upb_fieldtype_t field_type = self->field_type; + + size_t elem_size = native_slot_size(field_type); + size_t off = 0; + VALUE ary = rb_ary_new2(self->size); + for (int i = 0; i < self->size; i++, off += elem_size) { + void* mem = ((uint8_t *)self->elements) + off; + VALUE elem = native_slot_get(field_type, self->field_type_class, mem); + + rb_ary_push(ary, elem); + } + return ary; +} + +/* + * call-seq: * RepeatedField.==(other) => boolean * * Compares this repeated field to another. Repeated fields are equal if their @@ -335,15 +358,9 @@ VALUE RepeatedField_eq(VALUE _self, VALUE _other) { } RepeatedField* self = ruby_to_RepeatedField(_self); - // Inefficient but workable: to support comparison to a generic array, we - // build a temporary RepeatedField of our type. if (TYPE(_other) == T_ARRAY) { - VALUE new_rptfield = RepeatedField_new_this_type(_self); - for (int i = 0; i < RARRAY_LEN(_other); i++) { - VALUE elem = rb_ary_entry(_other, i); - RepeatedField_push(new_rptfield, elem); - } - _other = new_rptfield; + VALUE self_ary = RepeatedField_to_ary(_self); + return rb_equal(self_ary, _other); } RepeatedField* other = ruby_to_RepeatedField(_other); @@ -401,29 +418,8 @@ VALUE RepeatedField_hash(VALUE _self) { * representation computed by its own #inspect method. */ VALUE RepeatedField_inspect(VALUE _self) { - RepeatedField* self = ruby_to_RepeatedField(_self); - - VALUE str = rb_str_new2("["); - - bool first = true; - - upb_fieldtype_t field_type = self->field_type; - VALUE field_type_class = self->field_type_class; - size_t elem_size = native_slot_size(field_type); - size_t off = 0; - for (int i = 0; i < self->size; i++, off += elem_size) { - void* mem = ((uint8_t *)self->elements) + off; - VALUE elem = native_slot_get(field_type, field_type_class, mem); - if (!first) { - str = rb_str_cat2(str, ", "); - } else { - first = false; - } - str = rb_str_append(str, rb_funcall(elem, rb_intern("inspect"), 0)); - } - - str = rb_str_cat2(str, "]"); - return str; + VALUE self_ary = RepeatedField_to_ary(_self); + return rb_funcall(self_ary, rb_intern("inspect"), 0); } /* @@ -594,6 +590,7 @@ void RepeatedField_register(VALUE module) { // Also define #clone so that we don't inherit Object#clone. rb_define_method(klass, "clone", RepeatedField_dup, 0); rb_define_method(klass, "==", RepeatedField_eq, 1); + rb_define_method(klass, "to_ary", RepeatedField_to_ary, 0); rb_define_method(klass, "hash", RepeatedField_hash, 0); rb_define_method(klass, "inspect", RepeatedField_inspect, 0); rb_define_method(klass, "+", RepeatedField_plus, 1); diff --git a/ruby/ext/google/protobuf_c/storage.c b/ruby/ext/google/protobuf_c/storage.c index 14f49d44..5b1549d2 100644 --- a/ruby/ext/google/protobuf_c/storage.c +++ b/ruby/ext/google/protobuf_c/storage.c @@ -109,6 +109,17 @@ void native_slot_validate_string_encoding(upb_fieldtype_t type, VALUE value) { void native_slot_set(upb_fieldtype_t type, VALUE type_class, void* memory, VALUE value) { + native_slot_set_value_and_case(type, type_class, memory, value, NULL, 0); +} + +void native_slot_set_value_and_case(upb_fieldtype_t type, VALUE type_class, + void* memory, VALUE value, + uint32_t* case_memory, + uint32_t case_number) { + // Note that in order to atomically change the value in memory and the case + // value (w.r.t. Ruby VM calls), we must set the value at |memory| only after + // all Ruby VM calls are complete. The case is then set at the bottom of this + // function. switch (type) { case UPB_TYPE_FLOAT: if (!is_ruby_num(value)) { @@ -198,6 +209,10 @@ void native_slot_set(upb_fieldtype_t type, VALUE type_class, default: break; } + + if (case_memory != NULL) { + *case_memory = case_number; + } } VALUE native_slot_get(upb_fieldtype_t type, @@ -366,24 +381,94 @@ const upb_fielddef* map_entry_value(const upb_msgdef* msgdef) { // Memory layout management. // ----------------------------------------------------------------------------- +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); +} + MessageLayout* create_layout(const upb_msgdef* msgdef) { MessageLayout* layout = ALLOC(MessageLayout); int nfields = upb_msgdef_numfields(msgdef); - layout->offsets = ALLOC_N(size_t, nfields); + layout->fields = ALLOC_N(MessageField, nfields); - upb_msg_iter it; + upb_msg_field_iter it; size_t off = 0; - for (upb_msg_begin(&it, msgdef); !upb_msg_done(&it); upb_msg_next(&it)) { + 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_containingoneof(field)) { + // Oneofs are handled separately below. + continue; + } + + // Allocate |field_size| bytes for this field in the layout. size_t field_size = 0; if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { field_size = sizeof(VALUE); } else { field_size = native_slot_size(upb_fielddef_type(field)); } - // align current offset + // Align current offset up to |size| granularity. + off = align_up_to(off, field_size); + layout->fields[upb_fielddef_index(field)].offset = off; + layout->fields[upb_fielddef_index(field)].case_offset = MESSAGE_FIELD_NO_CASE; + off += field_size; + } + + // Handle oneofs now -- we iterate over oneofs specifically and allocate only + // one slot per oneof. + // + // We assign all value slots first, then pack the 'case' fields at the end, + // since in the common case (modern 64-bit platform) these are 8 bytes and 4 + // bytes respectively and we want to avoid alignment overhead. + // + // Note that we reserve 4 bytes (a uint32) per 'case' slot because the value + // space for oneof cases is conceptually as wide as field tag numbers. In + // practice, it's unlikely that a oneof would have more than e.g. 256 or 64K + // members (8 or 16 bits respectively), so conceivably we could assign + // consecutive case numbers and then pick a smaller oneof case slot size, but + // the complexity to implement this indirection is probably not worthwhile. + upb_msg_oneof_iter oit; + for (upb_msg_oneof_begin(&oit, msgdef); + !upb_msg_oneof_done(&oit); + upb_msg_oneof_next(&oit)) { + const upb_oneofdef* oneof = upb_msg_iter_oneof(&oit); + + // Always allocate NATIVE_SLOT_MAX_SIZE bytes, but share the slot between + // all fields. + size_t field_size = NATIVE_SLOT_MAX_SIZE; + // Align the offset. + off = align_up_to(off, field_size); + // Assign all fields in the oneof this same offset. + upb_oneof_iter fit; + for (upb_oneof_begin(&fit, oneof); + !upb_oneof_done(&fit); + upb_oneof_next(&fit)) { + const upb_fielddef* field = upb_oneof_iter_field(&fit); + layout->fields[upb_fielddef_index(field)].offset = off; + } + off += field_size; + } + + // Now the case fields. + for (upb_msg_oneof_begin(&oit, msgdef); + !upb_msg_oneof_done(&oit); + upb_msg_oneof_next(&oit)) { + const upb_oneofdef* oneof = upb_msg_iter_oneof(&oit); + + size_t field_size = sizeof(uint32_t); + // Align the offset. off = (off + field_size - 1) & ~(field_size - 1); - layout->offsets[upb_fielddef_index(field)] = off; + // Assign all fields in the oneof this same offset. + upb_oneof_iter fit; + for (upb_oneof_begin(&fit, oneof); + !upb_oneof_done(&fit); + upb_oneof_next(&fit)) { + const upb_fielddef* field = upb_oneof_iter_field(&fit); + layout->fields[upb_fielddef_index(field)].case_offset = off; + } off += field_size; } @@ -396,7 +481,7 @@ MessageLayout* create_layout(const upb_msgdef* msgdef) { } void free_layout(MessageLayout* layout) { - xfree(layout->offsets); + xfree(layout->fields); upb_msgdef_unref(layout->msgdef, &layout->msgdef); xfree(layout); } @@ -415,12 +500,35 @@ VALUE field_type_class(const upb_fielddef* field) { return type_class; } +static void* slot_memory(MessageLayout* layout, + const void* storage, + const upb_fielddef* field) { + return ((uint8_t *)storage) + + layout->fields[upb_fielddef_index(field)].offset; +} + +static uint32_t* slot_oneof_case(MessageLayout* layout, + const void* storage, + const upb_fielddef* field) { + return (uint32_t *)(((uint8_t *)storage) + + layout->fields[upb_fielddef_index(field)].case_offset); +} + + VALUE layout_get(MessageLayout* layout, const void* storage, const upb_fielddef* field) { - void* memory = ((uint8_t *)storage) + - layout->offsets[upb_fielddef_index(field)]; - if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { + void* memory = slot_memory(layout, storage, field); + uint32_t* oneof_case = slot_oneof_case(layout, storage, field); + + if (upb_fielddef_containingoneof(field)) { + if (*oneof_case != upb_fielddef_number(field)) { + return Qnil; + } + 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 { return native_slot_get(upb_fielddef_type(field), @@ -484,9 +592,33 @@ void layout_set(MessageLayout* layout, void* storage, const upb_fielddef* field, VALUE val) { - void* memory = ((uint8_t *)storage) + - layout->offsets[upb_fielddef_index(field)]; - if (is_map_field(field)) { + 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_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) { @@ -500,15 +632,18 @@ void layout_set(MessageLayout* layout, void layout_init(MessageLayout* layout, void* storage) { - upb_msg_iter it; - for (upb_msg_begin(&it, layout->msgdef); - !upb_msg_done(&it); - upb_msg_next(&it)) { + 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 = ((uint8_t *)storage) + - layout->offsets[upb_fielddef_index(field)]; + void* memory = slot_memory(layout, storage, field); + uint32_t* oneof_case = slot_oneof_case(layout, storage, field); - if (is_map_field(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); @@ -555,15 +690,19 @@ void layout_init(MessageLayout* layout, } void layout_mark(MessageLayout* layout, void* storage) { - upb_msg_iter it; - for (upb_msg_begin(&it, layout->msgdef); - !upb_msg_done(&it); - upb_msg_next(&it)) { + 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 = ((uint8_t *)storage) + - layout->offsets[upb_fielddef_index(field)]; + void* memory = slot_memory(layout, storage, field); + uint32_t* oneof_case = slot_oneof_case(layout, storage, field); - if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { + if (upb_fielddef_containingoneof(field)) { + if (*oneof_case == upb_fielddef_number(field)) { + native_slot_mark(upb_fielddef_type(field), memory); + } + } else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { rb_gc_mark(DEREF(memory, VALUE)); } else { native_slot_mark(upb_fielddef_type(field), memory); @@ -572,17 +711,23 @@ void layout_mark(MessageLayout* layout, void* storage) { } void layout_dup(MessageLayout* layout, void* to, void* from) { - upb_msg_iter it; - for (upb_msg_begin(&it, layout->msgdef); - !upb_msg_done(&it); - upb_msg_next(&it)) { + 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 = ((uint8_t *)to) + - layout->offsets[upb_fielddef_index(field)]; - void* from_memory = ((uint8_t *)from) + - layout->offsets[upb_fielddef_index(field)]; - if (is_map_field(field)) { + 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_dup(upb_fielddef_type(field), to_memory, from_memory); + } + } else if (is_map_field(field)) { DEREF(to_memory, VALUE) = Map_dup(DEREF(from_memory, VALUE)); } else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { DEREF(to_memory, VALUE) = RepeatedField_dup(DEREF(from_memory, VALUE)); @@ -593,17 +738,23 @@ void layout_dup(MessageLayout* layout, void* to, void* from) { } void layout_deep_copy(MessageLayout* layout, void* to, void* from) { - upb_msg_iter it; - for (upb_msg_begin(&it, layout->msgdef); - !upb_msg_done(&it); - upb_msg_next(&it)) { + 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 = ((uint8_t *)to) + - layout->offsets[upb_fielddef_index(field)]; - void* from_memory = ((uint8_t *)from) + - layout->offsets[upb_fielddef_index(field)]; - if (is_map_field(field)) { + 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) { @@ -616,22 +767,35 @@ void layout_deep_copy(MessageLayout* layout, void* to, void* from) { } VALUE layout_eq(MessageLayout* layout, void* msg1, void* msg2) { - upb_msg_iter it; - for (upb_msg_begin(&it, layout->msgdef); - !upb_msg_done(&it); - upb_msg_next(&it)) { + 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 = ((uint8_t *)msg1) + - layout->offsets[upb_fielddef_index(field)]; - void* msg2_memory = ((uint8_t *)msg2) + - layout->offsets[upb_fielddef_index(field)]; - - if (is_map_field(field)) { - return Map_eq(DEREF(msg1_memory, VALUE), - DEREF(msg2_memory, VALUE)); + + 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) { - return RepeatedField_eq(DEREF(msg1_memory, VALUE), - DEREF(msg2_memory, VALUE)); + if (!RepeatedField_eq(DEREF(msg1_memory, VALUE), + DEREF(msg2_memory, VALUE))) { + return Qfalse; + } } else { if (!native_slot_eq(upb_fielddef_type(field), msg1_memory, msg2_memory)) { @@ -643,12 +807,12 @@ VALUE layout_eq(MessageLayout* layout, void* msg1, void* msg2) { } VALUE layout_hash(MessageLayout* layout, void* storage) { - upb_msg_iter it; + upb_msg_field_iter it; st_index_t h = rb_hash_start(0); VALUE hash_sym = rb_intern("hash"); - for (upb_msg_begin(&it, layout->msgdef); - !upb_msg_done(&it); - upb_msg_next(&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); VALUE field_val = layout_get(layout, storage, field); h = rb_hash_uint(h, NUM2LONG(rb_funcall(field_val, hash_sym, 0))); @@ -661,11 +825,11 @@ VALUE layout_hash(MessageLayout* layout, void* storage) { VALUE layout_inspect(MessageLayout* layout, void* storage) { VALUE str = rb_str_new2(""); - upb_msg_iter it; + upb_msg_field_iter it; bool first = true; - for (upb_msg_begin(&it, layout->msgdef); - !upb_msg_done(&it); - upb_msg_next(&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); VALUE field_val = layout_get(layout, storage, field); diff --git a/ruby/ext/google/protobuf_c/upb.c b/ruby/ext/google/protobuf_c/upb.c index 571c809f..20bd76bc 100644 --- a/ruby/ext/google/protobuf_c/upb.c +++ b/ruby/ext/google/protobuf_c/upb.c @@ -210,6 +210,21 @@ static bool upb_validate_field(upb_fielddef *f, upb_status *s) { upb_fielddef_setdefaultint32(f, upb_fielddef_defaultint32(f)); } + // Ensure that MapEntry submessages only appear as repeated fields, not + // optional/required (singular) fields. + if (upb_fielddef_type(f) == UPB_TYPE_MESSAGE && + upb_fielddef_msgsubdef(f) != NULL) { + const upb_msgdef *subdef = upb_fielddef_msgsubdef(f); + if (upb_msgdef_mapentry(subdef) && !upb_fielddef_isseq(f)) { + upb_status_seterrf(s, + "Field %s refers to mapentry message but is not " + "a repeated field", + upb_fielddef_name(f) ? upb_fielddef_name(f) : + "(unnamed)"); + return false; + } + } + return true; } @@ -247,10 +262,12 @@ static bool assign_msg_indices(upb_msgdef *m, upb_status *s) { upb_fielddef **fields = malloc(n * sizeof(*fields)); if (!fields) return false; - upb_msg_iter j; + upb_msg_field_iter j; int i; m->submsg_field_count = 0; - for(i = 0, upb_msg_begin(&j, m); !upb_msg_done(&j); upb_msg_next(&j), i++) { + for(i = 0, upb_msg_field_begin(&j, m); + !upb_msg_field_done(&j); + upb_msg_field_next(&j), i++) { upb_fielddef *f = upb_msg_iter_field(&j); assert(f->msg.def == m); if (!upb_validate_field(f, s)) { @@ -286,7 +303,9 @@ static bool assign_msg_indices(upb_msgdef *m, upb_status *s) { upb_selector_t sel; upb_inttable_insert(&t, UPB_STARTMSG_SELECTOR, v); upb_inttable_insert(&t, UPB_ENDMSG_SELECTOR, v); - for(upb_msg_begin(&j, m); !upb_msg_done(&j); upb_msg_next(&j)) { + for(upb_msg_field_begin(&j, m); + !upb_msg_field_done(&j); + upb_msg_field_next(&j)) { upb_fielddef *f = upb_msg_iter_field(&j); // These calls will assert-fail in upb_table if the value already exists. TRY(UPB_HANDLER_INT32); @@ -544,6 +563,9 @@ static void visitfield(const upb_refcounted *r, upb_refcounted_visit *visit, if (upb_fielddef_containingtype(f)) { visit(r, UPB_UPCAST2(upb_fielddef_containingtype(f)), closure); } + if (upb_fielddef_containingoneof(f)) { + visit(r, UPB_UPCAST2(upb_fielddef_containingoneof(f)), closure); + } if (upb_fielddef_subdef(f)) { visit(r, UPB_UPCAST(upb_fielddef_subdef(f)), closure); } @@ -619,6 +641,7 @@ upb_fielddef *upb_fielddef_new(const void *owner) { } f->msg.def = NULL; f->sub.def = NULL; + f->oneof = NULL; f->subdef_is_symbolic = false; f->msg_is_symbolic = false; f->label_ = UPB_LABEL_OPTIONAL; @@ -748,6 +771,10 @@ const upb_msgdef *upb_fielddef_containingtype(const upb_fielddef *f) { return f->msg_is_symbolic ? NULL : f->msg.def; } +const upb_oneofdef *upb_fielddef_containingoneof(const upb_fielddef *f) { + return f->oneof; +} + upb_msgdef *upb_fielddef_containingtype_mutable(upb_fielddef *f) { return (upb_msgdef*)upb_fielddef_containingtype(f); } @@ -776,6 +803,10 @@ bool upb_fielddef_setcontainingtypename(upb_fielddef *f, const char *name, } bool upb_fielddef_setname(upb_fielddef *f, const char *name, upb_status *s) { + if (upb_fielddef_containingtype(f) || upb_fielddef_containingoneof(f)) { + upb_status_seterrmsg(s, "Already added to message or oneof"); + return false; + } return upb_def_setfullname(UPB_UPCAST(f), name, s); } @@ -1226,6 +1257,11 @@ bool upb_fielddef_isprimitive(const upb_fielddef *f) { return !upb_fielddef_isstring(f) && !upb_fielddef_issubmsg(f); } +bool upb_fielddef_ismap(const upb_fielddef *f) { + return upb_fielddef_isseq(f) && upb_fielddef_issubmsg(f) && + upb_msgdef_mapentry(upb_fielddef_msgsubdef(f)); +} + bool upb_fielddef_hassubdef(const upb_fielddef *f) { return upb_fielddef_issubmsg(f) || upb_fielddef_type(f) == UPB_TYPE_ENUM; } @@ -1247,15 +1283,25 @@ bool upb_fielddef_checkdescriptortype(int32_t type) { static void visitmsg(const upb_refcounted *r, upb_refcounted_visit *visit, void *closure) { const upb_msgdef *m = (const upb_msgdef*)r; - upb_msg_iter i; - for(upb_msg_begin(&i, m); !upb_msg_done(&i); upb_msg_next(&i)) { + upb_msg_field_iter i; + for(upb_msg_field_begin(&i, m); + !upb_msg_field_done(&i); + upb_msg_field_next(&i)) { upb_fielddef *f = upb_msg_iter_field(&i); visit(r, UPB_UPCAST2(f), closure); } + upb_msg_oneof_iter o; + for(upb_msg_oneof_begin(&o, m); + !upb_msg_oneof_done(&o); + upb_msg_oneof_next(&o)) { + upb_oneofdef *f = upb_msg_iter_oneof(&o); + visit(r, UPB_UPCAST2(f), closure); + } } static void freemsg(upb_refcounted *r) { upb_msgdef *m = (upb_msgdef*)r; + upb_strtable_uninit(&m->ntoo); upb_strtable_uninit(&m->ntof); upb_inttable_uninit(&m->itof); upb_def_uninit(UPB_UPCAST(m)); @@ -1267,14 +1313,17 @@ upb_msgdef *upb_msgdef_new(const void *owner) { upb_msgdef *m = malloc(sizeof(*m)); if (!m) return NULL; if (!upb_def_init(UPB_UPCAST(m), UPB_DEF_MSG, &vtbl, owner)) goto err2; - if (!upb_inttable_init(&m->itof, UPB_CTYPE_PTR)) goto err2; - if (!upb_strtable_init(&m->ntof, UPB_CTYPE_PTR)) goto err1; + if (!upb_inttable_init(&m->itof, UPB_CTYPE_PTR)) goto err3; + if (!upb_strtable_init(&m->ntof, UPB_CTYPE_PTR)) goto err2; + if (!upb_strtable_init(&m->ntoo, UPB_CTYPE_PTR)) goto err1; m->map_entry = false; return m; err1: - upb_inttable_uninit(&m->itof); + upb_strtable_uninit(&m->ntof); err2: + upb_inttable_uninit(&m->itof); +err3: free(m); return NULL; } @@ -1286,14 +1335,28 @@ upb_msgdef *upb_msgdef_dup(const upb_msgdef *m, const void *owner) { upb_def_fullname(UPB_UPCAST(m)), NULL); newm->map_entry = m->map_entry; UPB_ASSERT_VAR(ok, ok); - upb_msg_iter i; - for(upb_msg_begin(&i, m); !upb_msg_done(&i); upb_msg_next(&i)) { + upb_msg_field_iter i; + for(upb_msg_field_begin(&i, m); + !upb_msg_field_done(&i); + upb_msg_field_next(&i)) { upb_fielddef *f = upb_fielddef_dup(upb_msg_iter_field(&i), &f); + // Fields in oneofs are dup'd below. + if (upb_fielddef_containingoneof(f)) continue; if (!f || !upb_msgdef_addfield(newm, f, &f, NULL)) { upb_msgdef_unref(newm, owner); return NULL; } } + upb_msg_oneof_iter o; + for(upb_msg_oneof_begin(&o, m); + !upb_msg_oneof_done(&o); + upb_msg_oneof_next(&o)) { + upb_oneofdef *f = upb_oneofdef_dup(upb_msg_iter_oneof(&o), &f); + if (!f || !upb_msgdef_addoneof(newm, f, &f, NULL)) { + upb_msgdef_unref(newm, owner); + return NULL; + } + } return newm; } @@ -1332,6 +1395,35 @@ bool upb_msgdef_setfullname(upb_msgdef *m, const char *fullname, return upb_def_setfullname(UPB_UPCAST(m), fullname, s); } +// Helper: check that the field |f| is safe to add to msgdef |m|. Set an error +// on status |s| and return false if not. +static bool check_field_add(const upb_msgdef *m, const upb_fielddef *f, + upb_status *s) { + if (upb_fielddef_containingtype(f) != NULL) { + upb_status_seterrmsg(s, "fielddef already belongs to a message"); + return false; + } else if (upb_fielddef_name(f) == NULL || upb_fielddef_number(f) == 0) { + upb_status_seterrmsg(s, "field name or number were not set"); + return false; + } else if (upb_msgdef_ntofz(m, upb_fielddef_name(f)) || + upb_msgdef_itof(m, upb_fielddef_number(f))) { + upb_status_seterrmsg(s, "duplicate field name or number for field"); + return false; + } + return true; +} + +static void add_field(upb_msgdef *m, upb_fielddef *f, const void *ref_donor) { + release_containingtype(f); + f->msg.def = m; + f->msg_is_symbolic = false; + upb_inttable_insert(&m->itof, upb_fielddef_number(f), upb_value_ptr(f)); + upb_strtable_insert(&m->ntof, upb_fielddef_name(f), upb_value_ptr(f)); + upb_ref2(f, m); + upb_ref2(m, f); + if (ref_donor) upb_fielddef_unref(f, ref_donor); +} + bool upb_msgdef_addfield(upb_msgdef *m, upb_fielddef *f, const void *ref_donor, upb_status *s) { // TODO: extensions need to have a separate namespace, because proto2 allows a @@ -1345,28 +1437,65 @@ bool upb_msgdef_addfield(upb_msgdef *m, upb_fielddef *f, const void *ref_donor, // We also need to validate that the field number is in an extension range iff // it is an extension. + // This method is idempotent. Check if |f| is already part of this msgdef and + // return immediately if so. + if (upb_fielddef_containingtype(f) == m) { + return true; + } + // Check constraints for all fields before performing any action. - if (upb_fielddef_containingtype(f) != NULL) { - upb_status_seterrmsg(s, "fielddef already belongs to a message"); + if (!check_field_add(m, f, s)) { return false; - } else if (upb_fielddef_name(f) == NULL || upb_fielddef_number(f) == 0) { - upb_status_seterrmsg(s, "field name or number were not set"); - return false; - } else if(upb_msgdef_itof(m, upb_fielddef_number(f)) || - upb_msgdef_ntofz(m, upb_fielddef_name(f))) { - upb_status_seterrmsg(s, "duplicate field name or number"); + } else if (upb_fielddef_containingoneof(f) != NULL) { + // Fields in a oneof can only be added by adding the oneof to the msgdef. + upb_status_seterrmsg(s, "fielddef is part of a oneof"); return false; } // Constraint checks ok, perform the action. - release_containingtype(f); - f->msg.def = m; - f->msg_is_symbolic = false; - upb_inttable_insert(&m->itof, upb_fielddef_number(f), upb_value_ptr(f)); - upb_strtable_insert(&m->ntof, upb_fielddef_name(f), upb_value_ptr(f)); - upb_ref2(f, m); - upb_ref2(m, f); - if (ref_donor) upb_fielddef_unref(f, ref_donor); + add_field(m, f, ref_donor); + return true; +} + +bool upb_msgdef_addoneof(upb_msgdef *m, upb_oneofdef *o, const void *ref_donor, + upb_status *s) { + // Check various conditions that would prevent this oneof from being added. + if (upb_oneofdef_containingtype(o)) { + upb_status_seterrmsg(s, "oneofdef already belongs to a message"); + return false; + } else if (upb_oneofdef_name(o) == NULL) { + upb_status_seterrmsg(s, "oneofdef name was not set"); + return false; + } else if (upb_msgdef_ntooz(m, upb_oneofdef_name(o))) { + upb_status_seterrmsg(s, "duplicate oneof name"); + return false; + } + + // Check that all of the oneof's fields do not conflict with names or numbers + // of fields already in the message. + upb_oneof_iter it; + for (upb_oneof_begin(&it, o); !upb_oneof_done(&it); upb_oneof_next(&it)) { + const upb_fielddef *f = upb_oneof_iter_field(&it); + if (!check_field_add(m, f, s)) { + return false; + } + } + + // Everything checks out -- commit now. + + // Add oneof itself first. + o->parent = m; + upb_strtable_insert(&m->ntoo, upb_oneofdef_name(o), upb_value_ptr(o)); + upb_ref2(o, m); + upb_ref2(m, o); + + // Add each field of the oneof directly to the msgdef. + for (upb_oneof_begin(&it, o); !upb_oneof_done(&it); upb_oneof_next(&it)) { + upb_fielddef *f = upb_oneof_iter_field(&it); + add_field(m, f, NULL); + } + + if (ref_donor) upb_oneofdef_unref(o, ref_donor); return true; } @@ -1384,10 +1513,21 @@ const upb_fielddef *upb_msgdef_ntof(const upb_msgdef *m, const char *name, upb_value_getptr(val) : NULL; } +const upb_oneofdef *upb_msgdef_ntoo(const upb_msgdef *m, const char *name, + size_t len) { + upb_value val; + return upb_strtable_lookup2(&m->ntoo, name, len, &val) ? + upb_value_getptr(val) : NULL; +} + int upb_msgdef_numfields(const upb_msgdef *m) { return upb_strtable_count(&m->ntof); } +int upb_msgdef_numoneofs(const upb_msgdef *m) { + return upb_strtable_count(&m->ntoo); +} + void upb_msgdef_setmapentry(upb_msgdef *m, bool map_entry) { assert(!upb_msgdef_isfrozen(m)); m->map_entry = map_entry; @@ -1397,19 +1537,246 @@ bool upb_msgdef_mapentry(const upb_msgdef *m) { return m->map_entry; } -void upb_msg_begin(upb_msg_iter *iter, const upb_msgdef *m) { +void upb_msg_field_begin(upb_msg_field_iter *iter, const upb_msgdef *m) { upb_inttable_begin(iter, &m->itof); } -void upb_msg_next(upb_msg_iter *iter) { upb_inttable_next(iter); } +void upb_msg_field_next(upb_msg_field_iter *iter) { upb_inttable_next(iter); } + +bool upb_msg_field_done(const upb_msg_field_iter *iter) { + return upb_inttable_done(iter); +} + +upb_fielddef *upb_msg_iter_field(const upb_msg_field_iter *iter) { + return (upb_fielddef*)upb_value_getptr(upb_inttable_iter_value(iter)); +} + +void upb_msg_field_iter_setdone(upb_msg_field_iter *iter) { + upb_inttable_iter_setdone(iter); +} + +void upb_msg_oneof_begin(upb_msg_oneof_iter *iter, const upb_msgdef *m) { + upb_strtable_begin(iter, &m->ntoo); +} + +void upb_msg_oneof_next(upb_msg_oneof_iter *iter) { upb_strtable_next(iter); } + +bool upb_msg_oneof_done(const upb_msg_oneof_iter *iter) { + return upb_strtable_done(iter); +} + +upb_oneofdef *upb_msg_iter_oneof(const upb_msg_oneof_iter *iter) { + return (upb_oneofdef*)upb_value_getptr(upb_strtable_iter_value(iter)); +} + +void upb_msg_oneof_iter_setdone(upb_msg_oneof_iter *iter) { + upb_strtable_iter_setdone(iter); +} + +/* upb_oneofdef ***************************************************************/ + +static void visitoneof(const upb_refcounted *r, upb_refcounted_visit *visit, + void *closure) { + const upb_oneofdef *o = (const upb_oneofdef*)r; + upb_oneof_iter i; + for (upb_oneof_begin(&i, o); !upb_oneof_done(&i); upb_oneof_next(&i)) { + const upb_fielddef *f = upb_oneof_iter_field(&i); + visit(r, UPB_UPCAST2(f), closure); + } + if (o->parent) { + visit(r, UPB_UPCAST2(o->parent), closure); + } +} + +static void freeoneof(upb_refcounted *r) { + upb_oneofdef *o = (upb_oneofdef*)r; + upb_strtable_uninit(&o->ntof); + upb_inttable_uninit(&o->itof); + upb_def_uninit(UPB_UPCAST(o)); + free(o); +} + +upb_oneofdef *upb_oneofdef_new(const void *owner) { + static const struct upb_refcounted_vtbl vtbl = {visitoneof, freeoneof}; + upb_oneofdef *o = malloc(sizeof(*o)); + o->parent = NULL; + if (!o) return NULL; + if (!upb_def_init(UPB_UPCAST(o), UPB_DEF_ONEOF, &vtbl, owner)) goto err2; + if (!upb_inttable_init(&o->itof, UPB_CTYPE_PTR)) goto err2; + if (!upb_strtable_init(&o->ntof, UPB_CTYPE_PTR)) goto err1; + return o; + +err1: + upb_inttable_uninit(&o->itof); +err2: + free(o); + return NULL; +} + +upb_oneofdef *upb_oneofdef_dup(const upb_oneofdef *o, const void *owner) { + upb_oneofdef *newo = upb_oneofdef_new(owner); + if (!newo) return NULL; + bool ok = upb_def_setfullname(UPB_UPCAST(newo), + upb_def_fullname(UPB_UPCAST(o)), NULL); + UPB_ASSERT_VAR(ok, ok); + upb_oneof_iter i; + for (upb_oneof_begin(&i, o); !upb_oneof_done(&i); upb_oneof_next(&i)) { + upb_fielddef *f = upb_fielddef_dup(upb_oneof_iter_field(&i), &f); + if (!f || !upb_oneofdef_addfield(newo, f, &f, NULL)) { + upb_oneofdef_unref(newo, owner); + return NULL; + } + } + return newo; +} + +bool upb_oneofdef_isfrozen(const upb_oneofdef *o) { + return upb_def_isfrozen(UPB_UPCAST(o)); +} + +void upb_oneofdef_ref(const upb_oneofdef *o, const void *owner) { + upb_def_ref(UPB_UPCAST(o), owner); +} + +void upb_oneofdef_unref(const upb_oneofdef *o, const void *owner) { + upb_def_unref(UPB_UPCAST(o), owner); +} + +void upb_oneofdef_donateref(const upb_oneofdef *o, const void *from, + const void *to) { + upb_def_donateref(UPB_UPCAST(o), from, to); +} + +void upb_oneofdef_checkref(const upb_oneofdef *o, const void *owner) { + upb_def_checkref(UPB_UPCAST(o), owner); +} + +const char *upb_oneofdef_name(const upb_oneofdef *o) { + return upb_def_fullname(UPB_UPCAST(o)); +} + +bool upb_oneofdef_setname(upb_oneofdef *o, const char *fullname, + upb_status *s) { + if (upb_oneofdef_containingtype(o)) { + upb_status_seterrmsg(s, "oneof already added to a message"); + return false; + } + return upb_def_setfullname(UPB_UPCAST(o), fullname, s); +} + +const upb_msgdef *upb_oneofdef_containingtype(const upb_oneofdef *o) { + return o->parent; +} + +int upb_oneofdef_numfields(const upb_oneofdef *o) { + return upb_strtable_count(&o->ntof); +} + +bool upb_oneofdef_addfield(upb_oneofdef *o, upb_fielddef *f, + const void *ref_donor, + upb_status *s) { + assert(!upb_oneofdef_isfrozen(o)); + assert(!o->parent || !upb_msgdef_isfrozen(o->parent)); + + // This method is idempotent. Check if |f| is already part of this oneofdef + // and return immediately if so. + if (upb_fielddef_containingoneof(f) == o) { + return true; + } + + // The field must have an OPTIONAL label. + if (upb_fielddef_label(f) != UPB_LABEL_OPTIONAL) { + upb_status_seterrmsg(s, "fields in oneof must have OPTIONAL label"); + return false; + } + + // Check that no field with this name or number exists already in the oneof. + // Also check that the field is not already part of a oneof. + if (upb_fielddef_name(f) == NULL || upb_fielddef_number(f) == 0) { + upb_status_seterrmsg(s, "field name or number were not set"); + return false; + } else if (upb_oneofdef_itof(o, upb_fielddef_number(f)) || + upb_oneofdef_ntofz(o, upb_fielddef_name(f))) { + upb_status_seterrmsg(s, "duplicate field name or number"); + return false; + } else if (upb_fielddef_containingoneof(f) != NULL) { + upb_status_seterrmsg(s, "fielddef already belongs to a oneof"); + return false; + } + + // We allow adding a field to the oneof either if the field is not part of a + // msgdef, or if it is and we are also part of the same msgdef. + if (o->parent == NULL) { + // If we're not in a msgdef, the field cannot be either. Otherwise we would + // need to magically add this oneof to a msgdef to remain consistent, which + // is surprising behavior. + if (upb_fielddef_containingtype(f) != NULL) { + upb_status_seterrmsg(s, "fielddef already belongs to a message, but " + "oneof does not"); + return false; + } + } else { + // If we're in a msgdef, the user can add fields that either aren't in any + // msgdef (in which case they're added to our msgdef) or already a part of + // our msgdef. + if (upb_fielddef_containingtype(f) != NULL && + upb_fielddef_containingtype(f) != o->parent) { + upb_status_seterrmsg(s, "fielddef belongs to a different message " + "than oneof"); + return false; + } + } + + // Commit phase. First add the field to our parent msgdef, if any, because + // that may fail; then add the field to our own tables. + + if (o->parent != NULL && upb_fielddef_containingtype(f) == NULL) { + if (!upb_msgdef_addfield((upb_msgdef*)o->parent, f, NULL, s)) { + return false; + } + } + + release_containingtype(f); + f->oneof = o; + upb_inttable_insert(&o->itof, upb_fielddef_number(f), upb_value_ptr(f)); + upb_strtable_insert(&o->ntof, upb_fielddef_name(f), upb_value_ptr(f)); + upb_ref2(f, o); + upb_ref2(o, f); + if (ref_donor) upb_fielddef_unref(f, ref_donor); + + return true; +} + +const upb_fielddef *upb_oneofdef_ntof(const upb_oneofdef *o, + const char *name, size_t length) { + upb_value val; + return upb_strtable_lookup2(&o->ntof, name, length, &val) ? + upb_value_getptr(val) : NULL; +} + +const upb_fielddef *upb_oneofdef_itof(const upb_oneofdef *o, uint32_t num) { + upb_value val; + return upb_inttable_lookup32(&o->itof, num, &val) ? + upb_value_getptr(val) : NULL; +} + +void upb_oneof_begin(upb_oneof_iter *iter, const upb_oneofdef *o) { + upb_inttable_begin(iter, &o->itof); +} + +void upb_oneof_next(upb_oneof_iter *iter) { + upb_inttable_next(iter); +} -bool upb_msg_done(const upb_msg_iter *iter) { return upb_inttable_done(iter); } +bool upb_oneof_done(upb_oneof_iter *iter) { + return upb_inttable_done(iter); +} -upb_fielddef *upb_msg_iter_field(const upb_msg_iter *iter) { +upb_fielddef *upb_oneof_iter_field(const upb_oneof_iter *iter) { return (upb_fielddef*)upb_value_getptr(upb_inttable_iter_value(iter)); } -void upb_msg_iter_setdone(upb_msg_iter *iter) { +void upb_oneof_iter_setdone(upb_oneof_iter *iter) { upb_inttable_iter_setdone(iter); } /* @@ -1452,8 +1819,10 @@ static void freehandlers(upb_refcounted *r) { static void visithandlers(const upb_refcounted *r, upb_refcounted_visit *visit, void *closure) { const upb_handlers *h = (const upb_handlers*)r; - upb_msg_iter i; - for(upb_msg_begin(&i, h->msg); !upb_msg_done(&i); upb_msg_next(&i)) { + upb_msg_field_iter i; + for(upb_msg_field_begin(&i, h->msg); + !upb_msg_field_done(&i); + upb_msg_field_next(&i)) { upb_fielddef *f = upb_msg_iter_field(&i); if (!upb_fielddef_issubmsg(f)) continue; const upb_handlers *sub = upb_handlers_getsubhandlers(h, f); @@ -1482,8 +1851,10 @@ static upb_handlers *newformsg(const upb_msgdef *m, const void *owner, // For each submessage field, get or create a handlers object and set it as // the subhandlers. - upb_msg_iter i; - for(upb_msg_begin(&i, m); !upb_msg_done(&i); upb_msg_next(&i)) { + upb_msg_field_iter i; + for(upb_msg_field_begin(&i, m); + !upb_msg_field_done(&i); + upb_msg_field_next(&i)) { upb_fielddef *f = upb_msg_iter_field(&i); if (!upb_fielddef_issubmsg(f)) continue; @@ -1840,8 +2211,10 @@ bool upb_handlers_freeze(upb_handlers *const*handlers, int n, upb_status *s) { // Check that there are no closure mismatches due to missing Start* handlers // or subhandlers with different type-level types. - upb_msg_iter j; - for(upb_msg_begin(&j, h->msg); !upb_msg_done(&j); upb_msg_next(&j)) { + upb_msg_field_iter j; + for(upb_msg_field_begin(&j, h->msg); + !upb_msg_field_done(&j); + upb_msg_field_next(&j)) { const upb_fielddef *f = upb_msg_iter_field(&j); if (upb_fielddef_isseq(f)) { @@ -3114,8 +3487,10 @@ static bool upb_resolve_dfs(const upb_def *def, upb_strtable *addtab, // For messages, continue the recursion by visiting all subdefs. const upb_msgdef *m = upb_dyncast_msgdef(def); if (m) { - upb_msg_iter i; - for(upb_msg_begin(&i, m); !upb_msg_done(&i); upb_msg_next(&i)) { + upb_msg_field_iter i; + for(upb_msg_field_begin(&i, m); + !upb_msg_field_done(&i); + upb_msg_field_next(&i)) { upb_fielddef *f = upb_msg_iter_field(&i); if (!upb_fielddef_hassubdef(f)) continue; // |= to avoid short-circuit; we need its side-effects. @@ -3268,8 +3643,10 @@ bool upb_symtab_add(upb_symtab *s, upb_def *const*defs, int n, void *ref_donor, // Type names are resolved relative to the message in which they appear. const char *base = upb_msgdef_fullname(m); - upb_msg_iter j; - for(upb_msg_begin(&j, m); !upb_msg_done(&j); upb_msg_next(&j)) { + upb_msg_field_iter j; + for(upb_msg_field_begin(&j, m); + !upb_msg_field_done(&j); + upb_msg_field_next(&j)) { upb_fielddef *f = upb_msg_iter_field(&j); const char *name = upb_fielddef_subdefname(f); if (name && !upb_fielddef_subdef(f)) { @@ -6462,8 +6839,10 @@ static void compile_method(compiler *c, upb_pbdecodermethod *method) { putsel(c, OP_STARTMSG, UPB_STARTMSG_SELECTOR, h); label(c, LABEL_FIELD); uint32_t* start_pc = c->pc; - upb_msg_iter i; - for(upb_msg_begin(&i, md); !upb_msg_done(&i); upb_msg_next(&i)) { + upb_msg_field_iter i; + for(upb_msg_field_begin(&i, md); + !upb_msg_field_done(&i); + upb_msg_field_next(&i)) { const upb_fielddef *f = upb_msg_iter_field(&i); upb_fieldtype_t type = upb_fielddef_type(f); @@ -6513,9 +6892,11 @@ static void find_methods(compiler *c, const upb_handlers *h) { newmethod(h, c->group); // Find submethods. - upb_msg_iter i; + upb_msg_field_iter i; const upb_msgdef *md = upb_handlers_msgdef(h); - for(upb_msg_begin(&i, md); !upb_msg_done(&i); upb_msg_next(&i)) { + for(upb_msg_field_begin(&i, md); + !upb_msg_field_done(&i); + upb_msg_field_next(&i)) { const upb_fielddef *f = upb_msg_iter_field(&i); const upb_handlers *sub_h; if (upb_fielddef_type(f) == UPB_TYPE_MESSAGE && @@ -6557,7 +6938,7 @@ static void set_bytecode_handlers(mgroup *g) { } -/* JIT setup. ******************************************************************/ +/* JIT setup. *****************************************************************/ #ifdef UPB_USE_JIT_X64 @@ -7980,8 +8361,10 @@ static void newhandlers_callback(const void *closure, upb_handlers *h) { upb_handlers_setendmsg(h, endmsg, NULL); const upb_msgdef *m = upb_handlers_msgdef(h); - upb_msg_iter i; - for(upb_msg_begin(&i, m); !upb_msg_done(&i); upb_msg_next(&i)) { + upb_msg_field_iter i; + for(upb_msg_field_begin(&i, m); + !upb_msg_field_done(&i); + upb_msg_field_next(&i)) { const upb_fielddef *f = upb_msg_iter_field(&i); bool packed = upb_fielddef_isseq(f) && upb_fielddef_isprimitive(f) && upb_fielddef_packed(f); @@ -8443,8 +8826,10 @@ static void onmreg(const void *c, upb_handlers *h) { upb_handlers_setstartmsg(h, textprinter_startmsg, NULL); upb_handlers_setendmsg(h, textprinter_endmsg, NULL); - upb_msg_iter i; - for(upb_msg_begin(&i, m); !upb_msg_done(&i); upb_msg_next(&i)) { + upb_msg_field_iter i; + for(upb_msg_field_begin(&i, m); + !upb_msg_field_done(&i); + upb_msg_field_next(&i)) { upb_fielddef *f = upb_msg_iter_field(&i); upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER; upb_handlerattr_sethandlerdata(&attr, f); @@ -8857,6 +9242,7 @@ badpadding: // the true value in a contiguous buffer. static void assert_accumulate_empty(upb_json_parser *p) { + UPB_UNUSED(p); assert(p->accumulated == NULL); assert(p->accumulated_len == 0); } @@ -9141,11 +9527,17 @@ static void start_number(upb_json_parser *p, const char *ptr) { capture_begin(p, ptr); } +static bool parse_number(upb_json_parser *p); + static bool end_number(upb_json_parser *p, const char *ptr) { if (!capture_end(p, ptr)) { return false; } + return parse_number(p); +} + +static bool parse_number(upb_json_parser *p) { // strtol() and friends unfortunately do not support specifying the length of // the input string, so we need to force a copy into a NULL-terminated buffer. if (!multipart_text(p, "\0", 1, false)) { @@ -9155,8 +9547,8 @@ static bool end_number(upb_json_parser *p, const char *ptr) { size_t len; const char *buf = accumulate_getptr(p, &len); const char *myend = buf + len - 1; // One for NULL. - char *end; + char *end; switch (upb_fielddef_type(p->top->f)) { case UPB_TYPE_ENUM: case UPB_TYPE_INT32: { @@ -9212,6 +9604,7 @@ static bool end_number(upb_json_parser *p, const char *ptr) { } multipart_end(p); + return true; err: @@ -9230,6 +9623,7 @@ static bool parser_putbool(upb_json_parser *p, bool val) { bool ok = upb_sink_putbool(&p->top->sink, parser_getsel(p), val); UPB_ASSERT_VAR(ok, ok); + return true; } @@ -9246,6 +9640,8 @@ static bool start_stringval(upb_json_parser *p) { upb_sink_startstr(&p->top->sink, sel, 0, &inner->sink); inner->m = p->top->m; inner->f = p->top->f; + inner->is_map = false; + inner->is_mapentry = false; p->top = inner; if (upb_fielddef_type(p->top->f) == UPB_TYPE_STRING) { @@ -9323,6 +9719,7 @@ static bool end_stringval(upb_json_parser *p) { } multipart_end(p); + return ok; } @@ -9331,54 +9728,217 @@ static void start_member(upb_json_parser *p) { multipart_startaccum(p); } -static bool end_member(upb_json_parser *p) { - assert(!p->top->f); +// Helper: invoked during parse_mapentry() to emit the mapentry message's key +// field based on the current contents of the accumulate buffer. +static bool parse_mapentry_key(upb_json_parser *p) { + size_t len; const char *buf = accumulate_getptr(p, &len); - const upb_fielddef *f = upb_msgdef_ntof(p->top->m, buf, len); + // Emit the key field. We do a bit of ad-hoc parsing here because the + // parser state machine has already decided that this is a string field + // name, and we are reinterpreting it as some arbitrary key type. In + // particular, integer and bool keys are quoted, so we need to parse the + // quoted string contents here. - if (!f) { - // TODO(haberman): Ignore unknown fields if requested/configured to do so. - upb_status_seterrf(p->status, "No such field: %.*s\n", (int)len, buf); + p->top->f = upb_msgdef_itof(p->top->m, UPB_MAPENTRY_KEY); + if (p->top->f == NULL) { + upb_status_seterrmsg(p->status, "mapentry message has no key"); return false; } + switch (upb_fielddef_type(p->top->f)) { + case UPB_TYPE_INT32: + case UPB_TYPE_INT64: + case UPB_TYPE_UINT32: + case UPB_TYPE_UINT64: + // Invoke end_number. The accum buffer has the number's text already. + if (!parse_number(p)) { + return false; + } + break; + case UPB_TYPE_BOOL: + if (len == 4 && !strncmp(buf, "true", 4)) { + if (!parser_putbool(p, true)) { + return false; + } + } else if (len == 5 && !strncmp(buf, "false", 5)) { + if (!parser_putbool(p, false)) { + return false; + } + } else { + upb_status_seterrmsg(p->status, + "Map bool key not 'true' or 'false'"); + return false; + } + multipart_end(p); + break; + case UPB_TYPE_STRING: + case UPB_TYPE_BYTES: { + upb_sink subsink; + upb_selector_t sel = getsel_for_handlertype(p, UPB_HANDLER_STARTSTR); + upb_sink_startstr(&p->top->sink, sel, len, &subsink); + sel = getsel_for_handlertype(p, UPB_HANDLER_STRING); + upb_sink_putstring(&subsink, sel, buf, len, NULL); + sel = getsel_for_handlertype(p, UPB_HANDLER_ENDSTR); + upb_sink_endstr(&subsink, sel); + multipart_end(p); + break; + } + default: + upb_status_seterrmsg(p->status, "Invalid field type for map key"); + return false; + } - p->top->f = f; - multipart_end(p); + return true; +} + +// Helper: emit one map entry (as a submessage in the map field sequence). This +// is invoked from end_membername(), at the end of the map entry's key string, +// with the map key in the accumulate buffer. It parses the key from that +// buffer, emits the handler calls to start the mapentry submessage (setting up +// its subframe in the process), and sets up state in the subframe so that the +// value parser (invoked next) will emit the mapentry's value field and then +// end the mapentry message. + +static bool handle_mapentry(upb_json_parser *p) { + // Map entry: p->top->sink is the seq frame, so we need to start a frame + // for the mapentry itself, and then set |f| in that frame so that the map + // value field is parsed, and also set a flag to end the frame after the + // map-entry value is parsed. + if (!check_stack(p)) return false; + + const upb_fielddef *mapfield = p->top->mapfield; + const upb_msgdef *mapentrymsg = upb_fielddef_msgsubdef(mapfield); + + upb_jsonparser_frame *inner = p->top + 1; + p->top->f = mapfield; + upb_selector_t sel = getsel_for_handlertype(p, UPB_HANDLER_STARTSUBMSG); + upb_sink_startsubmsg(&p->top->sink, sel, &inner->sink); + inner->m = mapentrymsg; + inner->mapfield = mapfield; + inner->is_map = false; + + // Don't set this to true *yet* -- we reuse parsing handlers below to push + // the key field value to the sink, and these handlers will pop the frame + // if they see is_mapentry (when invoked by the parser state machine, they + // would have just seen the map-entry value, not key). + inner->is_mapentry = false; + p->top = inner; + + // send STARTMSG in submsg frame. + upb_sink_startmsg(&p->top->sink); + + parse_mapentry_key(p); + + // Set up the value field to receive the map-entry value. + p->top->f = upb_msgdef_itof(p->top->m, UPB_MAPENTRY_VALUE); + p->top->is_mapentry = true; // set up to pop frame after value is parsed. + p->top->mapfield = mapfield; + if (p->top->f == NULL) { + upb_status_seterrmsg(p->status, "mapentry message has no value"); + return false; + } return true; } -static void clear_member(upb_json_parser *p) { p->top->f = NULL; } +static bool end_membername(upb_json_parser *p) { + assert(!p->top->f); + + if (p->top->is_map) { + return handle_mapentry(p); + } else { + size_t len; + const char *buf = accumulate_getptr(p, &len); + const upb_fielddef *f = upb_msgdef_ntof(p->top->m, buf, len); + + if (!f) { + // TODO(haberman): Ignore unknown fields if requested/configured to do so. + upb_status_seterrf(p->status, "No such field: %.*s\n", (int)len, buf); + return false; + } + + p->top->f = f; + multipart_end(p); + + return true; + } +} + +static void end_member(upb_json_parser *p) { + // If we just parsed a map-entry value, end that frame too. + if (p->top->is_mapentry) { + assert(p->top > p->stack); + // send ENDMSG on submsg. + upb_status s = UPB_STATUS_INIT; + upb_sink_endmsg(&p->top->sink, &s); + const upb_fielddef* mapfield = p->top->mapfield; + + // send ENDSUBMSG in repeated-field-of-mapentries frame. + p->top--; + upb_selector_t sel; + bool ok = upb_handlers_getselector(mapfield, + UPB_HANDLER_ENDSUBMSG, &sel); + UPB_ASSERT_VAR(ok, ok); + upb_sink_endsubmsg(&p->top->sink, sel); + } + + p->top->f = NULL; +} static bool start_subobject(upb_json_parser *p) { assert(p->top->f); - if (!upb_fielddef_issubmsg(p->top->f)) { + if (upb_fielddef_ismap(p->top->f)) { + // Beginning of a map. Start a new parser frame in a repeated-field + // context. + if (!check_stack(p)) return false; + + upb_jsonparser_frame *inner = p->top + 1; + upb_selector_t sel = getsel_for_handlertype(p, UPB_HANDLER_STARTSEQ); + upb_sink_startseq(&p->top->sink, sel, &inner->sink); + inner->m = upb_fielddef_msgsubdef(p->top->f); + inner->mapfield = p->top->f; + inner->f = NULL; + inner->is_map = true; + inner->is_mapentry = false; + p->top = inner; + + return true; + } else if (upb_fielddef_issubmsg(p->top->f)) { + // Beginning of a subobject. Start a new parser frame in the submsg + // context. + if (!check_stack(p)) return false; + + upb_jsonparser_frame *inner = p->top + 1; + + upb_selector_t sel = getsel_for_handlertype(p, UPB_HANDLER_STARTSUBMSG); + upb_sink_startsubmsg(&p->top->sink, sel, &inner->sink); + inner->m = upb_fielddef_msgsubdef(p->top->f); + inner->f = NULL; + inner->is_map = false; + inner->is_mapentry = false; + p->top = inner; + + return true; + } else { upb_status_seterrf(p->status, "Object specified for non-message/group field: %s", upb_fielddef_name(p->top->f)); return false; } - - if (!check_stack(p)) return false; - - upb_jsonparser_frame *inner = p->top + 1; - - upb_selector_t sel = getsel_for_handlertype(p, UPB_HANDLER_STARTSUBMSG); - upb_sink_startsubmsg(&p->top->sink, sel, &inner->sink); - inner->m = upb_fielddef_msgsubdef(p->top->f); - inner->f = NULL; - p->top = inner; - - return true; } static void end_subobject(upb_json_parser *p) { - p->top--; - upb_selector_t sel = getsel_for_handlertype(p, UPB_HANDLER_ENDSUBMSG); - upb_sink_endsubmsg(&p->top->sink, sel); + if (p->top->is_map) { + p->top--; + upb_selector_t sel = getsel_for_handlertype(p, UPB_HANDLER_ENDSEQ); + upb_sink_endseq(&p->top->sink, sel); + } else { + p->top--; + upb_selector_t sel = getsel_for_handlertype(p, UPB_HANDLER_ENDSUBMSG); + upb_sink_endsubmsg(&p->top->sink, sel); + } } static bool start_array(upb_json_parser *p) { @@ -9398,6 +9958,8 @@ static bool start_array(upb_json_parser *p) { upb_sink_startseq(&p->top->sink, sel, &inner->sink); inner->m = p->top->m; inner->f = p->top->f; + inner->is_map = false; + inner->is_mapentry = false; p->top = inner; return true; @@ -9412,12 +9974,16 @@ static void end_array(upb_json_parser *p) { } static void start_object(upb_json_parser *p) { - upb_sink_startmsg(&p->top->sink); + if (!p->top->is_map) { + upb_sink_startmsg(&p->top->sink); + } } static void end_object(upb_json_parser *p) { - upb_status status; - upb_sink_endmsg(&p->top->sink, &status); + if (!p->top->is_map) { + upb_status status; + upb_sink_endmsg(&p->top->sink, &status); + } } @@ -9442,11 +10008,11 @@ static void end_object(upb_json_parser *p) { // final state once, when the closing '"' is seen. -#line 904 "upb/json/parser.rl" +#line 1085 "upb/json/parser.rl" -#line 816 "upb/json/parser.c" +#line 997 "upb/json/parser.c" static const char _json_actions[] = { 0, 1, 0, 1, 2, 1, 3, 1, 5, 1, 6, 1, 7, 1, 8, 1, @@ -9597,7 +10163,7 @@ static const int json_en_value_machine = 27; static const int json_en_main = 1; -#line 907 "upb/json/parser.rl" +#line 1088 "upb/json/parser.rl" size_t parse(void *closure, const void *hd, const char *buf, size_t size, const upb_bufhandle *handle) { @@ -9617,7 +10183,7 @@ size_t parse(void *closure, const void *hd, const char *buf, size_t size, capture_resume(parser, buf); -#line 987 "upb/json/parser.c" +#line 1168 "upb/json/parser.c" { int _klen; unsigned int _trans; @@ -9692,118 +10258,118 @@ _match: switch ( *_acts++ ) { case 0: -#line 819 "upb/json/parser.rl" +#line 1000 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; case 1: -#line 820 "upb/json/parser.rl" +#line 1001 "upb/json/parser.rl" { p--; {stack[top++] = cs; cs = 10; goto _again;} } break; case 2: -#line 824 "upb/json/parser.rl" +#line 1005 "upb/json/parser.rl" { start_text(parser, p); } break; case 3: -#line 825 "upb/json/parser.rl" +#line 1006 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_text(parser, p)); } break; case 4: -#line 831 "upb/json/parser.rl" +#line 1012 "upb/json/parser.rl" { start_hex(parser); } break; case 5: -#line 832 "upb/json/parser.rl" +#line 1013 "upb/json/parser.rl" { hexdigit(parser, p); } break; case 6: -#line 833 "upb/json/parser.rl" +#line 1014 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_hex(parser)); } break; case 7: -#line 839 "upb/json/parser.rl" +#line 1020 "upb/json/parser.rl" { CHECK_RETURN_TOP(escape(parser, p)); } break; case 8: -#line 845 "upb/json/parser.rl" +#line 1026 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; case 9: -#line 848 "upb/json/parser.rl" +#line 1029 "upb/json/parser.rl" { {stack[top++] = cs; cs = 19; goto _again;} } break; case 10: -#line 850 "upb/json/parser.rl" +#line 1031 "upb/json/parser.rl" { p--; {stack[top++] = cs; cs = 27; goto _again;} } break; case 11: -#line 855 "upb/json/parser.rl" +#line 1036 "upb/json/parser.rl" { start_member(parser); } break; case 12: -#line 856 "upb/json/parser.rl" - { CHECK_RETURN_TOP(end_member(parser)); } +#line 1037 "upb/json/parser.rl" + { CHECK_RETURN_TOP(end_membername(parser)); } break; case 13: -#line 859 "upb/json/parser.rl" - { clear_member(parser); } +#line 1040 "upb/json/parser.rl" + { end_member(parser); } break; case 14: -#line 865 "upb/json/parser.rl" +#line 1046 "upb/json/parser.rl" { start_object(parser); } break; case 15: -#line 868 "upb/json/parser.rl" +#line 1049 "upb/json/parser.rl" { end_object(parser); } break; case 16: -#line 874 "upb/json/parser.rl" +#line 1055 "upb/json/parser.rl" { CHECK_RETURN_TOP(start_array(parser)); } break; case 17: -#line 878 "upb/json/parser.rl" +#line 1059 "upb/json/parser.rl" { end_array(parser); } break; case 18: -#line 883 "upb/json/parser.rl" +#line 1064 "upb/json/parser.rl" { start_number(parser, p); } break; case 19: -#line 884 "upb/json/parser.rl" +#line 1065 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_number(parser, p)); } break; case 20: -#line 886 "upb/json/parser.rl" +#line 1067 "upb/json/parser.rl" { CHECK_RETURN_TOP(start_stringval(parser)); } break; case 21: -#line 887 "upb/json/parser.rl" +#line 1068 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_stringval(parser)); } break; case 22: -#line 889 "upb/json/parser.rl" +#line 1070 "upb/json/parser.rl" { CHECK_RETURN_TOP(parser_putbool(parser, true)); } break; case 23: -#line 891 "upb/json/parser.rl" +#line 1072 "upb/json/parser.rl" { CHECK_RETURN_TOP(parser_putbool(parser, false)); } break; case 24: -#line 893 "upb/json/parser.rl" +#line 1074 "upb/json/parser.rl" { /* null value */ } break; case 25: -#line 895 "upb/json/parser.rl" +#line 1076 "upb/json/parser.rl" { CHECK_RETURN_TOP(start_subobject(parser)); } break; case 26: -#line 896 "upb/json/parser.rl" +#line 1077 "upb/json/parser.rl" { end_subobject(parser); } break; case 27: -#line 901 "upb/json/parser.rl" +#line 1082 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; -#line 1173 "upb/json/parser.c" +#line 1354 "upb/json/parser.c" } } @@ -9816,7 +10382,7 @@ _again: _out: {} } -#line 926 "upb/json/parser.rl" +#line 1107 "upb/json/parser.rl" if (p != pe) { upb_status_seterrf(parser->status, "Parse error at %s\n", p); @@ -9860,18 +10426,20 @@ void upb_json_parser_uninit(upb_json_parser *p) { void upb_json_parser_reset(upb_json_parser *p) { p->top = p->stack; p->top->f = NULL; + p->top->is_map = false; + p->top->is_mapentry = false; int cs; int top; // Emit Ragel initialization of the parser. -#line 1235 "upb/json/parser.c" +#line 1418 "upb/json/parser.c" { cs = json_start; top = 0; } -#line 974 "upb/json/parser.rl" +#line 1157 "upb/json/parser.rl" p->current_state = cs; p->parser_top = top; accumulate_clear(p); @@ -10072,13 +10640,23 @@ static bool putkey(void *closure, const void *handler_data) { return true; \ } \ static bool repeated_##type(void *closure, const void *handler_data, \ - type val) { \ + type val) { \ upb_json_printer *p = closure; \ print_comma(p); \ CHK(put##type(closure, handler_data, val)); \ return true; \ } +#define TYPE_HANDLERS_MAPKEY(type, fmt_func) \ + static bool putmapkey_##type(void *closure, const void *handler_data, \ + type val) { \ + upb_json_printer *p = closure; \ + print_data(p, "\"", 1); \ + CHK(put##type(closure, handler_data, val)); \ + print_data(p, "\":", 2); \ + return true; \ + } + TYPE_HANDLERS(double, fmt_double); TYPE_HANDLERS(float, fmt_float); TYPE_HANDLERS(bool, fmt_bool); @@ -10087,7 +10665,15 @@ TYPE_HANDLERS(uint32_t, fmt_int64); TYPE_HANDLERS(int64_t, fmt_int64); TYPE_HANDLERS(uint64_t, fmt_uint64); +// double and float are not allowed to be map keys. +TYPE_HANDLERS_MAPKEY(bool, fmt_bool); +TYPE_HANDLERS_MAPKEY(int32_t, fmt_int64); +TYPE_HANDLERS_MAPKEY(uint32_t, fmt_int64); +TYPE_HANDLERS_MAPKEY(int64_t, fmt_int64); +TYPE_HANDLERS_MAPKEY(uint64_t, fmt_uint64); + #undef TYPE_HANDLERS +#undef TYPE_HANDLERS_MAPKEY typedef struct { void *keyname; @@ -10112,20 +10698,36 @@ static bool scalar_enum(void *closure, const void *handler_data, return true; } -static bool repeated_enum(void *closure, const void *handler_data, - int32_t val) { - const EnumHandlerData *hd = handler_data; - upb_json_printer *p = closure; - print_comma(p); - - const char *symbolic_name = upb_enumdef_iton(hd->enumdef, val); +static void print_enum_symbolic_name(upb_json_printer *p, + const upb_enumdef *def, + int32_t val) { + const char *symbolic_name = upb_enumdef_iton(def, val); if (symbolic_name) { print_data(p, "\"", 1); putstring(p, symbolic_name, strlen(symbolic_name)); print_data(p, "\"", 1); } else { - putint32_t(closure, NULL, val); + putint32_t(p, NULL, val); } +} + +static bool repeated_enum(void *closure, const void *handler_data, + int32_t val) { + const EnumHandlerData *hd = handler_data; + upb_json_printer *p = closure; + print_comma(p); + + print_enum_symbolic_name(p, hd->enumdef, val); + + return true; +} + +static bool mapvalue_enum(void *closure, const void *handler_data, + int32_t val) { + const EnumHandlerData *hd = handler_data; + upb_json_printer *p = closure; + + print_enum_symbolic_name(p, hd->enumdef, val); return true; } @@ -10141,25 +10743,35 @@ static void *repeated_startsubmsg(void *closure, const void *handler_data) { return closure; } -static bool startmap(void *closure, const void *handler_data) { +static void start_frame(upb_json_printer *p) { + p->depth_++; + p->first_elem_[p->depth_] = true; + print_data(p, "{", 1); +} + +static void end_frame(upb_json_printer *p) { + print_data(p, "}", 1); + p->depth_--; +} + +static bool printer_startmsg(void *closure, const void *handler_data) { UPB_UNUSED(handler_data); upb_json_printer *p = closure; - if (p->depth_++ == 0) { + if (p->depth_ == 0) { upb_bytessink_start(p->output_, 0, &p->subc_); } - p->first_elem_[p->depth_] = true; - print_data(p, "{", 1); + start_frame(p); return true; } -static bool endmap(void *closure, const void *handler_data, upb_status *s) { +static bool printer_endmsg(void *closure, const void *handler_data, upb_status *s) { UPB_UNUSED(handler_data); UPB_UNUSED(s); upb_json_printer *p = closure; - if (--p->depth_ == 0) { + end_frame(p); + if (p->depth_ == 0) { upb_bytessink_end(p->output_); } - print_data(p, "}", 1); return true; } @@ -10180,6 +10792,23 @@ static bool endseq(void *closure, const void *handler_data) { return true; } +static void *startmap(void *closure, const void *handler_data) { + upb_json_printer *p = closure; + CHK(putkey(closure, handler_data)); + p->depth_++; + p->first_elem_[p->depth_] = true; + print_data(p, "{", 1); + return closure; +} + +static bool endmap(void *closure, const void *handler_data) { + UPB_UNUSED(handler_data); + upb_json_printer *p = closure; + print_data(p, "}", 1); + p->depth_--; + return true; +} + static size_t putstr(void *closure, const void *handler_data, const char *str, size_t len, const upb_bufhandle *handle) { UPB_UNUSED(handler_data); @@ -10294,6 +10923,36 @@ static bool repeated_endstr(void *closure, const void *handler_data) { return true; } +static void *mapkeyval_startstr(void *closure, const void *handler_data, + size_t size_hint) { + UPB_UNUSED(handler_data); + UPB_UNUSED(size_hint); + upb_json_printer *p = closure; + print_data(p, "\"", 1); + return p; +} + +static size_t mapkey_str(void *closure, const void *handler_data, + const char *str, size_t len, + const upb_bufhandle *handle) { + CHK(putstr(closure, handler_data, str, len, handle)); + return len; +} + +static bool mapkey_endstr(void *closure, const void *handler_data) { + UPB_UNUSED(handler_data); + upb_json_printer *p = closure; + print_data(p, "\":", 2); + return true; +} + +static bool mapvalue_endstr(void *closure, const void *handler_data) { + UPB_UNUSED(handler_data); + upb_json_printer *p = closure; + print_data(p, "\"", 1); + return true; +} + static size_t scalar_bytes(void *closure, const void *handler_data, const char *str, size_t len, const upb_bufhandle *handle) { @@ -10311,31 +10970,161 @@ static size_t repeated_bytes(void *closure, const void *handler_data, return len; } -void printer_sethandlers(const void *closure, upb_handlers *h) { +static size_t mapkey_bytes(void *closure, const void *handler_data, + const char *str, size_t len, + const upb_bufhandle *handle) { + upb_json_printer *p = closure; + CHK(putbytes(closure, handler_data, str, len, handle)); + print_data(p, ":", 1); + return len; +} + +static void set_enum_hd(upb_handlers *h, + const upb_fielddef *f, + upb_handlerattr *attr) { + EnumHandlerData *hd = malloc(sizeof(EnumHandlerData)); + hd->enumdef = (const upb_enumdef *)upb_fielddef_subdef(f); + hd->keyname = newstrpc(h, f); + upb_handlers_addcleanup(h, hd, free); + upb_handlerattr_sethandlerdata(attr, hd); +} + +// Set up handlers for a mapentry submessage (i.e., an individual key/value pair +// in a map). +// +// TODO: Handle missing key, missing value, out-of-order key/value, or repeated +// key or value cases properly. The right way to do this is to allocate a +// temporary structure at the start of a mapentry submessage, store key and +// value data in it as key and value handlers are called, and then print the +// key/value pair once at the end of the submessage. If we don't do this, we +// should at least detect the case and throw an error. However, so far all of +// our sources that emit mapentry messages do so canonically (with one key +// field, and then one value field), so this is not a pressing concern at the +// moment. +void printer_sethandlers_mapentry(const void *closure, upb_handlers *h) { UPB_UNUSED(closure); + const upb_msgdef *md = upb_handlers_msgdef(h); + // A mapentry message is printed simply as '"key": value'. Rather than + // special-case key and value for every type below, we just handle both + // fields explicitly here. + const upb_fielddef* key_field = upb_msgdef_itof(md, UPB_MAPENTRY_KEY); + const upb_fielddef* value_field = upb_msgdef_itof(md, UPB_MAPENTRY_VALUE); + + upb_handlerattr empty_attr = UPB_HANDLERATTR_INITIALIZER; + + switch (upb_fielddef_type(key_field)) { + case UPB_TYPE_INT32: + upb_handlers_setint32(h, key_field, putmapkey_int32_t, &empty_attr); + break; + case UPB_TYPE_INT64: + upb_handlers_setint64(h, key_field, putmapkey_int64_t, &empty_attr); + break; + case UPB_TYPE_UINT32: + upb_handlers_setuint32(h, key_field, putmapkey_uint32_t, &empty_attr); + break; + case UPB_TYPE_UINT64: + upb_handlers_setuint64(h, key_field, putmapkey_uint64_t, &empty_attr); + break; + case UPB_TYPE_BOOL: + upb_handlers_setbool(h, key_field, putmapkey_bool, &empty_attr); + break; + case UPB_TYPE_STRING: + upb_handlers_setstartstr(h, key_field, mapkeyval_startstr, &empty_attr); + upb_handlers_setstring(h, key_field, mapkey_str, &empty_attr); + upb_handlers_setendstr(h, key_field, mapkey_endstr, &empty_attr); + break; + case UPB_TYPE_BYTES: + upb_handlers_setstring(h, key_field, mapkey_bytes, &empty_attr); + break; + default: + assert(false); + break; + } + + switch (upb_fielddef_type(value_field)) { + case UPB_TYPE_INT32: + upb_handlers_setint32(h, value_field, putint32_t, &empty_attr); + break; + case UPB_TYPE_INT64: + upb_handlers_setint64(h, value_field, putint64_t, &empty_attr); + break; + case UPB_TYPE_UINT32: + upb_handlers_setuint32(h, value_field, putuint32_t, &empty_attr); + break; + case UPB_TYPE_UINT64: + upb_handlers_setuint64(h, value_field, putuint64_t, &empty_attr); + break; + case UPB_TYPE_BOOL: + upb_handlers_setbool(h, value_field, putbool, &empty_attr); + break; + case UPB_TYPE_FLOAT: + upb_handlers_setfloat(h, value_field, putfloat, &empty_attr); + break; + case UPB_TYPE_DOUBLE: + upb_handlers_setdouble(h, value_field, putdouble, &empty_attr); + break; + case UPB_TYPE_STRING: + upb_handlers_setstartstr(h, value_field, mapkeyval_startstr, &empty_attr); + upb_handlers_setstring(h, value_field, putstr, &empty_attr); + upb_handlers_setendstr(h, value_field, mapvalue_endstr, &empty_attr); + break; + case UPB_TYPE_BYTES: + upb_handlers_setstring(h, value_field, putbytes, &empty_attr); + break; + case UPB_TYPE_ENUM: { + upb_handlerattr enum_attr = UPB_HANDLERATTR_INITIALIZER; + set_enum_hd(h, value_field, &enum_attr); + upb_handlers_setint32(h, value_field, mapvalue_enum, &enum_attr); + upb_handlerattr_uninit(&enum_attr); + break; + } + case UPB_TYPE_MESSAGE: + // No handler necessary -- the submsg handlers will print the message + // as appropriate. + break; + } + + upb_handlerattr_uninit(&empty_attr); +} + +void printer_sethandlers(const void *closure, upb_handlers *h) { + UPB_UNUSED(closure); + const upb_msgdef *md = upb_handlers_msgdef(h); + bool is_mapentry = upb_msgdef_mapentry(md); upb_handlerattr empty_attr = UPB_HANDLERATTR_INITIALIZER; - upb_handlers_setstartmsg(h, startmap, &empty_attr); - upb_handlers_setendmsg(h, endmap, &empty_attr); - -#define TYPE(type, name, ctype) \ - case type: \ - if (upb_fielddef_isseq(f)) { \ - upb_handlers_set##name(h, f, repeated_##ctype, &empty_attr); \ - } else { \ - upb_handlers_set##name(h, f, scalar_##ctype, &name_attr); \ - } \ + + if (is_mapentry) { + // mapentry messages are sufficiently different that we handle them + // separately. + printer_sethandlers_mapentry(closure, h); + return; + } + + upb_handlers_setstartmsg(h, printer_startmsg, &empty_attr); + upb_handlers_setendmsg(h, printer_endmsg, &empty_attr); + +#define TYPE(type, name, ctype) \ + case type: \ + if (upb_fielddef_isseq(f)) { \ + upb_handlers_set##name(h, f, repeated_##ctype, &empty_attr); \ + } else { \ + upb_handlers_set##name(h, f, scalar_##ctype, &name_attr); \ + } \ break; - upb_msg_iter i; - upb_msg_begin(&i, upb_handlers_msgdef(h)); - for(; !upb_msg_done(&i); upb_msg_next(&i)) { + upb_msg_field_iter i; + upb_msg_field_begin(&i, md); + for(; !upb_msg_field_done(&i); upb_msg_field_next(&i)) { const upb_fielddef *f = upb_msg_iter_field(&i); upb_handlerattr name_attr = UPB_HANDLERATTR_INITIALIZER; upb_handlerattr_sethandlerdata(&name_attr, newstrpc(h, f)); - if (upb_fielddef_isseq(f)) { + if (upb_fielddef_ismap(f)) { + upb_handlers_setstartseq(h, f, startmap, &name_attr); + upb_handlers_setendseq(h, f, endmap, &name_attr); + } else if (upb_fielddef_isseq(f)) { upb_handlers_setstartseq(h, f, startseq, &name_attr); upb_handlers_setendseq(h, f, endseq, &empty_attr); } @@ -10352,12 +11141,8 @@ void printer_sethandlers(const void *closure, upb_handlers *h) { // For now, we always emit symbolic names for enums. We may want an // option later to control this behavior, but we will wait for a real // need first. - EnumHandlerData *hd = malloc(sizeof(EnumHandlerData)); - hd->enumdef = (const upb_enumdef *)upb_fielddef_subdef(f); - hd->keyname = newstrpc(h, f); - upb_handlers_addcleanup(h, hd, free); upb_handlerattr enum_attr = UPB_HANDLERATTR_INITIALIZER; - upb_handlerattr_sethandlerdata(&enum_attr, hd); + set_enum_hd(h, f, &enum_attr); if (upb_fielddef_isseq(f)) { upb_handlers_setint32(h, f, repeated_enum, &enum_attr); diff --git a/ruby/ext/google/protobuf_c/upb.h b/ruby/ext/google/protobuf_c/upb.h index fbcb8e99..8f6d3643 100644 --- a/ruby/ext/google/protobuf_c/upb.h +++ b/ruby/ext/google/protobuf_c/upb.h @@ -710,6 +710,9 @@ typedef struct { #define UPB_STRTABLE_INIT(count, mask, ctype, size_lg2, entries) \ {{count, mask, ctype, size_lg2, entries}} +#define UPB_EMPTY_STRTABLE_INIT(ctype) \ + UPB_STRTABLE_INIT(0, 0, ctype, 0, NULL) + typedef struct { upb_table t; // For entries that don't fit in the array part. const _upb_value *array; // Array part of the table. See const note above. @@ -1129,6 +1132,7 @@ class Def; class EnumDef; class FieldDef; class MessageDef; +class OneofDef; } #endif @@ -1136,6 +1140,7 @@ UPB_DECLARE_TYPE(upb::Def, upb_def); UPB_DECLARE_TYPE(upb::EnumDef, upb_enumdef); UPB_DECLARE_TYPE(upb::FieldDef, upb_fielddef); UPB_DECLARE_TYPE(upb::MessageDef, upb_msgdef); +UPB_DECLARE_TYPE(upb::OneofDef, upb_oneofdef); // Maximum field number allowed for FieldDefs. This is an inherent limit of the // protobuf wire format. @@ -1159,6 +1164,7 @@ typedef enum { UPB_DEF_MSG, UPB_DEF_FIELD, UPB_DEF_ENUM, + UPB_DEF_ONEOF, UPB_DEF_SERVICE, // Not yet implemented. UPB_DEF_ANY = -1, // Wildcard for upb_symtab_get*() } upb_deftype_t; @@ -1443,6 +1449,10 @@ UPB_DEFINE_DEF(upb::FieldDef, fielddef, FIELD, const MessageDef* containing_type() const; const char* containing_type_name(); + // The OneofDef to which this field belongs, or NULL if this field is not part + // of a oneof. + const OneofDef* containing_oneof() const; + // The field's type according to the enum in descriptor.proto. This is not // the same as UPB_TYPE_*, because it distinguishes between (for example) // INT32 and SINT32, whereas our "type" enum does not. This return of @@ -1456,6 +1466,7 @@ UPB_DEFINE_DEF(upb::FieldDef, fielddef, FIELD, bool IsString() const; bool IsSequence() const; bool IsPrimitive() const; + bool IsMap() const; // How integers are encoded. Only meaningful for integer types. // Defaults to UPB_INTFMT_VARIABLE, and is reset when "type" changes. @@ -1616,6 +1627,7 @@ UPB_DEFINE_STRUCT(upb_fielddef, upb_def, } sub; // The msgdef or enumdef for this field, if upb_hassubdef(f). bool subdef_is_symbolic; bool msg_is_symbolic; + const upb_oneofdef *oneof; bool default_is_string; bool type_is_set_; // False until type is explicitly set. bool is_extension_; @@ -1631,11 +1643,11 @@ UPB_DEFINE_STRUCT(upb_fielddef, upb_def, )); #define UPB_FIELDDEF_INIT(label, type, intfmt, tagdelim, is_extension, lazy, \ - packed, name, num, msgdef, subdef, selector_base, \ + packed, name, num, msgdef, subdef, selector_base, \ index, defaultval, refs, ref2s) \ { \ UPB_DEF_INIT(name, UPB_DEF_FIELD, refs, ref2s), defaultval, {msgdef}, \ - {subdef}, false, false, \ + {subdef}, NULL, false, false, \ type == UPB_TYPE_STRING || type == UPB_TYPE_BYTES, true, is_extension, \ lazy, packed, intfmt, tagdelim, type, label, num, selector_base, index \ } @@ -1669,6 +1681,7 @@ bool upb_fielddef_isextension(const upb_fielddef *f); bool upb_fielddef_lazy(const upb_fielddef *f); bool upb_fielddef_packed(const upb_fielddef *f); const upb_msgdef *upb_fielddef_containingtype(const upb_fielddef *f); +const upb_oneofdef *upb_fielddef_containingoneof(const upb_fielddef *f); upb_msgdef *upb_fielddef_containingtype_mutable(upb_fielddef *f); const char *upb_fielddef_containingtypename(upb_fielddef *f); upb_intfmt_t upb_fielddef_intfmt(const upb_fielddef *f); @@ -1678,6 +1691,7 @@ bool upb_fielddef_issubmsg(const upb_fielddef *f); bool upb_fielddef_isstring(const upb_fielddef *f); bool upb_fielddef_isseq(const upb_fielddef *f); bool upb_fielddef_isprimitive(const upb_fielddef *f); +bool upb_fielddef_ismap(const upb_fielddef *f); int64_t upb_fielddef_defaultint64(const upb_fielddef *f); int32_t upb_fielddef_defaultint32(const upb_fielddef *f); uint64_t upb_fielddef_defaultuint64(const upb_fielddef *f); @@ -1736,7 +1750,8 @@ UPB_END_EXTERN_C // } /* upb::MessageDef ************************************************************/ -typedef upb_inttable_iter upb_msg_iter; +typedef upb_inttable_iter upb_msg_field_iter; +typedef upb_strtable_iter upb_msg_oneof_iter; // Structure that describes a single .proto message type. // @@ -1766,14 +1781,37 @@ UPB_DEFINE_DEF(upb::MessageDef, msgdef, MSG, UPB_QUOTE( // The number of fields that belong to the MessageDef. int field_count() const; + // The number of oneofs that belong to the MessageDef. + int oneof_count() const; + // Adds a field (upb_fielddef object) to a msgdef. Requires that the msgdef // and the fielddefs are mutable. The fielddef's name and number must be // set, and the message may not already contain any field with this name or // number, and this fielddef may not be part of another message. In error // cases false is returned and the msgdef is unchanged. + // + // If the given field is part of a oneof, this call succeeds if and only if + // that oneof is already part of this msgdef. (Note that adding a oneof to a + // msgdef automatically adds all of its fields to the msgdef at the time that + // the oneof is added, so it is usually more idiomatic to add the oneof's + // fields first then add the oneof to the msgdef. This case is supported for + // convenience.) + // + // If |f| is already part of this MessageDef, this method performs no action + // and returns true (success). Thus, this method is idempotent. bool AddField(FieldDef* f, Status* s); bool AddField(const reffed_ptr<FieldDef>& f, Status* s); + // Adds a oneof (upb_oneofdef object) to a msgdef. Requires that the msgdef, + // oneof, and any fielddefs are mutable, that the fielddefs contained in the + // oneof do not have any name or number conflicts with existing fields in the + // msgdef, and that the oneof's name is unique among all oneofs in the msgdef. + // If the oneof is added successfully, all of its fields will be added + // directly to the msgdef as well. In error cases, false is returned and the + // msgdef is unchanged. + bool AddOneof(OneofDef* o, Status* s); + bool AddOneof(const reffed_ptr<OneofDef>& o, Status* s); + // These return NULL if the field is not found. FieldDef* FindFieldByNumber(uint32_t number); FieldDef* FindFieldByName(const char *name, size_t len); @@ -1797,6 +1835,25 @@ UPB_DEFINE_DEF(upb::MessageDef, msgdef, MSG, UPB_QUOTE( return FindFieldByName(str.c_str(), str.size()); } + OneofDef* FindOneofByName(const char* name, size_t len); + const OneofDef* FindOneofByName(const char* name, size_t len) const; + + OneofDef* FindOneofByName(const char* name) { + return FindOneofByName(name, strlen(name)); + } + const OneofDef* FindOneofByName(const char* name) const { + return FindOneofByName(name, strlen(name)); + } + + template<class T> + OneofDef* FindOneofByName(const T& str) { + return FindOneofByName(str.c_str(), str.size()); + } + template<class T> + const OneofDef* FindOneofByName(const T& str) const { + return FindOneofByName(str.c_str(), str.size()); + } + // Returns a new msgdef that is a copy of the given msgdef (and a copy of all // the fields) but with any references to submessages broken and replaced // with just the name of the submessage. Returns NULL if memory allocation @@ -1812,39 +1869,117 @@ UPB_DEFINE_DEF(upb::MessageDef, msgdef, MSG, UPB_QUOTE( bool mapentry() const; // Iteration over fields. The order is undefined. - class iterator : public std::iterator<std::forward_iterator_tag, FieldDef*> { + class field_iterator + : public std::iterator<std::forward_iterator_tag, FieldDef*> { public: - explicit iterator(MessageDef* md); - static iterator end(MessageDef* md); + explicit field_iterator(MessageDef* md); + static field_iterator end(MessageDef* md); void operator++(); FieldDef* operator*() const; - bool operator!=(const iterator& other) const; - bool operator==(const iterator& other) const; + bool operator!=(const field_iterator& other) const; + bool operator==(const field_iterator& other) const; private: - upb_msg_iter iter_; + upb_msg_field_iter iter_; }; - class const_iterator + class const_field_iterator : public std::iterator<std::forward_iterator_tag, const FieldDef*> { public: - explicit const_iterator(const MessageDef* md); - static const_iterator end(const MessageDef* md); + explicit const_field_iterator(const MessageDef* md); + static const_field_iterator end(const MessageDef* md); void operator++(); const FieldDef* operator*() const; - bool operator!=(const const_iterator& other) const; - bool operator==(const const_iterator& other) const; + bool operator!=(const const_field_iterator& other) const; + bool operator==(const const_field_iterator& other) const; private: - upb_msg_iter iter_; + upb_msg_field_iter iter_; }; - iterator begin(); - iterator end(); - const_iterator begin() const; - const_iterator end() const; + // Iteration over oneofs. The order is undefined. + class oneof_iterator + : public std::iterator<std::forward_iterator_tag, FieldDef*> { + public: + explicit oneof_iterator(MessageDef* md); + static oneof_iterator end(MessageDef* md); + + void operator++(); + OneofDef* operator*() const; + bool operator!=(const oneof_iterator& other) const; + bool operator==(const oneof_iterator& other) const; + + private: + upb_msg_oneof_iter iter_; + }; + + class const_oneof_iterator + : public std::iterator<std::forward_iterator_tag, const FieldDef*> { + public: + explicit const_oneof_iterator(const MessageDef* md); + static const_oneof_iterator end(const MessageDef* md); + + void operator++(); + const OneofDef* operator*() const; + bool operator!=(const const_oneof_iterator& other) const; + bool operator==(const const_oneof_iterator& other) const; + + private: + upb_msg_oneof_iter iter_; + }; + + class FieldAccessor { + public: + explicit FieldAccessor(MessageDef* msg) : msg_(msg) {} + field_iterator begin() { return msg_->field_begin(); } + field_iterator end() { return msg_->field_end(); } + private: + MessageDef* msg_; + }; + + class ConstFieldAccessor { + public: + explicit ConstFieldAccessor(const MessageDef* msg) : msg_(msg) {} + const_field_iterator begin() { return msg_->field_begin(); } + const_field_iterator end() { return msg_->field_end(); } + private: + const MessageDef* msg_; + }; + + class OneofAccessor { + public: + explicit OneofAccessor(MessageDef* msg) : msg_(msg) {} + oneof_iterator begin() { return msg_->oneof_begin(); } + oneof_iterator end() { return msg_->oneof_end(); } + private: + MessageDef* msg_; + }; + + class ConstOneofAccessor { + public: + explicit ConstOneofAccessor(const MessageDef* msg) : msg_(msg) {} + const_oneof_iterator begin() { return msg_->oneof_begin(); } + const_oneof_iterator end() { return msg_->oneof_end(); } + private: + const MessageDef* msg_; + }; + + field_iterator field_begin(); + field_iterator field_end(); + const_field_iterator field_begin() const; + const_field_iterator field_end() const; + + oneof_iterator oneof_begin(); + oneof_iterator oneof_end(); + const_oneof_iterator oneof_begin() const; + const_oneof_iterator oneof_end() const; + + FieldAccessor fields() { return FieldAccessor(this); } + ConstFieldAccessor fields() const { return ConstFieldAccessor(this); } + OneofAccessor oneofs() { return OneofAccessor(this); } + ConstOneofAccessor oneofs() const { return ConstOneofAccessor(this); } private: UPB_DISALLOW_POD_OPS(MessageDef, upb::MessageDef); @@ -1857,6 +1992,9 @@ UPB_DEFINE_STRUCT(upb_msgdef, upb_def, upb_inttable itof; // int to field upb_strtable ntof; // name to field + // Tables for looking up oneofs by name. + upb_strtable ntoo; // name to oneof + // Is this a map-entry message? // TODO: set this flag properly for static descriptors; regenerate // descriptor.upb.c. @@ -1865,11 +2003,14 @@ UPB_DEFINE_STRUCT(upb_msgdef, upb_def, // TODO(haberman): proper extension ranges (there can be multiple). )); +// TODO: also support static initialization of the oneofs table. This will be +// needed if we compile in descriptors that contain oneofs. #define UPB_MSGDEF_INIT(name, selector_count, submsg_field_count, itof, ntof, \ refs, ref2s) \ { \ UPB_DEF_INIT(name, UPB_DEF_MSG, refs, ref2s), selector_count, \ - submsg_field_count, itof, ntof, false \ + submsg_field_count, itof, ntof, \ + UPB_EMPTY_STRTABLE_INIT(UPB_CTYPE_PTR), false \ } UPB_BEGIN_EXTERN_C // { @@ -1893,6 +2034,8 @@ bool upb_msgdef_setfullname(upb_msgdef *m, const char *fullname, upb_status *s); upb_msgdef *upb_msgdef_dup(const upb_msgdef *m, const void *owner); bool upb_msgdef_addfield(upb_msgdef *m, upb_fielddef *f, const void *ref_donor, upb_status *s); +bool upb_msgdef_addoneof(upb_msgdef *m, upb_oneofdef *o, const void *ref_donor, + upb_status *s); // Field lookup in a couple of different variations: // - itof = int to field @@ -1917,11 +2060,38 @@ UPB_INLINE upb_fielddef *upb_msgdef_ntof_mutable(upb_msgdef *m, return (upb_fielddef *)upb_msgdef_ntof(m, name, len); } +// Oneof lookup: +// - ntoo = name to oneof +// - ntooz = name to oneof, null-terminated string. +const upb_oneofdef *upb_msgdef_ntoo(const upb_msgdef *m, const char *name, + size_t len); +int upb_msgdef_numoneofs(const upb_msgdef *m); + +UPB_INLINE const upb_oneofdef *upb_msgdef_ntooz(const upb_msgdef *m, + const char *name) { + return upb_msgdef_ntoo(m, name, strlen(name)); +} + +UPB_INLINE upb_oneofdef *upb_msgdef_ntoo_mutable(upb_msgdef *m, + const char *name, size_t len) { + return (upb_oneofdef *)upb_msgdef_ntoo(m, name, len); +} + void upb_msgdef_setmapentry(upb_msgdef *m, bool map_entry); bool upb_msgdef_mapentry(const upb_msgdef *m); -// upb_msg_iter i; -// for(upb_msg_begin(&i, m); !upb_msg_done(&i); upb_msg_next(&i)) { +// Well-known field tag numbers for map-entry messages. +#define UPB_MAPENTRY_KEY 1 +#define UPB_MAPENTRY_VALUE 2 + +const upb_oneofdef *upb_msgdef_findoneof(const upb_msgdef *m, + const char *name); +int upb_msgdef_numoneofs(const upb_msgdef *m); + +// upb_msg_field_iter i; +// for(upb_msg_field_begin(&i, m); +// !upb_msg_field_done(&i); +// upb_msg_field_next(&i)) { // upb_fielddef *f = upb_msg_iter_field(&i); // // ... // } @@ -1929,11 +2099,18 @@ bool upb_msgdef_mapentry(const upb_msgdef *m); // For C we don't have separate iterators for const and non-const. // It is the caller's responsibility to cast the upb_fielddef* to // const if the upb_msgdef* is const. -void upb_msg_begin(upb_msg_iter *iter, const upb_msgdef *m); -void upb_msg_next(upb_msg_iter *iter); -bool upb_msg_done(const upb_msg_iter *iter); -upb_fielddef *upb_msg_iter_field(const upb_msg_iter *iter); -void upb_msg_iter_setdone(upb_msg_iter *iter); +void upb_msg_field_begin(upb_msg_field_iter *iter, const upb_msgdef *m); +void upb_msg_field_next(upb_msg_field_iter *iter); +bool upb_msg_field_done(const upb_msg_field_iter *iter); +upb_fielddef *upb_msg_iter_field(const upb_msg_field_iter *iter); +void upb_msg_field_iter_setdone(upb_msg_field_iter *iter); + +// Similar to above, we also support iterating through the oneofs in a msgdef. +void upb_msg_oneof_begin(upb_msg_oneof_iter *iter, const upb_msgdef *m); +void upb_msg_oneof_next(upb_msg_oneof_iter *iter); +bool upb_msg_oneof_done(const upb_msg_oneof_iter *iter); +upb_oneofdef *upb_msg_iter_oneof(const upb_msg_oneof_iter *iter); +void upb_msg_oneof_iter_setdone(upb_msg_oneof_iter *iter); UPB_END_EXTERN_C // } @@ -2075,6 +2252,172 @@ int32_t upb_enum_iter_number(upb_enum_iter *iter); UPB_END_EXTERN_C // } +/* upb::OneofDef **************************************************************/ + +typedef upb_inttable_iter upb_oneof_iter; + +// Class that represents a oneof. Its base class is upb::Def (convert with +// upb::upcast()). +UPB_DEFINE_DEF(upb::OneofDef, oneofdef, ONEOF, UPB_QUOTE( + public: + // Returns NULL if memory allocation failed. + static reffed_ptr<OneofDef> New(); + + // Functionality from upb::RefCounted. + bool IsFrozen() const; + void Ref(const void* owner) const; + void Unref(const void* owner) const; + void DonateRef(const void* from, const void* to) const; + void CheckRef(const void* owner) const; + + // Functionality from upb::Def. + const char* full_name() const; + + // Returns the MessageDef that owns this OneofDef. + const MessageDef* containing_type() const; + + // Returns the name of this oneof. This is the name used to look up the oneof + // by name once added to a message def. + const char* name() const; + bool set_name(const char* name, Status* s); + + // Returns the number of fields currently defined in the oneof. + int field_count() const; + + // Adds a field to the oneof. The field must not have been added to any other + // oneof or msgdef. If the oneof is not yet part of a msgdef, then when the + // oneof is eventually added to a msgdef, all fields added to the oneof will + // also be added to the msgdef at that time. If the oneof is already part of a + // msgdef, the field must either be a part of that msgdef already, or must not + // be a part of any msgdef; in the latter case, the field is added to the + // msgdef as a part of this operation. + // + // The field may only have an OPTIONAL label, never REQUIRED or REPEATED. + // + // If |f| is already part of this MessageDef, this method performs no action + // and returns true (success). Thus, this method is idempotent. + bool AddField(FieldDef* field, Status* s); + bool AddField(const reffed_ptr<FieldDef>& field, Status* s); + + // Looks up by name. + const FieldDef* FindFieldByName(const char* name, size_t len) const; + FieldDef* FindFieldByName(const char* name, size_t len); + const FieldDef* FindFieldByName(const char* name) const { + return FindFieldByName(name, strlen(name)); + } + FieldDef* FindFieldByName(const char* name) { + return FindFieldByName(name, strlen(name)); + } + + template <class T> + FieldDef* FindFieldByName(const T& str) { + return FindFieldByName(str.c_str(), str.size()); + } + template <class T> + const FieldDef* FindFieldByName(const T& str) const { + return FindFieldByName(str.c_str(), str.size()); + } + + // Looks up by tag number. + const FieldDef* FindFieldByNumber(uint32_t num) const; + + // Returns a new OneofDef with all the same fields. The OneofDef will be owned + // by the given owner. + OneofDef* Dup(const void* owner) const; + + // Iteration over fields. The order is undefined. + class iterator : public std::iterator<std::forward_iterator_tag, FieldDef*> { + public: + explicit iterator(OneofDef* md); + static iterator end(OneofDef* md); + + void operator++(); + FieldDef* operator*() const; + bool operator!=(const iterator& other) const; + bool operator==(const iterator& other) const; + + private: + upb_oneof_iter iter_; + }; + + class const_iterator + : public std::iterator<std::forward_iterator_tag, const FieldDef*> { + public: + explicit const_iterator(const OneofDef* md); + static const_iterator end(const OneofDef* md); + + void operator++(); + const FieldDef* operator*() const; + bool operator!=(const const_iterator& other) const; + bool operator==(const const_iterator& other) const; + + private: + upb_oneof_iter iter_; + }; + + iterator begin(); + iterator end(); + const_iterator begin() const; + const_iterator end() const; + + private: + UPB_DISALLOW_POD_OPS(OneofDef, upb::OneofDef); +), +UPB_DEFINE_STRUCT(upb_oneofdef, upb_def, + upb_strtable ntof; + upb_inttable itof; + const upb_msgdef *parent; +)); + +#define UPB_ONEOFDEF_INIT(name, ntof, itof, refs, ref2s) \ + { UPB_DEF_INIT(name, UPB_DEF_ENUM, refs, ref2s), ntof, itof } + +UPB_BEGIN_EXTERN_C // { + +// Native C API. +upb_oneofdef *upb_oneofdef_new(const void *owner); +upb_oneofdef *upb_oneofdef_dup(const upb_oneofdef *o, const void *owner); + +// From upb_refcounted. +void upb_oneofdef_unref(const upb_oneofdef *o, const void *owner); +bool upb_oneofdef_isfrozen(const upb_oneofdef *e); +void upb_oneofdef_ref(const upb_oneofdef *o, const void *owner); +void upb_oneofdef_donateref(const upb_oneofdef *m, const void *from, + const void *to); +void upb_oneofdef_checkref(const upb_oneofdef *o, const void *owner); + +const char *upb_oneofdef_name(const upb_oneofdef *o); +bool upb_oneofdef_setname(upb_oneofdef *o, const char *name, upb_status *s); + +const upb_msgdef *upb_oneofdef_containingtype(const upb_oneofdef *o); +int upb_oneofdef_numfields(const upb_oneofdef *o); +bool upb_oneofdef_addfield(upb_oneofdef *o, upb_fielddef *f, + const void *ref_donor, + upb_status *s); + +// Oneof lookups: +// - ntof: look up a field by name. +// - ntofz: look up a field by name (as a null-terminated string). +// - itof: look up a field by number. +const upb_fielddef *upb_oneofdef_ntof(const upb_oneofdef *o, + const char *name, size_t length); +UPB_INLINE const upb_fielddef *upb_oneofdef_ntofz(const upb_oneofdef *o, + const char *name) { + return upb_oneofdef_ntof(o, name, strlen(name)); +} +const upb_fielddef *upb_oneofdef_itof(const upb_oneofdef *o, uint32_t num); + +// upb_oneof_iter i; +// for(upb_oneof_begin(&i, e); !upb_oneof_done(&i); upb_oneof_next(&i)) { +// // ... +// } +void upb_oneof_begin(upb_oneof_iter *iter, const upb_oneofdef *o); +void upb_oneof_next(upb_oneof_iter *iter); +bool upb_oneof_done(upb_oneof_iter *iter); +upb_fielddef *upb_oneof_iter_field(const upb_oneof_iter *iter); +void upb_oneof_iter_setdone(upb_oneof_iter *iter); + +UPB_END_EXTERN_C // } #ifdef __cplusplus @@ -2201,6 +2544,9 @@ inline void FieldDef::set_packed(bool packed) { inline const MessageDef* FieldDef::containing_type() const { return upb_fielddef_containingtype(this); } +inline const OneofDef* FieldDef::containing_oneof() const { + return upb_fielddef_containingoneof(this); +} inline const char* FieldDef::containing_type_name() { return upb_fielddef_containingtypename(this); } @@ -2237,6 +2583,7 @@ inline bool FieldDef::IsSubMessage() const { } inline bool FieldDef::IsString() const { return upb_fielddef_isstring(this); } inline bool FieldDef::IsSequence() const { return upb_fielddef_isseq(this); } +inline bool FieldDef::IsMap() const { return upb_fielddef_ismap(this); } inline int64_t FieldDef::default_int64() const { return upb_fielddef_defaultint64(this); } @@ -2351,12 +2698,21 @@ inline bool MessageDef::Freeze(Status* status) { inline int MessageDef::field_count() const { return upb_msgdef_numfields(this); } +inline int MessageDef::oneof_count() const { + return upb_msgdef_numoneofs(this); +} inline bool MessageDef::AddField(upb_fielddef* f, Status* s) { return upb_msgdef_addfield(this, f, NULL, s); } inline bool MessageDef::AddField(const reffed_ptr<FieldDef>& f, Status* s) { return upb_msgdef_addfield(this, f.get(), NULL, s); } +inline bool MessageDef::AddOneof(upb_oneofdef* o, Status* s) { + return upb_msgdef_addoneof(this, o, NULL, s); +} +inline bool MessageDef::AddOneof(const reffed_ptr<OneofDef>& o, Status* s) { + return upb_msgdef_addoneof(this, o.get(), NULL, s); +} inline FieldDef* MessageDef::FindFieldByNumber(uint32_t number) { return upb_msgdef_itof_mutable(this, number); } @@ -2370,6 +2726,13 @@ inline const FieldDef *MessageDef::FindFieldByName(const char *name, size_t len) const { return upb_msgdef_ntof(this, name, len); } +inline OneofDef* MessageDef::FindOneofByName(const char* name, size_t len) { + return upb_msgdef_ntoo_mutable(this, name, len); +} +inline const OneofDef* MessageDef::FindOneofByName(const char* name, + size_t len) const { + return upb_msgdef_ntoo(this, name, len); +} inline MessageDef* MessageDef::Dup(const void *owner) const { return upb_msgdef_dup(this, owner); } @@ -2379,55 +2742,127 @@ inline void MessageDef::setmapentry(bool map_entry) { inline bool MessageDef::mapentry() const { return upb_msgdef_mapentry(this); } -inline MessageDef::iterator MessageDef::begin() { return iterator(this); } -inline MessageDef::iterator MessageDef::end() { return iterator::end(this); } -inline MessageDef::const_iterator MessageDef::begin() const { - return const_iterator(this); +inline MessageDef::field_iterator MessageDef::field_begin() { + return field_iterator(this); } -inline MessageDef::const_iterator MessageDef::end() const { - return const_iterator::end(this); +inline MessageDef::field_iterator MessageDef::field_end() { + return field_iterator::end(this); +} +inline MessageDef::const_field_iterator MessageDef::field_begin() const { + return const_field_iterator(this); +} +inline MessageDef::const_field_iterator MessageDef::field_end() const { + return const_field_iterator::end(this); } -inline MessageDef::iterator::iterator(MessageDef* md) { - upb_msg_begin(&iter_, md); +inline MessageDef::oneof_iterator MessageDef::oneof_begin() { + return oneof_iterator(this); +} +inline MessageDef::oneof_iterator MessageDef::oneof_end() { + return oneof_iterator::end(this); +} +inline MessageDef::const_oneof_iterator MessageDef::oneof_begin() const { + return const_oneof_iterator(this); +} +inline MessageDef::const_oneof_iterator MessageDef::oneof_end() const { + return const_oneof_iterator::end(this); } -inline MessageDef::iterator MessageDef::iterator::end(MessageDef* md) { - MessageDef::iterator iter(md); - upb_msg_iter_setdone(&iter.iter_); + +inline MessageDef::field_iterator::field_iterator(MessageDef* md) { + upb_msg_field_begin(&iter_, md); +} +inline MessageDef::field_iterator MessageDef::field_iterator::end( + MessageDef* md) { + MessageDef::field_iterator iter(md); + upb_msg_field_iter_setdone(&iter.iter_); return iter; } -inline FieldDef* MessageDef::iterator::operator*() const { +inline FieldDef* MessageDef::field_iterator::operator*() const { return upb_msg_iter_field(&iter_); } -inline void MessageDef::iterator::operator++() { return upb_msg_next(&iter_); } -inline bool MessageDef::iterator::operator==(const iterator &other) const { +inline void MessageDef::field_iterator::operator++() { + return upb_msg_field_next(&iter_); +} +inline bool MessageDef::field_iterator::operator==( + const field_iterator &other) const { return upb_inttable_iter_isequal(&iter_, &other.iter_); } -inline bool MessageDef::iterator::operator!=(const iterator &other) const { +inline bool MessageDef::field_iterator::operator!=( + const field_iterator &other) const { return !(*this == other); } -inline MessageDef::const_iterator::const_iterator(const MessageDef* md) { - upb_msg_begin(&iter_, md); +inline MessageDef::const_field_iterator::const_field_iterator( + const MessageDef* md) { + upb_msg_field_begin(&iter_, md); } -inline MessageDef::const_iterator MessageDef::const_iterator::end( +inline MessageDef::const_field_iterator MessageDef::const_field_iterator::end( const MessageDef *md) { - MessageDef::const_iterator iter(md); - upb_msg_iter_setdone(&iter.iter_); + MessageDef::const_field_iterator iter(md); + upb_msg_field_iter_setdone(&iter.iter_); return iter; } -inline const FieldDef* MessageDef::const_iterator::operator*() const { +inline const FieldDef* MessageDef::const_field_iterator::operator*() const { return upb_msg_iter_field(&iter_); } -inline void MessageDef::const_iterator::operator++() { - return upb_msg_next(&iter_); +inline void MessageDef::const_field_iterator::operator++() { + return upb_msg_field_next(&iter_); } -inline bool MessageDef::const_iterator::operator==( - const const_iterator &other) const { +inline bool MessageDef::const_field_iterator::operator==( + const const_field_iterator &other) const { return upb_inttable_iter_isequal(&iter_, &other.iter_); } -inline bool MessageDef::const_iterator::operator!=( - const const_iterator &other) const { +inline bool MessageDef::const_field_iterator::operator!=( + const const_field_iterator &other) const { + return !(*this == other); +} + +inline MessageDef::oneof_iterator::oneof_iterator(MessageDef* md) { + upb_msg_oneof_begin(&iter_, md); +} +inline MessageDef::oneof_iterator MessageDef::oneof_iterator::end( + MessageDef* md) { + MessageDef::oneof_iterator iter(md); + upb_msg_oneof_iter_setdone(&iter.iter_); + return iter; +} +inline OneofDef* MessageDef::oneof_iterator::operator*() const { + return upb_msg_iter_oneof(&iter_); +} +inline void MessageDef::oneof_iterator::operator++() { + return upb_msg_oneof_next(&iter_); +} +inline bool MessageDef::oneof_iterator::operator==( + const oneof_iterator &other) const { + return upb_strtable_iter_isequal(&iter_, &other.iter_); +} +inline bool MessageDef::oneof_iterator::operator!=( + const oneof_iterator &other) const { + return !(*this == other); +} + +inline MessageDef::const_oneof_iterator::const_oneof_iterator( + const MessageDef* md) { + upb_msg_oneof_begin(&iter_, md); +} +inline MessageDef::const_oneof_iterator MessageDef::const_oneof_iterator::end( + const MessageDef *md) { + MessageDef::const_oneof_iterator iter(md); + upb_msg_oneof_iter_setdone(&iter.iter_); + return iter; +} +inline const OneofDef* MessageDef::const_oneof_iterator::operator*() const { + return upb_msg_iter_oneof(&iter_); +} +inline void MessageDef::const_oneof_iterator::operator++() { + return upb_msg_oneof_next(&iter_); +} +inline bool MessageDef::const_oneof_iterator::operator==( + const const_oneof_iterator &other) const { + return upb_strtable_iter_isequal(&iter_, &other.iter_); +} +inline bool MessageDef::const_oneof_iterator::operator!=( + const const_oneof_iterator &other) const { return !(*this == other); } @@ -2495,6 +2930,105 @@ inline const char* EnumDef::Iterator::name() { } inline bool EnumDef::Iterator::Done() { return upb_enum_done(&iter_); } inline void EnumDef::Iterator::Next() { return upb_enum_next(&iter_); } + +inline reffed_ptr<OneofDef> OneofDef::New() { + upb_oneofdef *o = upb_oneofdef_new(&o); + return reffed_ptr<OneofDef>(o, &o); +} +inline bool OneofDef::IsFrozen() const { return upb_oneofdef_isfrozen(this); } +inline void OneofDef::Ref(const void* owner) const { + return upb_oneofdef_ref(this, owner); +} +inline void OneofDef::Unref(const void* owner) const { + return upb_oneofdef_unref(this, owner); +} +inline void OneofDef::DonateRef(const void* from, const void* to) const { + return upb_oneofdef_donateref(this, from, to); +} +inline void OneofDef::CheckRef(const void* owner) const { + return upb_oneofdef_checkref(this, owner); +} +inline const char* OneofDef::full_name() const { + return upb_oneofdef_name(this); +} + +inline const MessageDef* OneofDef::containing_type() const { + return upb_oneofdef_containingtype(this); +} +inline const char* OneofDef::name() const { + return upb_oneofdef_name(this); +} +inline bool OneofDef::set_name(const char* name, Status* s) { + return upb_oneofdef_setname(this, name, s); +} +inline int OneofDef::field_count() const { + return upb_oneofdef_numfields(this); +} +inline bool OneofDef::AddField(FieldDef* field, Status* s) { + return upb_oneofdef_addfield(this, field, NULL, s); +} +inline bool OneofDef::AddField(const reffed_ptr<FieldDef>& field, Status* s) { + return upb_oneofdef_addfield(this, field.get(), NULL, s); +} +inline const FieldDef* OneofDef::FindFieldByName(const char* name, + size_t len) const { + return upb_oneofdef_ntof(this, name, len); +} +inline const FieldDef* OneofDef::FindFieldByNumber(uint32_t num) const { + return upb_oneofdef_itof(this, num); +} +inline OneofDef::iterator OneofDef::begin() { return iterator(this); } +inline OneofDef::iterator OneofDef::end() { return iterator::end(this); } +inline OneofDef::const_iterator OneofDef::begin() const { + return const_iterator(this); +} +inline OneofDef::const_iterator OneofDef::end() const { + return const_iterator::end(this); +} + +inline OneofDef::iterator::iterator(OneofDef* o) { + upb_oneof_begin(&iter_, o); +} +inline OneofDef::iterator OneofDef::iterator::end(OneofDef* o) { + OneofDef::iterator iter(o); + upb_oneof_iter_setdone(&iter.iter_); + return iter; +} +inline FieldDef* OneofDef::iterator::operator*() const { + return upb_oneof_iter_field(&iter_); +} +inline void OneofDef::iterator::operator++() { return upb_oneof_next(&iter_); } +inline bool OneofDef::iterator::operator==(const iterator &other) const { + return upb_inttable_iter_isequal(&iter_, &other.iter_); +} +inline bool OneofDef::iterator::operator!=(const iterator &other) const { + return !(*this == other); +} + +inline OneofDef::const_iterator::const_iterator(const OneofDef* md) { + upb_oneof_begin(&iter_, md); +} +inline OneofDef::const_iterator OneofDef::const_iterator::end( + const OneofDef *md) { + OneofDef::const_iterator iter(md); + upb_oneof_iter_setdone(&iter.iter_); + return iter; +} +inline const FieldDef* OneofDef::const_iterator::operator*() const { + return upb_msg_iter_field(&iter_); +} +inline void OneofDef::const_iterator::operator++() { + return upb_oneof_next(&iter_); +} +inline bool OneofDef::const_iterator::operator==( + const const_iterator &other) const { + return upb_inttable_iter_isequal(&iter_, &other.iter_); +} +inline bool OneofDef::const_iterator::operator!=( + const const_iterator &other) const { + return !(*this == other); +} + } // namespace upb #endif @@ -7295,12 +7829,30 @@ class Parser; UPB_DECLARE_TYPE(upb::json::Parser, upb_json_parser); -// Internal-only struct used by the parser. +// Internal-only struct used by the parser. A parser frame corresponds +// one-to-one with a handler (sink) frame. typedef struct { UPB_PRIVATE_FOR_CPP upb_sink sink; + // The current message in which we're parsing, and the field whose value we're + // expecting next. const upb_msgdef *m; const upb_fielddef *f; + + // We are in a repeated-field context, ready to emit mapentries as + // submessages. This flag alters the start-of-object (open-brace) behavior to + // begin a sequence of mapentry messages rather than a single submessage. + bool is_map; + // We are in a map-entry message context. This flag is set when parsing the + // value field of a single map entry and indicates to all value-field parsers + // (subobjects, strings, numbers, and bools) that the map-entry submessage + // should end as soon as the value is parsed. + bool is_mapentry; + // If |is_map| or |is_mapentry| is true, |mapfield| refers to the parent + // message's map field that we're currently parsing. This differs from |f| + // because |f| is the field in the *current* message (i.e., the map-entry + // message itself), not the parent's field that leads to this map. + const upb_fielddef *mapfield; } upb_jsonparser_frame; diff --git a/ruby/google-protobuf.gemspec b/ruby/google-protobuf.gemspec index 87033ac4..371009bb 100644 --- a/ruby/google-protobuf.gemspec +++ b/ruby/google-protobuf.gemspec @@ -7,7 +7,7 @@ end Gem::Specification.new do |s| s.name = "google-protobuf" - s.version = "3.0.0.alpha.2" + s.version = "3.0.0.alpha.3.0.pre" s.licenses = ["BSD"] s.summary = "Protocol Buffers" s.description = "Protocol Buffers are Google's data interchange format." @@ -18,5 +18,7 @@ Gem::Specification.new do |s| s.files = ["lib/google/protobuf.rb"] + # extension C source find_c_source("ext/google/protobuf_c") - s.test_files = `git ls-files -- tests`.split + s.test_files = ["tests/basic.rb", + "tests/stress.rb", + "tests/generated_code_test.rb"] end diff --git a/ruby/tests/basic.rb b/ruby/tests/basic.rb index 92655033..a78cc394 100644 --- a/ruby/tests/basic.rb +++ b/ruby/tests/basic.rb @@ -73,6 +73,15 @@ module BasicTest optional :key, :string, 1 optional :value, :message, 2, "TestMessage2" end + + add_message "OneofMessage" do + oneof :my_oneof do + optional :a, :string, 1 + optional :b, :int32, 2 + optional :c, :message, 3, "TestMessage2" + optional :d, :enum, 4, "TestEnum" + end + end end TestMessage = pool.lookup("TestMessage").msgclass @@ -87,6 +96,7 @@ module BasicTest pool.lookup("MapMessageWireEquiv_entry1").msgclass MapMessageWireEquiv_entry2 = pool.lookup("MapMessageWireEquiv_entry2").msgclass + OneofMessage = pool.lookup("OneofMessage").msgclass # ------------ test cases --------------- @@ -226,7 +236,8 @@ module BasicTest assert l.count == 0 l = Google::Protobuf::RepeatedField.new(:int32, [1, 2, 3]) assert l.count == 3 - assert l == [1, 2, 3] + assert_equal [1, 2, 3], l + assert_equal l, [1, 2, 3] l.push 4 assert l == [1, 2, 3, 4] dst_list = [] @@ -380,7 +391,7 @@ module BasicTest # We only assert on inspect value when there is one map entry because the # order in which elements appear is unspecified (depends on the internal # hash function). We don't want a brittle test. - assert m.inspect == "{\"jkl;\" => 42}" + assert m.inspect == "{\"jkl;\"=>42}" assert m.keys == ["jkl;"] assert m.values == [42] @@ -582,6 +593,91 @@ module BasicTest "b" => TestMessage2.new(:foo => 2)} end + def test_oneof_descriptors + d = OneofMessage.descriptor + o = d.lookup_oneof("my_oneof") + assert o != nil + assert o.class == Google::Protobuf::OneofDescriptor + assert o.name == "my_oneof" + oneof_count = 0 + d.each_oneof{ |oneof| + oneof_count += 1 + assert oneof == o + } + assert oneof_count == 1 + assert o.count == 4 + field_names = o.map{|f| f.name}.sort + assert field_names == ["a", "b", "c", "d"] + end + + def test_oneof + d = OneofMessage.new + assert d.a == nil + assert d.b == nil + assert d.c == nil + assert d.d == nil + assert d.my_oneof == nil + + d.a = "hi" + assert d.a == "hi" + assert d.b == nil + assert d.c == nil + assert d.d == nil + assert d.my_oneof == :a + + d.b = 42 + assert d.a == nil + assert d.b == 42 + assert d.c == nil + assert d.d == nil + assert d.my_oneof == :b + + d.c = TestMessage2.new(:foo => 100) + assert d.a == nil + assert d.b == nil + assert d.c.foo == 100 + assert d.d == nil + assert d.my_oneof == :c + + d.d = :C + assert d.a == nil + assert d.b == nil + assert d.c == nil + assert d.d == :C + assert d.my_oneof == :d + + d2 = OneofMessage.decode(OneofMessage.encode(d)) + assert d2 == d + + encoded_field_a = OneofMessage.encode(OneofMessage.new(:a => "string")) + encoded_field_b = OneofMessage.encode(OneofMessage.new(:b => 1000)) + encoded_field_c = OneofMessage.encode( + OneofMessage.new(:c => TestMessage2.new(:foo => 1))) + encoded_field_d = OneofMessage.encode(OneofMessage.new(:d => :B)) + + d3 = OneofMessage.decode( + encoded_field_c + encoded_field_a + encoded_field_d) + assert d3.a == nil + assert d3.b == nil + assert d3.c == nil + assert d3.d == :B + + d4 = OneofMessage.decode( + encoded_field_c + encoded_field_a + encoded_field_d + + encoded_field_c) + assert d4.a == nil + assert d4.b == nil + assert d4.c.foo == 1 + assert d4.d == nil + + d5 = OneofMessage.new(:a => "hello") + assert d5.a != nil + d5.a = nil + assert d5.a == nil + assert OneofMessage.encode(d5) == '' + assert d5.my_oneof == nil + end + def test_enum_field m = TestMessage.new assert m.optional_enum == :Default @@ -621,6 +717,14 @@ module BasicTest assert m.repeated_msg[0].object_id != m2.repeated_msg[0].object_id end + def test_eq + m = TestMessage.new(:optional_int32 => 42, + :repeated_int32 => [1, 2, 3]) + m2 = TestMessage.new(:optional_int32 => 43, + :repeated_int32 => [1, 2, 3]) + assert m != m2 + end + def test_enum_lookup assert TestEnum::A == 1 assert TestEnum::B == 2 @@ -888,5 +992,13 @@ module BasicTest m2 = TestMessage.decode_json(json_text) assert m == m2 end + + def test_json_maps + m = MapMessage.new(:map_string_int32 => {"a" => 1}) + expected = '{"map_string_int32":{"a":1},"map_string_msg":{}}' + assert MapMessage.encode_json(m) == expected + m2 = MapMessage.decode_json(MapMessage.encode_json(m)) + assert m == m2 + end end end diff --git a/ruby/tests/generated_code.proto b/ruby/tests/generated_code.proto new file mode 100644 index 00000000..b1d63232 --- /dev/null +++ b/ruby/tests/generated_code.proto @@ -0,0 +1,67 @@ +syntax = "proto3"; + +package A.B.C; + +message TestMessage { + optional int32 optional_int32 = 1; + optional int64 optional_int64 = 2; + optional uint32 optional_uint32 = 3; + optional uint64 optional_uint64 = 4; + optional bool optional_bool = 5; + optional double optional_double = 6; + optional float optional_float = 7; + optional string optional_string = 8; + optional bytes optional_bytes = 9; + optional TestEnum optional_enum = 10; + optional TestMessage optional_msg = 11; + + repeated int32 repeated_int32 = 21; + repeated int64 repeated_int64 = 22; + repeated uint32 repeated_uint32 = 23; + repeated uint64 repeated_uint64 = 24; + repeated bool repeated_bool = 25; + repeated double repeated_double = 26; + repeated float repeated_float = 27; + repeated string repeated_string = 28; + repeated bytes repeated_bytes = 29; + repeated TestEnum repeated_enum = 30; + repeated TestMessage repeated_msg = 31; + + oneof my_oneof { + int32 oneof_int32 = 41; + int64 oneof_int64 = 42; + uint32 oneof_uint32 = 43; + uint64 oneof_uint64 = 44; + bool oneof_bool = 45; + double oneof_double = 46; + float oneof_float = 47; + string oneof_string = 48; + bytes oneof_bytes = 49; + TestEnum oneof_enum = 50; + TestMessage oneof_msg = 51; + } + + map<int32, string> map_int32_string = 61; + map<int64, string> map_int64_string = 62; + map<uint32, string> map_uint32_string = 63; + map<uint64, string> map_uint64_string = 64; + map<bool, string> map_bool_string = 65; + map<string, string> map_string_string = 66; + map<string, TestMessage> map_string_msg = 67; + map<string, TestEnum> map_string_enum = 68; + map<string, int32> map_string_int32 = 69; + map<string, bool> map_string_bool = 70; + + message NestedMessage { + optional int32 foo = 1; + } + + optional NestedMessage nested_message = 80; +} + +enum TestEnum { + Default = 0; + A = 1; + B = 2; + C = 3; +} diff --git a/ruby/tests/generated_code.rb b/ruby/tests/generated_code.rb new file mode 100644 index 00000000..5a685433 --- /dev/null +++ b/ruby/tests/generated_code.rb @@ -0,0 +1,74 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: generated_code.proto + +require 'google/protobuf' + +Google::Protobuf::DescriptorPool.generated_pool.build do + add_message "A.B.C.TestMessage" do + optional :optional_int32, :int32, 1 + optional :optional_int64, :int64, 2 + optional :optional_uint32, :uint32, 3 + optional :optional_uint64, :uint64, 4 + optional :optional_bool, :bool, 5 + optional :optional_double, :double, 6 + optional :optional_float, :float, 7 + optional :optional_string, :string, 8 + optional :optional_bytes, :string, 9 + optional :optional_enum, :enum, 10, "A.B.C.TestEnum" + optional :optional_msg, :message, 11, "A.B.C.TestMessage" + repeated :repeated_int32, :int32, 21 + repeated :repeated_int64, :int64, 22 + repeated :repeated_uint32, :uint32, 23 + repeated :repeated_uint64, :uint64, 24 + repeated :repeated_bool, :bool, 25 + repeated :repeated_double, :double, 26 + repeated :repeated_float, :float, 27 + repeated :repeated_string, :string, 28 + repeated :repeated_bytes, :string, 29 + repeated :repeated_enum, :enum, 30, "A.B.C.TestEnum" + repeated :repeated_msg, :message, 31, "A.B.C.TestMessage" + map :map_int32_string, :int32, :string, 61 + map :map_int64_string, :int64, :string, 62 + map :map_uint32_string, :uint32, :string, 63 + map :map_uint64_string, :uint64, :string, 64 + map :map_bool_string, :bool, :string, 65 + map :map_string_string, :string, :string, 66 + map :map_string_msg, :string, :message, 67, "A.B.C.TestMessage" + map :map_string_enum, :string, :enum, 68, "A.B.C.TestEnum" + map :map_string_int32, :string, :int32, 69 + map :map_string_bool, :string, :bool, 70 + optional :nested_message, :message, 80, "A.B.C.TestMessage.NestedMessage" + oneof :my_oneof do + optional :oneof_int32, :int32, 41 + optional :oneof_int64, :int64, 42 + optional :oneof_uint32, :uint32, 43 + optional :oneof_uint64, :uint64, 44 + optional :oneof_bool, :bool, 45 + optional :oneof_double, :double, 46 + optional :oneof_float, :float, 47 + optional :oneof_string, :string, 48 + optional :oneof_bytes, :string, 49 + optional :oneof_enum, :enum, 50, "A.B.C.TestEnum" + optional :oneof_msg, :message, 51, "A.B.C.TestMessage" + end + end + add_message "A.B.C.TestMessage.NestedMessage" do + optional :foo, :int32, 1 + end + add_enum "A.B.C.TestEnum" do + value :Default, 0 + value :A, 1 + value :B, 2 + value :C, 3 + end +end + +module A + module B + module C + TestMessage = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestMessage").msgclass + TestMessage::NestedMessage = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestMessage.NestedMessage").msgclass + TestEnum = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestEnum").enummodule + end + end +end diff --git a/ruby/tests/generated_code_test.rb b/ruby/tests/generated_code_test.rb new file mode 100644 index 00000000..daef357a --- /dev/null +++ b/ruby/tests/generated_code_test.rb @@ -0,0 +1,17 @@ +#!/usr/bin/ruby + +# generated_code.rb is in the same directory as this test. +$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__))) + +require 'generated_code' +require 'test/unit' + +class GeneratedCodeTest < Test::Unit::TestCase + def test_generated_msg + # just test that we can instantiate the message. The purpose of this test + # is to ensure that the output of the code generator is valid Ruby and + # successfully creates message definitions and classes, not to test every + # aspect of the extension (basic.rb is for that). + m = A::B::C::TestMessage.new() + end +end |