From fd1a3ff11d5854c34ba66c63598cdc5fd234e399 Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Tue, 6 Jan 2015 15:44:09 -0800 Subject: Support for maps in the MRI C Ruby extension. This adds the Map container and support for parsing and serializing maps in the protobuf wire format (as defined by the C++ implementation, with MapEntry submessages in a repeated field). JSON map serialization/parsing are not yet supported as these will require some changes to upb as well. --- ruby/ext/google/protobuf_c/defs.c | 110 +++- ruby/ext/google/protobuf_c/encode_decode.c | 386 +++++++++--- ruby/ext/google/protobuf_c/extconf.rb | 2 +- ruby/ext/google/protobuf_c/map.c | 883 ++++++++++++++++++++++++++++ ruby/ext/google/protobuf_c/message.c | 15 +- ruby/ext/google/protobuf_c/protobuf.c | 1 + ruby/ext/google/protobuf_c/protobuf.h | 69 ++- ruby/ext/google/protobuf_c/repeated_field.c | 6 +- ruby/ext/google/protobuf_c/storage.c | 173 ++++-- ruby/ext/google/protobuf_c/upb.c | 75 ++- ruby/ext/google/protobuf_c/upb.h | 58 +- ruby/tests/basic.rb | 258 +++++++- 12 files changed, 1873 insertions(+), 163 deletions(-) create mode 100644 ruby/ext/google/protobuf_c/map.c (limited to 'ruby') diff --git a/ruby/ext/google/protobuf_c/defs.c b/ruby/ext/google/protobuf_c/defs.c index bb6f10e1..5c94a74a 100644 --- a/ruby/ext/google/protobuf_c/defs.c +++ b/ruby/ext/google/protobuf_c/defs.c @@ -923,6 +923,7 @@ DEFINE_CLASS(MessageBuilderContext, void MessageBuilderContext_mark(void* _self) { MessageBuilderContext* self = _self; rb_gc_mark(self->descriptor); + rb_gc_mark(self->builder); } void MessageBuilderContext_free(void* _self) { @@ -935,6 +936,7 @@ VALUE MessageBuilderContext_alloc(VALUE klass) { VALUE ret = TypedData_Wrap_Struct( klass, &_MessageBuilderContext_type, self); self->descriptor = Qnil; + self->builder = Qnil; return ret; } @@ -943,24 +945,29 @@ void MessageBuilderContext_register(VALUE module) { module, "MessageBuilderContext", rb_cObject); rb_define_alloc_func(klass, MessageBuilderContext_alloc); rb_define_method(klass, "initialize", - MessageBuilderContext_initialize, 1); + MessageBuilderContext_initialize, 2); rb_define_method(klass, "optional", MessageBuilderContext_optional, -1); rb_define_method(klass, "required", MessageBuilderContext_required, -1); rb_define_method(klass, "repeated", MessageBuilderContext_repeated, -1); + rb_define_method(klass, "map", MessageBuilderContext_map, -1); cMessageBuilderContext = klass; rb_gc_register_address(&cMessageBuilderContext); } /* * call-seq: - * MessageBuilderContext.new(desc) => context + * MessageBuilderContext.new(desc, builder) => context * - * Create a new builder context around the given message descriptor. This class - * is intended to serve as a DSL context to be used with #instance_eval. + * Create a new message builder context around the given message descriptor and + * builder context. This class is intended to serve as a DSL context to be used + * with #instance_eval. */ -VALUE MessageBuilderContext_initialize(VALUE _self, VALUE msgdef) { +VALUE MessageBuilderContext_initialize(VALUE _self, + VALUE msgdef, + VALUE builder) { DEFINE_SELF(MessageBuilderContext, self, _self); self->descriptor = msgdef; + self->builder = builder; return Qnil; } @@ -1065,6 +1072,96 @@ VALUE MessageBuilderContext_repeated(int argc, VALUE* argv, VALUE _self) { name, type, number, type_class); } +/* + * call-seq: + * MessageBuilderContext.map(name, key_type, value_type, number, + * value_type_class = nil) + * + * Defines a new map field on this message type with the given key and value types, tag + * number, and type class (for message and enum value types). The key type must + * be :int32/:uint32/:int64/:uint64, :bool, or :string. The value type type must + * be a Ruby symbol (as accepted by FieldDescriptor#type=) and the type_class + * must be a string, if present (as accepted by FieldDescriptor#submsg_name=). + */ +VALUE MessageBuilderContext_map(int argc, VALUE* argv, VALUE _self) { + DEFINE_SELF(MessageBuilderContext, self, _self); + + if (argc < 4) { + rb_raise(rb_eArgError, "Expected at least 4 arguments."); + } + VALUE name = argv[0]; + VALUE key_type = argv[1]; + VALUE value_type = argv[2]; + VALUE number = argv[3]; + VALUE type_class = (argc > 4) ? argv[4] : Qnil; + + // Validate the key type. We can't accept enums, messages, or floats/doubles + // as map keys. (We exclude these explicitly, and the field-descriptor setter + // below then ensures that the type is one of the remaining valid options.) + if (SYM2ID(key_type) == rb_intern("float") || + SYM2ID(key_type) == rb_intern("double") || + SYM2ID(key_type) == rb_intern("enum") || + SYM2ID(key_type) == rb_intern("message")) { + rb_raise(rb_eArgError, + "Cannot add a map field with a float, double, enum, or message " + "type."); + } + + // Create a new message descriptor for the map entry message, and create a + // repeated submessage field here with that type. + VALUE mapentry_desc = rb_class_new_instance(0, NULL, cDescriptor); + VALUE mapentry_desc_name = rb_funcall(self->descriptor, rb_intern("name"), 0); + mapentry_desc_name = rb_str_cat2(mapentry_desc_name, "_MapEntry_"); + mapentry_desc_name = rb_str_cat2(mapentry_desc_name, + rb_id2name(SYM2ID(name))); + Descriptor_name_set(mapentry_desc, mapentry_desc_name); + + // The 'mapentry' attribute has no Ruby setter because we do not want the user + // attempting to DIY the setup below; we want to ensure that the fields are + // correct. So we reach into the msgdef here to set the bit manually. + Descriptor* mapentry_desc_self = ruby_to_Descriptor(mapentry_desc); + upb_msgdef_setmapentry((upb_msgdef*)mapentry_desc_self->msgdef, true); + + // optional key = 1; + VALUE key_field = rb_class_new_instance(0, NULL, cFieldDescriptor); + FieldDescriptor_name_set(key_field, rb_str_new2("key")); + FieldDescriptor_label_set(key_field, ID2SYM(rb_intern("optional"))); + FieldDescriptor_number_set(key_field, INT2NUM(1)); + FieldDescriptor_type_set(key_field, key_type); + Descriptor_add_field(mapentry_desc, key_field); + + // optional value = 2; + VALUE value_field = rb_class_new_instance(0, NULL, cFieldDescriptor); + FieldDescriptor_name_set(value_field, rb_str_new2("value")); + FieldDescriptor_label_set(value_field, ID2SYM(rb_intern("optional"))); + FieldDescriptor_number_set(value_field, INT2NUM(2)); + FieldDescriptor_type_set(value_field, value_type); + if (type_class != Qnil) { + VALUE submsg_name = rb_str_new2("."); // prepend '.' to make name absolute. + submsg_name = rb_str_append(submsg_name, type_class); + FieldDescriptor_submsg_name_set(value_field, submsg_name); + } + Descriptor_add_field(mapentry_desc, value_field); + + // Add the map-entry message type to the current builder, and use the type to + // create the map field itself. + Builder* builder_self = ruby_to_Builder(self->builder); + rb_ary_push(builder_self->pending_list, mapentry_desc); + + VALUE map_field = rb_class_new_instance(0, NULL, cFieldDescriptor); + VALUE name_str = rb_str_new2(rb_id2name(SYM2ID(name))); + FieldDescriptor_name_set(map_field, name_str); + FieldDescriptor_number_set(map_field, number); + FieldDescriptor_label_set(map_field, ID2SYM(rb_intern("repeated"))); + FieldDescriptor_type_set(map_field, ID2SYM(rb_intern("message"))); + VALUE submsg_name = rb_str_new2("."); // prepend '.' to make name absolute. + submsg_name = rb_str_append(submsg_name, mapentry_desc_name); + FieldDescriptor_submsg_name_set(map_field, submsg_name); + Descriptor_add_field(self->descriptor, map_field); + + return Qnil; +} + // ----------------------------------------------------------------------------- // EnumBuilderContext. // ----------------------------------------------------------------------------- @@ -1190,7 +1287,8 @@ void Builder_register(VALUE module) { VALUE Builder_add_message(VALUE _self, VALUE name) { DEFINE_SELF(Builder, self, _self); VALUE msgdef = rb_class_new_instance(0, NULL, cDescriptor); - VALUE ctx = rb_class_new_instance(1, &msgdef, cMessageBuilderContext); + VALUE args[2] = { msgdef, _self }; + VALUE ctx = rb_class_new_instance(2, args, cMessageBuilderContext); VALUE block = rb_block_proc(); rb_funcall(msgdef, rb_intern("name="), 1, name); rb_funcall_with_block(ctx, rb_intern("instance_eval"), 0, NULL, block); diff --git a/ruby/ext/google/protobuf_c/encode_decode.c b/ruby/ext/google/protobuf_c/encode_decode.c index 8aba3c9e..6263edcc 100644 --- a/ruby/ext/google/protobuf_c/encode_decode.c +++ b/ruby/ext/google/protobuf_c/encode_decode.c @@ -174,9 +174,222 @@ static void *submsg_handler(void *closure, const void *hd) { return submsg; } +// Handler data for startmap/endmap handlers. +typedef struct { + size_t ofs; + const upb_fielddef* key_field; + const upb_fielddef* value_field; + VALUE value_field_typeclass; +} map_handlerdata_t; + +// Temporary frame for map parsing: at the beginning of a map entry message, a +// submsg handler allocates a frame to hold (i) a reference to the Map object +// into which this message will be inserted and (ii) storage slots to +// temporarily hold the key and value for this map entry until the end of the +// submessage. When the submessage ends, another handler is called to insert the +// value into the map. +typedef struct { + VALUE map; + char key_storage[NATIVE_SLOT_MAX_SIZE]; + char value_storage[NATIVE_SLOT_MAX_SIZE]; +} map_parse_frame_t; + +// Handler to begin a sequence of map entries: simple no-op that exists only to +// set context for the map entry handlers. +static void *startmap_handler(void *closure, const void *hd) { + return closure; +} + +// Handler to begin a map entry: allocates a temporary frame. This is the +// 'startsubmsg' handler on the msgdef that contains the map field. +static void *startmapentry_handler(void *closure, const void *hd) { + MessageHeader* msg = closure; + const map_handlerdata_t* mapdata = hd; + VALUE map_rb = DEREF(Message_data(msg), mapdata->ofs, VALUE); + + map_parse_frame_t* frame = ALLOC(map_parse_frame_t); + frame->map = map_rb; + + native_slot_init(upb_fielddef_type(mapdata->key_field), + &frame->key_storage); + native_slot_init(upb_fielddef_type(mapdata->value_field), + &frame->value_storage); + + return frame; +} + +// Handler to end a map entry: inserts the value defined during the message into +// the map. This is the 'endmsg' handler on the map entry msgdef. +static bool endmap_handler(void *closure, const void *hd, upb_status* s) { + map_parse_frame_t* frame = closure; + const map_handlerdata_t* mapdata = hd; + + VALUE key = native_slot_get( + upb_fielddef_type(mapdata->key_field), Qnil, + &frame->key_storage); + VALUE value = native_slot_get( + upb_fielddef_type(mapdata->value_field), mapdata->value_field_typeclass, + &frame->value_storage); + + Map_index_set(frame->map, key, value); + free(frame); + + return true; +} + +// Allocates a new map_handlerdata_t given the map entry message definition. If +// the offset of the field within the parent message is also given, that is +// added to the handler data as well. Note that this is called *twice* per map +// field: once in the parent message handler setup when setting the startsubmsg +// handler and once in the map entry message handler setup when setting the +// key/value and endmsg handlers. The reason is that there is no easy way to +// pass the handlerdata down to the sub-message handler setup. +static map_handlerdata_t* new_map_handlerdata( + size_t ofs, + const upb_msgdef* mapentry_def) { + + map_handlerdata_t* hd = ALLOC(map_handlerdata_t); + hd->ofs = ofs; + hd->key_field = upb_msgdef_itof(mapentry_def, 1); + assert(hd->key_field != NULL); + hd->value_field = upb_msgdef_itof(mapentry_def, 2); + assert(hd->value_field != NULL); + hd->value_field_typeclass = field_type_class(hd->value_field); + + return hd; +} + +// Set up handlers for a repeated field. +static void add_handlers_for_repeated_field(upb_handlers *h, + const upb_fielddef *f, + size_t offset) { + upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER; + upb_handlerattr_sethandlerdata(&attr, newhandlerdata(h, offset)); + upb_handlers_setstartseq(h, f, startseq_handler, &attr); + upb_handlerattr_uninit(&attr); + + switch (upb_fielddef_type(f)) { + +#define SET_HANDLER(utype, ltype) \ + case utype: \ + upb_handlers_set##ltype(h, f, append##ltype##_handler, NULL); \ + 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 ? + appendbytes_handler : appendstr_handler, + NULL); + upb_handlers_setstring(h, f, stringdata_handler, NULL); + } + case UPB_TYPE_MESSAGE: { + upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER; + upb_handlerattr_sethandlerdata(&attr, newsubmsghandlerdata(h, 0, f)); + upb_handlers_setstartsubmsg(h, f, appendsubmsg_handler, &attr); + upb_handlerattr_uninit(&attr); + break; + } + } +} + +// Set up handlers for a singular field. +static void add_handlers_for_singular_field(upb_handlers *h, + const upb_fielddef *f, + size_t offset) { + switch (upb_fielddef_type(f)) { + case UPB_TYPE_BOOL: + case UPB_TYPE_INT32: + case UPB_TYPE_UINT32: + case UPB_TYPE_ENUM: + case UPB_TYPE_FLOAT: + case UPB_TYPE_INT64: + case UPB_TYPE_UINT64: + case UPB_TYPE_DOUBLE: + // The shim writes directly at the given offset (instead of using + // DEREF()) so we need to add the msg overhead. + upb_shim_set(h, f, offset + sizeof(MessageHeader), -1); + break; + case UPB_TYPE_STRING: + case UPB_TYPE_BYTES: { + bool is_bytes = upb_fielddef_type(f) == UPB_TYPE_BYTES; + upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER; + upb_handlerattr_sethandlerdata(&attr, newhandlerdata(h, offset)); + upb_handlers_setstartstr(h, f, + is_bytes ? bytes_handler : str_handler, + &attr); + upb_handlers_setstring(h, f, stringdata_handler, &attr); + upb_handlerattr_uninit(&attr); + break; + } + case UPB_TYPE_MESSAGE: { + upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER; + upb_handlerattr_sethandlerdata(&attr, newsubmsghandlerdata(h, offset, f)); + upb_handlers_setstartsubmsg(h, f, submsg_handler, &attr); + upb_handlerattr_uninit(&attr); + break; + } + } +} + +// Adds handlers to a map field. +static void add_handlers_for_mapfield(upb_handlers* h, + const upb_fielddef* fielddef, + size_t offset) { + const upb_msgdef* map_msgdef = upb_fielddef_msgsubdef(fielddef); + map_handlerdata_t* hd = new_map_handlerdata(offset, map_msgdef); + upb_handlers_addcleanup(h, hd, free); + upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER; + upb_handlerattr_sethandlerdata(&attr, hd); + upb_handlers_setstartseq(h, fielddef, startmap_handler, &attr); + upb_handlers_setstartsubmsg(h, fielddef, startmapentry_handler, &attr); + upb_handlerattr_uninit(&attr); +} + +// Adds handlers to a map-entry msgdef. +static void add_handlers_for_mapentry(const upb_msgdef* msgdef, + upb_handlers* h) { + map_handlerdata_t* hd = new_map_handlerdata(0, msgdef); + upb_handlers_addcleanup(h, hd, free); + upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER; + upb_handlerattr_sethandlerdata(&attr, hd); + upb_handlers_setendmsg(h, endmap_handler, &attr); + + add_handlers_for_singular_field( + h, hd->key_field, + // Convert the offset into map_parse_frame_t to an offset understood by the + // singular field handlers, so that we don't have to use special + // map-key/value-specific handlers. The ordinary singular field handlers expect + // a Message* and assume offset is relative to the data section at the end, so + // we compensate for that addition. + offsetof(map_parse_frame_t, key_storage) - sizeof(MessageHeader)); + add_handlers_for_singular_field( + h, hd->value_field, + offsetof(map_parse_frame_t, value_storage) - sizeof(MessageHeader)); +} + static void add_handlers_for_message(const void *closure, upb_handlers *h) { - Descriptor* desc = ruby_to_Descriptor( - get_def_obj((void*)upb_handlers_msgdef(h))); + const upb_msgdef* msgdef = upb_handlers_msgdef(h); + Descriptor* desc = ruby_to_Descriptor(get_def_obj((void*)msgdef)); + + // If this is a mapentry message type, set up a special set of handlers and + // bail out of the normal (user-defined) message type handling. + if (upb_msgdef_mapentry(msgdef)) { + add_handlers_for_mapentry(msgdef, h); + return; + } + // Ensure layout exists. We may be invoked to create handlers for a given // message if we are included as a submsg of another message type before our // class is actually built, so to work around this, we just create the layout @@ -193,80 +406,12 @@ static void add_handlers_for_message(const void *closure, upb_handlers *h) { const upb_fielddef *f = upb_msg_iter_field(&i); size_t offset = desc->layout->offsets[upb_fielddef_index(f)]; - if (upb_fielddef_isseq(f)) { - upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER; - upb_handlerattr_sethandlerdata(&attr, newhandlerdata(h, offset)); - upb_handlers_setstartseq(h, f, startseq_handler, &attr); - upb_handlerattr_uninit(&attr); - - switch (upb_fielddef_type(f)) { - -#define SET_HANDLER(utype, ltype) \ - case utype: \ - upb_handlers_set##ltype(h, f, append##ltype##_handler, NULL); \ - 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 ? - appendbytes_handler : appendstr_handler, - NULL); - upb_handlers_setstring(h, f, stringdata_handler, NULL); - } - case UPB_TYPE_MESSAGE: { - upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER; - upb_handlerattr_sethandlerdata(&attr, newsubmsghandlerdata(h, 0, f)); - upb_handlers_setstartsubmsg(h, f, appendsubmsg_handler, &attr); - upb_handlerattr_uninit(&attr); - break; - } - } - } - - switch (upb_fielddef_type(f)) { - case UPB_TYPE_BOOL: - case UPB_TYPE_INT32: - case UPB_TYPE_UINT32: - case UPB_TYPE_ENUM: - case UPB_TYPE_FLOAT: - case UPB_TYPE_INT64: - case UPB_TYPE_UINT64: - case UPB_TYPE_DOUBLE: - // The shim writes directly at the given offset (instead of using - // DEREF()) so we need to add the msg overhead. - upb_shim_set(h, f, offset + sizeof(MessageHeader), -1); - break; - case UPB_TYPE_STRING: - case UPB_TYPE_BYTES: { - bool is_bytes = upb_fielddef_type(f) == UPB_TYPE_BYTES; - upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER; - upb_handlerattr_sethandlerdata(&attr, newhandlerdata(h, offset)); - upb_handlers_setstartstr(h, f, - is_bytes ? bytes_handler : str_handler, - &attr); - upb_handlers_setstring(h, f, stringdata_handler, &attr); - upb_handlerattr_uninit(&attr); - break; - } - case UPB_TYPE_MESSAGE: { - upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER; - upb_handlerattr_sethandlerdata(&attr, newsubmsghandlerdata(h, offset, f)); - upb_handlers_setstartsubmsg(h, f, submsg_handler, &attr); - upb_handlerattr_uninit(&attr); - break; - } + if (is_map_field(f)) { + add_handlers_for_mapfield(h, f, offset); + } else if (upb_fielddef_isseq(f)) { + add_handlers_for_repeated_field(h, f, offset); + } else { + add_handlers_for_singular_field(h, f, offset); } } } @@ -558,6 +703,88 @@ static void putary(VALUE ary, const upb_fielddef *f, upb_sink *sink, upb_sink_endseq(sink, getsel(f, UPB_HANDLER_ENDSEQ)); } +static void put_ruby_value(VALUE value, + const upb_fielddef *f, + VALUE type_class, + int depth, + upb_sink *sink) { + upb_selector_t sel = 0; + if (upb_fielddef_isprimitive(f)) { + sel = getsel(f, upb_handlers_getprimitivehandlertype(f)); + } + + switch (upb_fielddef_type(f)) { + case UPB_TYPE_INT32: + upb_sink_putint32(sink, sel, NUM2INT(value)); + break; + case UPB_TYPE_INT64: + upb_sink_putint64(sink, sel, NUM2LL(value)); + break; + case UPB_TYPE_UINT32: + upb_sink_putuint32(sink, sel, NUM2UINT(value)); + break; + case UPB_TYPE_UINT64: + upb_sink_putuint64(sink, sel, NUM2ULL(value)); + break; + case UPB_TYPE_FLOAT: + upb_sink_putfloat(sink, sel, NUM2DBL(value)); + break; + case UPB_TYPE_DOUBLE: + upb_sink_putdouble(sink, sel, NUM2DBL(value)); + break; + case UPB_TYPE_ENUM: { + if (TYPE(value) == T_SYMBOL) { + value = rb_funcall(type_class, rb_intern("resolve"), 1, value); + } + upb_sink_putint32(sink, sel, NUM2INT(value)); + break; + } + case UPB_TYPE_BOOL: + upb_sink_putbool(sink, sel, value == Qtrue); + break; + case UPB_TYPE_STRING: + case UPB_TYPE_BYTES: + putstr(value, f, sink); + break; + case UPB_TYPE_MESSAGE: + putsubmsg(value, f, sink, depth); + } +} + +static void putmap(VALUE map, const upb_fielddef *f, upb_sink *sink, + int depth) { + if (map == Qnil) return; + Map* self = ruby_to_Map(map); + + upb_sink subsink; + + upb_sink_startseq(sink, getsel(f, UPB_HANDLER_STARTSEQ), &subsink); + + assert(upb_fielddef_type(f) == UPB_TYPE_MESSAGE); + const upb_fielddef* key_field = map_field_key(f); + const upb_fielddef* value_field = map_field_value(f); + + Map_iter it; + for (Map_begin(map, &it); !Map_done(&it); Map_next(&it)) { + VALUE key = Map_iter_key(&it); + VALUE value = Map_iter_value(&it); + + upb_sink entry_sink; + upb_sink_startsubmsg(&subsink, getsel(f, UPB_HANDLER_STARTSUBMSG), &entry_sink); + upb_sink_startmsg(&entry_sink); + + put_ruby_value(key, key_field, Qnil, depth + 1, &entry_sink); + put_ruby_value(value, value_field, self->value_type_class, depth + 1, + &entry_sink); + + upb_status status; + upb_sink_endmsg(&entry_sink, &status); + upb_sink_endsubmsg(&subsink, getsel(f, UPB_HANDLER_ENDSUBMSG)); + } + + upb_sink_endseq(sink, getsel(f, UPB_HANDLER_ENDSEQ)); +} + static void putmsg(VALUE msg_rb, const Descriptor* desc, upb_sink *sink, int depth) { upb_sink_startmsg(sink); @@ -580,7 +807,12 @@ static void putmsg(VALUE msg_rb, const Descriptor* desc, upb_fielddef *f = upb_msg_iter_field(&i); uint32_t offset = desc->layout->offsets[upb_fielddef_index(f)]; - if (upb_fielddef_isseq(f)) { + if (is_map_field(f)) { + VALUE map = DEREF(msg_data, offset, VALUE); + if (map != Qnil) { + putmap(map, f, sink, depth); + } + } else if (upb_fielddef_isseq(f)) { VALUE ary = DEREF(msg_data, offset, VALUE); if (ary != Qnil) { putary(ary, f, sink, depth); diff --git a/ruby/ext/google/protobuf_c/extconf.rb b/ruby/ext/google/protobuf_c/extconf.rb index 7cf7bf6a..8d60392c 100644 --- a/ruby/ext/google/protobuf_c/extconf.rb +++ b/ruby/ext/google/protobuf_c/extconf.rb @@ -5,6 +5,6 @@ require 'mkmf' $CFLAGS += " -O3 -std=c99 -Wno-unused-function -DNDEBUG " $objs = ["protobuf.o", "defs.o", "storage.o", "message.o", - "repeated_field.o", "encode_decode.o", "upb.o"] + "repeated_field.o", "map.o", "encode_decode.o", "upb.o"] create_makefile("google/protobuf_c") diff --git a/ruby/ext/google/protobuf_c/map.c b/ruby/ext/google/protobuf_c/map.c new file mode 100644 index 00000000..9d5de9cd --- /dev/null +++ b/ruby/ext/google/protobuf_c/map.c @@ -0,0 +1,883 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2014 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "protobuf.h" + +// ----------------------------------------------------------------------------- +// Basic map operations on top of upb's strtable. +// ----------------------------------------------------------------------------- + +// Map values are stored using the native_slot abstraction (as with repeated +// field values), but keys are a bit special. Since we use a strtable, we need +// to store keys as sequences of bytes such that equality of those bytes maps +// one-to-one to equality of keys. We store strings directly (i.e., they map to +// their own bytes) and integers as sequences of either 4 or 8 bytes in +// host-byte-order as either a uint32_t or a uint64_t. + +// Forms a key to use with the underlying strtable from a Ruby key value. |buf| +// must point to TABLE_KEY_BUF_LENGTH bytes of temporary space, used to +// construct a key byte sequence if needed. |out_key| and |out_length| provide +// the resulting key data/length. +#define TABLE_KEY_BUF_LENGTH 8 // sizeof(uint64_t) +static void table_key(Map* self, VALUE key, + char* buf, + const char** out_key, + size_t* out_length) { + switch (self->key_type) { + case UPB_TYPE_BYTES: + case UPB_TYPE_STRING: + // Strings: use string content directly. + Check_Type(key, T_STRING); + native_slot_validate_string_encoding(self->key_type, key); + *out_key = RSTRING_PTR(key); + *out_length = RSTRING_LEN(key); + break; + + case UPB_TYPE_BOOL: + case UPB_TYPE_INT32: + case UPB_TYPE_INT64: { + // Signed numeric types: use an int64 in host-native byte order. + int64_t key_val = 0; + + // Do a range/value check. + switch (self->key_type) { + case UPB_TYPE_BOOL: + if (key != Qtrue && key != Qfalse) { + rb_raise(rb_eTypeError, "Key must be true or false"); + } + key_val = (key == Qtrue) ? 1 : 0; + break; + case UPB_TYPE_INT32: + native_slot_check_int_range_precision(self->key_type, key); + key_val = NUM2INT(key); + break; + case UPB_TYPE_INT64: + native_slot_check_int_range_precision(self->key_type, key); + key_val = NUM2LL(key); + break; + default: + break; + } + + int64_t* int64_key = (int64_t*)buf; + *int64_key = key_val; + *out_key = buf; + *out_length = sizeof(int64_t); + break; + } + + case UPB_TYPE_UINT32: + case UPB_TYPE_UINT64: { + // Unsigned numeric types: use a uint64 in host-native byte order. + uint64_t key_val = 0; + + // Do a range/value check. + native_slot_check_int_range_precision(self->key_type, key); + switch (self->key_type) { + case UPB_TYPE_UINT32: + key_val = NUM2UINT(key); + break; + case UPB_TYPE_UINT64: + key_val = NUM2ULL(key); + break; + default: + break; + } + + uint64_t* uint64_key = (uint64_t*)buf; + *uint64_key = key_val; + *out_key = buf; + *out_length = sizeof(uint64_t); + break; + } + + default: + // Map constructor should not allow a Map with another key type to be + // constructed. + assert(false); + break; + } +} + +static VALUE table_key_to_ruby(Map* self, const char* buf, size_t length) { + switch (self->key_type) { + case UPB_TYPE_BYTES: + case UPB_TYPE_STRING: { + VALUE ret = rb_str_new(buf, length); + rb_enc_associate(ret, + (self->key_type == UPB_TYPE_BYTES) ? + kRubyString8bitEncoding : kRubyStringUtf8Encoding); + return ret; + } + + case UPB_TYPE_BOOL: + case UPB_TYPE_INT32: + case UPB_TYPE_INT64: { + assert(length == sizeof(int64_t)); + int64_t* int64_key = (int64_t*)buf; + + if (self->key_type == UPB_TYPE_BOOL) { + return *int64_key ? Qtrue : Qfalse; + } else { + return LL2NUM(*int64_key); + } + } + + case UPB_TYPE_UINT32: + case UPB_TYPE_UINT64: { + assert(length == sizeof(uint64_t)); + uint64_t* uint64_key = (uint64_t*)buf; + return ULL2NUM(*uint64_key); + } + + default: + assert(false); + return Qnil; + } +} + +static upb_ctype_t upb_table_value_type(upb_fieldtype_t value_type) { + switch (value_type) { + case UPB_TYPE_BOOL: + case UPB_TYPE_INT32: + case UPB_TYPE_INT64: + case UPB_TYPE_UINT32: + case UPB_TYPE_UINT64: + case UPB_TYPE_ENUM: + case UPB_TYPE_FLOAT: + case UPB_TYPE_DOUBLE: + case UPB_TYPE_STRING: + case UPB_TYPE_BYTES: + case UPB_TYPE_MESSAGE: + return UPB_CTYPE_UINT64; + + default: + assert(false); + return 0; + } +} + +static void* value_memory(upb_value* v) { + return (void*)(&v->val.uint64); +} + +// ----------------------------------------------------------------------------- +// Map container type. +// ----------------------------------------------------------------------------- + +const rb_data_type_t Map_type = { + "Google::Protobuf::Map", + { Map_mark, Map_free, NULL }, +}; + +VALUE cMap; + +Map* ruby_to_Map(VALUE _self) { + Map* self; + TypedData_Get_Struct(_self, Map, &Map_type, self); + return self; +} + +void Map_mark(void* _self) { + Map* self = _self; + + rb_gc_mark(self->value_type_class); + + if (self->value_type == UPB_TYPE_STRING || + self->value_type == UPB_TYPE_BYTES || + self->value_type == UPB_TYPE_MESSAGE) { + upb_strtable_iter it; + for (upb_strtable_begin(&it, &self->table); + !upb_strtable_done(&it); + upb_strtable_next(&it)) { + upb_value v = upb_strtable_iter_value(&it); + void* mem = value_memory(&v); + native_slot_mark(self->value_type, mem); + } + } +} + +void Map_free(void* _self) { + Map* self = _self; + upb_strtable_uninit(&self->table); + xfree(self); +} + +VALUE Map_alloc(VALUE klass) { + Map* self = ALLOC(Map); + memset(self, 0, sizeof(Map)); + self->value_type_class = Qnil; + VALUE ret = TypedData_Wrap_Struct(klass, &Map_type, self); + return ret; +} + +static bool needs_typeclass(upb_fieldtype_t type) { + switch (type) { + case UPB_TYPE_MESSAGE: + case UPB_TYPE_ENUM: + return true; + default: + return false; + } +} + +/* + * call-seq: + * Map.new(key_type, value_type, value_typeclass = nil, init_hashmap = {}) + * => new map + * + * Allocates a new Map container. This constructor may be called with 2, 3, or 4 + * arguments. The first two arguments are always present and are symbols (taking + * on the same values as field-type symbols in message descriptors) that + * indicate the type of the map key and value fields. + * + * The supported key types are: :int32, :int64, :uint32, :uint64, :bool, + * :string, :bytes. + * + * The supported value types are: :int32, :int64, :uint32, :uint64, :bool, + * :string, :bytes, :enum, :message. + * + * The third argument, value_typeclass, must be present if value_type is :enum + * or :message. As in RepeatedField#new, this argument must be a message class + * (for :message) or enum module (for :enum). + * + * The last argument, if present, provides initial content for map. Note that + * this may be an ordinary Ruby hashmap or another Map instance with identical + * key and value types. Also note that this argument may be rpesent whether or + * not value_typeclass is present (and it is unambiguously separate from + * value_typeclass because value_typeclass's presence is strictly determined by + * value_type). + */ +VALUE Map_init(int argc, VALUE* argv, VALUE _self) { + Map* self = ruby_to_Map(_self); + + // We take either two args (:key_type, :value_type), three args (:key_type, + // :value_type, "ValueMessageType"), or four args (the above plus an initial + // hashmap). + if (argc < 2 || argc > 4) { + rb_raise(rb_eArgError, "Map constructor expects 2, 3 or 4 arguments."); + } + + self->key_type = ruby_to_fieldtype(argv[0]); + self->value_type = ruby_to_fieldtype(argv[1]); + + // Check that the key type is an allowed type. + switch (self->key_type) { + case UPB_TYPE_INT32: + case UPB_TYPE_INT64: + case UPB_TYPE_UINT32: + case UPB_TYPE_UINT64: + case UPB_TYPE_BOOL: + case UPB_TYPE_STRING: + case UPB_TYPE_BYTES: + // These are OK. + break; + default: + rb_raise(rb_eArgError, "Invalid key type for map."); + } + + int init_value_arg = 2; + if (needs_typeclass(self->value_type) && argc > 2) { + self->value_type_class = argv[2]; + validate_type_class(self->value_type, self->value_type_class); + init_value_arg = 3; + } + + if (!upb_strtable_init(&self->table, upb_table_value_type(self->value_type))) { + rb_raise(rb_eRuntimeError, "Could not allocate table."); + } + + if (argc > init_value_arg) { + Map_merge_into_self(_self, argv[init_value_arg]); + } + + return Qnil; +} + +/* + * call-seq: + * Map.each(&block) + * + * Invokes &block on each |key, value| pair in the map, in unspecified order. + * Note that Map also includes Enumerable; map thus acts like a normal Ruby + * sequence. + */ +VALUE Map_each(VALUE _self) { + Map* self = ruby_to_Map(_self); + + upb_strtable_iter it; + for (upb_strtable_begin(&it, &self->table); + !upb_strtable_done(&it); + upb_strtable_next(&it)) { + + VALUE key = table_key_to_ruby( + self, upb_strtable_iter_key(&it), upb_strtable_iter_keylength(&it)); + + upb_value v = upb_strtable_iter_value(&it); + void* mem = value_memory(&v); + VALUE value = native_slot_get(self->value_type, + self->value_type_class, + mem); + + rb_yield_values(2, key, value); + } + + return Qnil; +} + +/* + * call-seq: + * Map.keys => [list_of_keys] + * + * Returns the list of keys contained in the map, in unspecified order. + */ +VALUE Map_keys(VALUE _self) { + Map* self = ruby_to_Map(_self); + + VALUE ret = rb_ary_new(); + upb_strtable_iter it; + for (upb_strtable_begin(&it, &self->table); + !upb_strtable_done(&it); + upb_strtable_next(&it)) { + + VALUE key = table_key_to_ruby( + self, upb_strtable_iter_key(&it), upb_strtable_iter_keylength(&it)); + + rb_ary_push(ret, key); + } + + return ret; +} + +/* + * call-seq: + * Map.values => [list_of_values] + * + * Returns the list of values contained in the map, in unspecified order. + */ +VALUE Map_values(VALUE _self) { + Map* self = ruby_to_Map(_self); + + VALUE ret = rb_ary_new(); + upb_strtable_iter it; + for (upb_strtable_begin(&it, &self->table); + !upb_strtable_done(&it); + upb_strtable_next(&it)) { + + upb_value v = upb_strtable_iter_value(&it); + void* mem = value_memory(&v); + VALUE value = native_slot_get(self->value_type, + self->value_type_class, + mem); + + rb_ary_push(ret, value); + } + + return ret; +} + +/* + * call-seq: + * Map.[](key) => value + * + * Accesses the element at the given key. Throws an exception if the key type is + * incorrect. Returns nil when the key is not present in the map. + */ +VALUE Map_index(VALUE _self, VALUE key) { + Map* self = ruby_to_Map(_self); + + char keybuf[TABLE_KEY_BUF_LENGTH]; + const char* keyval = NULL; + size_t length = 0; + table_key(self, key, keybuf, &keyval, &length); + + upb_value v; + if (upb_strtable_lookup2(&self->table, keyval, length, &v)) { + void* mem = value_memory(&v); + return native_slot_get(self->value_type, self->value_type_class, mem); + } else { + return Qnil; + } +} + +/* + * call-seq: + * Map.[]=(key, value) => value + * + * Inserts or overwrites the value at the given key with the given new value. + * Throws an exception if the key type is incorrect. Returns the new value that + * was just inserted. + */ +VALUE Map_index_set(VALUE _self, VALUE key, VALUE value) { + Map* self = ruby_to_Map(_self); + + char keybuf[TABLE_KEY_BUF_LENGTH]; + const char* keyval = NULL; + size_t length = 0; + table_key(self, key, keybuf, &keyval, &length); + + upb_value v; + void* mem = value_memory(&v); + native_slot_set(self->value_type, self->value_type_class, mem, value); + + // Replace any existing value by issuing a 'remove' operation first. + upb_value oldv; + upb_strtable_remove2(&self->table, keyval, length, &oldv); + if (!upb_strtable_insert2(&self->table, keyval, length, v)) { + rb_raise(rb_eRuntimeError, "Could not insert into table"); + } + + // Ruby hashmap's :[]= method also returns the inserted value. + return value; +} + +/* + * call-seq: + * Map.has_key?(key) => bool + * + * Returns true if the given key is present in the map. Throws an exception if + * the key has the wrong type. + */ +VALUE Map_has_key(VALUE _self, VALUE key) { + Map* self = ruby_to_Map(_self); + + char keybuf[TABLE_KEY_BUF_LENGTH]; + const char* keyval = NULL; + size_t length = 0; + table_key(self, key, keybuf, &keyval, &length); + + upb_value v; + if (upb_strtable_lookup2(&self->table, keyval, length, &v)) { + return Qtrue; + } else { + return Qfalse; + } +} + +/* + * call-seq: + * Map.delete(key) => old_value + * + * Deletes the value at the given key, if any, returning either the old value or + * nil if none was present. Throws an exception if the key is of the wrong type. + */ +VALUE Map_delete(VALUE _self, VALUE key) { + Map* self = ruby_to_Map(_self); + + char keybuf[TABLE_KEY_BUF_LENGTH]; + const char* keyval = NULL; + size_t length = 0; + table_key(self, key, keybuf, &keyval, &length); + + upb_value v; + if (upb_strtable_remove2(&self->table, keyval, length, &v)) { + void* mem = value_memory(&v); + return native_slot_get(self->value_type, self->value_type_class, mem); + } else { + return Qnil; + } +} + +/* + * call-seq: + * Map.clear + * + * Removes all entries from the map. + */ +VALUE Map_clear(VALUE _self) { + Map* self = ruby_to_Map(_self); + + // Uninit and reinit the table -- this is faster than iterating and doing a + // delete-lookup on each key. + upb_strtable_uninit(&self->table); + if (!upb_strtable_init(&self->table, + upb_table_value_type(self->value_type))) { + rb_raise(rb_eRuntimeError, "Unable to re-initialize table"); + } + return Qnil; +} + +/* + * call-seq: + * Map.length + * + * Returns the number of entries (key-value pairs) in the map. + */ +VALUE Map_length(VALUE _self) { + Map* self = ruby_to_Map(_self); + return INT2NUM(upb_strtable_count(&self->table)); +} + +static VALUE Map_new_this_type(VALUE _self) { + Map* self = ruby_to_Map(_self); + VALUE new_map = Qnil; + VALUE key_type = fieldtype_to_ruby(self->key_type); + VALUE value_type = fieldtype_to_ruby(self->value_type); + if (self->value_type_class != Qnil) { + new_map = rb_funcall(CLASS_OF(_self), rb_intern("new"), 3, + key_type, value_type, self->value_type_class); + } else { + new_map = rb_funcall(CLASS_OF(_self), rb_intern("new"), 2, + key_type, value_type); + } + return new_map; +} + +/* + * call-seq: + * Map.dup => new_map + * + * Duplicates this map with a shallow copy. References to all non-primitive + * element objects (e.g., submessages) are shared. + */ +VALUE Map_dup(VALUE _self) { + Map* self = ruby_to_Map(_self); + VALUE new_map = Map_new_this_type(_self); + Map* new_self = ruby_to_Map(new_map); + + upb_strtable_iter it; + for (upb_strtable_begin(&it, &self->table); + !upb_strtable_done(&it); + upb_strtable_next(&it)) { + + upb_value v = upb_strtable_iter_value(&it); + void* mem = value_memory(&v); + upb_value dup; + void* dup_mem = value_memory(&dup); + native_slot_dup(self->value_type, dup_mem, mem); + + if (!upb_strtable_insert2(&new_self->table, + upb_strtable_iter_key(&it), + upb_strtable_iter_keylength(&it), + dup)) { + rb_raise(rb_eRuntimeError, "Error inserting value into new table"); + } + } + + return new_map; +} + +// Used by Google::Protobuf.deep_copy but not exposed directly. +VALUE Map_deep_copy(VALUE _self) { + Map* self = ruby_to_Map(_self); + VALUE new_map = Map_new_this_type(_self); + Map* new_self = ruby_to_Map(new_map); + + upb_strtable_iter it; + for (upb_strtable_begin(&it, &self->table); + !upb_strtable_done(&it); + upb_strtable_next(&it)) { + + upb_value v = upb_strtable_iter_value(&it); + void* mem = value_memory(&v); + upb_value dup; + void* dup_mem = value_memory(&dup); + native_slot_deep_copy(self->value_type, dup_mem, mem); + + if (!upb_strtable_insert2(&new_self->table, + upb_strtable_iter_key(&it), + upb_strtable_iter_keylength(&it), + dup)) { + rb_raise(rb_eRuntimeError, "Error inserting value into new table"); + } + } + + return new_map; +} + +/* + * call-seq: + * Map.==(other) => boolean + * + * Compares this map to another. Maps are equal if they have identical key sets, + * and for each key, the values in both maps compare equal. Elements are + * compared as per normal Ruby semantics, by calling their :== methods (or + * performing a more efficient comparison for primitive types). + * + * Maps with dissimilar key types or value types/typeclasses are never equal, + * even if value comparison (for example, between integers and floats) would + * have otherwise indicated that every element has equal value. + */ +VALUE Map_eq(VALUE _self, VALUE _other) { + Map* self = ruby_to_Map(_self); + + // Allow comparisons to Ruby hashmaps by converting to a temporary Map + // instance. Slow, but workable. + if (TYPE(_other) == T_HASH) { + VALUE other_map = Map_new_this_type(_self); + Map_merge_into_self(other_map, _other); + _other = other_map; + } + + Map* other = ruby_to_Map(_other); + + if (self == other) { + return Qtrue; + } + if (self->key_type != other->key_type || + self->value_type != other->value_type || + self->value_type_class != other->value_type_class) { + return Qfalse; + } + if (upb_strtable_count(&self->table) != upb_strtable_count(&other->table)) { + return Qfalse; + } + + // For each member of self, check that an equal member exists at the same key + // in other. + upb_strtable_iter it; + for (upb_strtable_begin(&it, &self->table); + !upb_strtable_done(&it); + upb_strtable_next(&it)) { + + upb_value v = upb_strtable_iter_value(&it); + void* mem = value_memory(&v); + upb_value other_v; + void* other_mem = value_memory(&other_v); + + if (!upb_strtable_lookup2(&other->table, + upb_strtable_iter_key(&it), + upb_strtable_iter_keylength(&it), + &other_v)) { + // Not present in other map. + return Qfalse; + } + + if (!native_slot_eq(self->value_type, mem, other_mem)) { + // Present, but value not equal. + return Qfalse; + } + } + + // For each member of other, check that a member exists at the same key in + // self. We don't need to compare values here -- if the key exists in both, we + // compared values above; if not, we already know that the maps are not equal. + for (upb_strtable_begin(&it, &other->table); + !upb_strtable_done(&it); + upb_strtable_next(&it)) { + upb_value v; + if (!upb_strtable_lookup2(&self->table, + upb_strtable_iter_key(&it), + upb_strtable_iter_keylength(&it), + &v)) { + return Qfalse; + } + } + + return Qtrue; +} + +/* + * call-seq: + * Map.hash => hash_value + * + * Returns a hash value based on this map's contents. + */ +VALUE Map_hash(VALUE _self) { + Map* self = ruby_to_Map(_self); + + st_index_t h = rb_hash_start(0); + VALUE hash_sym = rb_intern("hash"); + + upb_strtable_iter it; + for (upb_strtable_begin(&it, &self->table); + !upb_strtable_done(&it); + upb_strtable_next(&it)) { + VALUE key = table_key_to_ruby( + self, upb_strtable_iter_key(&it), upb_strtable_iter_keylength(&it)); + + upb_value v = upb_strtable_iter_value(&it); + void* mem = value_memory(&v); + VALUE value = native_slot_get(self->value_type, + self->value_type_class, + mem); + + h = rb_hash_uint(h, NUM2LONG(rb_funcall(key, hash_sym, 0))); + h = rb_hash_uint(h, NUM2LONG(rb_funcall(value, hash_sym, 0))); + } + + return INT2FIX(h); +} + +/* + * call-seq: + * Map.inspect => string + * + * Returns a string representing this map's elements. It will be formatted as + * "{key => value, key => value, ...}", with each key and value string + * representation computed by its own #inspect method. + */ +VALUE Map_inspect(VALUE _self) { + Map* self = ruby_to_Map(_self); + + VALUE str = rb_str_new2("{"); + + bool first = true; + VALUE inspect_sym = rb_intern("inspect"); + + upb_strtable_iter it; + for (upb_strtable_begin(&it, &self->table); + !upb_strtable_done(&it); + upb_strtable_next(&it)) { + VALUE key = table_key_to_ruby( + self, upb_strtable_iter_key(&it), upb_strtable_iter_keylength(&it)); + + upb_value v = upb_strtable_iter_value(&it); + void* mem = value_memory(&v); + VALUE value = native_slot_get(self->value_type, + self->value_type_class, + mem); + + if (!first) { + str = rb_str_cat2(str, ", "); + } else { + first = false; + } + str = rb_str_append(str, rb_funcall(key, inspect_sym, 0)); + str = rb_str_cat2(str, " => "); + str = rb_str_append(str, rb_funcall(value, inspect_sym, 0)); + } + + str = rb_str_cat2(str, "}"); + return str; +} + +/* + * call-seq: + * Map.merge(other_map) => map + * + * Copies key/value pairs from other_map into a copy of this map. If a key is + * set in other_map and this map, the value from other_map overwrites the value + * in the new copy of this map. Returns the new copy of this map with merged + * contents. + */ +VALUE Map_merge(VALUE _self, VALUE hashmap) { + VALUE dupped = Map_dup(_self); + return Map_merge_into_self(dupped, hashmap); +} + +static int merge_into_self_callback(VALUE key, VALUE value, VALUE self) { + Map_index_set(self, key, value); + return ST_CONTINUE; +} + +// Used only internally -- shared by #merge and #initialize. +VALUE Map_merge_into_self(VALUE _self, VALUE hashmap) { + if (TYPE(hashmap) == T_HASH) { + rb_hash_foreach(hashmap, merge_into_self_callback, _self); + } else if (RB_TYPE_P(hashmap, T_DATA) && RTYPEDDATA_P(hashmap) && + RTYPEDDATA_TYPE(hashmap) == &Map_type) { + + Map* self = ruby_to_Map(_self); + Map* other = ruby_to_Map(hashmap); + + if (self->key_type != other->key_type || + self->value_type != other->value_type || + self->value_type_class != other->value_type_class) { + rb_raise(rb_eArgError, "Attempt to merge Map with mismatching types"); + } + + upb_strtable_iter it; + for (upb_strtable_begin(&it, &other->table); + !upb_strtable_done(&it); + upb_strtable_next(&it)) { + + // Replace any existing value by issuing a 'remove' operation first. + upb_value oldv; + upb_strtable_remove2(&self->table, + upb_strtable_iter_key(&it), + upb_strtable_iter_keylength(&it), + &oldv); + + upb_value v = upb_strtable_iter_value(&it); + upb_strtable_insert2(&self->table, + upb_strtable_iter_key(&it), + upb_strtable_iter_keylength(&it), + v); + } + } else { + rb_raise(rb_eArgError, "Unknown type merging into Map"); + } + return _self; +} + +// Internal method: map iterator initialization (used for serialization). +void Map_begin(VALUE _self, Map_iter* iter) { + Map* self = ruby_to_Map(_self); + iter->self = self; + upb_strtable_begin(&iter->it, &self->table); +} + +void Map_next(Map_iter* iter) { + upb_strtable_next(&iter->it); +} + +bool Map_done(Map_iter* iter) { + return upb_strtable_done(&iter->it); +} + +VALUE Map_iter_key(Map_iter* iter) { + return table_key_to_ruby( + iter->self, + upb_strtable_iter_key(&iter->it), + upb_strtable_iter_keylength(&iter->it)); +} + +VALUE Map_iter_value(Map_iter* iter) { + upb_value v = upb_strtable_iter_value(&iter->it); + void* mem = value_memory(&v); + return native_slot_get(iter->self->value_type, + iter->self->value_type_class, + mem); +} + +void Map_register(VALUE module) { + VALUE klass = rb_define_class_under(module, "Map", rb_cObject); + rb_define_alloc_func(klass, Map_alloc); + cMap = klass; + rb_gc_register_address(&cMap); + + rb_define_method(klass, "initialize", Map_init, -1); + rb_define_method(klass, "each", Map_each, 0); + rb_define_method(klass, "keys", Map_keys, 0); + rb_define_method(klass, "values", Map_values, 0); + rb_define_method(klass, "[]", Map_index, 1); + rb_define_method(klass, "[]=", Map_index_set, 2); + rb_define_method(klass, "has_key?", Map_has_key, 1); + rb_define_method(klass, "delete", Map_delete, 1); + rb_define_method(klass, "clear", Map_clear, 0); + rb_define_method(klass, "length", Map_length, 0); + rb_define_method(klass, "dup", Map_dup, 0); + rb_define_method(klass, "==", Map_eq, 1); + rb_define_method(klass, "hash", Map_hash, 0); + rb_define_method(klass, "inspect", Map_inspect, 0); + rb_define_method(klass, "merge", Map_merge, 1); + rb_include_module(klass, rb_mEnumerable); +} diff --git a/ruby/ext/google/protobuf_c/message.c b/ruby/ext/google/protobuf_c/message.c index 105b7807..de38dd7b 100644 --- a/ruby/ext/google/protobuf_c/message.c +++ b/ruby/ext/google/protobuf_c/message.c @@ -139,7 +139,14 @@ int Message_initialize_kwarg(VALUE key, VALUE val, VALUE _self) { "Unknown field name in initialization map entry."); } - if (upb_fielddef_label(f) == UPB_LABEL_REPEATED) { + if (is_map_field(f)) { + if (TYPE(val) != T_HASH) { + rb_raise(rb_eArgError, + "Expected hashmap as initializer value for map field."); + } + VALUE map = layout_get(self->descriptor->layout, Message_data(self), f); + Map_merge_into_self(map, val); + } else if (upb_fielddef_label(f) == UPB_LABEL_REPEATED) { if (TYPE(val) != T_ARRAY) { rb_raise(rb_eArgError, "Expected array as initializer value for repeated field."); @@ -450,13 +457,15 @@ VALUE build_module_from_enumdesc(EnumDescriptor* enumdesc) { * call-seq: * Google::Protobuf.deep_copy(obj) => copy_of_obj * - * Performs a deep copy of either a RepeatedField instance or a message object, - * recursively copying its members. + * Performs a deep copy of a RepeatedField instance, a Map instance, or a + * message object, recursively copying its members. */ VALUE Google_Protobuf_deep_copy(VALUE self, VALUE obj) { VALUE klass = CLASS_OF(obj); if (klass == cRepeatedField) { return RepeatedField_deep_copy(obj); + } else if (klass == cMap) { + return Map_deep_copy(obj); } else { return Message_deep_copy(obj); } diff --git a/ruby/ext/google/protobuf_c/protobuf.c b/ruby/ext/google/protobuf_c/protobuf.c index d5862284..30552705 100644 --- a/ruby/ext/google/protobuf_c/protobuf.c +++ b/ruby/ext/google/protobuf_c/protobuf.c @@ -82,6 +82,7 @@ void Init_protobuf_c() { EnumBuilderContext_register(internal); Builder_register(internal); RepeatedField_register(protobuf); + Map_register(protobuf); rb_define_singleton_method(protobuf, "encode", Google_Protobuf_encode, 1); rb_define_singleton_method(protobuf, "decode", Google_Protobuf_decode, 2); diff --git a/ruby/ext/google/protobuf_c/protobuf.h b/ruby/ext/google/protobuf_c/protobuf.h index c3a5d653..91a97a68 100644 --- a/ruby/ext/google/protobuf_c/protobuf.h +++ b/ruby/ext/google/protobuf_c/protobuf.h @@ -123,6 +123,7 @@ struct EnumDescriptor { struct MessageBuilderContext { VALUE descriptor; + VALUE builder; }; struct EnumBuilderContext { @@ -213,10 +214,13 @@ void MessageBuilderContext_free(void* _self); VALUE MessageBuilderContext_alloc(VALUE klass); void MessageBuilderContext_register(VALUE module); MessageBuilderContext* ruby_to_MessageBuilderContext(VALUE value); -VALUE MessageBuilderContext_initialize(VALUE _self, VALUE descriptor); +VALUE MessageBuilderContext_initialize(VALUE _self, + VALUE descriptor, + VALUE builder); 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); void EnumBuilderContext_mark(void* _self); void EnumBuilderContext_free(void* _self); @@ -239,6 +243,8 @@ VALUE Builder_finalize_to_pool(VALUE _self, VALUE pool_rb); // Native slot storage abstraction. // ----------------------------------------------------------------------------- +#define NATIVE_SLOT_MAX_SIZE sizeof(void*) + size_t native_slot_size(upb_fieldtype_t type); void native_slot_set(upb_fieldtype_t type, VALUE type_class, @@ -254,11 +260,18 @@ void native_slot_deep_copy(upb_fieldtype_t type, void* to, void* from); bool native_slot_eq(upb_fieldtype_t type, void* mem1, void* mem2); void native_slot_validate_string_encoding(upb_fieldtype_t type, VALUE value); +void native_slot_check_int_range_precision(upb_fieldtype_t type, VALUE value); extern rb_encoding* kRubyStringUtf8Encoding; extern rb_encoding* kRubyStringASCIIEncoding; extern rb_encoding* kRubyString8bitEncoding; +VALUE field_type_class(const upb_fielddef* field); + +bool is_map_field(const upb_fielddef* field); +const upb_fielddef* map_field_key(const upb_fielddef* field); +const upb_fielddef* map_field_value(const upb_fielddef* field); + // ----------------------------------------------------------------------------- // Repeated field container type. // ----------------------------------------------------------------------------- @@ -282,7 +295,6 @@ extern VALUE cRepeatedField; RepeatedField* ruby_to_RepeatedField(VALUE value); -void RepeatedField_register(VALUE module); VALUE RepeatedField_each(VALUE _self); VALUE RepeatedField_index(VALUE _self, VALUE _index); void* RepeatedField_index_native(VALUE _self, int index); @@ -302,6 +314,59 @@ VALUE RepeatedField_hash(VALUE _self); VALUE RepeatedField_inspect(VALUE _self); VALUE RepeatedField_plus(VALUE _self, VALUE list); +// Defined in repeated_field.c; also used by Map. +void validate_type_class(upb_fieldtype_t type, VALUE klass); + +// ----------------------------------------------------------------------------- +// Map container type. +// ----------------------------------------------------------------------------- + +typedef struct { + upb_fieldtype_t key_type; + upb_fieldtype_t value_type; + VALUE value_type_class; + upb_strtable table; +} Map; + +void Map_mark(void* self); +void Map_free(void* self); +VALUE Map_alloc(VALUE klass); +VALUE Map_init(int argc, VALUE* argv, VALUE self); +void Map_register(VALUE module); + +extern const rb_data_type_t Map_type; +extern VALUE cMap; + +Map* ruby_to_Map(VALUE value); + +VALUE Map_each(VALUE _self); +VALUE Map_keys(VALUE _self); +VALUE Map_values(VALUE _self); +VALUE Map_index(VALUE _self, VALUE key); +VALUE Map_index_set(VALUE _self, VALUE key, VALUE value); +VALUE Map_has_key(VALUE _self, VALUE key); +VALUE Map_delete(VALUE _self, VALUE key); +VALUE Map_clear(VALUE _self); +VALUE Map_length(VALUE _self); +VALUE Map_dup(VALUE _self); +VALUE Map_deep_copy(VALUE _self); +VALUE Map_eq(VALUE _self, VALUE _other); +VALUE Map_hash(VALUE _self); +VALUE Map_inspect(VALUE _self); +VALUE Map_merge(VALUE _self, VALUE hashmap); +VALUE Map_merge_into_self(VALUE _self, VALUE hashmap); + +typedef struct { + Map* self; + upb_strtable_iter it; +} Map_iter; + +void Map_begin(VALUE _self, Map_iter* iter); +void Map_next(Map_iter* iter); +bool Map_done(Map_iter* iter); +VALUE Map_iter_key(Map_iter* iter); +VALUE Map_iter_value(Map_iter* iter); + // ----------------------------------------------------------------------------- // Message layout / storage. // ----------------------------------------------------------------------------- diff --git a/ruby/ext/google/protobuf_c/repeated_field.c b/ruby/ext/google/protobuf_c/repeated_field.c index 6bd13b07..6e3f0bc7 100644 --- a/ruby/ext/google/protobuf_c/repeated_field.c +++ b/ruby/ext/google/protobuf_c/repeated_field.c @@ -324,6 +324,10 @@ VALUE RepeatedField_deep_copy(VALUE _self) { * element types are equal, their lengths are equal, and each element is equal. * Elements are compared as per normal Ruby semantics, by calling their :== * methods (or performing a more efficient comparison for primitive types). + * + * Repeated fields with dissimilar element types are never equal, even if value + * comparison (for example, between integers and floats) would have otherwise + * indicated that every element has equal value. */ VALUE RepeatedField_eq(VALUE _self, VALUE _other) { if (_self == _other) { @@ -458,7 +462,7 @@ VALUE RepeatedField_plus(VALUE _self, VALUE list) { return dupped; } -static void validate_type_class(upb_fieldtype_t type, VALUE klass) { +void validate_type_class(upb_fieldtype_t type, VALUE klass) { if (rb_iv_get(klass, kDescriptorInstanceVar) == Qnil) { rb_raise(rb_eArgError, "Type class has no descriptor. Please pass a " diff --git a/ruby/ext/google/protobuf_c/storage.c b/ruby/ext/google/protobuf_c/storage.c index c4d801af..f20ddec2 100644 --- a/ruby/ext/google/protobuf_c/storage.c +++ b/ruby/ext/google/protobuf_c/storage.c @@ -57,7 +57,17 @@ size_t native_slot_size(upb_fieldtype_t type) { } } -static void check_int_range_precision(upb_fieldtype_t type, VALUE val) { +static bool is_ruby_num(VALUE value) { + return (TYPE(value) == T_FLOAT || + TYPE(value) == T_FIXNUM || + TYPE(value) == T_BIGNUM); +} + +void native_slot_check_int_range_precision(upb_fieldtype_t type, VALUE val) { + if (!is_ruby_num(val)) { + rb_raise(rb_eTypeError, "Expected number type for integral field."); + } + // NUM2{INT,UINT,LL,ULL} macros do the appropriate range checks on upper // bound; we just need to do precision checks (i.e., disallow rounding) and // check for < 0 on unsigned types. @@ -76,12 +86,6 @@ static void check_int_range_precision(upb_fieldtype_t type, VALUE val) { } } -static bool is_ruby_num(VALUE value) { - return (TYPE(value) == T_FLOAT || - TYPE(value) == T_FIXNUM || - TYPE(value) == T_BIGNUM); -} - void native_slot_validate_string_encoding(upb_fieldtype_t type, VALUE value) { bool bad_encoding = false; rb_encoding* string_encoding = rb_enc_from_index(ENCODING_GET(value)); @@ -156,14 +160,14 @@ void native_slot_set(upb_fieldtype_t type, VALUE type_class, int32_t int_val = 0; if (TYPE(value) == T_SYMBOL) { // Ensure that the given symbol exists in the enum module. - VALUE lookup = rb_const_get(type_class, SYM2ID(value)); + VALUE lookup = rb_funcall(type_class, rb_intern("resolve"), 1, value); if (lookup == Qnil) { rb_raise(rb_eRangeError, "Unknown symbol value for enum field."); } else { int_val = NUM2INT(lookup); } } else { - check_int_range_precision(UPB_TYPE_INT32, value); + native_slot_check_int_range_precision(UPB_TYPE_INT32, value); int_val = NUM2INT(value); } DEREF(memory, int32_t) = int_val; @@ -173,10 +177,7 @@ void native_slot_set(upb_fieldtype_t type, VALUE type_class, case UPB_TYPE_INT64: case UPB_TYPE_UINT32: case UPB_TYPE_UINT64: - if (!is_ruby_num(value)) { - rb_raise(rb_eTypeError, "Expected number type for integral field."); - } - check_int_range_precision(type, value); + native_slot_check_int_range_precision(type, value); switch (type) { case UPB_TYPE_INT32: DEREF(memory, int32_t) = NUM2INT(value); @@ -246,8 +247,9 @@ void native_slot_init(upb_fieldtype_t type, void* memory) { break; case UPB_TYPE_STRING: case UPB_TYPE_BYTES: - // TODO(cfallin): set encoding appropriately DEREF(memory, VALUE) = rb_str_new2(""); + rb_enc_associate(DEREF(memory, VALUE), (type == UPB_TYPE_BYTES) ? + kRubyString8bitEncoding : kRubyStringUtf8Encoding); break; case UPB_TYPE_MESSAGE: DEREF(memory, VALUE) = Qnil; @@ -321,6 +323,35 @@ bool native_slot_eq(upb_fieldtype_t type, void* mem1, void* mem2) { } } +// ----------------------------------------------------------------------------- +// Map field utilities. +// ----------------------------------------------------------------------------- + +bool is_map_field(const upb_fielddef* field) { + if (upb_fielddef_label(field) != UPB_LABEL_REPEATED || + upb_fielddef_type(field) != UPB_TYPE_MESSAGE) { + return false; + } + const upb_msgdef* subdef = (const upb_msgdef*)upb_fielddef_subdef(field); + return upb_msgdef_mapentry(subdef); +} + +const upb_fielddef* map_field_key(const upb_fielddef* field) { + assert(is_map_field(field)); + const upb_msgdef* subdef = (const upb_msgdef*)upb_fielddef_subdef(field); + const upb_fielddef* key_field = upb_msgdef_itof(subdef, 1); + assert(key_field != NULL); + return key_field; +} + +const upb_fielddef* map_field_value(const upb_fielddef* field) { + assert(is_map_field(field)); + const upb_msgdef* subdef = (const upb_msgdef*)upb_fielddef_subdef(field); + const upb_fielddef* value_field = upb_msgdef_itof(subdef, 2); + assert(value_field != NULL); + return value_field; +} + // ----------------------------------------------------------------------------- // Memory layout management. // ----------------------------------------------------------------------------- @@ -334,9 +365,12 @@ MessageLayout* create_layout(const upb_msgdef* msgdef) { size_t off = 0; for (upb_msg_begin(&it, msgdef); !upb_msg_done(&it); upb_msg_next(&it)) { const upb_fielddef* field = upb_msg_iter_field(&it); - size_t field_size = - (upb_fielddef_label(field) == UPB_LABEL_REPEATED) ? - sizeof(VALUE) : native_slot_size(upb_fielddef_type(field)); + 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 off = (off + field_size - 1) & ~(field_size - 1); layout->offsets[upb_fielddef_index(field)] = off; @@ -357,7 +391,7 @@ void free_layout(MessageLayout* layout) { xfree(layout); } -static VALUE get_type_class(const upb_fielddef* field) { +VALUE field_type_class(const upb_fielddef* field) { VALUE type_class = Qnil; if (upb_fielddef_type(field) == UPB_TYPE_MESSAGE) { VALUE submsgdesc = @@ -380,7 +414,7 @@ VALUE layout_get(MessageLayout* layout, return *((VALUE *)memory); } else { return native_slot_get(upb_fielddef_type(field), - get_type_class(field), + field_type_class(field), memory); } } @@ -398,9 +432,8 @@ static void check_repeated_field_type(VALUE val, const upb_fielddef* field) { rb_raise(rb_eTypeError, "Repeated field array has wrong element type"); } - if (upb_fielddef_type(field) == UPB_TYPE_MESSAGE || - upb_fielddef_type(field) == UPB_TYPE_ENUM) { - RepeatedField* self = ruby_to_RepeatedField(val); + if (self->field_type == UPB_TYPE_MESSAGE || + self->field_type == UPB_TYPE_ENUM) { if (self->field_type_class != get_def_obj(upb_fielddef_subdef(field))) { rb_raise(rb_eTypeError, @@ -409,17 +442,48 @@ static void check_repeated_field_type(VALUE val, const upb_fielddef* field) { } } +static void check_map_field_type(VALUE val, const upb_fielddef* field) { + assert(is_map_field(field)); + const upb_fielddef* key_field = map_field_key(field); + const upb_fielddef* value_field = map_field_value(field); + + if (!RB_TYPE_P(val, T_DATA) || !RTYPEDDATA_P(val) || + RTYPEDDATA_TYPE(val) != &Map_type) { + rb_raise(rb_eTypeError, "Expected Map instance"); + } + + Map* self = ruby_to_Map(val); + if (self->key_type != upb_fielddef_type(key_field)) { + rb_raise(rb_eTypeError, "Map key type does not match field's key type"); + } + if (self->value_type != upb_fielddef_type(value_field)) { + rb_raise(rb_eTypeError, "Map value type does not match field's value type"); + } + if (upb_fielddef_type(value_field) == UPB_TYPE_MESSAGE || + upb_fielddef_type(value_field) == UPB_TYPE_ENUM) { + if (self->value_type_class != + get_def_obj(upb_fielddef_subdef(value_field))) { + rb_raise(rb_eTypeError, + "Map value type has wrong message/enum class"); + } + } +} + + void layout_set(MessageLayout* layout, void* storage, const upb_fielddef* field, VALUE val) { void* memory = ((uint8_t *)storage) + layout->offsets[upb_fielddef_index(field)]; - if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { + if (is_map_field(field)) { + check_map_field_type(val, field); + DEREF(memory, VALUE) = val; + } else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { check_repeated_field_type(val, field); - *((VALUE *)memory) = val; + DEREF(memory, VALUE) = val; } else { - native_slot_set(upb_fielddef_type(field), get_type_class(field), + native_slot_set(upb_fielddef_type(field), field_type_class(field), memory, val); } } @@ -434,9 +498,34 @@ void layout_init(MessageLayout* layout, void* memory = ((uint8_t *)storage) + layout->offsets[upb_fielddef_index(field)]; - if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { + if (is_map_field(field)) { + VALUE map = Qnil; + + const upb_fielddef* key_field = map_field_key(field); + const upb_fielddef* value_field = map_field_value(field); + VALUE type_class = field_type_class(value_field); + + if (type_class != Qnil) { + VALUE args[3] = { + fieldtype_to_ruby(upb_fielddef_type(key_field)), + fieldtype_to_ruby(upb_fielddef_type(value_field)), + type_class, + }; + map = rb_class_new_instance(3, args, cMap); + } else { + VALUE args[2] = { + fieldtype_to_ruby(upb_fielddef_type(key_field)), + fieldtype_to_ruby(upb_fielddef_type(value_field)), + }; + map = rb_class_new_instance(2, args, cMap); + } + + DEREF(memory, VALUE) = map; + } else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { VALUE ary = Qnil; - VALUE type_class = get_type_class(field); + + VALUE type_class = field_type_class(field); + if (type_class != Qnil) { VALUE args[2] = { fieldtype_to_ruby(upb_fielddef_type(field)), @@ -447,7 +536,8 @@ void layout_init(MessageLayout* layout, VALUE args[1] = { fieldtype_to_ruby(upb_fielddef_type(field)) }; ary = rb_class_new_instance(1, args, cRepeatedField); } - *((VALUE *)memory) = ary; + + DEREF(memory, VALUE) = ary; } else { native_slot_init(upb_fielddef_type(field), memory); } @@ -464,7 +554,7 @@ void layout_mark(MessageLayout* layout, void* storage) { layout->offsets[upb_fielddef_index(field)]; if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { - rb_gc_mark(*((VALUE *)memory)); + rb_gc_mark(DEREF(memory, VALUE)); } else { native_slot_mark(upb_fielddef_type(field), memory); } @@ -482,8 +572,10 @@ void layout_dup(MessageLayout* layout, void* to, void* from) { void* from_memory = ((uint8_t *)from) + layout->offsets[upb_fielddef_index(field)]; - if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { - *((VALUE *)to_memory) = RepeatedField_dup(*((VALUE *)from_memory)); + 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)); } else { native_slot_dup(upb_fielddef_type(field), to_memory, from_memory); } @@ -501,8 +593,12 @@ void layout_deep_copy(MessageLayout* layout, void* to, void* from) { void* from_memory = ((uint8_t *)from) + layout->offsets[upb_fielddef_index(field)]; - if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { - *((VALUE *)to_memory) = RepeatedField_deep_copy(*((VALUE *)from_memory)); + if (is_map_field(field)) { + DEREF(to_memory, VALUE) = + Map_deep_copy(DEREF(from_memory, VALUE)); + } else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { + DEREF(to_memory, VALUE) = + RepeatedField_deep_copy(DEREF(from_memory, VALUE)); } else { native_slot_deep_copy(upb_fielddef_type(field), to_memory, from_memory); } @@ -520,11 +616,12 @@ VALUE layout_eq(MessageLayout* layout, void* msg1, void* msg2) { void* msg2_memory = ((uint8_t *)msg2) + layout->offsets[upb_fielddef_index(field)]; - if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { - if (RepeatedField_eq(*((VALUE *)msg1_memory), - *((VALUE *)msg2_memory)) == Qfalse) { - return Qfalse; - } + if (is_map_field(field)) { + return Map_eq(DEREF(msg1_memory, VALUE), + DEREF(msg2_memory, VALUE)); + } else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { + return RepeatedField_eq(DEREF(msg1_memory, VALUE), + DEREF(msg2_memory, VALUE)); } else { if (!native_slot_eq(upb_fielddef_type(field), msg1_memory, msg2_memory)) { diff --git a/ruby/ext/google/protobuf_c/upb.c b/ruby/ext/google/protobuf_c/upb.c index c9f47195..0015aad1 100644 --- a/ruby/ext/google/protobuf_c/upb.c +++ b/ruby/ext/google/protobuf_c/upb.c @@ -1269,6 +1269,7 @@ upb_msgdef *upb_msgdef_new(const void *owner) { 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; + m->map_entry = false; return m; err1: @@ -1283,6 +1284,7 @@ upb_msgdef *upb_msgdef_dup(const upb_msgdef *m, const void *owner) { if (!newm) return NULL; bool ok = upb_def_setfullname(UPB_UPCAST(newm), 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)) { @@ -1386,6 +1388,15 @@ int upb_msgdef_numfields(const upb_msgdef *m) { return upb_strtable_count(&m->ntof); } +void upb_msgdef_setmapentry(upb_msgdef *m, bool map_entry) { + assert(!upb_msgdef_isfrozen(m)); + m->map_entry = map_entry; +} + +bool upb_msgdef_mapentry(const upb_msgdef *m) { + return m->map_entry; +} + void upb_msg_begin(upb_msg_iter *iter, const upb_msgdef *m) { upb_inttable_begin(iter, &m->itof); } @@ -3401,31 +3412,28 @@ int log2ceil(uint64_t v) { } char *upb_strdup(const char *s) { - size_t n = strlen(s) + 1; + return upb_strdup2(s, strlen(s)); +} + +char *upb_strdup2(const char *s, size_t len) { + // Always null-terminate, even if binary data; but don't rely on the input to + // have a null-terminating byte since it may be a raw binary buffer. + size_t n = len + 1; char *p = malloc(n); - if (p) memcpy(p, s, n); + if (p) memcpy(p, s, len); + p[len] = 0; return p; } // A type to represent the lookup key of either a strtable or an inttable. -// This is like upb_tabkey, but can carry a size also to allow lookups of -// non-NULL-terminated strings (we don't store string lengths in the table). typedef struct { upb_tabkey key; - uint32_t len; // For string keys only. } lookupkey_t; -static lookupkey_t strkey(const char *str) { - lookupkey_t k; - k.key.str = (char*)str; - k.len = strlen(str); - return k; -} - static lookupkey_t strkey2(const char *str, size_t len) { lookupkey_t k; - k.key.str = (char*)str; - k.len = len; + k.key.s.str = (char*)str; + k.key.s.length = len; return k; } @@ -3607,11 +3615,12 @@ static size_t begin(const upb_table *t) { // A simple "subclass" of upb_table that only adds a hash function for strings. static uint32_t strhash(upb_tabkey key) { - return MurmurHash2(key.str, strlen(key.str), 0); + return MurmurHash2(key.s.str, key.s.length, 0); } static bool streql(upb_tabkey k1, lookupkey_t k2) { - return strncmp(k1.str, k2.key.str, k2.len) == 0 && k1.str[k2.len] == '\0'; + return k1.s.length == k2.key.s.length && + memcmp(k1.s.str, k2.key.s.str, k1.s.length) == 0; } bool upb_strtable_init(upb_strtable *t, upb_ctype_t ctype) { @@ -3620,7 +3629,7 @@ bool upb_strtable_init(upb_strtable *t, upb_ctype_t ctype) { void upb_strtable_uninit(upb_strtable *t) { for (size_t i = 0; i < upb_table_size(&t->t); i++) - free((void*)t->t.entries[i].key.str); + free((void*)t->t.entries[i].key.s.str); uninit(&t->t); } @@ -3631,26 +3640,30 @@ bool upb_strtable_resize(upb_strtable *t, size_t size_lg2) { upb_strtable_iter i; upb_strtable_begin(&i, t); for ( ; !upb_strtable_done(&i); upb_strtable_next(&i)) { - upb_strtable_insert( - &new_table, upb_strtable_iter_key(&i), upb_strtable_iter_value(&i)); + upb_strtable_insert2( + &new_table, + upb_strtable_iter_key(&i), + upb_strtable_iter_keylength(&i), + upb_strtable_iter_value(&i)); } upb_strtable_uninit(t); *t = new_table; return true; } -bool upb_strtable_insert(upb_strtable *t, const char *k, upb_value v) { +bool upb_strtable_insert2(upb_strtable *t, const char *k, size_t len, + upb_value v) { if (isfull(&t->t)) { // Need to resize. New table of double the size, add old elements to it. if (!upb_strtable_resize(t, t->t.size_lg2 + 1)) { return false; } } - if ((k = upb_strdup(k)) == NULL) return false; + if ((k = upb_strdup2(k, len)) == NULL) return false; - lookupkey_t key = strkey(k); - uint32_t hash = MurmurHash2(key.key.str, key.len, 0); - insert(&t->t, strkey(k), v, hash, &strhash, &streql); + lookupkey_t key = strkey2(k, len); + uint32_t hash = MurmurHash2(key.key.s.str, key.key.s.length, 0); + insert(&t->t, key, v, hash, &strhash, &streql); return true; } @@ -3660,11 +3673,12 @@ bool upb_strtable_lookup2(const upb_strtable *t, const char *key, size_t len, return lookup(&t->t, strkey2(key, len), v, hash, &streql); } -bool upb_strtable_remove(upb_strtable *t, const char *key, upb_value *val) { +bool upb_strtable_remove2(upb_strtable *t, const char *key, size_t len, + upb_value *val) { uint32_t hash = MurmurHash2(key, strlen(key), 0); upb_tabkey tabkey; - if (rm(&t->t, strkey(key), val, &tabkey, hash, &streql)) { - free((void*)tabkey.str); + if (rm(&t->t, strkey2(key, len), val, &tabkey, hash, &streql)) { + free((void*)tabkey.s.str); return true; } else { return false; @@ -3693,7 +3707,12 @@ bool upb_strtable_done(const upb_strtable_iter *i) { const char *upb_strtable_iter_key(upb_strtable_iter *i) { assert(!upb_strtable_done(i)); - return str_tabent(i)->key.str; + return str_tabent(i)->key.s.str; +} + +size_t upb_strtable_iter_keylength(upb_strtable_iter *i) { + assert(!upb_strtable_done(i)); + return str_tabent(i)->key.s.length; } upb_value upb_strtable_iter_value(const upb_strtable_iter *i) { diff --git a/ruby/ext/google/protobuf_c/upb.h b/ruby/ext/google/protobuf_c/upb.h index 150aef10..0e98bbab 100644 --- a/ruby/ext/google/protobuf_c/upb.h +++ b/ruby/ext/google/protobuf_c/upb.h @@ -600,6 +600,9 @@ typedef struct { // Like strdup(), which isn't always available since it's not ANSI C. char *upb_strdup(const char *s); +// Variant that works with a length-delimited rather than NULL-delimited string, +// as supported by strtable. +char *upb_strdup2(const char *s, size_t len); UPB_INLINE void _upb_value_setval(upb_value *v, _upb_value val, upb_ctype_t ctype) { @@ -654,12 +657,24 @@ FUNCS(fptr, fptr, upb_func*, UPB_CTYPE_FPTR); typedef union { uintptr_t num; - const char *str; // We own, nullz. + struct { + // We own this. NULL-terminated but may also contain binary data; see + // explicit length below. + // TODO: move the length to the start of the string in order to reduce + // tabkey's size (to one machine word) in a way that supports static + // initialization. + const char *str; + size_t length; + } s; } upb_tabkey; #define UPB_TABKEY_NUM(n) {n} #ifdef UPB_C99 -#define UPB_TABKEY_STR(s) {.str = s} +// Given that |s| is a string literal, sizeof(s) gives us a +// compile-time-constant strlen(). We must ensure that this works for static +// data initializers. +#define UPB_TABKEY_STR(strval) { .s = { .str = strval, \ + .length = sizeof(strval) - 1 } } #endif // TODO(haberman): C++ #define UPB_TABKEY_NONE {0} @@ -765,7 +780,14 @@ UPB_INLINE size_t upb_strtable_count(const upb_strtable *t) { // If a table resize was required but memory allocation failed, false is // returned and the table is unchanged. bool upb_inttable_insert(upb_inttable *t, uintptr_t key, upb_value val); -bool upb_strtable_insert(upb_strtable *t, const char *key, upb_value val); +bool upb_strtable_insert2(upb_strtable *t, const char *key, size_t len, + upb_value val); + +// For NULL-terminated strings. +UPB_INLINE bool upb_strtable_insert(upb_strtable *t, const char *key, + upb_value val) { + return upb_strtable_insert2(t, key, strlen(key), val); +} // Looks up key in this table, returning "true" if the key was found. // If v is non-NULL, copies the value for this key into *v. @@ -782,7 +804,14 @@ UPB_INLINE bool upb_strtable_lookup(const upb_strtable *t, const char *key, // Removes an item from the table. Returns true if the remove was successful, // and stores the removed item in *val if non-NULL. bool upb_inttable_remove(upb_inttable *t, uintptr_t key, upb_value *val); -bool upb_strtable_remove(upb_strtable *t, const char *key, upb_value *val); +bool upb_strtable_remove2(upb_strtable *t, const char *key, size_t len, + upb_value *val); + +// For NULL-terminated strings. +UPB_INLINE bool upb_strtable_remove(upb_strtable *t, const char *key, + upb_value *v) { + return upb_strtable_remove2(t, key, strlen(key), v); +} // Updates an existing entry in an inttable. If the entry does not exist, // returns false and does nothing. Unlike insert/remove, this does not @@ -876,6 +905,7 @@ void upb_strtable_begin(upb_strtable_iter *i, const upb_strtable *t); void upb_strtable_next(upb_strtable_iter *i); bool upb_strtable_done(const upb_strtable_iter *i); const char *upb_strtable_iter_key(upb_strtable_iter *i); +size_t upb_strtable_iter_keylength(upb_strtable_iter *i); upb_value upb_strtable_iter_value(const upb_strtable_iter *i); void upb_strtable_iter_setdone(upb_strtable_iter *i); bool upb_strtable_iter_isequal(const upb_strtable_iter *i1, @@ -1777,6 +1807,10 @@ UPB_DEFINE_DEF(upb::MessageDef, msgdef, MSG, UPB_QUOTE( // just be moved into symtab.c? MessageDef* Dup(const void* owner) const; + // Is this message a map entry? + void setmapentry(bool map_entry); + bool mapentry() const; + // Iteration over fields. The order is undefined. class iterator : public std::iterator { public: @@ -1823,6 +1857,11 @@ UPB_DEFINE_STRUCT(upb_msgdef, upb_def, upb_inttable itof; // int to field upb_strtable ntof; // name to field + // Is this a map-entry message? + // TODO: set this flag properly for static descriptors; regenerate + // descriptor.upb.c. + bool map_entry; + // TODO(haberman): proper extension ranges (there can be multiple). )); @@ -1830,7 +1869,7 @@ UPB_DEFINE_STRUCT(upb_msgdef, upb_def, refs, ref2s) \ { \ UPB_DEF_INIT(name, UPB_DEF_MSG, refs, ref2s), selector_count, \ - submsg_field_count, itof, ntof \ + submsg_field_count, itof, ntof, false \ } UPB_BEGIN_EXTERN_C // { @@ -1878,6 +1917,9 @@ UPB_INLINE upb_fielddef *upb_msgdef_ntof_mutable(upb_msgdef *m, return (upb_fielddef *)upb_msgdef_ntof(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)) { // upb_fielddef *f = upb_msg_iter_field(&i); @@ -2331,6 +2373,12 @@ inline const FieldDef *MessageDef::FindFieldByName(const char *name, inline MessageDef* MessageDef::Dup(const void *owner) const { return upb_msgdef_dup(this, owner); } +inline void MessageDef::setmapentry(bool map_entry) { + upb_msgdef_setmapentry(this, 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 { diff --git a/ruby/tests/basic.rb b/ruby/tests/basic.rb index 05b3a0fa..2620fa9b 100644 --- a/ruby/tests/basic.rb +++ b/ruby/tests/basic.rb @@ -36,23 +36,43 @@ module BasicTest add_message "TestMessage2" do optional :foo, :int32, 1 end + add_message "Recursive1" do optional :foo, :message, 1, "Recursive2" end add_message "Recursive2" do optional :foo, :message, 1, "Recursive1" end + add_enum "TestEnum" do value :Default, 0 value :A, 1 value :B, 2 value :C, 3 end + add_message "BadFieldNames" do optional :dup, :int32, 1 optional :class, :int32, 2 optional :"a.b", :int32, 3 end + + add_message "MapMessage" do + map :map_string_int32, :string, :int32, 1 + map :map_string_msg, :string, :message, 2, "TestMessage2" + end + add_message "MapMessageWireEquiv" do + repeated :map_string_int32, :message, 1, "MapMessageWireEquiv_entry1" + repeated :map_string_msg, :message, 2, "MapMessageWireEquiv_entry2" + end + add_message "MapMessageWireEquiv_entry1" do + optional :key, :string, 1 + optional :value, :int32, 2 + end + add_message "MapMessageWireEquiv_entry2" do + optional :key, :string, 1 + optional :value, :message, 2, "TestMessage2" + end end TestMessage = pool.lookup("TestMessage").msgclass @@ -61,6 +81,12 @@ module BasicTest Recursive2 = pool.lookup("Recursive2").msgclass TestEnum = pool.lookup("TestEnum").enummodule BadFieldNames = pool.lookup("BadFieldNames").msgclass + MapMessage = pool.lookup("MapMessage").msgclass + MapMessageWireEquiv = pool.lookup("MapMessageWireEquiv").msgclass + MapMessageWireEquiv_entry1 = + pool.lookup("MapMessageWireEquiv_entry1").msgclass + MapMessageWireEquiv_entry2 = + pool.lookup("MapMessageWireEquiv_entry2").msgclass # ------------ test cases --------------- @@ -300,7 +326,7 @@ module BasicTest l.push :B l.push :C assert l.count == 3 - assert_raise NameError do + assert_raise RangeError do l.push :D end assert l[0] == :A @@ -324,12 +350,240 @@ module BasicTest end end + def test_map_basic + # allowed key types: + # :int32, :int64, :uint32, :uint64, :bool, :string, :bytes. + + m = Google::Protobuf::Map.new(:string, :int32) + m["asdf"] = 1 + assert m["asdf"] == 1 + m["jkl;"] = 42 + assert m == { "jkl;" => 42, "asdf" => 1 } + assert m.has_key?("asdf") + assert !m.has_key?("qwerty") + assert m.length == 2 + + m2 = m.dup + assert m == m2 + assert m.hash != 0 + assert m.hash == m2.hash + + collected = {} + m.each { |k,v| collected[v] = k } + assert collected == { 42 => "jkl;", 1 => "asdf" } + + assert m.delete("asdf") == 1 + assert !m.has_key?("asdf") + assert m["asdf"] == nil + assert !m.has_key?("asdf") + + # 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.keys == ["jkl;"] + assert m.values == [42] + + m.clear + assert m.length == 0 + assert m == {} + + assert_raise TypeError do + m[1] = 1 + end + assert_raise RangeError do + m["asdf"] = 0x1_0000_0000 + end + end + + def test_map_ctor + m = Google::Protobuf::Map.new(:string, :int32, + {"a" => 1, "b" => 2, "c" => 3}) + assert m == {"a" => 1, "c" => 3, "b" => 2} + end + + def test_map_keytypes + m = Google::Protobuf::Map.new(:int32, :int32) + m[1] = 42 + m[-1] = 42 + assert_raise RangeError do + m[0x8000_0000] = 1 + end + assert_raise TypeError do + m["asdf"] = 1 + end + + m = Google::Protobuf::Map.new(:int64, :int32) + m[0x1000_0000_0000_0000] = 1 + assert_raise RangeError do + m[0x1_0000_0000_0000_0000] = 1 + end + assert_raise TypeError do + m["asdf"] = 1 + end + + m = Google::Protobuf::Map.new(:uint32, :int32) + m[0x8000_0000] = 1 + assert_raise RangeError do + m[0x1_0000_0000] = 1 + end + assert_raise RangeError do + m[-1] = 1 + end + + m = Google::Protobuf::Map.new(:uint64, :int32) + m[0x8000_0000_0000_0000] = 1 + assert_raise RangeError do + m[0x1_0000_0000_0000_0000] = 1 + end + assert_raise RangeError do + m[-1] = 1 + end + + m = Google::Protobuf::Map.new(:bool, :int32) + m[true] = 1 + m[false] = 2 + assert_raise TypeError do + m[1] = 1 + end + assert_raise TypeError do + m["asdf"] = 1 + end + + m = Google::Protobuf::Map.new(:string, :int32) + m["asdf"] = 1 + assert_raise TypeError do + m[1] = 1 + end + assert_raise TypeError do + bytestring = ["FFFF"].pack("H*") + m[bytestring] = 1 + end + + m = Google::Protobuf::Map.new(:bytes, :int32) + bytestring = ["FFFF"].pack("H*") + m[bytestring] = 1 + assert_raise TypeError do + m["asdf"] = 1 + end + assert_raise TypeError do + m[1] = 1 + end + end + + def test_map_msg_enum_valuetypes + m = Google::Protobuf::Map.new(:string, :message, TestMessage) + m["asdf"] = TestMessage.new + assert_raise TypeError do + m["jkl;"] = TestMessage2.new + end + + m = Google::Protobuf::Map.new(:string, :message, TestMessage, + { "a" => TestMessage.new(:optional_int32 => 42), + "b" => TestMessage.new(:optional_int32 => 84) }) + assert m.length == 2 + assert m.values.map{|msg| msg.optional_int32}.sort == [42, 84] + + m = Google::Protobuf::Map.new(:string, :enum, TestEnum, + { "x" => :A, "y" => :B, "z" => :C }) + assert m.length == 3 + assert m["z"] == :C + m["z"] = 2 + assert m["z"] == :B + m["z"] = 4 + assert m["z"] == 4 + assert_raise RangeError do + m["z"] = :Z + end + assert_raise TypeError do + m["z"] = "z" + end + end + + def test_map_dup_deep_copy + m = Google::Protobuf::Map.new(:string, :message, TestMessage, + { "a" => TestMessage.new(:optional_int32 => 42), + "b" => TestMessage.new(:optional_int32 => 84) }) + + m2 = m.dup + assert m == m2 + assert m.object_id != m2.object_id + assert m["a"].object_id == m2["a"].object_id + assert m["b"].object_id == m2["b"].object_id + + m2 = Google::Protobuf.deep_copy(m) + assert m == m2 + assert m.object_id != m2.object_id + assert m["a"].object_id != m2["a"].object_id + assert m["b"].object_id != m2["b"].object_id + end + + def test_map_field + m = MapMessage.new + assert m.map_string_int32 == {} + assert m.map_string_msg == {} + + m = MapMessage.new(:map_string_int32 => {"a" => 1, "b" => 2}, + :map_string_msg => {"a" => TestMessage2.new(:foo => 1), + "b" => TestMessage2.new(:foo => 2)}) + assert m.map_string_int32.keys.sort == ["a", "b"] + assert m.map_string_int32["a"] == 1 + assert m.map_string_msg["b"].foo == 2 + + m.map_string_int32["c"] = 3 + assert m.map_string_int32["c"] == 3 + m.map_string_msg["c"] = TestMessage2.new(:foo => 3) + assert m.map_string_msg["c"] == TestMessage2.new(:foo => 3) + m.map_string_msg.delete("b") + m.map_string_msg.delete("c") + assert m.map_string_msg == { "a" => TestMessage2.new(:foo => 1) } + + assert_raise TypeError do + m.map_string_msg["e"] = TestMessage.new # wrong value type + end + # ensure nothing was added by the above + assert m.map_string_msg == { "a" => TestMessage2.new(:foo => 1) } + + m.map_string_int32 = Google::Protobuf::Map.new(:string, :int32) + assert_raise TypeError do + m.map_string_int32 = Google::Protobuf::Map.new(:string, :int64) + end + assert_raise TypeError do + m.map_string_int32 = {} + end + + assert_raise TypeError do + m = MapMessage.new(:map_string_int32 => { 1 => "I am not a number" }) + end + end + + def test_map_encode_decode + m = MapMessage.new(:map_string_int32 => {"a" => 1, "b" => 2}, + :map_string_msg => {"a" => TestMessage2.new(:foo => 1), + "b" => TestMessage2.new(:foo => 2)}) + m2 = MapMessage.decode(MapMessage.encode(m)) + assert m == m2 + + m3 = MapMessageWireEquiv.decode(MapMessage.encode(m)) + assert m3.map_string_int32.length == 2 + + kv = {} + m3.map_string_int32.map { |msg| kv[msg.key] = msg.value } + assert kv == {"a" => 1, "b" => 2} + + kv = {} + m3.map_string_msg.map { |msg| kv[msg.key] = msg.value } + assert kv == {"a" => TestMessage2.new(:foo => 1), + "b" => TestMessage2.new(:foo => 2)} + end + def test_enum_field m = TestMessage.new assert m.optional_enum == :Default m.optional_enum = :A assert m.optional_enum == :A - assert_raise NameError do + assert_raise RangeError do m.optional_enum = :ASDF end m.optional_enum = 1 -- cgit v1.2.3 From 80276ac0218f6d8fcdbad0fb09b233b31d2bc0fb Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Tue, 6 Jan 2015 18:01:32 -0800 Subject: Addressed code-review comments. --- ruby/ext/google/protobuf_c/defs.c | 11 ++++---- ruby/ext/google/protobuf_c/encode_decode.c | 42 ++++++++++++++---------------- ruby/ext/google/protobuf_c/map.c | 11 ++++++++ ruby/ext/google/protobuf_c/message.c | 2 +- ruby/ext/google/protobuf_c/protobuf.h | 9 +++++++ ruby/ext/google/protobuf_c/storage.c | 16 +++++++++--- 6 files changed, 59 insertions(+), 32 deletions(-) (limited to 'ruby') diff --git a/ruby/ext/google/protobuf_c/defs.c b/ruby/ext/google/protobuf_c/defs.c index 5c94a74a..499e041b 100644 --- a/ruby/ext/google/protobuf_c/defs.c +++ b/ruby/ext/google/protobuf_c/defs.c @@ -1077,11 +1077,12 @@ VALUE MessageBuilderContext_repeated(int argc, VALUE* argv, VALUE _self) { * MessageBuilderContext.map(name, key_type, value_type, number, * value_type_class = nil) * - * Defines a new map field on this message type with the given key and value types, tag - * number, and type class (for message and enum value types). The key type must - * be :int32/:uint32/:int64/:uint64, :bool, or :string. The value type type must - * be a Ruby symbol (as accepted by FieldDescriptor#type=) and the type_class - * must be a string, if present (as accepted by FieldDescriptor#submsg_name=). + * Defines a new map field on this message type with the given key and value + * types, tag number, and type class (for message and enum value types). The key + * type must be :int32/:uint32/:int64/:uint64, :bool, or :string. The value type + * type must be a Ruby symbol (as accepted by FieldDescriptor#type=) and the + * type_class must be a string, if present (as accepted by + * FieldDescriptor#submsg_name=). */ VALUE MessageBuilderContext_map(int argc, VALUE* argv, VALUE _self) { DEFINE_SELF(MessageBuilderContext, self, _self); diff --git a/ruby/ext/google/protobuf_c/encode_decode.c b/ruby/ext/google/protobuf_c/encode_decode.c index 6263edcc..f1b951fc 100644 --- a/ruby/ext/google/protobuf_c/encode_decode.c +++ b/ruby/ext/google/protobuf_c/encode_decode.c @@ -177,8 +177,8 @@ static void *submsg_handler(void *closure, const void *hd) { // Handler data for startmap/endmap handlers. typedef struct { size_t ofs; - const upb_fielddef* key_field; - const upb_fielddef* value_field; + upb_fieldtype_t key_field_type; + upb_fieldtype_t value_field_type; VALUE value_field_typeclass; } map_handlerdata_t; @@ -194,12 +194,6 @@ typedef struct { char value_storage[NATIVE_SLOT_MAX_SIZE]; } map_parse_frame_t; -// Handler to begin a sequence of map entries: simple no-op that exists only to -// set context for the map entry handlers. -static void *startmap_handler(void *closure, const void *hd) { - return closure; -} - // Handler to begin a map entry: allocates a temporary frame. This is the // 'startsubmsg' handler on the msgdef that contains the map field. static void *startmapentry_handler(void *closure, const void *hd) { @@ -210,10 +204,8 @@ static void *startmapentry_handler(void *closure, const void *hd) { map_parse_frame_t* frame = ALLOC(map_parse_frame_t); frame->map = map_rb; - native_slot_init(upb_fielddef_type(mapdata->key_field), - &frame->key_storage); - native_slot_init(upb_fielddef_type(mapdata->value_field), - &frame->value_storage); + native_slot_init(mapdata->key_field_type, &frame->key_storage); + native_slot_init(mapdata->value_field_type, &frame->value_storage); return frame; } @@ -225,10 +217,10 @@ static bool endmap_handler(void *closure, const void *hd, upb_status* s) { const map_handlerdata_t* mapdata = hd; VALUE key = native_slot_get( - upb_fielddef_type(mapdata->key_field), Qnil, + mapdata->key_field_type, Qnil, &frame->key_storage); VALUE value = native_slot_get( - upb_fielddef_type(mapdata->value_field), mapdata->value_field_typeclass, + mapdata->value_field_type, mapdata->value_field_typeclass, &frame->value_storage); Map_index_set(frame->map, key, value); @@ -250,11 +242,15 @@ static map_handlerdata_t* new_map_handlerdata( map_handlerdata_t* hd = ALLOC(map_handlerdata_t); hd->ofs = ofs; - hd->key_field = upb_msgdef_itof(mapentry_def, 1); - assert(hd->key_field != NULL); - hd->value_field = upb_msgdef_itof(mapentry_def, 2); - assert(hd->value_field != NULL); - hd->value_field_typeclass = field_type_class(hd->value_field); + const upb_fielddef* key_field = upb_msgdef_itof(mapentry_def, + MAP_KEY_FIELD); + assert(key_field != NULL); + hd->key_field_type = upb_fielddef_type(key_field); + const upb_fielddef* value_field = upb_msgdef_itof(mapentry_def, + 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); return hd; } @@ -293,6 +289,7 @@ static void add_handlers_for_repeated_field(upb_handlers *h, appendbytes_handler : appendstr_handler, NULL); upb_handlers_setstring(h, f, stringdata_handler, NULL); + break; } case UPB_TYPE_MESSAGE: { upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER; @@ -352,7 +349,6 @@ static void add_handlers_for_mapfield(upb_handlers* h, upb_handlers_addcleanup(h, hd, free); upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER; upb_handlerattr_sethandlerdata(&attr, hd); - upb_handlers_setstartseq(h, fielddef, startmap_handler, &attr); upb_handlers_setstartsubmsg(h, fielddef, startmapentry_handler, &attr); upb_handlerattr_uninit(&attr); } @@ -360,6 +356,8 @@ static void add_handlers_for_mapfield(upb_handlers* h, // Adds handlers to a map-entry msgdef. static void add_handlers_for_mapentry(const upb_msgdef* msgdef, upb_handlers* h) { + const upb_fielddef* key_field = map_entry_key(msgdef); + const upb_fielddef* value_field = map_entry_value(msgdef); map_handlerdata_t* hd = new_map_handlerdata(0, msgdef); upb_handlers_addcleanup(h, hd, free); upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER; @@ -367,7 +365,7 @@ static void add_handlers_for_mapentry(const upb_msgdef* msgdef, upb_handlers_setendmsg(h, endmap_handler, &attr); add_handlers_for_singular_field( - h, hd->key_field, + h, key_field, // Convert the offset into map_parse_frame_t to an offset understood by the // singular field handlers, so that we don't have to use special // map-key/value-specific handlers. The ordinary singular field handlers expect @@ -375,7 +373,7 @@ static void add_handlers_for_mapentry(const upb_msgdef* msgdef, // we compensate for that addition. offsetof(map_parse_frame_t, key_storage) - sizeof(MessageHeader)); add_handlers_for_singular_field( - h, hd->value_field, + h, value_field, offsetof(map_parse_frame_t, value_storage) - sizeof(MessageHeader)); } diff --git a/ruby/ext/google/protobuf_c/map.c b/ruby/ext/google/protobuf_c/map.c index 9d5de9cd..56548799 100644 --- a/ruby/ext/google/protobuf_c/map.c +++ b/ruby/ext/google/protobuf_c/map.c @@ -32,6 +32,17 @@ // ----------------------------------------------------------------------------- // Basic map operations on top of upb's strtable. +// +// Note that we roll our own `Map` container here because, as for +// `RepeatedField`, we want a strongly-typed container. This is so that any user +// errors due to incorrect map key or value types are raised as close as +// possible to the error site, rather than at some deferred point (e.g., +// serialization). +// +// We build our `Map` on top of upb_strtable so that we're able to take +// advantage of the native_slot storage abstraction, as RepeatedField does. +// (This is not quite a perfect mapping -- see the key conversions below -- but +// gives us full support and error-checking for all value types for free.) // ----------------------------------------------------------------------------- // Map values are stored using the native_slot abstraction (as with repeated diff --git a/ruby/ext/google/protobuf_c/message.c b/ruby/ext/google/protobuf_c/message.c index de38dd7b..ee8881d4 100644 --- a/ruby/ext/google/protobuf_c/message.c +++ b/ruby/ext/google/protobuf_c/message.c @@ -142,7 +142,7 @@ int Message_initialize_kwarg(VALUE key, VALUE val, VALUE _self) { if (is_map_field(f)) { if (TYPE(val) != T_HASH) { rb_raise(rb_eArgError, - "Expected hashmap as initializer value for map field."); + "Expected Hash object as initializer value for map field."); } VALUE map = layout_get(self->descriptor->layout, Message_data(self), f); Map_merge_into_self(map, val); diff --git a/ruby/ext/google/protobuf_c/protobuf.h b/ruby/ext/google/protobuf_c/protobuf.h index 91a97a68..6dac6029 100644 --- a/ruby/ext/google/protobuf_c/protobuf.h +++ b/ruby/ext/google/protobuf_c/protobuf.h @@ -268,10 +268,19 @@ extern rb_encoding* kRubyString8bitEncoding; VALUE field_type_class(const upb_fielddef* field); +#define MAP_KEY_FIELD 1 +#define MAP_VALUE_FIELD 2 + +// 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); const upb_fielddef* map_field_key(const upb_fielddef* field); const upb_fielddef* map_field_value(const upb_fielddef* field); +// These operate on a map-entry msgdef. +const upb_fielddef* map_entry_key(const upb_msgdef* msgdef); +const upb_fielddef* map_entry_value(const upb_msgdef* msgdef); + // ----------------------------------------------------------------------------- // Repeated field container type. // ----------------------------------------------------------------------------- diff --git a/ruby/ext/google/protobuf_c/storage.c b/ruby/ext/google/protobuf_c/storage.c index f20ddec2..235fbff2 100644 --- a/ruby/ext/google/protobuf_c/storage.c +++ b/ruby/ext/google/protobuf_c/storage.c @@ -339,15 +339,23 @@ bool is_map_field(const upb_fielddef* field) { const upb_fielddef* map_field_key(const upb_fielddef* field) { assert(is_map_field(field)); const upb_msgdef* subdef = (const upb_msgdef*)upb_fielddef_subdef(field); - const upb_fielddef* key_field = upb_msgdef_itof(subdef, 1); - assert(key_field != NULL); - return key_field; + return map_entry_key(subdef); } const upb_fielddef* map_field_value(const upb_fielddef* field) { assert(is_map_field(field)); const upb_msgdef* subdef = (const upb_msgdef*)upb_fielddef_subdef(field); - const upb_fielddef* value_field = upb_msgdef_itof(subdef, 2); + return map_entry_value(subdef); +} + +const upb_fielddef* map_entry_key(const upb_msgdef* msgdef) { + const upb_fielddef* key_field = upb_msgdef_itof(msgdef, MAP_KEY_FIELD); + assert(key_field != NULL); + return key_field; +} + +const upb_fielddef* map_entry_value(const upb_msgdef* msgdef) { + const upb_fielddef* value_field = upb_msgdef_itof(msgdef, MAP_VALUE_FIELD); assert(value_field != NULL); return value_field; } -- cgit v1.2.3 From 4c92289766d76f276b322ab254ef039f670f41b1 Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Fri, 9 Jan 2015 15:29:45 -0800 Subject: Addressed code-review comments. --- ruby/ext/google/protobuf_c/defs.c | 2 + ruby/ext/google/protobuf_c/encode_decode.c | 66 ++++++++--------- ruby/ext/google/protobuf_c/map.c | 111 +++++------------------------ ruby/ext/google/protobuf_c/protobuf.h | 8 ++- ruby/ext/google/protobuf_c/storage.c | 14 ++-- ruby/ext/google/protobuf_c/upb.c | 6 +- 6 files changed, 72 insertions(+), 135 deletions(-) (limited to 'ruby') diff --git a/ruby/ext/google/protobuf_c/defs.c b/ruby/ext/google/protobuf_c/defs.c index 499e041b..a18aaac4 100644 --- a/ruby/ext/google/protobuf_c/defs.c +++ b/ruby/ext/google/protobuf_c/defs.c @@ -226,6 +226,7 @@ DEFINE_CLASS(Descriptor, "Google::Protobuf::Descriptor"); void Descriptor_mark(void* _self) { Descriptor* self = _self; rb_gc_mark(self->klass); + rb_gc_mark(self->typeclass_references); } void Descriptor_free(void* _self) { @@ -270,6 +271,7 @@ VALUE Descriptor_alloc(VALUE klass) { self->fill_method = NULL; self->pb_serialize_handlers = NULL; self->json_serialize_handlers = NULL; + self->typeclass_references = rb_ary_new(); return ret; } diff --git a/ruby/ext/google/protobuf_c/encode_decode.c b/ruby/ext/google/protobuf_c/encode_decode.c index f1b951fc..1cff9049 100644 --- a/ruby/ext/google/protobuf_c/encode_decode.c +++ b/ruby/ext/google/protobuf_c/encode_decode.c @@ -64,7 +64,7 @@ static const void *newsubmsghandlerdata(upb_handlers* h, uint32_t ofs, static void *startseq_handler(void* closure, const void* hd) { MessageHeader* msg = closure; const size_t *ofs = hd; - return (void*)DEREF(Message_data(msg), *ofs, VALUE); + return (void*)DEREF(msg, *ofs, VALUE); } // Handlers that append primitive values to a repeated field (a regular Ruby @@ -115,7 +115,7 @@ static void* str_handler(void *closure, const size_t *ofs = hd; VALUE str = rb_str_new2(""); rb_enc_associate(str, kRubyStringUtf8Encoding); - DEREF(Message_data(msg), *ofs, VALUE) = str; + DEREF(msg, *ofs, VALUE) = str; return (void*)str; } @@ -127,7 +127,7 @@ static void* bytes_handler(void *closure, const size_t *ofs = hd; VALUE str = rb_str_new2(""); rb_enc_associate(str, kRubyString8bitEncoding); - DEREF(Message_data(msg), *ofs, VALUE) = str; + DEREF(msg, *ofs, VALUE) = str; return (void*)str; } @@ -163,12 +163,12 @@ static void *submsg_handler(void *closure, const void *hd) { get_def_obj((void*)submsgdata->md); VALUE subklass = Descriptor_msgclass(subdesc); - if (DEREF(Message_data(msg), submsgdata->ofs, VALUE) == Qnil) { - DEREF(Message_data(msg), submsgdata->ofs, VALUE) = + if (DEREF(msg, submsgdata->ofs, VALUE) == Qnil) { + DEREF(msg, submsgdata->ofs, VALUE) = rb_class_new_instance(0, NULL, subklass); } - VALUE submsg_rb = DEREF(Message_data(msg), submsgdata->ofs, VALUE); + VALUE submsg_rb = DEREF(msg, submsgdata->ofs, VALUE); MessageHeader* submsg; TypedData_Get_Struct(submsg_rb, MessageHeader, &Message_type, submsg); return submsg; @@ -199,7 +199,7 @@ typedef struct { static void *startmapentry_handler(void *closure, const void *hd) { MessageHeader* msg = closure; const map_handlerdata_t* mapdata = hd; - VALUE map_rb = DEREF(Message_data(msg), mapdata->ofs, VALUE); + VALUE map_rb = DEREF(msg, mapdata->ofs, VALUE); map_parse_frame_t* frame = ALLOC(map_parse_frame_t); frame->map = map_rb; @@ -238,7 +238,8 @@ static bool endmap_handler(void *closure, const void *hd, upb_status* s) { // pass the handlerdata down to the sub-message handler setup. static map_handlerdata_t* new_map_handlerdata( size_t ofs, - const upb_msgdef* mapentry_def) { + const upb_msgdef* mapentry_def, + Descriptor* desc) { map_handlerdata_t* hd = ALLOC(map_handlerdata_t); hd->ofs = ofs; @@ -252,6 +253,11 @@ static map_handlerdata_t* new_map_handlerdata( 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. + if (hd->value_field_typeclass != Qnil) { + rb_ary_push(desc->typeclass_references, hd->value_field_typeclass); + } + return hd; } @@ -314,9 +320,7 @@ static void add_handlers_for_singular_field(upb_handlers *h, case UPB_TYPE_INT64: case UPB_TYPE_UINT64: case UPB_TYPE_DOUBLE: - // The shim writes directly at the given offset (instead of using - // DEREF()) so we need to add the msg overhead. - upb_shim_set(h, f, offset + sizeof(MessageHeader), -1); + upb_shim_set(h, f, offset, -1); break; case UPB_TYPE_STRING: case UPB_TYPE_BYTES: { @@ -343,9 +347,10 @@ static void add_handlers_for_singular_field(upb_handlers *h, // Adds handlers to a map field. static void add_handlers_for_mapfield(upb_handlers* h, const upb_fielddef* fielddef, - size_t offset) { + size_t offset, + Descriptor* desc) { const upb_msgdef* map_msgdef = upb_fielddef_msgsubdef(fielddef); - map_handlerdata_t* hd = new_map_handlerdata(offset, map_msgdef); + map_handlerdata_t* hd = new_map_handlerdata(offset, map_msgdef, desc); upb_handlers_addcleanup(h, hd, free); upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER; upb_handlerattr_sethandlerdata(&attr, hd); @@ -355,10 +360,11 @@ static void add_handlers_for_mapfield(upb_handlers* h, // Adds handlers to a map-entry msgdef. static void add_handlers_for_mapentry(const upb_msgdef* msgdef, - upb_handlers* h) { + upb_handlers* h, + Descriptor* desc) { const upb_fielddef* key_field = map_entry_key(msgdef); const upb_fielddef* value_field = map_entry_value(msgdef); - map_handlerdata_t* hd = new_map_handlerdata(0, msgdef); + map_handlerdata_t* hd = new_map_handlerdata(0, msgdef, desc); upb_handlers_addcleanup(h, hd, free); upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER; upb_handlerattr_sethandlerdata(&attr, hd); @@ -366,15 +372,10 @@ static void add_handlers_for_mapentry(const upb_msgdef* msgdef, add_handlers_for_singular_field( h, key_field, - // Convert the offset into map_parse_frame_t to an offset understood by the - // singular field handlers, so that we don't have to use special - // map-key/value-specific handlers. The ordinary singular field handlers expect - // a Message* and assume offset is relative to the data section at the end, so - // we compensate for that addition. - offsetof(map_parse_frame_t, key_storage) - sizeof(MessageHeader)); + offsetof(map_parse_frame_t, key_storage)); add_handlers_for_singular_field( h, value_field, - offsetof(map_parse_frame_t, value_storage) - sizeof(MessageHeader)); + offsetof(map_parse_frame_t, value_storage)); } static void add_handlers_for_message(const void *closure, upb_handlers *h) { @@ -384,7 +385,7 @@ static void add_handlers_for_message(const void *closure, upb_handlers *h) { // If this is a mapentry message type, set up a special set of handlers and // bail out of the normal (user-defined) message type handling. if (upb_msgdef_mapentry(msgdef)) { - add_handlers_for_mapentry(msgdef, h); + add_handlers_for_mapentry(msgdef, h, desc); return; } @@ -402,10 +403,11 @@ static void add_handlers_for_message(const void *closure, upb_handlers *h) { !upb_msg_done(&i); upb_msg_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->offsets[upb_fielddef_index(f)] + + sizeof(MessageHeader); if (is_map_field(f)) { - add_handlers_for_mapfield(h, f, offset); + add_handlers_for_mapfield(h, f, offset, desc); } else if (upb_fielddef_isseq(f)) { add_handlers_for_repeated_field(h, f, offset); } else { @@ -796,38 +798,38 @@ static void putmsg(VALUE msg_rb, const Descriptor* desc, MessageHeader* msg; TypedData_Get_Struct(msg_rb, MessageHeader, &Message_type, msg); - void* msg_data = Message_data(msg); upb_msg_iter i; for (upb_msg_begin(&i, desc->msgdef); !upb_msg_done(&i); upb_msg_next(&i)) { upb_fielddef *f = upb_msg_iter_field(&i); - uint32_t offset = desc->layout->offsets[upb_fielddef_index(f)]; + uint32_t offset = + desc->layout->offsets[upb_fielddef_index(f)] + sizeof(MessageHeader); if (is_map_field(f)) { - VALUE map = DEREF(msg_data, offset, VALUE); + VALUE map = DEREF(msg, offset, VALUE); if (map != Qnil) { putmap(map, f, sink, depth); } } else if (upb_fielddef_isseq(f)) { - VALUE ary = DEREF(msg_data, offset, VALUE); + VALUE ary = DEREF(msg, offset, VALUE); if (ary != Qnil) { putary(ary, f, sink, depth); } } else if (upb_fielddef_isstring(f)) { - VALUE str = DEREF(msg_data, offset, VALUE); + VALUE str = DEREF(msg, offset, VALUE); if (RSTRING_LEN(str) > 0) { putstr(str, f, sink); } } else if (upb_fielddef_issubmsg(f)) { - putsubmsg(DEREF(msg_data, offset, VALUE), f, sink, depth); + putsubmsg(DEREF(msg, offset, VALUE), f, sink, depth); } else { upb_selector_t sel = getsel(f, upb_handlers_getprimitivehandlertype(f)); #define T(upbtypeconst, upbtype, ctype, default_value) \ case upbtypeconst: { \ - ctype value = DEREF(msg_data, offset, ctype); \ + ctype value = DEREF(msg, offset, ctype); \ if (value != default_value) { \ upb_sink_put##upbtype(sink, sel, value); \ } \ diff --git a/ruby/ext/google/protobuf_c/map.c b/ruby/ext/google/protobuf_c/map.c index 56548799..2c2ae240 100644 --- a/ruby/ext/google/protobuf_c/map.c +++ b/ruby/ext/google/protobuf_c/map.c @@ -49,8 +49,14 @@ // field values), but keys are a bit special. Since we use a strtable, we need // to store keys as sequences of bytes such that equality of those bytes maps // one-to-one to equality of keys. We store strings directly (i.e., they map to -// their own bytes) and integers as sequences of either 4 or 8 bytes in -// host-byte-order as either a uint32_t or a uint64_t. +// their own bytes) and integers as native integers (using the native_slot +// abstraction). + +// Note that there is another tradeoff here in keeping string keys as native +// strings rather than Ruby strings: traversing the Map requires conversion to +// Ruby string values on every traversal, potentially creating more garbage. We +// should consider ways to cache a Ruby version of the key if this becomes an +// issue later. // Forms a key to use with the underlying strtable from a Ruby key value. |buf| // must point to TABLE_KEY_BUF_LENGTH bytes of temporary space, used to @@ -73,61 +79,13 @@ static void table_key(Map* self, VALUE key, case UPB_TYPE_BOOL: case UPB_TYPE_INT32: - case UPB_TYPE_INT64: { - // Signed numeric types: use an int64 in host-native byte order. - int64_t key_val = 0; - - // Do a range/value check. - switch (self->key_type) { - case UPB_TYPE_BOOL: - if (key != Qtrue && key != Qfalse) { - rb_raise(rb_eTypeError, "Key must be true or false"); - } - key_val = (key == Qtrue) ? 1 : 0; - break; - case UPB_TYPE_INT32: - native_slot_check_int_range_precision(self->key_type, key); - key_val = NUM2INT(key); - break; - case UPB_TYPE_INT64: - native_slot_check_int_range_precision(self->key_type, key); - key_val = NUM2LL(key); - break; - default: - break; - } - - int64_t* int64_key = (int64_t*)buf; - *int64_key = key_val; - *out_key = buf; - *out_length = sizeof(int64_t); - break; - } - + case UPB_TYPE_INT64: case UPB_TYPE_UINT32: - case UPB_TYPE_UINT64: { - // Unsigned numeric types: use a uint64 in host-native byte order. - uint64_t key_val = 0; - - // Do a range/value check. - native_slot_check_int_range_precision(self->key_type, key); - switch (self->key_type) { - case UPB_TYPE_UINT32: - key_val = NUM2UINT(key); - break; - case UPB_TYPE_UINT64: - key_val = NUM2ULL(key); - break; - default: - break; - } - - uint64_t* uint64_key = (uint64_t*)buf; - *uint64_key = key_val; + case UPB_TYPE_UINT64: + native_slot_set(self->key_type, Qnil, buf, key); *out_key = buf; - *out_length = sizeof(uint64_t); + *out_length = native_slot_size(self->key_type); break; - } default: // Map constructor should not allow a Map with another key type to be @@ -148,50 +106,16 @@ static VALUE table_key_to_ruby(Map* self, const char* buf, size_t length) { return ret; } - case UPB_TYPE_BOOL: - case UPB_TYPE_INT32: - case UPB_TYPE_INT64: { - assert(length == sizeof(int64_t)); - int64_t* int64_key = (int64_t*)buf; - - if (self->key_type == UPB_TYPE_BOOL) { - return *int64_key ? Qtrue : Qfalse; - } else { - return LL2NUM(*int64_key); - } - } - - case UPB_TYPE_UINT32: - case UPB_TYPE_UINT64: { - assert(length == sizeof(uint64_t)); - uint64_t* uint64_key = (uint64_t*)buf; - return ULL2NUM(*uint64_key); - } - - default: - assert(false); - return Qnil; - } -} - -static upb_ctype_t upb_table_value_type(upb_fieldtype_t value_type) { - switch (value_type) { case UPB_TYPE_BOOL: case UPB_TYPE_INT32: case UPB_TYPE_INT64: case UPB_TYPE_UINT32: case UPB_TYPE_UINT64: - case UPB_TYPE_ENUM: - case UPB_TYPE_FLOAT: - case UPB_TYPE_DOUBLE: - case UPB_TYPE_STRING: - case UPB_TYPE_BYTES: - case UPB_TYPE_MESSAGE: - return UPB_CTYPE_UINT64; + return native_slot_get(self->key_type, Qnil, buf); default: assert(false); - return 0; + return Qnil; } } @@ -321,7 +245,9 @@ VALUE Map_init(int argc, VALUE* argv, VALUE _self) { init_value_arg = 3; } - if (!upb_strtable_init(&self->table, upb_table_value_type(self->value_type))) { + // Table value type is always UINT64: this ensures enough space to store the + // native_slot value. + if (!upb_strtable_init(&self->table, UPB_CTYPE_UINT64)) { rb_raise(rb_eRuntimeError, "Could not allocate table."); } @@ -528,8 +454,7 @@ VALUE Map_clear(VALUE _self) { // Uninit and reinit the table -- this is faster than iterating and doing a // delete-lookup on each key. upb_strtable_uninit(&self->table); - if (!upb_strtable_init(&self->table, - upb_table_value_type(self->value_type))) { + if (!upb_strtable_init(&self->table, UPB_CTYPE_INT64)) { rb_raise(rb_eRuntimeError, "Unable to re-initialize table"); } return Qnil; diff --git a/ruby/ext/google/protobuf_c/protobuf.h b/ruby/ext/google/protobuf_c/protobuf.h index 6dac6029..88ae62e4 100644 --- a/ruby/ext/google/protobuf_c/protobuf.h +++ b/ruby/ext/google/protobuf_c/protobuf.h @@ -110,6 +110,10 @@ struct Descriptor { const upb_pbdecodermethod* fill_method; const upb_handlers* pb_serialize_handlers; const upb_handlers* json_serialize_handlers; + // Handlers hold type class references for sub-message fields directly in some + // cases. We need to keep these rooted because they might otherwise be + // collected. + VALUE typeclass_references; }; struct FieldDescriptor { @@ -252,7 +256,7 @@ void native_slot_set(upb_fieldtype_t type, VALUE value); VALUE native_slot_get(upb_fieldtype_t type, VALUE type_class, - void* memory); + const void* memory); void native_slot_init(upb_fieldtype_t type, void* memory); void native_slot_mark(upb_fieldtype_t type, void* memory); void native_slot_dup(upb_fieldtype_t type, void* to, void* from); @@ -389,7 +393,7 @@ struct MessageLayout { MessageLayout* create_layout(const upb_msgdef* msgdef); void free_layout(MessageLayout* layout); VALUE layout_get(MessageLayout* layout, - void* storage, + const void* storage, const upb_fielddef* field); void layout_set(MessageLayout* layout, void* storage, diff --git a/ruby/ext/google/protobuf_c/storage.c b/ruby/ext/google/protobuf_c/storage.c index 235fbff2..14f49d44 100644 --- a/ruby/ext/google/protobuf_c/storage.c +++ b/ruby/ext/google/protobuf_c/storage.c @@ -200,7 +200,9 @@ void native_slot_set(upb_fieldtype_t type, VALUE type_class, } } -VALUE native_slot_get(upb_fieldtype_t type, VALUE type_class, void* memory) { +VALUE native_slot_get(upb_fieldtype_t type, + VALUE type_class, + const void* memory) { switch (type) { case UPB_TYPE_FLOAT: return DBL2NUM(DEREF(memory, float)); @@ -211,7 +213,7 @@ VALUE native_slot_get(upb_fieldtype_t type, VALUE type_class, void* memory) { case UPB_TYPE_STRING: case UPB_TYPE_BYTES: case UPB_TYPE_MESSAGE: - return *((VALUE *)memory); + return DEREF(memory, VALUE); case UPB_TYPE_ENUM: { int32_t val = DEREF(memory, int32_t); VALUE symbol = enum_lookup(type_class, INT2NUM(val)); @@ -332,19 +334,19 @@ bool is_map_field(const upb_fielddef* field) { upb_fielddef_type(field) != UPB_TYPE_MESSAGE) { return false; } - const upb_msgdef* subdef = (const upb_msgdef*)upb_fielddef_subdef(field); + const upb_msgdef* subdef = upb_fielddef_msgsubdef(field); return upb_msgdef_mapentry(subdef); } const upb_fielddef* map_field_key(const upb_fielddef* field) { assert(is_map_field(field)); - const upb_msgdef* subdef = (const upb_msgdef*)upb_fielddef_subdef(field); + const upb_msgdef* subdef = upb_fielddef_msgsubdef(field); return map_entry_key(subdef); } const upb_fielddef* map_field_value(const upb_fielddef* field) { assert(is_map_field(field)); - const upb_msgdef* subdef = (const upb_msgdef*)upb_fielddef_subdef(field); + const upb_msgdef* subdef = upb_fielddef_msgsubdef(field); return map_entry_value(subdef); } @@ -414,7 +416,7 @@ VALUE field_type_class(const upb_fielddef* field) { } VALUE layout_get(MessageLayout* layout, - void* storage, + const void* storage, const upb_fielddef* field) { void* memory = ((uint8_t *)storage) + layout->offsets[upb_fielddef_index(field)]; diff --git a/ruby/ext/google/protobuf_c/upb.c b/ruby/ext/google/protobuf_c/upb.c index 0015aad1..abea7711 100644 --- a/ruby/ext/google/protobuf_c/upb.c +++ b/ruby/ext/google/protobuf_c/upb.c @@ -3420,8 +3420,10 @@ char *upb_strdup2(const char *s, size_t len) { // have a null-terminating byte since it may be a raw binary buffer. size_t n = len + 1; char *p = malloc(n); - if (p) memcpy(p, s, len); - p[len] = 0; + if (p) { + memcpy(p, s, len); + p[len] = 0; + } return p; } -- cgit v1.2.3 From 97b663a8be6764a3489d7150f77503ecaf40df5f Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Fri, 9 Jan 2015 16:15:22 -0800 Subject: Update upb amalgamation. --- ruby/ext/google/protobuf_c/upb.c | 1122 +++++++++++++++++++++++++------------- ruby/ext/google/protobuf_c/upb.h | 25 +- 2 files changed, 747 insertions(+), 400 deletions(-) (limited to 'ruby') diff --git a/ruby/ext/google/protobuf_c/upb.c b/ruby/ext/google/protobuf_c/upb.c index abea7711..571c809f 100644 --- a/ruby/ext/google/protobuf_c/upb.c +++ b/ruby/ext/google/protobuf_c/upb.c @@ -3416,6 +3416,8 @@ char *upb_strdup(const char *s) { } char *upb_strdup2(const char *s, size_t len) { + // Prevent overflow errors. + if (len == SIZE_MAX) return NULL; // Always null-terminate, even if binary data; but don't rely on the input to // have a null-terminating byte since it may be a raw binary buffer. size_t n = len + 1; @@ -4230,8 +4232,10 @@ static void nullz(upb_status *status) { } void upb_status_clear(upb_status *status) { - upb_status blank = UPB_STATUS_INIT; - upb_status_copy(status, &blank); + if (!status) return; + status->ok_ = true; + status->code_ = 0; + status->msg[0] = '\0'; } bool upb_ok(const upb_status *status) { return status->ok_; } @@ -5998,6 +6002,7 @@ static void putop(compiler *c, opcode op, ...) { case OP_SETDELIM: case OP_HALT: case OP_RET: + case OP_DISPATCH: put32(c, op); break; case OP_PARSE_DOUBLE: @@ -6078,7 +6083,7 @@ const char *upb_pbdecoder_getopname(unsigned int op) { OP(ENDSUBMSG), OP(STARTSTR), OP(STRING), OP(ENDSTR), OP(CALL), OP(RET), OP(PUSHLENDELIM), OP(PUSHTAGDELIM), OP(SETDELIM), OP(CHECKDELIM), OP(BRANCH), OP(TAG1), OP(TAG2), OP(TAGN), OP(SETDISPATCH), OP(POP), - OP(SETBIGGROUPNUM), OP(HALT), + OP(SETBIGGROUPNUM), OP(DISPATCH), OP(HALT), }; return op > OP_HALT ? names[0] : names[op]; #undef OP @@ -6110,6 +6115,7 @@ static void dumpbc(uint32_t *p, uint32_t *end, FILE *f) { upb_handlers_msgdef(method->dest_handlers_))); break; } + case OP_DISPATCH: case OP_STARTMSG: case OP_ENDMSG: case OP_PUSHLENDELIM: @@ -6455,6 +6461,7 @@ static void compile_method(compiler *c, upb_pbdecodermethod *method) { putop(c, OP_SETDISPATCH, &method->dispatch); 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)) { const upb_fielddef *f = upb_msg_iter_field(&i); @@ -6470,8 +6477,18 @@ static void compile_method(compiler *c, upb_pbdecodermethod *method) { } } + // If there were no fields, or if no handlers were defined, we need to + // generate a non-empty loop body so that we can at least dispatch for unknown + // fields and check for the end of the message. + if (c->pc == start_pc) { + // Check for end-of-message. + putop(c, OP_CHECKDELIM, LABEL_ENDMSG); + // Unconditionally dispatch. + putop(c, OP_DISPATCH, 0); + } + // For now we just loop back to the last field of the message (or if none, - // the DISPATCH opcode for the message. + // the DISPATCH opcode for the message). putop(c, OP_BRANCH, -LABEL_FIELD); // Insert both a label and a dispatch table entry for this end-of-msg. @@ -7455,6 +7472,9 @@ size_t upb_pbdecoder_decode(void *closure, const void *hd, const char *buf, if (result == DECODE_MISMATCH) goto badtag; if (result >= 0) return result; }) + VMCASE(OP_DISPATCH, { + CHECK_RETURN(dispatch(d)); + }) VMCASE(OP_HALT, { return size; }) @@ -7513,7 +7533,8 @@ bool upb_pbdecoder_end(void *closure, const void *handler_data) { // Rewind from OP_TAG* to OP_CHECKDELIM. assert(getop(*d->pc) == OP_TAG1 || getop(*d->pc) == OP_TAG2 || - getop(*d->pc) == OP_TAGN); + getop(*d->pc) == OP_TAGN || + getop(*d->pc == OP_DISPATCH)); d->pc = p; } upb_pbdecoder_decode(closure, handler_data, &dummy, 0, NULL); @@ -8648,6 +8669,9 @@ upb_decoderet upb_vdecode_max8_wright(upb_decoderet r) { #define PARSER_CHECK_RETURN(x) if (!(x)) return false +// Used to signal that a capture has been suspended. +static char suspend_capture; + static upb_selector_t getsel_for_handlertype(upb_json_parser *p, upb_handlertype_t type) { upb_selector_t sel; @@ -8661,41 +8685,6 @@ static upb_selector_t parser_getsel(upb_json_parser *p) { p, upb_handlers_getprimitivehandlertype(p->top->f)); } -static void start_member(upb_json_parser *p) { - assert(!p->top->f); - assert(!p->accumulated); - p->accumulated_len = 0; -} - -static bool end_member(upb_json_parser *p) { - // TODO(haberman): support keys that span buffers or have escape sequences. - assert(!p->top->f); - assert(p->accumulated); - const upb_fielddef *f = - upb_msgdef_ntof(p->top->m, p->accumulated, p->accumulated_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)p->accumulated_len, p->accumulated); - return false; - } - - p->top->f = f; - p->accumulated = NULL; - - return true; -} - -static void start_object(upb_json_parser *p) { - upb_sink_startmsg(&p->top->sink); -} - -static void end_object(upb_json_parser *p) { - upb_status status; - upb_sink_endmsg(&p->top->sink, &status); -} - static bool check_stack(upb_json_parser *p) { if ((p->top + 1) == p->limit) { upb_status_seterrmsg(p->status, "Nesting too deep"); @@ -8705,83 +8694,28 @@ static bool check_stack(upb_json_parser *p) { return true; } -static bool start_subobject(upb_json_parser *p) { - assert(p->top->f); - - if (!upb_fielddef_issubmsg(p->top->f)) { - 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; +// There are GCC/Clang built-ins for overflow checking which we could start +// using if there was any performance benefit to it. +static bool checked_add(size_t a, size_t b, size_t *c) { + if (SIZE_MAX - a < b) return false; + *c = a + b; 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); -} - -static bool start_array(upb_json_parser *p) { - assert(p->top->f); - - if (!upb_fielddef_isseq(p->top->f)) { - upb_status_seterrf(p->status, - "Array specified for non-repeated field: %s", - upb_fielddef_name(p->top->f)); - return false; +static size_t saturating_multiply(size_t a, size_t b) { + // size_t is unsigned, so this is defined behavior even on overflow. + size_t ret = a * b; + if (b != 0 && ret / b != a) { + ret = SIZE_MAX; } - - 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 = p->top->m; - inner->f = p->top->f; - p->top = inner; - - return true; + return ret; } -static void end_array(upb_json_parser *p) { - assert(p->top > p->stack); - - p->top--; - upb_selector_t sel = getsel_for_handlertype(p, UPB_HANDLER_ENDSEQ); - upb_sink_endseq(&p->top->sink, sel); -} -static void clear_member(upb_json_parser *p) { p->top->f = NULL; } +/* Base64 decoding ************************************************************/ -static bool parser_putbool(upb_json_parser *p, bool val) { - if (upb_fielddef_type(p->top->f) != UPB_TYPE_BOOL) { - upb_status_seterrf(p->status, - "Boolean value specified for non-bool field: %s", - upb_fielddef_name(p->top->f)); - return false; - } - - bool ok = upb_sink_putbool(&p->top->sink, parser_getsel(p), val); - UPB_ASSERT_VAR(ok, ok); - return true; -} - -static void start_text(upb_json_parser *p, const char *ptr) { - p->text_begin = ptr; -} +// TODO(haberman): make this streaming. static const signed char b64table[] = { -1, -1, -1, -1, -1, -1, -1, -1, @@ -8901,89 +8835,326 @@ badpadding: return false; } -static bool end_text(upb_json_parser *p, const char *ptr, bool is_num) { - assert(!p->accumulated); // TODO: handle this case. - p->accumulated = p->text_begin; - p->accumulated_len = ptr - p->text_begin; - if (p->top->f && upb_fielddef_isstring(p->top->f)) { - // This is a string field (as opposed to a member name). - upb_selector_t sel = getsel_for_handlertype(p, UPB_HANDLER_STRING); - if (upb_fielddef_type(p->top->f) == UPB_TYPE_BYTES) { - PARSER_CHECK_RETURN(base64_push(p, sel, p->accumulated, - p->accumulated_len)); - } else { - upb_sink_putstring(&p->top->sink, sel, p->accumulated, p->accumulated_len, NULL); - } - p->accumulated = NULL; - } else if (p->top->f && - upb_fielddef_type(p->top->f) == UPB_TYPE_ENUM && - !is_num) { - - // Enum case: resolve enum symbolic name to integer value. - const upb_enumdef *enumdef = - (const upb_enumdef*)upb_fielddef_subdef(p->top->f); - - int32_t int_val = 0; - if (upb_enumdef_ntoi(enumdef, p->accumulated, p->accumulated_len, - &int_val)) { - upb_selector_t sel = parser_getsel(p); - upb_sink_putint32(&p->top->sink, sel, int_val); - } else { - upb_status_seterrmsg(p->status, "Enum value name unknown"); +/* Accumulate buffer **********************************************************/ + +// Functionality for accumulating a buffer. +// +// Some parts of the parser need an entire value as a contiguous string. For +// example, to look up a member name in a hash table, or to turn a string into +// a number, the relevant library routines need the input string to be in +// contiguous memory, even if the value spanned two or more buffers in the +// input. These routines handle that. +// +// In the common case we can just point to the input buffer to get this +// contiguous string and avoid any actual copy. So we optimistically begin +// this way. But there are a few cases where we must instead copy into a +// separate buffer: +// +// 1. The string was not contiguous in the input (it spanned buffers). +// +// 2. The string included escape sequences that need to be interpreted to get +// the true value in a contiguous buffer. + +static void assert_accumulate_empty(upb_json_parser *p) { + assert(p->accumulated == NULL); + assert(p->accumulated_len == 0); +} + +static void accumulate_clear(upb_json_parser *p) { + p->accumulated = NULL; + p->accumulated_len = 0; +} + +// Used internally by accumulate_append(). +static bool accumulate_realloc(upb_json_parser *p, size_t need) { + size_t new_size = UPB_MAX(p->accumulate_buf_size, 128); + while (new_size < need) { + new_size = saturating_multiply(new_size, 2); + } + + void *mem = realloc(p->accumulate_buf, new_size); + if (!mem) { + upb_status_seterrmsg(p->status, "Out of memory allocating buffer."); + return false; + } + + p->accumulate_buf = mem; + p->accumulate_buf_size = new_size; + return true; +} + +// Logically appends the given data to the append buffer. +// If "can_alias" is true, we will try to avoid actually copying, but the buffer +// must be valid until the next accumulate_append() call (if any). +static bool accumulate_append(upb_json_parser *p, const char *buf, size_t len, + bool can_alias) { + if (!p->accumulated && can_alias) { + p->accumulated = buf; + p->accumulated_len = len; + return true; + } + + size_t need; + if (!checked_add(p->accumulated_len, len, &need)) { + upb_status_seterrmsg(p->status, "Integer overflow."); + return false; + } + + if (need > p->accumulate_buf_size && !accumulate_realloc(p, need)) { + return false; + } + + if (p->accumulated != p->accumulate_buf) { + memcpy(p->accumulate_buf, p->accumulated, p->accumulated_len); + p->accumulated = p->accumulate_buf; + } + + memcpy(p->accumulate_buf + p->accumulated_len, buf, len); + p->accumulated_len += len; + return true; +} + +// Returns a pointer to the data accumulated since the last accumulate_clear() +// call, and writes the length to *len. This with point either to the input +// buffer or a temporary accumulate buffer. +static const char *accumulate_getptr(upb_json_parser *p, size_t *len) { + assert(p->accumulated); + *len = p->accumulated_len; + return p->accumulated; +} + + +/* Mult-part text data ********************************************************/ + +// When we have text data in the input, it can often come in multiple segments. +// For example, there may be some raw string data followed by an escape +// sequence. The two segments are processed with different logic. Also buffer +// seams in the input can cause multiple segments. +// +// As we see segments, there are two main cases for how we want to process them: +// +// 1. we want to push the captured input directly to string handlers. +// +// 2. we need to accumulate all the parts into a contiguous buffer for further +// processing (field name lookup, string->number conversion, etc). + +// This is the set of states for p->multipart_state. +enum { + // We are not currently processing multipart data. + MULTIPART_INACTIVE = 0, + + // We are processing multipart data by accumulating it into a contiguous + // buffer. + MULTIPART_ACCUMULATE = 1, + + // We are processing multipart data by pushing each part directly to the + // current string handlers. + MULTIPART_PUSHEAGERLY = 2 +}; + +// Start a multi-part text value where we accumulate the data for processing at +// the end. +static void multipart_startaccum(upb_json_parser *p) { + assert_accumulate_empty(p); + assert(p->multipart_state == MULTIPART_INACTIVE); + p->multipart_state = MULTIPART_ACCUMULATE; +} + +// Start a multi-part text value where we immediately push text data to a string +// value with the given selector. +static void multipart_start(upb_json_parser *p, upb_selector_t sel) { + assert_accumulate_empty(p); + assert(p->multipart_state == MULTIPART_INACTIVE); + p->multipart_state = MULTIPART_PUSHEAGERLY; + p->string_selector = sel; +} + +static bool multipart_text(upb_json_parser *p, const char *buf, size_t len, + bool can_alias) { + switch (p->multipart_state) { + case MULTIPART_INACTIVE: + upb_status_seterrmsg( + p->status, "Internal error: unexpected state MULTIPART_INACTIVE"); return false; + + case MULTIPART_ACCUMULATE: + if (!accumulate_append(p, buf, len, can_alias)) { + return false; + } + break; + + case MULTIPART_PUSHEAGERLY: { + const upb_bufhandle *handle = can_alias ? p->handle : NULL; + upb_sink_putstring(&p->top->sink, p->string_selector, buf, len, handle); + break; } - p->accumulated = NULL; } return true; } -static bool start_stringval(upb_json_parser *p) { - assert(p->top->f); +// Note: this invalidates the accumulate buffer! Call only after reading its +// contents. +static void multipart_end(upb_json_parser *p) { + assert(p->multipart_state != MULTIPART_INACTIVE); + p->multipart_state = MULTIPART_INACTIVE; + accumulate_clear(p); +} - if (upb_fielddef_isstring(p->top->f)) { - if (!check_stack(p)) return false; - // Start a new parser frame: parser frames correspond one-to-one with - // handler frames, and string events occur in a sub-frame. - upb_jsonparser_frame *inner = p->top + 1; - upb_selector_t sel = getsel_for_handlertype(p, UPB_HANDLER_STARTSTR); - upb_sink_startstr(&p->top->sink, sel, 0, &inner->sink); - inner->m = p->top->m; - inner->f = p->top->f; - p->top = inner; +/* Input capture **************************************************************/ - return true; - } else if (upb_fielddef_type(p->top->f) == UPB_TYPE_ENUM) { - // Do nothing -- symbolic enum names in quotes remain in the - // current parser frame. +// Functionality for capturing a region of the input as text. Gracefully +// handles the case where a buffer seam occurs in the middle of the captured +// region. + +static void capture_begin(upb_json_parser *p, const char *ptr) { + assert(p->multipart_state != MULTIPART_INACTIVE); + assert(p->capture == NULL); + p->capture = ptr; +} + +static bool capture_end(upb_json_parser *p, const char *ptr) { + assert(p->capture); + if (multipart_text(p, p->capture, ptr - p->capture, true)) { + p->capture = NULL; return true; } else { - upb_status_seterrf(p->status, - "String specified for non-string/non-enum field: %s", - upb_fielddef_name(p->top->f)); return false; } +} + +// This is called at the end of each input buffer (ie. when we have hit a +// buffer seam). If we are in the middle of capturing the input, this +// processes the unprocessed capture region. +static void capture_suspend(upb_json_parser *p, const char **ptr) { + if (!p->capture) return; + + if (multipart_text(p, p->capture, *ptr - p->capture, false)) { + // We use this as a signal that we were in the middle of capturing, and + // that capturing should resume at the beginning of the next buffer. + // + // We can't use *ptr here, because we have no guarantee that this pointer + // will be valid when we resume (if the underlying memory is freed, then + // using the pointer at all, even to compare to NULL, is likely undefined + // behavior). + p->capture = &suspend_capture; + } else { + // Need to back up the pointer to the beginning of the capture, since + // we were not able to actually preserve it. + *ptr = p->capture; + } +} +static void capture_resume(upb_json_parser *p, const char *ptr) { + if (p->capture) { + assert(p->capture == &suspend_capture); + p->capture = ptr; + } } -static void end_stringval(upb_json_parser *p) { - if (upb_fielddef_isstring(p->top->f)) { - upb_selector_t sel = getsel_for_handlertype(p, UPB_HANDLER_ENDSTR); - upb_sink_endstr(&p->top->sink, sel); - p->top--; + +/* Callbacks from the parser **************************************************/ + +// These are the functions called directly from the parser itself. +// We define these in the same order as their declarations in the parser. + +static char escape_char(char in) { + switch (in) { + case 'r': return '\r'; + case 't': return '\t'; + case 'n': return '\n'; + case 'f': return '\f'; + case 'b': return '\b'; + case '/': return '/'; + case '"': return '"'; + case '\\': return '\\'; + default: + assert(0); + return 'x'; } } +static bool escape(upb_json_parser *p, const char *ptr) { + char ch = escape_char(*ptr); + return multipart_text(p, &ch, 1, false); +} + +static void start_hex(upb_json_parser *p) { + p->digit = 0; +} + +static void hexdigit(upb_json_parser *p, const char *ptr) { + char ch = *ptr; + + p->digit <<= 4; + + if (ch >= '0' && ch <= '9') { + p->digit += (ch - '0'); + } else if (ch >= 'a' && ch <= 'f') { + p->digit += ((ch - 'a') + 10); + } else { + assert(ch >= 'A' && ch <= 'F'); + p->digit += ((ch - 'A') + 10); + } +} + +static bool end_hex(upb_json_parser *p) { + uint32_t codepoint = p->digit; + + // emit the codepoint as UTF-8. + char utf8[3]; // support \u0000 -- \uFFFF -- need only three bytes. + int length = 0; + if (codepoint <= 0x7F) { + utf8[0] = codepoint; + length = 1; + } else if (codepoint <= 0x07FF) { + utf8[1] = (codepoint & 0x3F) | 0x80; + codepoint >>= 6; + utf8[0] = (codepoint & 0x1F) | 0xC0; + length = 2; + } else /* codepoint <= 0xFFFF */ { + utf8[2] = (codepoint & 0x3F) | 0x80; + codepoint >>= 6; + utf8[1] = (codepoint & 0x3F) | 0x80; + codepoint >>= 6; + utf8[0] = (codepoint & 0x0F) | 0xE0; + length = 3; + } + // TODO(haberman): Handle high surrogates: if codepoint is a high surrogate + // we have to wait for the next escape to get the full code point). + + return multipart_text(p, utf8, length, false); +} + +static void start_text(upb_json_parser *p, const char *ptr) { + capture_begin(p, ptr); +} + +static bool end_text(upb_json_parser *p, const char *ptr) { + return capture_end(p, ptr); +} + static void start_number(upb_json_parser *p, const char *ptr) { - start_text(p, ptr); - assert(p->accumulated == NULL); + multipart_startaccum(p); + capture_begin(p, ptr); } -static void end_number(upb_json_parser *p, const char *ptr) { - end_text(p, ptr, true); - const char *myend = p->accumulated + p->accumulated_len; +static bool end_number(upb_json_parser *p, const char *ptr) { + if (!capture_end(p, ptr)) { + return false; + } + + // 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)) { + return false; + } + + size_t len; + const char *buf = accumulate_getptr(p, &len); + const char *myend = buf + len - 1; // One for NULL. char *end; switch (upb_fielddef_type(p->top->f)) { @@ -8991,7 +9162,7 @@ static void end_number(upb_json_parser *p, const char *ptr) { case UPB_TYPE_INT32: { long val = strtol(p->accumulated, &end, 0); if (val > INT32_MAX || val < INT32_MIN || errno == ERANGE || end != myend) - assert(false); + goto err; else upb_sink_putint32(&p->top->sink, parser_getsel(p), val); break; @@ -8999,7 +9170,7 @@ static void end_number(upb_json_parser *p, const char *ptr) { case UPB_TYPE_INT64: { long long val = strtoll(p->accumulated, &end, 0); if (val > INT64_MAX || val < INT64_MIN || errno == ERANGE || end != myend) - assert(false); + goto err; else upb_sink_putint64(&p->top->sink, parser_getsel(p), val); break; @@ -9007,7 +9178,7 @@ static void end_number(upb_json_parser *p, const char *ptr) { case UPB_TYPE_UINT32: { unsigned long val = strtoul(p->accumulated, &end, 0); if (val > UINT32_MAX || errno == ERANGE || end != myend) - assert(false); + goto err; else upb_sink_putuint32(&p->top->sink, parser_getsel(p), val); break; @@ -9015,7 +9186,7 @@ static void end_number(upb_json_parser *p, const char *ptr) { case UPB_TYPE_UINT64: { unsigned long long val = strtoull(p->accumulated, &end, 0); if (val > UINT64_MAX || errno == ERANGE || end != myend) - assert(false); + goto err; else upb_sink_putuint64(&p->top->sink, parser_getsel(p), val); break; @@ -9023,7 +9194,7 @@ static void end_number(upb_json_parser *p, const char *ptr) { case UPB_TYPE_DOUBLE: { double val = strtod(p->accumulated, &end); if (errno == ERANGE || end != myend) - assert(false); + goto err; else upb_sink_putdouble(&p->top->sink, parser_getsel(p), val); break; @@ -9031,7 +9202,7 @@ static void end_number(upb_json_parser *p, const char *ptr) { case UPB_TYPE_FLOAT: { float val = strtof(p->accumulated, &end); if (errno == ERANGE || end != myend) - assert(false); + goto err; else upb_sink_putfloat(&p->top->sink, parser_getsel(p), val); break; @@ -9040,230 +9211,380 @@ static void end_number(upb_json_parser *p, const char *ptr) { assert(false); } - p->accumulated = NULL; + multipart_end(p); + return true; + +err: + upb_status_seterrf(p->status, "error parsing number: %s", buf); + multipart_end(p); + return false; } -static char escape_char(char in) { - switch (in) { - case 'r': return '\r'; - case 't': return '\t'; - case 'n': return '\n'; - case 'f': return '\f'; - case 'b': return '\b'; - case '/': return '/'; - case '"': return '"'; - case '\\': return '\\'; +static bool parser_putbool(upb_json_parser *p, bool val) { + if (upb_fielddef_type(p->top->f) != UPB_TYPE_BOOL) { + upb_status_seterrf(p->status, + "Boolean value specified for non-bool field: %s", + upb_fielddef_name(p->top->f)); + return false; + } + + bool ok = upb_sink_putbool(&p->top->sink, parser_getsel(p), val); + UPB_ASSERT_VAR(ok, ok); + return true; +} + +static bool start_stringval(upb_json_parser *p) { + assert(p->top->f); + + if (upb_fielddef_isstring(p->top->f)) { + if (!check_stack(p)) return false; + + // Start a new parser frame: parser frames correspond one-to-one with + // handler frames, and string events occur in a sub-frame. + upb_jsonparser_frame *inner = p->top + 1; + upb_selector_t sel = getsel_for_handlertype(p, UPB_HANDLER_STARTSTR); + upb_sink_startstr(&p->top->sink, sel, 0, &inner->sink); + inner->m = p->top->m; + inner->f = p->top->f; + p->top = inner; + + if (upb_fielddef_type(p->top->f) == UPB_TYPE_STRING) { + // For STRING fields we push data directly to the handlers as it is + // parsed. We don't do this yet for BYTES fields, because our base64 + // decoder is not streaming. + // + // TODO(haberman): make base64 decoding streaming also. + multipart_start(p, getsel_for_handlertype(p, UPB_HANDLER_STRING)); + return true; + } else { + multipart_startaccum(p); + return true; + } + } else if (upb_fielddef_type(p->top->f) == UPB_TYPE_ENUM) { + // No need to push a frame -- symbolic enum names in quotes remain in the + // current parser frame. + // + // Enum string values must accumulate so we can look up the value in a table + // once it is complete. + multipart_startaccum(p); + return true; + } else { + upb_status_seterrf(p->status, + "String specified for non-string/non-enum field: %s", + upb_fielddef_name(p->top->f)); + return false; + } +} + +static bool end_stringval(upb_json_parser *p) { + bool ok = true; + + switch (upb_fielddef_type(p->top->f)) { + case UPB_TYPE_BYTES: + if (!base64_push(p, getsel_for_handlertype(p, UPB_HANDLER_STRING), + p->accumulated, p->accumulated_len)) { + return false; + } + // Fall through. + + case UPB_TYPE_STRING: { + upb_selector_t sel = getsel_for_handlertype(p, UPB_HANDLER_ENDSTR); + upb_sink_endstr(&p->top->sink, sel); + p->top--; + break; + } + + case UPB_TYPE_ENUM: { + // Resolve enum symbolic name to integer value. + const upb_enumdef *enumdef = + (const upb_enumdef*)upb_fielddef_subdef(p->top->f); + + size_t len; + const char *buf = accumulate_getptr(p, &len); + + int32_t int_val = 0; + ok = upb_enumdef_ntoi(enumdef, buf, len, &int_val); + + if (ok) { + upb_selector_t sel = parser_getsel(p); + upb_sink_putint32(&p->top->sink, sel, int_val); + } else { + upb_status_seterrf(p->status, "Enum value unknown: '%.*s'", len, buf); + } + + break; + } + default: - assert(0); - return 'x'; + assert(false); + upb_status_seterrmsg(p->status, "Internal error in JSON decoder"); + ok = false; + break; } + + multipart_end(p); + return ok; } -static void escape(upb_json_parser *p, const char *ptr) { - char ch = escape_char(*ptr); - upb_selector_t sel = getsel_for_handlertype(p, UPB_HANDLER_STRING); - upb_sink_putstring(&p->top->sink, sel, &ch, 1, NULL); +static void start_member(upb_json_parser *p) { + assert(!p->top->f); + multipart_startaccum(p); } -static uint8_t hexdigit(char ch) { - if (ch >= '0' && ch <= '9') { - return ch - '0'; - } else if (ch >= 'a' && ch <= 'f') { - return ch - 'a' + 10; - } else { - assert(ch >= 'A' && ch <= 'F'); - return ch - 'A' + 10; +static bool end_member(upb_json_parser *p) { + assert(!p->top->f); + 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 clear_member(upb_json_parser *p) { p->top->f = NULL; } + +static bool start_subobject(upb_json_parser *p) { + assert(p->top->f); + + if (!upb_fielddef_issubmsg(p->top->f)) { + 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 start_hex(upb_json_parser *p, const char *ptr) { - start_text(p, ptr); +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); } -static void hex(upb_json_parser *p, const char *end) { - const char *start = p->text_begin; - UPB_ASSERT_VAR(end, end - start == 4); - uint16_t codepoint = - (hexdigit(start[0]) << 12) | - (hexdigit(start[1]) << 8) | - (hexdigit(start[2]) << 4) | - hexdigit(start[3]); - // emit the codepoint as UTF-8. - char utf8[3]; // support \u0000 -- \uFFFF -- need only three bytes. - int length = 0; - if (codepoint <= 0x7F) { - utf8[0] = codepoint; - length = 1; - } else if (codepoint <= 0x07FF) { - utf8[1] = (codepoint & 0x3F) | 0x80; - codepoint >>= 6; - utf8[0] = (codepoint & 0x1F) | 0xC0; - length = 2; - } else /* codepoint <= 0xFFFF */ { - utf8[2] = (codepoint & 0x3F) | 0x80; - codepoint >>= 6; - utf8[1] = (codepoint & 0x3F) | 0x80; - codepoint >>= 6; - utf8[0] = (codepoint & 0x0F) | 0xE0; - length = 3; +static bool start_array(upb_json_parser *p) { + assert(p->top->f); + + if (!upb_fielddef_isseq(p->top->f)) { + upb_status_seterrf(p->status, + "Array specified for non-repeated field: %s", + upb_fielddef_name(p->top->f)); + return false; } - // TODO(haberman): Handle high surrogates: if codepoint is a high surrogate - // we have to wait for the next escape to get the full code point). - upb_selector_t sel = getsel_for_handlertype(p, UPB_HANDLER_STRING); - upb_sink_putstring(&p->top->sink, sel, utf8, length, NULL); + 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 = p->top->m; + inner->f = p->top->f; + p->top = inner; + + return true; +} + +static void end_array(upb_json_parser *p) { + assert(p->top > p->stack); + + p->top--; + upb_selector_t sel = getsel_for_handlertype(p, UPB_HANDLER_ENDSEQ); + upb_sink_endseq(&p->top->sink, sel); +} + +static void start_object(upb_json_parser *p) { + upb_sink_startmsg(&p->top->sink); +} + +static void end_object(upb_json_parser *p) { + upb_status status; + upb_sink_endmsg(&p->top->sink, &status); } + #define CHECK_RETURN_TOP(x) if (!(x)) goto error + +/* The actual parser **********************************************************/ + // What follows is the Ragel parser itself. The language is specified in Ragel // and the actions call our C functions above. +// +// Ragel has an extensive set of functionality, and we use only a small part of +// it. There are many action types but we only use a few: +// +// ">" -- transition into a machine +// "%" -- transition out of a machine +// "@" -- transition into a final state of a machine. +// +// "@" transitions are tricky because a machine can transition into a final +// state repeatedly. But in some cases we know this can't happen, for example +// a string which is delimited by a final '"' can only transition into its +// final state once, when the closing '"' is seen. + -#line 596 "upb/json/parser.rl" +#line 904 "upb/json/parser.rl" -#line 514 "upb/json/parser.c" +#line 816 "upb/json/parser.c" static const char _json_actions[] = { 0, 1, 0, 1, 2, 1, 3, 1, - 4, 1, 5, 1, 6, 1, 7, 1, - 9, 1, 11, 1, 12, 1, 13, 1, - 14, 1, 15, 1, 16, 1, 24, 1, - 26, 2, 3, 7, 2, 5, 2, 2, - 5, 7, 2, 10, 8, 2, 12, 14, - 2, 13, 14, 2, 17, 1, 2, 18, - 26, 2, 19, 8, 2, 20, 26, 2, - 21, 26, 2, 22, 26, 2, 23, 26, - 2, 25, 26, 3, 13, 10, 8 + 5, 1, 6, 1, 7, 1, 8, 1, + 10, 1, 12, 1, 13, 1, 14, 1, + 15, 1, 16, 1, 17, 1, 21, 1, + 25, 1, 27, 2, 3, 8, 2, 4, + 5, 2, 6, 2, 2, 6, 8, 2, + 11, 9, 2, 13, 15, 2, 14, 15, + 2, 18, 1, 2, 19, 27, 2, 20, + 9, 2, 22, 27, 2, 23, 27, 2, + 24, 27, 2, 26, 27, 3, 14, 11, + 9 }; static const unsigned char _json_key_offsets[] = { - 0, 0, 4, 9, 14, 18, 22, 27, - 32, 37, 41, 45, 48, 51, 53, 57, - 61, 63, 65, 70, 72, 74, 83, 89, - 95, 101, 107, 109, 118, 118, 118, 123, - 128, 133, 133, 134, 135, 136, 137, 137, - 138, 139, 140, 140, 141, 142, 143, 143, - 148, 153, 157, 161, 166, 171, 176, 180, - 180, 183, 183, 183 + 0, 0, 4, 9, 14, 15, 19, 24, + 29, 34, 38, 42, 45, 48, 50, 54, + 58, 60, 62, 67, 69, 71, 80, 86, + 92, 98, 104, 106, 115, 116, 116, 116, + 121, 126, 131, 132, 133, 134, 135, 135, + 136, 137, 138, 138, 139, 140, 141, 141, + 146, 151, 152, 156, 161, 166, 171, 175, + 175, 178, 178, 178 }; static const char _json_trans_keys[] = { 32, 123, 9, 13, 32, 34, 125, 9, - 13, 32, 34, 125, 9, 13, 32, 58, - 9, 13, 32, 58, 9, 13, 32, 93, - 125, 9, 13, 32, 44, 125, 9, 13, - 32, 44, 125, 9, 13, 32, 34, 9, - 13, 45, 48, 49, 57, 48, 49, 57, - 46, 69, 101, 48, 57, 69, 101, 48, - 57, 43, 45, 48, 57, 48, 57, 48, - 57, 46, 69, 101, 48, 57, 34, 92, - 34, 92, 34, 47, 92, 98, 102, 110, - 114, 116, 117, 48, 57, 65, 70, 97, - 102, 48, 57, 65, 70, 97, 102, 48, - 57, 65, 70, 97, 102, 48, 57, 65, - 70, 97, 102, 34, 92, 34, 45, 91, - 102, 110, 116, 123, 48, 57, 32, 93, - 125, 9, 13, 32, 44, 93, 9, 13, - 32, 93, 125, 9, 13, 97, 108, 115, - 101, 117, 108, 108, 114, 117, 101, 32, - 34, 125, 9, 13, 32, 34, 125, 9, - 13, 32, 58, 9, 13, 32, 58, 9, - 13, 32, 93, 125, 9, 13, 32, 44, - 125, 9, 13, 32, 44, 125, 9, 13, - 32, 34, 9, 13, 32, 9, 13, 0 + 13, 32, 34, 125, 9, 13, 34, 32, + 58, 9, 13, 32, 93, 125, 9, 13, + 32, 44, 125, 9, 13, 32, 44, 125, + 9, 13, 32, 34, 9, 13, 45, 48, + 49, 57, 48, 49, 57, 46, 69, 101, + 48, 57, 69, 101, 48, 57, 43, 45, + 48, 57, 48, 57, 48, 57, 46, 69, + 101, 48, 57, 34, 92, 34, 92, 34, + 47, 92, 98, 102, 110, 114, 116, 117, + 48, 57, 65, 70, 97, 102, 48, 57, + 65, 70, 97, 102, 48, 57, 65, 70, + 97, 102, 48, 57, 65, 70, 97, 102, + 34, 92, 34, 45, 91, 102, 110, 116, + 123, 48, 57, 34, 32, 93, 125, 9, + 13, 32, 44, 93, 9, 13, 32, 93, + 125, 9, 13, 97, 108, 115, 101, 117, + 108, 108, 114, 117, 101, 32, 34, 125, + 9, 13, 32, 34, 125, 9, 13, 34, + 32, 58, 9, 13, 32, 93, 125, 9, + 13, 32, 44, 125, 9, 13, 32, 44, + 125, 9, 13, 32, 34, 9, 13, 32, + 9, 13, 0 }; static const char _json_single_lengths[] = { - 0, 2, 3, 3, 2, 2, 3, 3, + 0, 2, 3, 3, 1, 2, 3, 3, 3, 2, 2, 1, 3, 0, 2, 2, 0, 0, 3, 2, 2, 9, 0, 0, - 0, 0, 2, 7, 0, 0, 3, 3, - 3, 0, 1, 1, 1, 1, 0, 1, + 0, 0, 2, 7, 1, 0, 0, 3, + 3, 3, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 3, - 3, 2, 2, 3, 3, 3, 2, 0, + 3, 1, 2, 3, 3, 3, 2, 0, 1, 0, 0, 0 }; static const char _json_range_lengths[] = { - 0, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 3, 3, - 3, 3, 0, 1, 0, 0, 1, 1, - 1, 0, 0, 0, 0, 0, 0, 0, + 3, 3, 0, 1, 0, 0, 0, 1, + 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - 1, 1, 1, 1, 1, 1, 1, 0, + 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0 }; static const short _json_index_offsets[] = { - 0, 0, 4, 9, 14, 18, 22, 27, - 32, 37, 41, 45, 48, 52, 54, 58, - 62, 64, 66, 71, 74, 77, 87, 91, - 95, 99, 103, 106, 115, 116, 117, 122, - 127, 132, 133, 135, 137, 139, 141, 142, - 144, 146, 148, 149, 151, 153, 155, 156, - 161, 166, 170, 174, 179, 184, 189, 193, - 194, 197, 198, 199 + 0, 0, 4, 9, 14, 16, 20, 25, + 30, 35, 39, 43, 46, 50, 52, 56, + 60, 62, 64, 69, 72, 75, 85, 89, + 93, 97, 101, 104, 113, 115, 116, 117, + 122, 127, 132, 134, 136, 138, 140, 141, + 143, 145, 147, 148, 150, 152, 154, 155, + 160, 165, 167, 171, 176, 181, 186, 190, + 191, 194, 195, 196 }; static const char _json_indicies[] = { 0, 2, 0, 1, 3, 4, 5, 3, - 1, 6, 7, 8, 6, 1, 9, 10, - 9, 1, 11, 12, 11, 1, 12, 1, - 1, 12, 13, 14, 15, 16, 14, 1, - 17, 18, 8, 17, 1, 18, 7, 18, - 1, 19, 20, 21, 1, 20, 21, 1, - 23, 24, 24, 22, 25, 1, 24, 24, - 25, 22, 26, 26, 27, 1, 27, 1, - 27, 22, 23, 24, 24, 21, 22, 29, - 30, 28, 32, 33, 31, 34, 34, 34, - 34, 34, 34, 34, 34, 35, 1, 36, - 36, 36, 1, 37, 37, 37, 1, 38, - 38, 38, 1, 39, 39, 39, 1, 41, - 42, 40, 43, 44, 45, 46, 47, 48, - 49, 44, 1, 50, 51, 53, 54, 1, + 1, 6, 7, 8, 6, 1, 9, 1, + 10, 11, 10, 1, 11, 1, 1, 11, + 12, 13, 14, 15, 13, 1, 16, 17, + 8, 16, 1, 17, 7, 17, 1, 18, + 19, 20, 1, 19, 20, 1, 22, 23, + 23, 21, 24, 1, 23, 23, 24, 21, + 25, 25, 26, 1, 26, 1, 26, 21, + 22, 23, 23, 20, 21, 28, 29, 27, + 31, 32, 30, 33, 33, 33, 33, 33, + 33, 33, 33, 34, 1, 35, 35, 35, + 1, 36, 36, 36, 1, 37, 37, 37, + 1, 38, 38, 38, 1, 40, 41, 39, + 42, 43, 44, 45, 46, 47, 48, 43, + 1, 49, 1, 50, 51, 53, 54, 1, 53, 52, 55, 56, 54, 55, 1, 56, - 1, 1, 56, 52, 57, 58, 1, 59, - 1, 60, 1, 61, 1, 62, 63, 1, - 64, 1, 65, 1, 66, 67, 1, 68, - 1, 69, 1, 70, 71, 72, 73, 71, - 1, 74, 75, 76, 74, 1, 77, 78, - 77, 1, 79, 80, 79, 1, 80, 1, - 1, 80, 81, 82, 83, 84, 82, 1, - 85, 86, 76, 85, 1, 86, 75, 86, - 1, 87, 88, 88, 1, 1, 1, 1, - 0 + 1, 1, 56, 52, 57, 1, 58, 1, + 59, 1, 60, 1, 61, 62, 1, 63, + 1, 64, 1, 65, 66, 1, 67, 1, + 68, 1, 69, 70, 71, 72, 70, 1, + 73, 74, 75, 73, 1, 76, 1, 77, + 78, 77, 1, 78, 1, 1, 78, 79, + 80, 81, 82, 80, 1, 83, 84, 75, + 83, 1, 84, 74, 84, 1, 85, 86, + 86, 1, 1, 1, 1, 0 }; static const char _json_trans_targs[] = { 1, 0, 2, 3, 4, 56, 3, 4, - 56, 5, 6, 5, 6, 7, 8, 9, - 56, 8, 9, 11, 12, 18, 57, 13, - 15, 14, 16, 17, 20, 58, 21, 20, - 58, 21, 19, 22, 23, 24, 25, 26, - 20, 58, 21, 28, 29, 30, 34, 39, - 43, 47, 59, 59, 31, 30, 33, 31, - 32, 59, 35, 36, 37, 38, 59, 40, - 41, 42, 59, 44, 45, 46, 59, 48, - 49, 55, 48, 49, 55, 50, 51, 50, - 51, 52, 53, 54, 55, 53, 54, 59, - 56 + 56, 5, 5, 6, 7, 8, 9, 56, + 8, 9, 11, 12, 18, 57, 13, 15, + 14, 16, 17, 20, 58, 21, 20, 58, + 21, 19, 22, 23, 24, 25, 26, 20, + 58, 21, 28, 30, 31, 34, 39, 43, + 47, 29, 59, 59, 32, 31, 29, 32, + 33, 35, 36, 37, 38, 59, 40, 41, + 42, 59, 44, 45, 46, 59, 48, 49, + 55, 48, 49, 55, 50, 50, 51, 52, + 53, 54, 55, 53, 54, 59, 56 }; static const char _json_trans_actions[] = { - 0, 0, 0, 21, 75, 48, 0, 42, - 23, 17, 17, 0, 0, 15, 19, 19, - 45, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 3, 13, 0, 0, - 33, 5, 11, 0, 7, 0, 0, 0, - 36, 39, 9, 57, 51, 25, 0, 0, - 0, 29, 60, 54, 15, 0, 27, 0, - 0, 31, 0, 0, 0, 0, 66, 0, - 0, 0, 69, 0, 0, 0, 63, 21, - 75, 48, 0, 42, 23, 17, 17, 0, - 0, 15, 19, 19, 45, 0, 0, 72, - 0 + 0, 0, 0, 21, 77, 53, 0, 47, + 23, 17, 0, 0, 15, 19, 19, 50, + 0, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 3, 13, 0, 0, 35, + 5, 11, 0, 38, 7, 7, 7, 41, + 44, 9, 62, 56, 25, 0, 0, 0, + 31, 29, 33, 59, 15, 0, 27, 0, + 0, 0, 0, 0, 0, 68, 0, 0, + 0, 71, 0, 0, 0, 65, 21, 77, + 53, 0, 47, 23, 17, 0, 0, 15, + 19, 19, 50, 0, 0, 74, 0 }; static const int json_start = 1; @@ -9276,13 +9597,14 @@ static const int json_en_value_machine = 27; static const int json_en_main = 1; -#line 599 "upb/json/parser.rl" +#line 907 "upb/json/parser.rl" size_t parse(void *closure, const void *hd, const char *buf, size_t size, const upb_bufhandle *handle) { UPB_UNUSED(hd); UPB_UNUSED(handle); upb_json_parser *parser = closure; + parser->handle = handle; // Variables used by Ragel's generated code. int cs = parser->current_state; @@ -9292,8 +9614,10 @@ size_t parse(void *closure, const void *hd, const char *buf, size_t size, const char *p = buf; const char *pe = buf + size; + capture_resume(parser, buf); + -#line 684 "upb/json/parser.c" +#line 987 "upb/json/parser.c" { int _klen; unsigned int _trans; @@ -9368,114 +9692,118 @@ _match: switch ( *_acts++ ) { case 0: -#line 517 "upb/json/parser.rl" +#line 819 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; case 1: -#line 518 "upb/json/parser.rl" +#line 820 "upb/json/parser.rl" { p--; {stack[top++] = cs; cs = 10; goto _again;} } break; case 2: -#line 522 "upb/json/parser.rl" +#line 824 "upb/json/parser.rl" { start_text(parser, p); } break; case 3: -#line 523 "upb/json/parser.rl" - { CHECK_RETURN_TOP(end_text(parser, p, false)); } +#line 825 "upb/json/parser.rl" + { CHECK_RETURN_TOP(end_text(parser, p)); } break; case 4: -#line 529 "upb/json/parser.rl" - { start_hex(parser, p); } +#line 831 "upb/json/parser.rl" + { start_hex(parser); } break; case 5: -#line 530 "upb/json/parser.rl" - { hex(parser, p); } +#line 832 "upb/json/parser.rl" + { hexdigit(parser, p); } break; case 6: -#line 536 "upb/json/parser.rl" - { escape(parser, p); } +#line 833 "upb/json/parser.rl" + { CHECK_RETURN_TOP(end_hex(parser)); } break; case 7: -#line 539 "upb/json/parser.rl" - { {cs = stack[--top]; goto _again;} } +#line 839 "upb/json/parser.rl" + { CHECK_RETURN_TOP(escape(parser, p)); } break; case 8: -#line 540 "upb/json/parser.rl" - { {stack[top++] = cs; cs = 19; goto _again;} } +#line 845 "upb/json/parser.rl" + { p--; {cs = stack[--top]; goto _again;} } break; case 9: -#line 542 "upb/json/parser.rl" - { p--; {stack[top++] = cs; cs = 27; goto _again;} } +#line 848 "upb/json/parser.rl" + { {stack[top++] = cs; cs = 19; goto _again;} } break; case 10: -#line 547 "upb/json/parser.rl" - { start_member(parser); } +#line 850 "upb/json/parser.rl" + { p--; {stack[top++] = cs; cs = 27; goto _again;} } break; case 11: -#line 548 "upb/json/parser.rl" - { CHECK_RETURN_TOP(end_member(parser)); } +#line 855 "upb/json/parser.rl" + { start_member(parser); } break; case 12: -#line 551 "upb/json/parser.rl" - { clear_member(parser); } +#line 856 "upb/json/parser.rl" + { CHECK_RETURN_TOP(end_member(parser)); } break; case 13: -#line 557 "upb/json/parser.rl" - { start_object(parser); } +#line 859 "upb/json/parser.rl" + { clear_member(parser); } break; case 14: -#line 560 "upb/json/parser.rl" - { end_object(parser); } +#line 865 "upb/json/parser.rl" + { start_object(parser); } break; case 15: -#line 566 "upb/json/parser.rl" - { CHECK_RETURN_TOP(start_array(parser)); } +#line 868 "upb/json/parser.rl" + { end_object(parser); } break; case 16: -#line 570 "upb/json/parser.rl" - { end_array(parser); } +#line 874 "upb/json/parser.rl" + { CHECK_RETURN_TOP(start_array(parser)); } break; case 17: -#line 575 "upb/json/parser.rl" - { start_number(parser, p); } +#line 878 "upb/json/parser.rl" + { end_array(parser); } break; case 18: -#line 576 "upb/json/parser.rl" - { end_number(parser, p); } +#line 883 "upb/json/parser.rl" + { start_number(parser, p); } break; case 19: -#line 578 "upb/json/parser.rl" - { CHECK_RETURN_TOP(start_stringval(parser)); } +#line 884 "upb/json/parser.rl" + { CHECK_RETURN_TOP(end_number(parser, p)); } break; case 20: -#line 579 "upb/json/parser.rl" - { end_stringval(parser); } +#line 886 "upb/json/parser.rl" + { CHECK_RETURN_TOP(start_stringval(parser)); } break; case 21: -#line 581 "upb/json/parser.rl" - { CHECK_RETURN_TOP(parser_putbool(parser, true)); } +#line 887 "upb/json/parser.rl" + { CHECK_RETURN_TOP(end_stringval(parser)); } break; case 22: -#line 583 "upb/json/parser.rl" - { CHECK_RETURN_TOP(parser_putbool(parser, false)); } +#line 889 "upb/json/parser.rl" + { CHECK_RETURN_TOP(parser_putbool(parser, true)); } break; case 23: -#line 585 "upb/json/parser.rl" - { /* null value */ } +#line 891 "upb/json/parser.rl" + { CHECK_RETURN_TOP(parser_putbool(parser, false)); } break; case 24: -#line 587 "upb/json/parser.rl" - { CHECK_RETURN_TOP(start_subobject(parser)); } +#line 893 "upb/json/parser.rl" + { /* null value */ } break; case 25: -#line 588 "upb/json/parser.rl" - { end_subobject(parser); } +#line 895 "upb/json/parser.rl" + { CHECK_RETURN_TOP(start_subobject(parser)); } break; case 26: -#line 593 "upb/json/parser.rl" +#line 896 "upb/json/parser.rl" + { end_subobject(parser); } + break; + case 27: +#line 901 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; -#line 866 "upb/json/parser.c" +#line 1173 "upb/json/parser.c" } } @@ -9488,10 +9816,12 @@ _again: _out: {} } -#line 615 "upb/json/parser.rl" +#line 926 "upb/json/parser.rl" if (p != pe) { upb_status_seterrf(parser->status, "Parse error at %s\n", p); + } else { + capture_suspend(parser, &p); } error: @@ -9508,8 +9838,13 @@ bool end(void *closure, const void *hd) { return true; } + +/* Public API *****************************************************************/ + void upb_json_parser_init(upb_json_parser *p, upb_status *status) { p->limit = p->stack + UPB_JSON_MAX_DEPTH; + p->accumulate_buf = NULL; + p->accumulate_buf_size = 0; upb_byteshandler_init(&p->input_handler_); upb_byteshandler_setstring(&p->input_handler_, parse, NULL); upb_byteshandler_setendstr(&p->input_handler_, end, NULL); @@ -9519,6 +9854,7 @@ void upb_json_parser_init(upb_json_parser *p, upb_status *status) { void upb_json_parser_uninit(upb_json_parser *p) { upb_byteshandler_uninit(&p->input_handler_); + free(p->accumulate_buf); } void upb_json_parser_reset(upb_json_parser *p) { @@ -9529,18 +9865,18 @@ void upb_json_parser_reset(upb_json_parser *p) { int top; // Emit Ragel initialization of the parser. -#line 920 "upb/json/parser.c" +#line 1235 "upb/json/parser.c" { cs = json_start; top = 0; } -#line 655 "upb/json/parser.rl" +#line 974 "upb/json/parser.rl" p->current_state = cs; p->parser_top = top; - p->text_begin = NULL; - p->accumulated = NULL; - p->accumulated_len = 0; + accumulate_clear(p); + p->multipart_state = MULTIPART_INACTIVE; + p->capture = NULL; } void upb_json_parser_resetoutput(upb_json_parser *p, upb_sink *sink) { diff --git a/ruby/ext/google/protobuf_c/upb.h b/ruby/ext/google/protobuf_c/upb.h index 0e98bbab..fbcb8e99 100644 --- a/ruby/ext/google/protobuf_c/upb.h +++ b/ruby/ext/google/protobuf_c/upb.h @@ -6662,7 +6662,9 @@ typedef enum { // | unused (24) | opc | // | upb_inttable* (32 or 64) | - OP_HALT = 36, // No arg. + OP_DISPATCH = 36, // No arg. + + OP_HALT = 37, // No arg. } opcode; #define OP_MAX OP_HALT @@ -7339,15 +7341,24 @@ UPB_DEFINE_STRUCT0(upb_json_parser, int parser_stack[UPB_JSON_MAX_DEPTH]; int parser_top; - // A pointer to the beginning of whatever text we are currently parsing. - const char *text_begin; + // The handle for the current buffer. + const upb_bufhandle *handle; - // We have to accumulate text for member names, integers, unicode escapes, and - // base64 partial results. + // Accumulate buffer. See details in parser.rl. const char *accumulated; size_t accumulated_len; - // TODO: add members and code for allocating a buffer when necessary (when the - // member spans input buffers or contains escapes). + char *accumulate_buf; + size_t accumulate_buf_size; + + // Multi-part text data. See details in parser.rl. + int multipart_state; + upb_selector_t string_selector; + + // Input capture. See details in parser.rl. + const char *capture; + + // Intermediate result of parsing a unicode escape sequence. + uint32_t digit; )); UPB_BEGIN_EXTERN_C -- cgit v1.2.3 From addd26cbb3e7af05917bfc9bc965b0ada4bc80b0 Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Mon, 12 Jan 2015 16:09:35 -0800 Subject: Addressed code-review comments. --- ruby/ext/google/protobuf_c/encode_decode.c | 7 ++++++- ruby/ext/google/protobuf_c/map.c | 30 ++++++++---------------------- 2 files changed, 14 insertions(+), 23 deletions(-) (limited to 'ruby') diff --git a/ruby/ext/google/protobuf_c/encode_decode.c b/ruby/ext/google/protobuf_c/encode_decode.c index 1cff9049..e5e1514b 100644 --- a/ruby/ext/google/protobuf_c/encode_decode.c +++ b/ruby/ext/google/protobuf_c/encode_decode.c @@ -253,7 +253,12 @@ static map_handlerdata_t* new_map_handlerdata( 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. + // 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); } diff --git a/ruby/ext/google/protobuf_c/map.c b/ruby/ext/google/protobuf_c/map.c index 2c2ae240..4ee71d18 100644 --- a/ruby/ext/google/protobuf_c/map.c +++ b/ruby/ext/google/protobuf_c/map.c @@ -205,10 +205,13 @@ static bool needs_typeclass(upb_fieldtype_t type) { * * The last argument, if present, provides initial content for map. Note that * this may be an ordinary Ruby hashmap or another Map instance with identical - * key and value types. Also note that this argument may be rpesent whether or + * key and value types. Also note that this argument may be present whether or * not value_typeclass is present (and it is unambiguously separate from * value_typeclass because value_typeclass's presence is strictly determined by - * value_type). + * value_type). The contents of this initial hashmap or Map instance are + * shallow-copied into the new Map: the original map is unmodified, but + * references to underlying objects will be shared if the value type is a + * message type. */ VALUE Map_init(int argc, VALUE* argv, VALUE _self) { Map* self = ruby_to_Map(_self); @@ -385,8 +388,7 @@ VALUE Map_index_set(VALUE _self, VALUE key, VALUE value) { native_slot_set(self->value_type, self->value_type_class, mem, value); // Replace any existing value by issuing a 'remove' operation first. - upb_value oldv; - upb_strtable_remove2(&self->table, keyval, length, &oldv); + upb_strtable_remove2(&self->table, keyval, length, NULL); if (!upb_strtable_insert2(&self->table, keyval, length, v)) { rb_raise(rb_eRuntimeError, "Could not insert into table"); } @@ -410,8 +412,7 @@ VALUE Map_has_key(VALUE _self, VALUE key) { size_t length = 0; table_key(self, key, keybuf, &keyval, &length); - upb_value v; - if (upb_strtable_lookup2(&self->table, keyval, length, &v)) { + if (upb_strtable_lookup2(&self->table, keyval, length, NULL)) { return Qtrue; } else { return Qfalse; @@ -468,7 +469,7 @@ VALUE Map_clear(VALUE _self) { */ VALUE Map_length(VALUE _self) { Map* self = ruby_to_Map(_self); - return INT2NUM(upb_strtable_count(&self->table)); + return ULL2NUM(upb_strtable_count(&self->table)); } static VALUE Map_new_this_type(VALUE _self) { @@ -612,21 +613,6 @@ VALUE Map_eq(VALUE _self, VALUE _other) { } } - // For each member of other, check that a member exists at the same key in - // self. We don't need to compare values here -- if the key exists in both, we - // compared values above; if not, we already know that the maps are not equal. - for (upb_strtable_begin(&it, &other->table); - !upb_strtable_done(&it); - upb_strtable_next(&it)) { - upb_value v; - if (!upb_strtable_lookup2(&self->table, - upb_strtable_iter_key(&it), - upb_strtable_iter_keylength(&it), - &v)) { - return Qfalse; - } - } - return Qtrue; } -- cgit v1.2.3 From ace4212e60bf1abd46181c9ddb9fe31b6d9fac45 Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Tue, 13 Jan 2015 13:47:58 -0800 Subject: Line-wraps at 80 chars. --- ruby/tests/basic.rb | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) (limited to 'ruby') diff --git a/ruby/tests/basic.rb b/ruby/tests/basic.rb index 2620fa9b..92655033 100644 --- a/ruby/tests/basic.rb +++ b/ruby/tests/basic.rb @@ -479,9 +479,10 @@ module BasicTest m["jkl;"] = TestMessage2.new end - m = Google::Protobuf::Map.new(:string, :message, TestMessage, - { "a" => TestMessage.new(:optional_int32 => 42), - "b" => TestMessage.new(:optional_int32 => 84) }) + m = Google::Protobuf::Map.new( + :string, :message, TestMessage, + { "a" => TestMessage.new(:optional_int32 => 42), + "b" => TestMessage.new(:optional_int32 => 84) }) assert m.length == 2 assert m.values.map{|msg| msg.optional_int32}.sort == [42, 84] @@ -502,9 +503,10 @@ module BasicTest end def test_map_dup_deep_copy - m = Google::Protobuf::Map.new(:string, :message, TestMessage, - { "a" => TestMessage.new(:optional_int32 => 42), - "b" => TestMessage.new(:optional_int32 => 84) }) + m = Google::Protobuf::Map.new( + :string, :message, TestMessage, + { "a" => TestMessage.new(:optional_int32 => 42), + "b" => TestMessage.new(:optional_int32 => 84) }) m2 = m.dup assert m == m2 @@ -524,9 +526,10 @@ module BasicTest assert m.map_string_int32 == {} assert m.map_string_msg == {} - m = MapMessage.new(:map_string_int32 => {"a" => 1, "b" => 2}, - :map_string_msg => {"a" => TestMessage2.new(:foo => 1), - "b" => TestMessage2.new(:foo => 2)}) + m = MapMessage.new( + :map_string_int32 => {"a" => 1, "b" => 2}, + :map_string_msg => {"a" => TestMessage2.new(:foo => 1), + "b" => TestMessage2.new(:foo => 2)}) assert m.map_string_int32.keys.sort == ["a", "b"] assert m.map_string_int32["a"] == 1 assert m.map_string_msg["b"].foo == 2 @@ -559,9 +562,10 @@ module BasicTest end def test_map_encode_decode - m = MapMessage.new(:map_string_int32 => {"a" => 1, "b" => 2}, - :map_string_msg => {"a" => TestMessage2.new(:foo => 1), - "b" => TestMessage2.new(:foo => 2)}) + m = MapMessage.new( + :map_string_int32 => {"a" => 1, "b" => 2}, + :map_string_msg => {"a" => TestMessage2.new(:foo => 1), + "b" => TestMessage2.new(:foo => 2)}) m2 = MapMessage.decode(MapMessage.encode(m)) assert m == m2 @@ -638,7 +642,8 @@ module BasicTest :repeated_string => ["a", "b", "c"], :repeated_int32 => [42, 43, 44], :repeated_enum => [:A, :B, :C, 100], - :repeated_msg => [TestMessage2.new(:foo => 1), TestMessage2.new(:foo => 2)]) + :repeated_msg => [TestMessage2.new(:foo => 1), + TestMessage2.new(:foo => 2)]) data = TestMessage.encode m m2 = TestMessage.decode data assert m == m2 -- cgit v1.2.3 From fcd8889d5b68be6ca7d1df705ad2159777e2f379 Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Tue, 13 Jan 2015 18:14:39 -0800 Subject: Support oneofs in MRI Ruby C extension. --- ruby/ext/google/protobuf_c/defs.c | 303 +++++++++++++- ruby/ext/google/protobuf_c/encode_decode.c | 190 ++++++++- ruby/ext/google/protobuf_c/protobuf.c | 2 + ruby/ext/google/protobuf_c/protobuf.h | 49 ++- ruby/ext/google/protobuf_c/storage.c | 250 +++++++++--- ruby/ext/google/protobuf_c/upb.c | 552 ++++++++++++++++++++----- ruby/ext/google/protobuf_c/upb.h | 635 ++++++++++++++++++++++++++--- ruby/tests/basic.rb | 92 +++++ 8 files changed, 1840 insertions(+), 233 deletions(-) (limited to 'ruby') diff --git a/ruby/ext/google/protobuf_c/defs.c b/ruby/ext/google/protobuf_c/defs.c index a18aaac4..371939ae 100644 --- a/ruby/ext/google/protobuf_c/defs.c +++ b/ruby/ext/google/protobuf_c/defs.c @@ -65,6 +65,10 @@ static upb_fielddef* check_field_notfrozen(const upb_fielddef* def) { return (upb_fielddef*)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) { return (upb_enumdef*)check_notfrozen((const upb_def*)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, "oneofs", Descriptor_oneofs, 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. @@ -377,6 +384,67 @@ VALUE Descriptor_add_field(VALUE _self, VALUE obj) { return Qnil; } +/* + * 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.oneofs => list of OneofDescriptors + * + * Returns a list of OneofDescriptors that are part of this message type. + */ +VALUE Descriptor_oneofs(VALUE _self) { + DEFINE_SELF(Descriptor, self, _self); + + VALUE ret = rb_ary_new(); + 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_ary_push(ret, obj); + } + return ret; +} + +/* + * 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 @@ -743,6 +811,120 @@ VALUE FieldDescriptor_set(VALUE _self, VALUE msg_rb, VALUE value) { return Qnil; } +// ----------------------------------------------------------------------------- +// 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..26a038b6 100644 --- a/ruby/ext/google/protobuf_c/encode_decode.c +++ b/ruby/ext/google/protobuf_c/encode_decode.c @@ -59,6 +59,30 @@ 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 tag; // tag number to place in data slot + 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; + hd->tag = 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 +91,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 +108,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 +119,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) { @@ -266,6 +289,75 @@ static map_handlerdata_t* new_map_handlerdata( 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->tag; \ + 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->tag; + 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->tag; + 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); + DEREF(msg, oneofdata->case_ofs, uint32_t) = oneofdata->tag; + + VALUE subdesc = + get_def_obj((void*)oneofdata->md); + VALUE subklass = Descriptor_msgclass(subdesc); + + if (oldcase != oneofdata->tag || + DEREF(msg, oneofdata->ofs, VALUE) == Qnil) { + DEREF(msg, oneofdata->ofs, VALUE) = + rb_class_new_instance(0, NULL, subklass); + } + + 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 +475,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 +541,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); + size_t oneof_case_offset = + desc->layout->fields[upb_fielddef_index(f)].case_offset + sizeof(MessageHeader); - if (is_map_field(f)) { + if (upb_fielddef_containingoneof(f)) { + 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 +947,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); + uint32_t oneof_case_offset = + desc->layout->fields[upb_fielddef_index(f)].case_offset + + sizeof(MessageHeader); + + if (upb_fielddef_containingoneof(f)) { + // 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/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..84f318b3 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_oneofs(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,7 +285,7 @@ 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, @@ -384,9 +422,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/storage.c b/ruby/ext/google/protobuf_c/storage.c index 14f49d44..d526679a 100644 --- a/ruby/ext/google/protobuf_c/storage.c +++ b/ruby/ext/google/protobuf_c/storage.c @@ -369,21 +369,79 @@ const upb_fielddef* map_entry_value(const upb_msgdef* msgdef) { 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 = (off + field_size - 1) & ~(field_size - 1); + 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. + 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 = (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)].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); + // 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 +454,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); } @@ -419,8 +477,18 @@ 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) { + layout->fields[upb_fielddef_index(field)].offset; + uint32_t* oneof_case = (uint32_t *)(((uint8_t *)storage) + + layout->fields[upb_fielddef_index(field)].case_offset); + + 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), @@ -485,8 +553,27 @@ void layout_set(MessageLayout* layout, const upb_fielddef* field, VALUE val) { void* memory = ((uint8_t *)storage) + - layout->offsets[upb_fielddef_index(field)]; - if (is_map_field(field)) { + layout->fields[upb_fielddef_index(field)].offset; + uint32_t* oneof_case = (uint32_t *)(((uint8_t *)storage) + + layout->fields[upb_fielddef_index(field)].case_offset); + + if (upb_fielddef_containingoneof(field)) { + if (val == Qnil) { + // Assigning nil to a oneof field clears the oneof completely. + *oneof_case = 0; + memset(memory, 0, NATIVE_SLOT_MAX_SIZE); + } else { + // Set the oneof case *first* in case a GC is triggered during + // native_slot_set(): layout_mark() depends on oneof_case to know whether + // the slot may be a Ruby VALUE and so we need that lifetime to start + // before we could possibly stick a VALUE in it. + *oneof_case = upb_fielddef_number(field); + // We just overwrite the value directly if we changed oneof cases: + // native_slot_set() does not depend on the old value in memory. + native_slot_set(upb_fielddef_type(field), field_type_class(field), + memory, val); + } + } 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 +587,20 @@ 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)]; - - if (is_map_field(field)) { + layout->fields[upb_fielddef_index(field)].offset; + uint32_t* oneof_case = (uint32_t *)(((uint8_t *)storage) + + layout->fields[upb_fielddef_index(field)].case_offset); + + if (upb_fielddef_containingoneof(field)) { + memset(memory, 0, NATIVE_SLOT_MAX_SIZE); + *oneof_case = 0; + } else if (is_map_field(field)) { VALUE map = Qnil; const upb_fielddef* key_field = map_field_key(field); @@ -555,15 +647,21 @@ 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)]; + layout->fields[upb_fielddef_index(field)].offset; + uint32_t* oneof_case = (uint32_t *)(((uint8_t *)storage) + + layout->fields[upb_fielddef_index(field)].case_offset); - 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 +670,26 @@ 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)]; + layout->fields[upb_fielddef_index(field)].offset; + uint32_t* to_oneof_case = (uint32_t *)(((uint8_t *)to) + + layout->fields[upb_fielddef_index(field)].case_offset); void* from_memory = ((uint8_t *)from) + - layout->offsets[upb_fielddef_index(field)]; - - if (is_map_field(field)) { + layout->fields[upb_fielddef_index(field)].offset; + uint32_t* from_oneof_case = (uint32_t *)(((uint8_t *)from) + + layout->fields[upb_fielddef_index(field)].case_offset); + + 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 +700,26 @@ 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)]; + layout->fields[upb_fielddef_index(field)].offset; + uint32_t* to_oneof_case = (uint32_t *)(((uint8_t *)to) + + layout->fields[upb_fielddef_index(field)].case_offset); void* from_memory = ((uint8_t *)from) + - layout->offsets[upb_fielddef_index(field)]; - - if (is_map_field(field)) { + layout->fields[upb_fielddef_index(field)].offset; + uint32_t* from_oneof_case = (uint32_t *)(((uint8_t *)from) + + layout->fields[upb_fielddef_index(field)].case_offset); + + 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 +732,38 @@ 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)]; + layout->fields[upb_fielddef_index(field)].offset; + uint32_t* msg1_oneof_case = (uint32_t *)(((uint8_t *)msg1) + + layout->fields[upb_fielddef_index(field)].case_offset); 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)); + layout->fields[upb_fielddef_index(field)].offset; + uint32_t* msg2_oneof_case = (uint32_t *)(((uint8_t *)msg2) + + layout->fields[upb_fielddef_index(field)].case_offset); + + 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 +775,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 +793,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..c223c568 100644 --- a/ruby/ext/google/protobuf_c/upb.c +++ b/ruby/ext/google/protobuf_c/upb.c @@ -247,10 +247,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 +288,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 +548,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 +626,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 +756,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 +788,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); } @@ -1247,15 +1263,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 +1293,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 +1315,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 +1375,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 +1417,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"); - 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"); + if (!check_field_add(m, f, s)) { 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 +1493,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 +1517,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_done(const upb_msg_iter *iter) { return upb_inttable_done(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_iter *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_iter_setdone(upb_msg_iter *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_oneof_done(upb_oneof_iter *iter) { + return upb_inttable_done(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_oneof_iter_setdone(upb_oneof_iter *iter) { upb_inttable_iter_setdone(iter); } /* @@ -1452,8 +1799,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 +1831,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 +2191,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 +3467,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 +3623,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)) { @@ -3416,16 +3773,12 @@ char *upb_strdup(const char *s) { } char *upb_strdup2(const char *s, size_t len) { - // Prevent overflow errors. - if (len == SIZE_MAX) return NULL; // Always null-terminate, even if binary data; but don't rely on the input to // have a null-terminating byte since it may be a raw binary buffer. size_t n = len + 1; char *p = malloc(n); - if (p) { - memcpy(p, s, len); - p[len] = 0; - } + if (p) memcpy(p, s, len); + p[len] = 0; return p; } @@ -6462,8 +6815,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 +6868,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 +6914,7 @@ static void set_bytecode_handlers(mgroup *g) { } -/* JIT setup. ******************************************************************/ +/* JIT setup. *****************************************************************/ #ifdef UPB_USE_JIT_X64 @@ -7980,8 +8337,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 +8802,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 +9218,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); } @@ -9442,11 +9804,11 @@ static void end_object(upb_json_parser *p) { // final state once, when the closing '"' is seen. -#line 904 "upb/json/parser.rl" +#line 905 "upb/json/parser.rl" -#line 816 "upb/json/parser.c" +#line 817 "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 +9959,7 @@ static const int json_en_value_machine = 27; static const int json_en_main = 1; -#line 907 "upb/json/parser.rl" +#line 908 "upb/json/parser.rl" size_t parse(void *closure, const void *hd, const char *buf, size_t size, const upb_bufhandle *handle) { @@ -9617,7 +9979,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 988 "upb/json/parser.c" { int _klen; unsigned int _trans; @@ -9692,118 +10054,118 @@ _match: switch ( *_acts++ ) { case 0: -#line 819 "upb/json/parser.rl" +#line 820 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; case 1: -#line 820 "upb/json/parser.rl" +#line 821 "upb/json/parser.rl" { p--; {stack[top++] = cs; cs = 10; goto _again;} } break; case 2: -#line 824 "upb/json/parser.rl" +#line 825 "upb/json/parser.rl" { start_text(parser, p); } break; case 3: -#line 825 "upb/json/parser.rl" +#line 826 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_text(parser, p)); } break; case 4: -#line 831 "upb/json/parser.rl" +#line 832 "upb/json/parser.rl" { start_hex(parser); } break; case 5: -#line 832 "upb/json/parser.rl" +#line 833 "upb/json/parser.rl" { hexdigit(parser, p); } break; case 6: -#line 833 "upb/json/parser.rl" +#line 834 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_hex(parser)); } break; case 7: -#line 839 "upb/json/parser.rl" +#line 840 "upb/json/parser.rl" { CHECK_RETURN_TOP(escape(parser, p)); } break; case 8: -#line 845 "upb/json/parser.rl" +#line 846 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; case 9: -#line 848 "upb/json/parser.rl" +#line 849 "upb/json/parser.rl" { {stack[top++] = cs; cs = 19; goto _again;} } break; case 10: -#line 850 "upb/json/parser.rl" +#line 851 "upb/json/parser.rl" { p--; {stack[top++] = cs; cs = 27; goto _again;} } break; case 11: -#line 855 "upb/json/parser.rl" +#line 856 "upb/json/parser.rl" { start_member(parser); } break; case 12: -#line 856 "upb/json/parser.rl" +#line 857 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_member(parser)); } break; case 13: -#line 859 "upb/json/parser.rl" +#line 860 "upb/json/parser.rl" { clear_member(parser); } break; case 14: -#line 865 "upb/json/parser.rl" +#line 866 "upb/json/parser.rl" { start_object(parser); } break; case 15: -#line 868 "upb/json/parser.rl" +#line 869 "upb/json/parser.rl" { end_object(parser); } break; case 16: -#line 874 "upb/json/parser.rl" +#line 875 "upb/json/parser.rl" { CHECK_RETURN_TOP(start_array(parser)); } break; case 17: -#line 878 "upb/json/parser.rl" +#line 879 "upb/json/parser.rl" { end_array(parser); } break; case 18: -#line 883 "upb/json/parser.rl" +#line 884 "upb/json/parser.rl" { start_number(parser, p); } break; case 19: -#line 884 "upb/json/parser.rl" +#line 885 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_number(parser, p)); } break; case 20: -#line 886 "upb/json/parser.rl" +#line 887 "upb/json/parser.rl" { CHECK_RETURN_TOP(start_stringval(parser)); } break; case 21: -#line 887 "upb/json/parser.rl" +#line 888 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_stringval(parser)); } break; case 22: -#line 889 "upb/json/parser.rl" +#line 890 "upb/json/parser.rl" { CHECK_RETURN_TOP(parser_putbool(parser, true)); } break; case 23: -#line 891 "upb/json/parser.rl" +#line 892 "upb/json/parser.rl" { CHECK_RETURN_TOP(parser_putbool(parser, false)); } break; case 24: -#line 893 "upb/json/parser.rl" +#line 894 "upb/json/parser.rl" { /* null value */ } break; case 25: -#line 895 "upb/json/parser.rl" +#line 896 "upb/json/parser.rl" { CHECK_RETURN_TOP(start_subobject(parser)); } break; case 26: -#line 896 "upb/json/parser.rl" +#line 897 "upb/json/parser.rl" { end_subobject(parser); } break; case 27: -#line 901 "upb/json/parser.rl" +#line 902 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; -#line 1173 "upb/json/parser.c" +#line 1174 "upb/json/parser.c" } } @@ -9816,7 +10178,7 @@ _again: _out: {} } -#line 926 "upb/json/parser.rl" +#line 927 "upb/json/parser.rl" if (p != pe) { upb_status_seterrf(parser->status, "Parse error at %s\n", p); @@ -9865,13 +10227,13 @@ void upb_json_parser_reset(upb_json_parser *p) { int top; // Emit Ragel initialization of the parser. -#line 1235 "upb/json/parser.c" +#line 1236 "upb/json/parser.c" { cs = json_start; top = 0; } -#line 974 "upb/json/parser.rl" +#line 975 "upb/json/parser.rl" p->current_state = cs; p->parser_top = top; accumulate_clear(p); @@ -10327,9 +10689,9 @@ void printer_sethandlers(const void *closure, upb_handlers *h) { } \ 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, upb_handlers_msgdef(h)); + 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; diff --git a/ruby/ext/google/protobuf_c/upb.h b/ruby/ext/google/protobuf_c/upb.h index fbcb8e99..572f6ad1 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 @@ -1616,6 +1626,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 +1642,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 +1680,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); @@ -1736,7 +1748,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 +1779,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& 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& 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 +1833,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 + OneofDef* FindOneofByName(const T& str) { + return FindOneofByName(str.c_str(), str.size()); + } + template + 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 +1867,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 { + class field_iterator + : public std::iterator { 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 { 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 { + 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 { + 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 +1990,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 +2001,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 +2032,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 +2058,34 @@ 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)) { +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 +2093,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 +2246,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 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& 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 + FieldDef* FindFieldByName(const T& str) { + return FindFieldByName(str.c_str(), str.size()); + } + template + 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 { + 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 { + 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 +2538,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); } @@ -2351,12 +2691,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& 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& 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 +2719,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 +2735,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::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::iterator(MessageDef* md) { - upb_msg_begin(&iter_, md); +inline MessageDef::field_iterator::field_iterator(MessageDef* md) { + upb_msg_field_begin(&iter_, md); } -inline MessageDef::iterator MessageDef::iterator::end(MessageDef* md) { - MessageDef::iterator iter(md); - upb_msg_iter_setdone(&iter.iter_); +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 +2923,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::New() { + upb_oneofdef *o = upb_oneofdef_new(&o); + return reffed_ptr(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& 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 diff --git a/ruby/tests/basic.rb b/ruby/tests/basic.rb index 92655033..ceeaa8ad 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 --------------- @@ -582,6 +592,80 @@ 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" + assert d.oneofs == [o] + 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 + + d.a = "hi" + assert d.a == "hi" + assert d.b == nil + assert d.c == nil + assert d.d == nil + + d.b = 42 + assert d.a == nil + assert d.b == 42 + assert d.c == nil + assert d.d == nil + + d.c = TestMessage2.new(:foo => 100) + assert d.a == nil + assert d.b == nil + assert d.c.foo == 100 + assert d.d == nil + + d.d = :C + assert d.a == nil + assert d.b == nil + assert d.c == nil + assert d.d == :C + + 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) == '' + end + def test_enum_field m = TestMessage.new assert m.optional_enum == :Default @@ -621,6 +705,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 -- cgit v1.2.3 From 3f3820d8f8b5c0b67aadc25ad5a2e728b6a3fe79 Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Wed, 14 Jan 2015 15:44:46 -0800 Subject: Two tests for Ruby code generator: - A golden-file test that ensures protoc produces known-valid output. - A Ruby test that loads that golden file and ensures it actually works with the extension. This split strategy allows us to test end-to-end without needing to integrate the Ruby gem build system and the protoc build system. This is desirable because we do not want a gem build/install to depend on building protoc, and we do not want building protoc to depend on building and testing the gem. --- ruby/google-protobuf.gemspec | 4 +- ruby/tests/generated_code.proto | 67 +++++++++++ ruby/tests/generated_code.rb | 124 +++++++++++++++++++++ ruby/tests/generated_code_test.rb | 17 +++ src/Makefile.am | 1 + .../compiler/ruby/ruby_generator_unittest.cc | 116 +++++++++++++++++++ 6 files changed, 328 insertions(+), 1 deletion(-) create mode 100644 ruby/tests/generated_code.proto create mode 100644 ruby/tests/generated_code.rb create mode 100644 ruby/tests/generated_code_test.rb create mode 100644 src/google/protobuf/compiler/ruby/ruby_generator_unittest.cc (limited to 'ruby') diff --git a/ruby/google-protobuf.gemspec b/ruby/google-protobuf.gemspec index 87033ac4..7bfa533c 100644 --- a/ruby/google-protobuf.gemspec +++ b/ruby/google-protobuf.gemspec @@ -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/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 map_int32_string = 61; + map map_int64_string = 62; + map map_uint32_string = 63; + map map_uint64_string = 64; + map map_bool_string = 65; + map map_string_string = 66; + map map_string_msg = 67; + map map_string_enum = 68; + map map_string_int32 = 69; + map 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..db762ad9 --- /dev/null +++ b/ruby/tests/generated_code.rb @@ -0,0 +1,124 @@ +# 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" + repeated :map_int32_string, :message, 61, "A.B.C.TestMessage.MapInt32StringEntry" + repeated :map_int64_string, :message, 62, "A.B.C.TestMessage.MapInt64StringEntry" + repeated :map_uint32_string, :message, 63, "A.B.C.TestMessage.MapUint32StringEntry" + repeated :map_uint64_string, :message, 64, "A.B.C.TestMessage.MapUint64StringEntry" + repeated :map_bool_string, :message, 65, "A.B.C.TestMessage.MapBoolStringEntry" + repeated :map_string_string, :message, 66, "A.B.C.TestMessage.MapStringStringEntry" + repeated :map_string_msg, :message, 67, "A.B.C.TestMessage.MapStringMsgEntry" + repeated :map_string_enum, :message, 68, "A.B.C.TestMessage.MapStringEnumEntry" + repeated :map_string_int32, :message, 69, "A.B.C.TestMessage.MapStringInt32Entry" + repeated :map_string_bool, :message, 70, "A.B.C.TestMessage.MapStringBoolEntry" + 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.MapInt32StringEntry" do + optional :key, :int32, 1 + optional :value, :string, 2 + end + add_message "A.B.C.TestMessage.MapInt64StringEntry" do + optional :key, :int64, 1 + optional :value, :string, 2 + end + add_message "A.B.C.TestMessage.MapUint32StringEntry" do + optional :key, :uint32, 1 + optional :value, :string, 2 + end + add_message "A.B.C.TestMessage.MapUint64StringEntry" do + optional :key, :uint64, 1 + optional :value, :string, 2 + end + add_message "A.B.C.TestMessage.MapBoolStringEntry" do + optional :key, :bool, 1 + optional :value, :string, 2 + end + add_message "A.B.C.TestMessage.MapStringStringEntry" do + optional :key, :string, 1 + optional :value, :string, 2 + end + add_message "A.B.C.TestMessage.MapStringMsgEntry" do + optional :key, :string, 1 + optional :value, :message, 2, "A.B.C.TestMessage" + end + add_message "A.B.C.TestMessage.MapStringEnumEntry" do + optional :key, :string, 1 + optional :value, :enum, 2, "A.B.C.TestEnum" + end + add_message "A.B.C.TestMessage.MapStringInt32Entry" do + optional :key, :string, 1 + optional :value, :int32, 2 + end + add_message "A.B.C.TestMessage.MapStringBoolEntry" do + optional :key, :string, 1 + optional :value, :bool, 2 + 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::MapInt32StringEntry = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestMessage.MapInt32StringEntry").msgclass + TestMessage::MapInt64StringEntry = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestMessage.MapInt64StringEntry").msgclass + TestMessage::MapUint32StringEntry = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestMessage.MapUint32StringEntry").msgclass + TestMessage::MapUint64StringEntry = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestMessage.MapUint64StringEntry").msgclass + TestMessage::MapBoolStringEntry = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestMessage.MapBoolStringEntry").msgclass + TestMessage::MapStringStringEntry = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestMessage.MapStringStringEntry").msgclass + TestMessage::MapStringMsgEntry = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestMessage.MapStringMsgEntry").msgclass + TestMessage::MapStringEnumEntry = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestMessage.MapStringEnumEntry").msgclass + TestMessage::MapStringInt32Entry = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestMessage.MapStringInt32Entry").msgclass + TestMessage::MapStringBoolEntry = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestMessage.MapStringBoolEntry").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 diff --git a/src/Makefile.am b/src/Makefile.am index 3a469fd7..6fd8bd2b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -458,6 +458,7 @@ protobuf_test_SOURCES = \ google/protobuf/compiler/java/java_plugin_unittest.cc \ google/protobuf/compiler/java/java_doc_comment_unittest.cc \ google/protobuf/compiler/python/python_plugin_unittest.cc \ + google/protobuf/compiler/ruby/ruby_generator_unittest.cc \ $(COMMON_TEST_SOURCES) nodist_protobuf_test_SOURCES = $(protoc_outputs) diff --git a/src/google/protobuf/compiler/ruby/ruby_generator_unittest.cc b/src/google/protobuf/compiler/ruby/ruby_generator_unittest.cc new file mode 100644 index 00000000..971fb739 --- /dev/null +++ b/src/google/protobuf/compiler/ruby/ruby_generator_unittest.cc @@ -0,0 +1,116 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2014 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include + +#include +#include +#include +#include + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace python { +namespace { + +class TestGenerator : public CodeGenerator { + public: + TestGenerator() {} + ~TestGenerator() {} + + virtual bool Generate(const FileDescriptor* file, + const string& parameter, + GeneratorContext* context, + string* error) const { + TryInsert("test_pb2.py", "imports", context); + TryInsert("test_pb2.py", "module_scope", context); + TryInsert("test_pb2.py", "class_scope:foo.Bar", context); + TryInsert("test_pb2.py", "class_scope:foo.Bar.Baz", context); + return true; + } + + void TryInsert(const string& filename, const string& insertion_point, + GeneratorContext* context) const { + google::protobuf::scoped_ptr output( + context->OpenForInsert(filename, insertion_point)); + io::Printer printer(output.get(), '$'); + printer.Print("// inserted $name$\n", "name", insertion_point); + } +}; + +// This test is a simple golden-file test over the output of the Ruby code +// generator. When we make changes to the Ruby extension and alter the Ruby code +// generator to use those changes, we should (i) manually test the output of the +// code generator with the extension, and (ii) update the golden output above. +// Some day, we may integrate build systems between protoc and the language +// extensions to the point where we can do this test in a more automated way. + +TEST(RubyGeneratorTest, GeneratorTest) { + google::protobuf::compiler::CommandLineInterface cli; + cli.SetInputsAreProtoPathRelative(true); + + ruby::Generator ruby_generator; + cli.RegisterGenerator("--ruby_out", &ruby_generator, ""); + + string path_arg = "-I" + TestSourceDir() + "/ruby/tests"; + string ruby_out = "--ruby_out=" + TestTempDir(); + const char* argv[] = { + "protoc", + path_arg.c_str(), + ruby_out.c_str(), + "generated_code.proto", + }; + + EXPECT_EQ(0, cli.Run(4, argv)); + + string output; + GOOGLE_CHECK_OK(File::GetContents(TestTempDir() + "/generated_code.rb", + &output, + true)); + + string expected_output; + GOOGLE_CHECK_OK(File::GetContents( + TestSourceDir() + "/ruby/tests/generated_code.rb", + &expected_output, + true)); + + EXPECT_EQ(expected_output, output); +} + +} // namespace +} // namespace python +} // namespace compiler +} // namespace protobuf +} // namespace google -- cgit v1.2.3 From e1b7d38d9ad5dfd3719b1e9b1d588e08aba1afe8 Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Wed, 14 Jan 2015 17:14:05 -0800 Subject: Addressed code-review comments. --- ruby/ext/google/protobuf_c/defs.c | 14 +++--- ruby/ext/google/protobuf_c/encode_decode.c | 29 +++++++---- ruby/ext/google/protobuf_c/protobuf.h | 2 +- ruby/ext/google/protobuf_c/storage.c | 78 +++++++++++++++--------------- ruby/tests/basic.rb | 7 ++- 5 files changed, 71 insertions(+), 59 deletions(-) (limited to 'ruby') diff --git a/ruby/ext/google/protobuf_c/defs.c b/ruby/ext/google/protobuf_c/defs.c index 371939ae..446a5586 100644 --- a/ruby/ext/google/protobuf_c/defs.c +++ b/ruby/ext/google/protobuf_c/defs.c @@ -287,7 +287,7 @@ void Descriptor_register(VALUE module) { 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, "oneofs", Descriptor_oneofs, 0); + 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); @@ -409,23 +409,23 @@ VALUE Descriptor_add_oneof(VALUE _self, VALUE obj) { /* * call-seq: - * Descriptor.oneofs => list of OneofDescriptors + * Descriptor.each_oneof(&block) => nil * - * Returns a list of OneofDescriptors that are part of this message type. + * Invokes the given block for each oneof in this message type, passing the + * corresponding OneofDescriptor. */ -VALUE Descriptor_oneofs(VALUE _self) { +VALUE Descriptor_each_oneof(VALUE _self) { DEFINE_SELF(Descriptor, self, _self); - VALUE ret = rb_ary_new(); 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_ary_push(ret, obj); + rb_yield(obj); } - return ret; + return Qnil; } /* diff --git a/ruby/ext/google/protobuf_c/encode_decode.c b/ruby/ext/google/protobuf_c/encode_decode.c index 26a038b6..5e53b694 100644 --- a/ruby/ext/google/protobuf_c/encode_decode.c +++ b/ruby/ext/google/protobuf_c/encode_decode.c @@ -60,10 +60,10 @@ static const void *newsubmsghandlerdata(upb_handlers* h, uint32_t ofs, } typedef struct { - size_t ofs; // union data slot - size_t case_ofs; // oneof_case field - uint32_t tag; // tag number to place in data slot - const upb_msgdef *md; // msgdef, for oneof submessage handler + 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, @@ -73,7 +73,12 @@ static const void *newoneofhandlerdata(upb_handlers *h, oneof_handlerdata_t *hd = ALLOC(oneof_handlerdata_t); hd->ofs = ofs; hd->case_ofs = case_ofs; - hd->tag = upb_fielddef_number(f); + // 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. + hd->oneof_case_num = upb_fielddef_number(f); if (upb_fielddef_type(f) == UPB_TYPE_MESSAGE) { hd->md = upb_fielddef_msgsubdef(f); } else { @@ -294,7 +299,8 @@ static map_handlerdata_t* new_map_handlerdata( 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->tag; \ + DEREF(closure, oneofdata->case_ofs, uint32_t) = \ + oneofdata->oneof_case_num; \ DEREF(closure, oneofdata->ofs, ctype) = val; \ return true; \ } @@ -317,7 +323,8 @@ static void *oneofstr_handler(void *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->tag; + DEREF(msg, oneofdata->case_ofs, uint32_t) = + oneofdata->oneof_case_num; DEREF(msg, oneofdata->ofs, VALUE) = str; return (void*)str; } @@ -329,7 +336,8 @@ static void *oneofbytes_handler(void *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->tag; + DEREF(msg, oneofdata->case_ofs, uint32_t) = + oneofdata->oneof_case_num; DEREF(msg, oneofdata->ofs, VALUE) = str; return (void*)str; } @@ -340,13 +348,14 @@ static void *oneofsubmsg_handler(void *closure, MessageHeader* msg = closure; const oneof_handlerdata_t *oneofdata = hd; uint32_t oldcase = DEREF(msg, oneofdata->case_ofs, uint32_t); - DEREF(msg, oneofdata->case_ofs, uint32_t) = oneofdata->tag; + DEREF(msg, oneofdata->case_ofs, uint32_t) = + oneofdata->oneof_case_num; VALUE subdesc = get_def_obj((void*)oneofdata->md); VALUE subklass = Descriptor_msgclass(subdesc); - if (oldcase != oneofdata->tag || + if (oldcase != oneofdata->oneof_case_num || DEREF(msg, oneofdata->ofs, VALUE) == Qnil) { DEREF(msg, oneofdata->ofs, VALUE) = rb_class_new_instance(0, NULL, subklass); diff --git a/ruby/ext/google/protobuf_c/protobuf.h b/ruby/ext/google/protobuf_c/protobuf.h index 84f318b3..5b94ae26 100644 --- a/ruby/ext/google/protobuf_c/protobuf.h +++ b/ruby/ext/google/protobuf_c/protobuf.h @@ -190,7 +190,7 @@ 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_oneofs(VALUE _self); +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; diff --git a/ruby/ext/google/protobuf_c/storage.c b/ruby/ext/google/protobuf_c/storage.c index d526679a..adb74394 100644 --- a/ruby/ext/google/protobuf_c/storage.c +++ b/ruby/ext/google/protobuf_c/storage.c @@ -473,13 +473,26 @@ 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->fields[upb_fielddef_index(field)].offset; - uint32_t* oneof_case = (uint32_t *)(((uint8_t *)storage) + - layout->fields[upb_fielddef_index(field)].case_offset); + 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)) { @@ -552,10 +565,8 @@ void layout_set(MessageLayout* layout, void* storage, const upb_fielddef* field, VALUE val) { - void* memory = ((uint8_t *)storage) + - layout->fields[upb_fielddef_index(field)].offset; - uint32_t* oneof_case = (uint32_t *)(((uint8_t *)storage) + - layout->fields[upb_fielddef_index(field)].case_offset); + 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) { @@ -592,10 +603,8 @@ void layout_init(MessageLayout* layout, !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->fields[upb_fielddef_index(field)].offset; - uint32_t* oneof_case = (uint32_t *)(((uint8_t *)storage) + - layout->fields[upb_fielddef_index(field)].case_offset); + void* memory = slot_memory(layout, storage, field); + uint32_t* oneof_case = slot_oneof_case(layout, storage, field); if (upb_fielddef_containingoneof(field)) { memset(memory, 0, NATIVE_SLOT_MAX_SIZE); @@ -652,10 +661,8 @@ void layout_mark(MessageLayout* layout, void* storage) { !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->fields[upb_fielddef_index(field)].offset; - uint32_t* oneof_case = (uint32_t *)(((uint8_t *)storage) + - layout->fields[upb_fielddef_index(field)].case_offset); + 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)) { @@ -675,14 +682,11 @@ void layout_dup(MessageLayout* layout, void* to, void* from) { !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->fields[upb_fielddef_index(field)].offset; - uint32_t* to_oneof_case = (uint32_t *)(((uint8_t *)to) + - layout->fields[upb_fielddef_index(field)].case_offset); - void* from_memory = ((uint8_t *)from) + - layout->fields[upb_fielddef_index(field)].offset; - uint32_t* from_oneof_case = (uint32_t *)(((uint8_t *)from) + - layout->fields[upb_fielddef_index(field)].case_offset); + + 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)) { @@ -705,14 +709,11 @@ void layout_deep_copy(MessageLayout* layout, void* to, void* from) { !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->fields[upb_fielddef_index(field)].offset; - uint32_t* to_oneof_case = (uint32_t *)(((uint8_t *)to) + - layout->fields[upb_fielddef_index(field)].case_offset); - void* from_memory = ((uint8_t *)from) + - layout->fields[upb_fielddef_index(field)].offset; - uint32_t* from_oneof_case = (uint32_t *)(((uint8_t *)from) + - layout->fields[upb_fielddef_index(field)].case_offset); + + 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)) { @@ -737,14 +738,11 @@ VALUE layout_eq(MessageLayout* layout, void* msg1, void* msg2) { !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->fields[upb_fielddef_index(field)].offset; - uint32_t* msg1_oneof_case = (uint32_t *)(((uint8_t *)msg1) + - layout->fields[upb_fielddef_index(field)].case_offset); - void* msg2_memory = ((uint8_t *)msg2) + - layout->fields[upb_fielddef_index(field)].offset; - uint32_t* msg2_oneof_case = (uint32_t *)(((uint8_t *)msg2) + - layout->fields[upb_fielddef_index(field)].case_offset); + + 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 || diff --git a/ruby/tests/basic.rb b/ruby/tests/basic.rb index ceeaa8ad..3e99f3b0 100644 --- a/ruby/tests/basic.rb +++ b/ruby/tests/basic.rb @@ -598,7 +598,12 @@ module BasicTest assert o != nil assert o.class == Google::Protobuf::OneofDescriptor assert o.name == "my_oneof" - assert d.oneofs == [o] + 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"] -- cgit v1.2.3 From e2debef5d8cd084946bd14fecabda5c328382114 Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Wed, 14 Jan 2015 18:02:27 -0800 Subject: Ruby extension: added oneof accessor. --- ruby/ext/google/protobuf_c/encode_decode.c | 3 +- ruby/ext/google/protobuf_c/message.c | 45 ++++++++++++++++++++++++++++++ ruby/tests/basic.rb | 6 ++++ 3 files changed, 53 insertions(+), 1 deletion(-) (limited to 'ruby') diff --git a/ruby/ext/google/protobuf_c/encode_decode.c b/ruby/ext/google/protobuf_c/encode_decode.c index 5e53b694..0db86209 100644 --- a/ruby/ext/google/protobuf_c/encode_decode.c +++ b/ruby/ext/google/protobuf_c/encode_decode.c @@ -77,7 +77,8 @@ static const void *newoneofhandlerdata(upb_handlers *h, // 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. + // 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); diff --git a/ruby/ext/google/protobuf_c/message.c b/ruby/ext/google/protobuf_c/message.c index ee8881d4..d0b09014 100644 --- a/ruby/ext/google/protobuf_c/message.c +++ b/ruby/ext/google/protobuf_c/message.c @@ -70,6 +70,36 @@ 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)); + + // oneof_case == 0 indicates no field set. + if (oneof_case == 0) { + 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 +112,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 +138,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/tests/basic.rb b/ruby/tests/basic.rb index 3e99f3b0..1b7508bb 100644 --- a/ruby/tests/basic.rb +++ b/ruby/tests/basic.rb @@ -615,30 +615,35 @@ module BasicTest 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 @@ -669,6 +674,7 @@ module BasicTest d5.a = nil assert d5.a == nil assert OneofMessage.encode(d5) == '' + assert d5.my_oneof == nil end def test_enum_field -- cgit v1.2.3 From 9de35e742167b1dd2941cefab6e99239207f8e2c Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Mon, 26 Jan 2015 11:23:19 -0800 Subject: Addressed code-review comments. --- ruby/ext/google/protobuf_c/defs.c | 4 ++-- ruby/ext/google/protobuf_c/encode_decode.c | 6 +++--- ruby/ext/google/protobuf_c/storage.c | 9 +++++++-- 3 files changed, 12 insertions(+), 7 deletions(-) (limited to 'ruby') diff --git a/ruby/ext/google/protobuf_c/defs.c b/ruby/ext/google/protobuf_c/defs.c index 446a5586..b39c27f4 100644 --- a/ruby/ext/google/protobuf_c/defs.c +++ b/ruby/ext/google/protobuf_c/defs.c @@ -58,11 +58,11 @@ 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) { diff --git a/ruby/ext/google/protobuf_c/encode_decode.c b/ruby/ext/google/protobuf_c/encode_decode.c index 0db86209..3a441be3 100644 --- a/ruby/ext/google/protobuf_c/encode_decode.c +++ b/ruby/ext/google/protobuf_c/encode_decode.c @@ -558,11 +558,11 @@ static void add_handlers_for_message(const void *closure, upb_handlers *h) { const upb_fielddef *f = upb_msg_iter_field(&i); size_t offset = desc->layout->fields[upb_fielddef_index(f)].offset + sizeof(MessageHeader); - size_t oneof_case_offset = - desc->layout->fields[upb_fielddef_index(f)].case_offset + - sizeof(MessageHeader); if (upb_fielddef_containingoneof(f)) { + size_t oneof_case_offset = + desc->layout->fields[upb_fielddef_index(f)].case_offset + + sizeof(MessageHeader); add_handlers_for_oneof_field(h, f, offset, oneof_case_offset); } else if (is_map_field(f)) { add_handlers_for_mapfield(h, f, offset, desc); diff --git a/ruby/ext/google/protobuf_c/storage.c b/ruby/ext/google/protobuf_c/storage.c index adb74394..fac06e0a 100644 --- a/ruby/ext/google/protobuf_c/storage.c +++ b/ruby/ext/google/protobuf_c/storage.c @@ -366,6 +366,11 @@ 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); @@ -391,7 +396,7 @@ MessageLayout* create_layout(const upb_msgdef* msgdef) { field_size = native_slot_size(upb_fielddef_type(field)); } // Align current offset up to |size| granularity. - off = (off + field_size - 1) & ~(field_size - 1); + 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; @@ -413,7 +418,7 @@ MessageLayout* create_layout(const upb_msgdef* msgdef) { // all fields. size_t field_size = NATIVE_SLOT_MAX_SIZE; // Align the offset. - off = (off + field_size - 1) & ~(field_size - 1); + 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); -- cgit v1.2.3 From 07b8b0f28d1ad88c7c1c64e8707dab8537313c26 Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Mon, 26 Jan 2015 13:52:51 -0800 Subject: Addressed code-review comments. --- ruby/ext/google/protobuf_c/encode_decode.c | 13 ++++++++----- ruby/ext/google/protobuf_c/storage.c | 25 ++++++++++++++++++------- 2 files changed, 26 insertions(+), 12 deletions(-) (limited to 'ruby') diff --git a/ruby/ext/google/protobuf_c/encode_decode.c b/ruby/ext/google/protobuf_c/encode_decode.c index 3a441be3..b9ecd456 100644 --- a/ruby/ext/google/protobuf_c/encode_decode.c +++ b/ruby/ext/google/protobuf_c/encode_decode.c @@ -349,8 +349,6 @@ static void *oneofsubmsg_handler(void *closure, MessageHeader* msg = closure; const oneof_handlerdata_t *oneofdata = hd; uint32_t oldcase = DEREF(msg, oneofdata->case_ofs, uint32_t); - DEREF(msg, oneofdata->case_ofs, uint32_t) = - oneofdata->oneof_case_num; VALUE subdesc = get_def_obj((void*)oneofdata->md); @@ -361,6 +359,11 @@ static void *oneofsubmsg_handler(void *closure, DEREF(msg, oneofdata->ofs, VALUE) = rb_class_new_instance(0, NULL, subklass); } + // Set the oneof case *after* allocating the new class instance -- see comment + // in layout_set() as to why. There are subtle interactions with possible GC + // points and oneof field type transitions. + DEREF(msg, oneofdata->case_ofs, uint32_t) = + oneofdata->oneof_case_num; VALUE submsg_rb = DEREF(msg, oneofdata->ofs, VALUE); MessageHeader* submsg; @@ -965,11 +968,11 @@ static void putmsg(VALUE msg_rb, const Descriptor* desc, uint32_t offset = desc->layout->fields[upb_fielddef_index(f)].offset + sizeof(MessageHeader); - uint32_t oneof_case_offset = - desc->layout->fields[upb_fielddef_index(f)].case_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) != diff --git a/ruby/ext/google/protobuf_c/storage.c b/ruby/ext/google/protobuf_c/storage.c index fac06e0a..662101f8 100644 --- a/ruby/ext/google/protobuf_c/storage.c +++ b/ruby/ext/google/protobuf_c/storage.c @@ -579,15 +579,26 @@ void layout_set(MessageLayout* layout, *oneof_case = 0; memset(memory, 0, NATIVE_SLOT_MAX_SIZE); } else { - // Set the oneof case *first* in case a GC is triggered during - // native_slot_set(): layout_mark() depends on oneof_case to know whether - // the slot may be a Ruby VALUE and so we need that lifetime to start - // before we could possibly stick a VALUE in it. - *oneof_case = upb_fielddef_number(field); - // We just overwrite the value directly if we changed oneof cases: - // native_slot_set() does not depend on the old value in memory. + // 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). + // + // native_slot_set() has two parts: (i) conversion of some sort, and (ii) + // setting the in-memory content to the new value. It guarantees that all + // calls to the Ruby VM are completed before the memory slot is altered. + // + // 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. Because + // we are guaranteed that no Ruby VM calls occur after native_slot_set() + // alters the memory slot and before it returns, we set the oneof case + // immediately after native_slot_set() returns. native_slot_set(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); -- cgit v1.2.3 From eb33f9d3d6f1f78ee70f4bea4326adaf035e1e67 Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Mon, 2 Feb 2015 13:03:08 -0800 Subject: Updated based on code-review comments. --- ruby/ext/google/protobuf_c/encode_decode.c | 9 ++++-- ruby/ext/google/protobuf_c/protobuf.h | 14 ++++++++++ ruby/ext/google/protobuf_c/storage.c | 44 +++++++++++++++++++++--------- 3 files changed, 51 insertions(+), 16 deletions(-) (limited to 'ruby') diff --git a/ruby/ext/google/protobuf_c/encode_decode.c b/ruby/ext/google/protobuf_c/encode_decode.c index b9ecd456..0630f567 100644 --- a/ruby/ext/google/protobuf_c/encode_decode.c +++ b/ruby/ext/google/protobuf_c/encode_decode.c @@ -359,9 +359,12 @@ static void *oneofsubmsg_handler(void *closure, DEREF(msg, oneofdata->ofs, VALUE) = rb_class_new_instance(0, NULL, subklass); } - // Set the oneof case *after* allocating the new class instance -- see comment - // in layout_set() as to why. There are subtle interactions with possible GC - // points and oneof field type transitions. + // 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; diff --git a/ruby/ext/google/protobuf_c/protobuf.h b/ruby/ext/google/protobuf_c/protobuf.h index 5b94ae26..d8a327aa 100644 --- a/ruby/ext/google/protobuf_c/protobuf.h +++ b/ruby/ext/google/protobuf_c/protobuf.h @@ -292,6 +292,15 @@ 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); @@ -313,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); diff --git a/ruby/ext/google/protobuf_c/storage.c b/ruby/ext/google/protobuf_c/storage.c index 662101f8..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, @@ -408,6 +423,13 @@ MessageLayout* create_layout(const upb_msgdef* msgdef) { // 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); @@ -576,7 +598,7 @@ void layout_set(MessageLayout* layout, if (upb_fielddef_containingoneof(field)) { if (val == Qnil) { // Assigning nil to a oneof field clears the oneof completely. - *oneof_case = 0; + *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 @@ -587,18 +609,14 @@ void layout_set(MessageLayout* layout, // what it thinks is a primitive field but is actually a VALUE for the new // field type). // - // native_slot_set() has two parts: (i) conversion of some sort, and (ii) - // setting the in-memory content to the new value. It guarantees that all - // calls to the Ruby VM are completed before the memory slot is altered. - // // 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. Because - // we are guaranteed that no Ruby VM calls occur after native_slot_set() - // alters the memory slot and before it returns, we set the oneof case - // immediately after native_slot_set() returns. - native_slot_set(upb_fielddef_type(field), field_type_class(field), - memory, val); - *oneof_case = upb_fielddef_number(field); + // 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); @@ -624,7 +642,7 @@ void layout_init(MessageLayout* layout, if (upb_fielddef_containingoneof(field)) { memset(memory, 0, NATIVE_SLOT_MAX_SIZE); - *oneof_case = 0; + *oneof_case = ONEOF_CASE_NONE; } else if (is_map_field(field)) { VALUE map = Qnil; -- cgit v1.2.3 From a3953da536751f2a77de0553b566bde5e8b5aa1e Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Mon, 2 Feb 2015 13:16:57 -0800 Subject: Updated based on code-review comments. --- ruby/ext/google/protobuf_c/message.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'ruby') diff --git a/ruby/ext/google/protobuf_c/message.c b/ruby/ext/google/protobuf_c/message.c index d0b09014..7e58a617 100644 --- a/ruby/ext/google/protobuf_c/message.c +++ b/ruby/ext/google/protobuf_c/message.c @@ -88,8 +88,7 @@ static VALUE which_oneof_field(MessageHeader* self, const upb_oneofdef* o) { fields[upb_fielddef_index(first_field)].case_offset; uint32_t oneof_case = *((uint32_t*)(Message_data(self) + case_ofs)); - // oneof_case == 0 indicates no field set. - if (oneof_case == 0) { + if (oneof_case == ONEOF_CASE_NONE) { return Qnil; } -- cgit v1.2.3 From a50759254fa9beb1369c2cc527ae576993a63be8 Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Mon, 2 Feb 2015 15:07:34 -0800 Subject: Updated to latest upb and added test for JSON map operation. --- ruby/ext/google/protobuf_c/upb.c | 629 ++++++++++++++++++++++++++++++++------- ruby/ext/google/protobuf_c/upb.h | 27 +- ruby/tests/basic.rb | 8 + 3 files changed, 560 insertions(+), 104 deletions(-) (limited to 'ruby') diff --git a/ruby/ext/google/protobuf_c/upb.c b/ruby/ext/google/protobuf_c/upb.c index c223c568..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; } @@ -1242,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; } @@ -3773,12 +3793,16 @@ char *upb_strdup(const char *s) { } char *upb_strdup2(const char *s, size_t len) { + // Prevent overflow errors. + if (len == SIZE_MAX) return NULL; // Always null-terminate, even if binary data; but don't rely on the input to // have a null-terminating byte since it may be a raw binary buffer. size_t n = len + 1; char *p = malloc(n); - if (p) memcpy(p, s, len); - p[len] = 0; + if (p) { + memcpy(p, s, len); + p[len] = 0; + } return p; } @@ -9503,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)) { @@ -9517,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: { @@ -9574,6 +9604,7 @@ static bool end_number(upb_json_parser *p, const char *ptr) { } multipart_end(p); + return true; err: @@ -9592,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; } @@ -9608,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) { @@ -9685,6 +9719,7 @@ static bool end_stringval(upb_json_parser *p) { } multipart_end(p); + return ok; } @@ -9693,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) { @@ -9760,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; @@ -9774,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); + } } @@ -9804,11 +10008,11 @@ static void end_object(upb_json_parser *p) { // final state once, when the closing '"' is seen. -#line 905 "upb/json/parser.rl" +#line 1085 "upb/json/parser.rl" -#line 817 "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, @@ -9959,7 +10163,7 @@ static const int json_en_value_machine = 27; static const int json_en_main = 1; -#line 908 "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) { @@ -9979,7 +10183,7 @@ size_t parse(void *closure, const void *hd, const char *buf, size_t size, capture_resume(parser, buf); -#line 988 "upb/json/parser.c" +#line 1168 "upb/json/parser.c" { int _klen; unsigned int _trans; @@ -10054,118 +10258,118 @@ _match: switch ( *_acts++ ) { case 0: -#line 820 "upb/json/parser.rl" +#line 1000 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; case 1: -#line 821 "upb/json/parser.rl" +#line 1001 "upb/json/parser.rl" { p--; {stack[top++] = cs; cs = 10; goto _again;} } break; case 2: -#line 825 "upb/json/parser.rl" +#line 1005 "upb/json/parser.rl" { start_text(parser, p); } break; case 3: -#line 826 "upb/json/parser.rl" +#line 1006 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_text(parser, p)); } break; case 4: -#line 832 "upb/json/parser.rl" +#line 1012 "upb/json/parser.rl" { start_hex(parser); } break; case 5: -#line 833 "upb/json/parser.rl" +#line 1013 "upb/json/parser.rl" { hexdigit(parser, p); } break; case 6: -#line 834 "upb/json/parser.rl" +#line 1014 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_hex(parser)); } break; case 7: -#line 840 "upb/json/parser.rl" +#line 1020 "upb/json/parser.rl" { CHECK_RETURN_TOP(escape(parser, p)); } break; case 8: -#line 846 "upb/json/parser.rl" +#line 1026 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; case 9: -#line 849 "upb/json/parser.rl" +#line 1029 "upb/json/parser.rl" { {stack[top++] = cs; cs = 19; goto _again;} } break; case 10: -#line 851 "upb/json/parser.rl" +#line 1031 "upb/json/parser.rl" { p--; {stack[top++] = cs; cs = 27; goto _again;} } break; case 11: -#line 856 "upb/json/parser.rl" +#line 1036 "upb/json/parser.rl" { start_member(parser); } break; case 12: -#line 857 "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 860 "upb/json/parser.rl" - { clear_member(parser); } +#line 1040 "upb/json/parser.rl" + { end_member(parser); } break; case 14: -#line 866 "upb/json/parser.rl" +#line 1046 "upb/json/parser.rl" { start_object(parser); } break; case 15: -#line 869 "upb/json/parser.rl" +#line 1049 "upb/json/parser.rl" { end_object(parser); } break; case 16: -#line 875 "upb/json/parser.rl" +#line 1055 "upb/json/parser.rl" { CHECK_RETURN_TOP(start_array(parser)); } break; case 17: -#line 879 "upb/json/parser.rl" +#line 1059 "upb/json/parser.rl" { end_array(parser); } break; case 18: -#line 884 "upb/json/parser.rl" +#line 1064 "upb/json/parser.rl" { start_number(parser, p); } break; case 19: -#line 885 "upb/json/parser.rl" +#line 1065 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_number(parser, p)); } break; case 20: -#line 887 "upb/json/parser.rl" +#line 1067 "upb/json/parser.rl" { CHECK_RETURN_TOP(start_stringval(parser)); } break; case 21: -#line 888 "upb/json/parser.rl" +#line 1068 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_stringval(parser)); } break; case 22: -#line 890 "upb/json/parser.rl" +#line 1070 "upb/json/parser.rl" { CHECK_RETURN_TOP(parser_putbool(parser, true)); } break; case 23: -#line 892 "upb/json/parser.rl" +#line 1072 "upb/json/parser.rl" { CHECK_RETURN_TOP(parser_putbool(parser, false)); } break; case 24: -#line 894 "upb/json/parser.rl" +#line 1074 "upb/json/parser.rl" { /* null value */ } break; case 25: -#line 896 "upb/json/parser.rl" +#line 1076 "upb/json/parser.rl" { CHECK_RETURN_TOP(start_subobject(parser)); } break; case 26: -#line 897 "upb/json/parser.rl" +#line 1077 "upb/json/parser.rl" { end_subobject(parser); } break; case 27: -#line 902 "upb/json/parser.rl" +#line 1082 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; -#line 1174 "upb/json/parser.c" +#line 1354 "upb/json/parser.c" } } @@ -10178,7 +10382,7 @@ _again: _out: {} } -#line 927 "upb/json/parser.rl" +#line 1107 "upb/json/parser.rl" if (p != pe) { upb_status_seterrf(parser->status, "Parse error at %s\n", p); @@ -10222,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 1236 "upb/json/parser.c" +#line 1418 "upb/json/parser.c" { cs = json_start; top = 0; } -#line 975 "upb/json/parser.rl" +#line 1157 "upb/json/parser.rl" p->current_state = cs; p->parser_top = top; accumulate_clear(p); @@ -10434,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); @@ -10449,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; @@ -10474,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; } @@ -10503,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; } @@ -10542,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); @@ -10656,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) { @@ -10673,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_field_iter i; - upb_msg_field_begin(&i, upb_handlers_msgdef(h)); + 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); } @@ -10714,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 572f6ad1..8f6d3643 100644 --- a/ruby/ext/google/protobuf_c/upb.h +++ b/ruby/ext/google/protobuf_c/upb.h @@ -1466,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. @@ -1690,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); @@ -2078,6 +2080,10 @@ UPB_INLINE upb_oneofdef *upb_msgdef_ntoo_mutable(upb_msgdef *m, void upb_msgdef_setmapentry(upb_msgdef *m, bool map_entry); bool upb_msgdef_mapentry(const upb_msgdef *m); +// 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); @@ -2577,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); } @@ -7822,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/tests/basic.rb b/ruby/tests/basic.rb index 1b7508bb..ca4c4ebe 100644 --- a/ruby/tests/basic.rb +++ b/ruby/tests/basic.rb @@ -991,5 +991,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 -- cgit v1.2.3 From ee5f6e9a35bec5c4e57a8da5d0c8357633200eb8 Mon Sep 17 00:00:00 2001 From: Isaiah Peng Date: Tue, 3 Feb 2015 16:45:12 +0100 Subject: add #to_ary to RepeatedField --- ruby/ext/google/protobuf_c/repeated_field.c | 59 ++++++++++++++--------------- ruby/tests/basic.rb | 3 +- 2 files changed, 30 insertions(+), 32 deletions(-) (limited to 'ruby') 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 @@ -316,6 +316,29 @@ VALUE RepeatedField_deep_copy(VALUE _self) { return new_rptfield; } +/* + * 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 @@ -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/tests/basic.rb b/ruby/tests/basic.rb index 92655033..d190f0af 100644 --- a/ruby/tests/basic.rb +++ b/ruby/tests/basic.rb @@ -226,7 +226,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 = [] -- cgit v1.2.3 From 06bf6308eae55571c36a580a55dab71856041de1 Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Thu, 5 Feb 2015 14:58:57 -0800 Subject: README.md update for Ruby gem. --- ruby/README.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'ruby') diff --git a/ruby/README.md b/ruby/README.md index c966a103..88e9c0e1 100644 --- a/ruby/README.md +++ b/ruby/README.md @@ -18,13 +18,7 @@ To build this Ruby extension, you will need: * 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 +26,7 @@ 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`. -- cgit v1.2.3 From a2bea0a0012b4adbc50c47246d968a85cb88cec2 Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Thu, 12 Feb 2015 16:08:01 -0800 Subject: Properly support maps in Ruby protoc and another bugfix. Previously, we supported map fields in the Ruby DSL. However, we never connected the final link in the chain and generated `map` DSL commands for map fields in `.proto` files. My apologies -- I had been testing with the DSL directly so I missed this. Also fixed a handlerdata-setup-infinite-loop when a map value field's type is its containing message. --- ruby/ext/google/protobuf_c/encode_decode.c | 23 +++---- ruby/tests/generated_code.rb | 70 +++----------------- .../protobuf/compiler/ruby/ruby_generator.cc | 75 +++++++++++++++++----- 3 files changed, 79 insertions(+), 89 deletions(-) (limited to 'ruby') diff --git a/ruby/ext/google/protobuf_c/encode_decode.c b/ruby/ext/google/protobuf_c/encode_decode.c index 0630f567..256fc829 100644 --- a/ruby/ext/google/protobuf_c/encode_decode.c +++ b/ruby/ext/google/protobuf_c/encode_decode.c @@ -208,7 +208,7 @@ typedef struct { size_t ofs; upb_fieldtype_t key_field_type; upb_fieldtype_t value_field_type; - VALUE value_field_typeclass; + const upb_def* value_field_subdef; } map_handlerdata_t; // Temporary frame for map parsing: at the beginning of a map entry message, a @@ -248,8 +248,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); @@ -280,17 +287,7 @@ 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; } diff --git a/ruby/tests/generated_code.rb b/ruby/tests/generated_code.rb index db762ad9..5a685433 100644 --- a/ruby/tests/generated_code.rb +++ b/ruby/tests/generated_code.rb @@ -27,16 +27,16 @@ Google::Protobuf::DescriptorPool.generated_pool.build do repeated :repeated_bytes, :string, 29 repeated :repeated_enum, :enum, 30, "A.B.C.TestEnum" repeated :repeated_msg, :message, 31, "A.B.C.TestMessage" - repeated :map_int32_string, :message, 61, "A.B.C.TestMessage.MapInt32StringEntry" - repeated :map_int64_string, :message, 62, "A.B.C.TestMessage.MapInt64StringEntry" - repeated :map_uint32_string, :message, 63, "A.B.C.TestMessage.MapUint32StringEntry" - repeated :map_uint64_string, :message, 64, "A.B.C.TestMessage.MapUint64StringEntry" - repeated :map_bool_string, :message, 65, "A.B.C.TestMessage.MapBoolStringEntry" - repeated :map_string_string, :message, 66, "A.B.C.TestMessage.MapStringStringEntry" - repeated :map_string_msg, :message, 67, "A.B.C.TestMessage.MapStringMsgEntry" - repeated :map_string_enum, :message, 68, "A.B.C.TestMessage.MapStringEnumEntry" - repeated :map_string_int32, :message, 69, "A.B.C.TestMessage.MapStringInt32Entry" - repeated :map_string_bool, :message, 70, "A.B.C.TestMessage.MapStringBoolEntry" + 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 @@ -52,46 +52,6 @@ Google::Protobuf::DescriptorPool.generated_pool.build do optional :oneof_msg, :message, 51, "A.B.C.TestMessage" end end - add_message "A.B.C.TestMessage.MapInt32StringEntry" do - optional :key, :int32, 1 - optional :value, :string, 2 - end - add_message "A.B.C.TestMessage.MapInt64StringEntry" do - optional :key, :int64, 1 - optional :value, :string, 2 - end - add_message "A.B.C.TestMessage.MapUint32StringEntry" do - optional :key, :uint32, 1 - optional :value, :string, 2 - end - add_message "A.B.C.TestMessage.MapUint64StringEntry" do - optional :key, :uint64, 1 - optional :value, :string, 2 - end - add_message "A.B.C.TestMessage.MapBoolStringEntry" do - optional :key, :bool, 1 - optional :value, :string, 2 - end - add_message "A.B.C.TestMessage.MapStringStringEntry" do - optional :key, :string, 1 - optional :value, :string, 2 - end - add_message "A.B.C.TestMessage.MapStringMsgEntry" do - optional :key, :string, 1 - optional :value, :message, 2, "A.B.C.TestMessage" - end - add_message "A.B.C.TestMessage.MapStringEnumEntry" do - optional :key, :string, 1 - optional :value, :enum, 2, "A.B.C.TestEnum" - end - add_message "A.B.C.TestMessage.MapStringInt32Entry" do - optional :key, :string, 1 - optional :value, :int32, 2 - end - add_message "A.B.C.TestMessage.MapStringBoolEntry" do - optional :key, :string, 1 - optional :value, :bool, 2 - end add_message "A.B.C.TestMessage.NestedMessage" do optional :foo, :int32, 1 end @@ -107,16 +67,6 @@ module A module B module C TestMessage = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestMessage").msgclass - TestMessage::MapInt32StringEntry = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestMessage.MapInt32StringEntry").msgclass - TestMessage::MapInt64StringEntry = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestMessage.MapInt64StringEntry").msgclass - TestMessage::MapUint32StringEntry = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestMessage.MapUint32StringEntry").msgclass - TestMessage::MapUint64StringEntry = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestMessage.MapUint64StringEntry").msgclass - TestMessage::MapBoolStringEntry = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestMessage.MapBoolStringEntry").msgclass - TestMessage::MapStringStringEntry = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestMessage.MapStringStringEntry").msgclass - TestMessage::MapStringMsgEntry = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestMessage.MapStringMsgEntry").msgclass - TestMessage::MapStringEnumEntry = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestMessage.MapStringEnumEntry").msgclass - TestMessage::MapStringInt32Entry = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestMessage.MapStringInt32Entry").msgclass - TestMessage::MapStringBoolEntry = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestMessage.MapStringBoolEntry").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 diff --git a/src/google/protobuf/compiler/ruby/ruby_generator.cc b/src/google/protobuf/compiler/ruby/ruby_generator.cc index be59fafd..88292891 100644 --- a/src/google/protobuf/compiler/ruby/ruby_generator.cc +++ b/src/google/protobuf/compiler/ruby/ruby_generator.cc @@ -102,24 +102,53 @@ std::string TypeName(const google::protobuf::FieldDescriptor* field) { void GenerateField(const google::protobuf::FieldDescriptor* field, google::protobuf::io::Printer* printer) { - printer->Print( - "$label$ :$name$, ", - "label", LabelForField(field), - "name", field->name()); - printer->Print( - ":$type$, $number$", - "type", TypeName(field), - "number", IntToString(field->number())); - if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { - printer->Print( - ", \"$subtype$\"\n", - "subtype", field->message_type()->full_name()); - } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) { + + if (field->is_map()) { + const FieldDescriptor* key_field = + field->message_type()->FindFieldByNumber(1); + const FieldDescriptor* value_field = + field->message_type()->FindFieldByNumber(2); + printer->Print( - ", \"$subtype$\"\n", - "subtype", field->enum_type()->full_name()); + "map :$name$, :$key_type$, :$value_type$, $number$", + "name", field->name(), + "key_type", TypeName(key_field), + "value_type", TypeName(value_field), + "number", IntToString(field->number())); + + if (value_field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + printer->Print( + ", \"$subtype$\"\n", + "subtype", value_field->message_type()->full_name()); + } else if (value_field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) { + printer->Print( + ", \"$subtype$\"\n", + "subtype", value_field->enum_type()->full_name()); + } else { + printer->Print("\n"); + } } else { - printer->Print("\n"); + + printer->Print( + "$label$ :$name$, ", + "label", LabelForField(field), + "name", field->name()); + printer->Print( + ":$type$, $number$", + "type", TypeName(field), + "number", IntToString(field->number())); + + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + printer->Print( + ", \"$subtype$\"\n", + "subtype", field->message_type()->full_name()); + } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) { + printer->Print( + ", \"$subtype$\"\n", + "subtype", field->enum_type()->full_name()); + } else { + printer->Print("\n"); + } } } @@ -141,6 +170,13 @@ void GenerateOneof(const google::protobuf::OneofDescriptor* oneof, void GenerateMessage(const google::protobuf::Descriptor* message, google::protobuf::io::Printer* printer) { + + // Don't generate MapEntry messages -- we use the Ruby extension's native + // support for map fields instead. + if (message->options().map_entry()) { + return; + } + printer->Print( "add_message \"$name$\" do\n", "name", message->full_name()); @@ -213,6 +249,13 @@ void GenerateMessageAssignment( const std::string& prefix, const google::protobuf::Descriptor* message, google::protobuf::io::Printer* printer) { + + // Don't generate MapEntry messages -- we use the Ruby extension's native + // support for map fields instead. + if (message->options().map_entry()) { + return; + } + printer->Print( "$prefix$$name$ = ", "prefix", prefix, -- cgit v1.2.3 From 315b93fdccd329a8492e6df8e06c7c305a678a5e Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Fri, 13 Feb 2015 14:32:09 -0800 Subject: Addressed code-review comment. --- ruby/ext/google/protobuf_c/encode_decode.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'ruby') diff --git a/ruby/ext/google/protobuf_c/encode_decode.c b/ruby/ext/google/protobuf_c/encode_decode.c index 256fc829..5730504d 100644 --- a/ruby/ext/google/protobuf_c/encode_decode.c +++ b/ruby/ext/google/protobuf_c/encode_decode.c @@ -208,6 +208,10 @@ typedef struct { size_t ofs; upb_fieldtype_t key_field_type; upb_fieldtype_t value_field_type; + + // 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; -- cgit v1.2.3 From 4502626fa7a58e7c2a7d0c829a158d87021818b3 Mon Sep 17 00:00:00 2001 From: Isaiah Peng Date: Sat, 14 Feb 2015 22:08:01 +0100 Subject: Google::Protobuf::Map#inspect should be consistent with Hash#inspect --- ruby/ext/google/protobuf_c/map.c | 2 +- ruby/tests/basic.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'ruby') 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/tests/basic.rb b/ruby/tests/basic.rb index 321b1d34..5df1528f 100644 --- a/ruby/tests/basic.rb +++ b/ruby/tests/basic.rb @@ -391,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] -- cgit v1.2.3 From 6ad8f547fee97798c11b37f5e887d02b6f3c8a2a Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Fri, 20 Feb 2015 17:49:14 -0800 Subject: Updated Ruby README. Change-Id: I8c3717f549c9b4e9d07c77ec5875c9cd62b296ac --- ruby/README.md | 1 - 1 file changed, 1 deletion(-) (limited to 'ruby') diff --git a/ruby/README.md b/ruby/README.md index 88e9c0e1..59d5ace8 100644 --- a/ruby/README.md +++ b/ruby/README.md @@ -16,7 +16,6 @@ To build this Ruby extension, you will need: * Bundler * Ruby development headers * a C compiler -* the upb submodule First, install the required Ruby gems: -- cgit v1.2.3 From 11ad1bd277237907a2b8683f7a8b4a92f1d1163b Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Fri, 20 Feb 2015 18:06:31 -0800 Subject: Update Ruby gem version to 3.0.0.alpha.2.0. This update conforms to our two-numbers-after-alpha scheme that allows us to bump the last number if we need to re-upload a gem. (Rubygems does not allow re-use of a version number once a gem is uploaded.) Change-Id: Ia8e7c129d19800afd66f8052785cf5a00462c7ba --- ruby/google-protobuf.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'ruby') diff --git a/ruby/google-protobuf.gemspec b/ruby/google-protobuf.gemspec index 7bfa533c..e294751f 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.2.0" s.licenses = ["BSD"] s.summary = "Protocol Buffers" s.description = "Protocol Buffers are Google's data interchange format." -- cgit v1.2.3 From 21fb217e6ae4c28e20f91b93d25f030f0bba237f Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Mon, 23 Feb 2015 12:27:52 -0800 Subject: Updated Ruby README with more details on getting started. Change-Id: I54df314660cdb861ad8c4da75a08d4cb97faf638 --- ruby/README.md | 47 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) (limited to 'ruby') diff --git a/ruby/README.md b/ruby/README.md index 59d5ace8..84f4a775 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: -- cgit v1.2.3 From 40f2df3c16b4a54f673108cad92f0da0be6efc21 Mon Sep 17 00:00:00 2001 From: Jisi Liu Date: Sat, 28 Feb 2015 15:01:24 -0800 Subject: Bump the version number to 3.0.0-alpha-3-pre Change-Id: I33479e529b060e4fed532a827a386d3baecc835e --- configure.ac | 2 +- java/pom.xml | 4 ++-- javanano/pom.xml | 4 ++-- python/setup.py | 2 +- ruby/google-protobuf.gemspec | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) (limited to 'ruby') diff --git a/configure.ac b/configure.ac index b5eb9399..14351a8b 100644 --- a/configure.ac +++ b/configure.ac @@ -12,7 +12,7 @@ AC_PREREQ(2.59) # In the SVN trunk, the version should always be the next anticipated release # version with the "-pre" suffix. (We used to use "-SNAPSHOT" but this pushed # the size of one file name in the dist tarfile over the 99-char limit.) -AC_INIT([Protocol Buffers],[3.0.0-alpha-2],[protobuf@googlegroups.com],[protobuf]) +AC_INIT([Protocol Buffers],[3.0.0-alpha-3-pre],[protobuf@googlegroups.com],[protobuf]) AM_MAINTAINER_MODE([enable]) diff --git a/java/pom.xml b/java/pom.xml index 75d4c7f6..2d1c05a0 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -10,7 +10,7 @@ com.google.protobuf protobuf-java - 3.0.0-alpha-2 + 3.0.0-alpha-3-pre bundle Protocol Buffer Java API @@ -152,7 +152,7 @@ https://developers.google.com/protocol-buffers/ com.google.protobuf - com.google.protobuf;version=3.0.0-alpha-2 + com.google.protobuf;version=3.0.0-alpha-3-pre diff --git a/javanano/pom.xml b/javanano/pom.xml index 50056cd3..53dd6665 100644 --- a/javanano/pom.xml +++ b/javanano/pom.xml @@ -10,7 +10,7 @@ com.google.protobuf.nano protobuf-javanano - 3.0.0-alpha-2 + 3.0.0-alpha-3-pre bundle Protocol Buffer JavaNano API @@ -156,7 +156,7 @@ https://developers.google.com/protocol-buffers/ com.google.protobuf - com.google.protobuf;version=3.0.0-alpha-2 + com.google.protobuf;version=3.0.0-alpha-3-pre diff --git a/python/setup.py b/python/setup.py index c6ff7454..b97fdaea 100755 --- a/python/setup.py +++ b/python/setup.py @@ -150,7 +150,7 @@ if __name__ == '__main__': os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] = 'cpp' setup(name = 'protobuf', - version = '3.0.0-alpha-2', + version = '3.0.0-alpha-3-pre', packages = [ 'google' ], namespace_packages = [ 'google' ], google_test_dir = "google/protobuf/internal", diff --git a/ruby/google-protobuf.gemspec b/ruby/google-protobuf.gemspec index e294751f..01d27636 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.0" + s.version = "3.0.0.alpha.3.pre" s.licenses = ["BSD"] s.summary = "Protocol Buffers" s.description = "Protocol Buffers are Google's data interchange format." -- cgit v1.2.3 From dfdec3b654cbf403197214f9354d9a2aff4d77d1 Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Tue, 3 Mar 2015 10:55:55 -0800 Subject: Updated Ruby gem version and added note to ruby/README.md regarding version number scheme. Change-Id: Idb29077c153530de78ce28c094442aa8f51ddd25 --- ruby/README.md | 24 ++++++++++++++++++++++++ ruby/google-protobuf.gemspec | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) (limited to 'ruby') diff --git a/ruby/README.md b/ruby/README.md index 84f4a775..8331d286 100644 --- a/ruby/README.md +++ b/ruby/README.md @@ -72,3 +72,27 @@ Then build the 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/google-protobuf.gemspec b/ruby/google-protobuf.gemspec index 01d27636..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.3.pre" + 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." -- cgit v1.2.3 From 27e2b57830c328b83286e055752bf92790587953 Mon Sep 17 00:00:00 2001 From: Isaiah Peng Date: Wed, 24 Dec 2014 15:48:41 +0100 Subject: add jruby support by protobuf-java reflection API --- ruby/.gitignore | 6 + ruby/Gemfile | 3 + ruby/Gemfile.lock | 25 + ruby/README.md | 17 +- ruby/Rakefile | 26 +- ruby/google-protobuf.gemspec | 27 +- ruby/lib/google/protobuf.rb | 7 +- ruby/pom.xml | 84 +++ .../com/google/protobuf/jruby/RubyBuilder.java | 167 +++++ .../com/google/protobuf/jruby/RubyDescriptor.java | 267 +++++++ .../google/protobuf/jruby/RubyDescriptorPool.java | 169 +++++ .../java/com/google/protobuf/jruby/RubyEnum.java | 86 +++ .../protobuf/jruby/RubyEnumBuilderContext.java | 82 +++ .../google/protobuf/jruby/RubyEnumDescriptor.java | 185 +++++ .../google/protobuf/jruby/RubyFieldDescriptor.java | 248 +++++++ .../java/com/google/protobuf/jruby/RubyMap.java | 434 ++++++++++++ .../com/google/protobuf/jruby/RubyMessage.java | 744 ++++++++++++++++++++ .../protobuf/jruby/RubyMessageBuilderContext.java | 217 ++++++ .../protobuf/jruby/RubyOneofBuilderContext.java | 84 +++ .../google/protobuf/jruby/RubyOneofDescriptor.java | 124 ++++ .../com/google/protobuf/jruby/RubyProtobuf.java | 118 ++++ .../google/protobuf/jruby/RubyRepeatedField.java | 391 +++++++++++ .../google/protobuf/jruby/SentinelOuterClass.java | 776 +++++++++++++++++++++ .../main/java/com/google/protobuf/jruby/Utils.java | 300 ++++++++ ruby/src/main/java/google/ProtobufJavaService.java | 60 ++ ruby/src/main/sentinel.proto | 15 + ruby/tests/basic.rb | 2 + ruby/tests/stress.rb | 2 +- 28 files changed, 4641 insertions(+), 25 deletions(-) create mode 100644 ruby/.gitignore create mode 100644 ruby/Gemfile create mode 100644 ruby/Gemfile.lock create mode 100644 ruby/pom.xml create mode 100644 ruby/src/main/java/com/google/protobuf/jruby/RubyBuilder.java create mode 100644 ruby/src/main/java/com/google/protobuf/jruby/RubyDescriptor.java create mode 100644 ruby/src/main/java/com/google/protobuf/jruby/RubyDescriptorPool.java create mode 100644 ruby/src/main/java/com/google/protobuf/jruby/RubyEnum.java create mode 100644 ruby/src/main/java/com/google/protobuf/jruby/RubyEnumBuilderContext.java create mode 100644 ruby/src/main/java/com/google/protobuf/jruby/RubyEnumDescriptor.java create mode 100644 ruby/src/main/java/com/google/protobuf/jruby/RubyFieldDescriptor.java create mode 100644 ruby/src/main/java/com/google/protobuf/jruby/RubyMap.java create mode 100644 ruby/src/main/java/com/google/protobuf/jruby/RubyMessage.java create mode 100644 ruby/src/main/java/com/google/protobuf/jruby/RubyMessageBuilderContext.java create mode 100644 ruby/src/main/java/com/google/protobuf/jruby/RubyOneofBuilderContext.java create mode 100644 ruby/src/main/java/com/google/protobuf/jruby/RubyOneofDescriptor.java create mode 100644 ruby/src/main/java/com/google/protobuf/jruby/RubyProtobuf.java create mode 100644 ruby/src/main/java/com/google/protobuf/jruby/RubyRepeatedField.java create mode 100644 ruby/src/main/java/com/google/protobuf/jruby/SentinelOuterClass.java create mode 100644 ruby/src/main/java/com/google/protobuf/jruby/Utils.java create mode 100644 ruby/src/main/java/google/ProtobufJavaService.java create mode 100644 ruby/src/main/sentinel.proto (limited to 'ruby') diff --git a/ruby/.gitignore b/ruby/.gitignore new file mode 100644 index 00000000..80c978f2 --- /dev/null +++ b/ruby/.gitignore @@ -0,0 +1,6 @@ +*.bundle +tags +.idea/ +lib/google/protobuf_java.jar +protobuf-jruby.iml +target/ diff --git a/ruby/Gemfile b/ruby/Gemfile new file mode 100644 index 00000000..fa75df15 --- /dev/null +++ b/ruby/Gemfile @@ -0,0 +1,3 @@ +source 'https://rubygems.org' + +gemspec diff --git a/ruby/Gemfile.lock b/ruby/Gemfile.lock new file mode 100644 index 00000000..89deb47d --- /dev/null +++ b/ruby/Gemfile.lock @@ -0,0 +1,25 @@ +PATH + remote: . + specs: + google-protobuf (3.0.0.alpha.2) + +GEM + remote: https://rubygems.org/ + specs: + power_assert (0.2.2) + rake (10.4.2) + rake-compiler (0.9.5) + rake + rubygems-tasks (0.2.4) + test-unit (3.0.9) + power_assert + +PLATFORMS + java + ruby + +DEPENDENCIES + google-protobuf! + rake-compiler + rubygems-tasks + test-unit diff --git a/ruby/README.md b/ruby/README.md index 8331d286..d2fa76ab 100644 --- a/ruby/README.md +++ b/ruby/README.md @@ -60,15 +60,28 @@ To build this Ruby extension, you will need: * Ruby development headers * a C compiler -First, install the required Ruby gems: +To Build the JRuby extension, you will need: - $ sudo gem install bundler rake rake-compiler rspec rubygems-tasks +* Maven +* The latest version of the protobuf java library +* Install JRuby via rbenv or RVM + +First switch to the desired platform with rbenv or RVM. + +Then install the required Ruby gems: + + $ gem install bundler + $ bundle Then build the Gem: $ rake gem $ gem install pkg/protobuf-$VERSION.gem +To run the specs: + + $ rake test + This gem includes the upb parsing and serialization library as a single-file amalgamation. It is up-to-date with upb git commit `535bc2fe2f2b467f59347ffc9449e11e47791257`. diff --git a/ruby/Rakefile b/ruby/Rakefile index ae7d8059..7c1d8495 100644 --- a/ruby/Rakefile +++ b/ruby/Rakefile @@ -1,20 +1,32 @@ -require "rake/extensiontask" +require "rubygems" +require "rubygems/package_task" +require "rake/extensiontask" unless RUBY_PLATFORM == "java" require "rake/testtask" spec = Gem::Specification.load("google-protobuf.gemspec") -Rake::ExtensionTask.new("protobuf_c", spec) do |ext| - ext.ext_dir = "ext/google/protobuf_c" - ext.lib_dir = "lib/google" -end +if RUBY_PLATFORM == "java" + task :clean do + system("mvn clean") + end -Rake::TestTask.new(:test => :build) do |t| - t.test_files = FileList["tests/*.rb"] + task :compile do + system("mvn package") + end +else + Rake::ExtensionTask.new("protobuf_c", spec) do |ext| + ext.ext_dir = "ext/google/protobuf_c" + ext.lib_dir = "lib/google" + end end Gem::PackageTask.new(spec) do |pkg| end +Rake::TestTask.new(:test => :build) do |t| + t.test_files = FileList["tests/*.rb"] +end + task :build => [:clean, :compile] task :default => [:build] diff --git a/ruby/google-protobuf.gemspec b/ruby/google-protobuf.gemspec index 371009bb..4c9449d2 100644 --- a/ruby/google-protobuf.gemspec +++ b/ruby/google-protobuf.gemspec @@ -1,10 +1,3 @@ -class << Gem::Specification - def find_c_source(dir) - `cd #{dir}; git ls-files "*.c" "*.h" extconf.rb Makefile`.split - .map{|f| "#{dir}/#{f}"} - end -end - Gem::Specification.new do |s| s.name = "google-protobuf" s.version = "3.0.0.alpha.3.0.pre" @@ -14,11 +7,17 @@ Gem::Specification.new do |s| s.authors = ["Protobuf Authors"] s.email = "protobuf@googlegroups.com" s.require_paths = ["lib"] - s.extensions = ["ext/google/protobuf_c/extconf.rb"] - s.files = ["lib/google/protobuf.rb"] + - # extension C source - find_c_source("ext/google/protobuf_c") - s.test_files = ["tests/basic.rb", - "tests/stress.rb", - "tests/generated_code_test.rb"] + s.files = ["lib/google/protobuf.rb"] + unless RUBY_PLATFORM == "java" + s.files += `git ls-files "*.c" "*.h" extconf.rb Makefile`.split + s.extensions= ["ext/google/protobuf_c/extconf.rb"] + else + s.files += ["lib/google/protobuf_java.jar"] + end + s.test_files = ["tests/basic.rb", + "tests/stress.rb", + "tests/generated_code_test.rb"] + s.add_development_dependency "rake-compiler" + s.add_development_dependency "test-unit" + s.add_development_dependency "rubygems-tasks" end diff --git a/ruby/lib/google/protobuf.rb b/ruby/lib/google/protobuf.rb index 74053332..75869dd8 100644 --- a/ruby/lib/google/protobuf.rb +++ b/ruby/lib/google/protobuf.rb @@ -28,4 +28,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -require 'google/protobuf_c' +if RUBY_PLATFORM == "java" + require 'json' + require 'google/protobuf_java' +else + require 'google/protobuf_c' +end diff --git a/ruby/pom.xml b/ruby/pom.xml new file mode 100644 index 00000000..1630fe84 --- /dev/null +++ b/ruby/pom.xml @@ -0,0 +1,84 @@ + + + 4.0.0 + + com.google + google + 1 + + + com.google.protobuf.jruby + protobuf-jruby + 1.0-SNAPSHOT + Protocol Buffer JRuby native extension + + Protocol Buffers are a way of encoding structured data in an efficient yet + extensible format. + + 2014 + https://developers.google.com/protocol-buffers/ + + + New BSD license + http://www.opensource.org/licenses/bsd-license.php + repo + + + + https://github.com/google/protobuf + + scm:git:https://github.com/google/protobuf.git + + + + + UTF-8 + lib/google + protobuf_java + + + + + org.apache.maven.plugins + maven-assembly-plugin + + ${jar.finalName} + ${ruby.sources} + false + + jar-with-dependencies + + + + + make-assembly + package + + single + + + + + + + + + com.fasterxml.jackson.core + jackson-core + 2.4.3 + + + org.jruby + jruby-complete + 1.7.13 + provided + + + com.google.protobuf + protobuf-java + 3.0.0-pre + + + diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyBuilder.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyBuilder.java new file mode 100644 index 00000000..5addae58 --- /dev/null +++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyBuilder.java @@ -0,0 +1,167 @@ +/* + * Protocol Buffers - Google's data interchange format + * Copyright 2014 Google Inc. All rights reserved. + * https://developers.google.com/protocol-buffers/ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.google.protobuf.jruby; + +import org.jruby.*; +import org.jruby.anno.JRubyClass; +import org.jruby.anno.JRubyMethod; +import org.jruby.runtime.*; +import org.jruby.runtime.builtin.IRubyObject; + +@JRubyClass(name = "Builder") +public class RubyBuilder extends RubyObject { + public static void createRubyBuilder(Ruby runtime) { + RubyModule protobuf = runtime.getClassFromPath("Google::Protobuf"); + RubyClass cBuilder = protobuf.defineClassUnder("Builder", runtime.getObject(), new ObjectAllocator() { + @Override + public IRubyObject allocate(Ruby runtime, RubyClass klazz) { + return new RubyBuilder(runtime, klazz); + } + }); + cBuilder.defineAnnotatedMethods(RubyBuilder.class); + } + + public RubyBuilder(Ruby runtime, RubyClass metaClass) { + super(runtime, metaClass); + this.cDescriptor = (RubyClass) runtime.getClassFromPath("Google::Protobuf::Descriptor"); + this.cEnumDescriptor = (RubyClass) runtime.getClassFromPath("Google::Protobuf::EnumDescriptor"); + this.cMessageBuilderContext = (RubyClass) runtime.getClassFromPath("Google::Protobuf::MessageBuilderContext"); + this.cEnumBuilderContext = (RubyClass) runtime.getClassFromPath("Google::Protobuf::EnumBuilderContext"); + } + + /* + * call-seq: + * Builder.new => builder + * + * Creates a new Builder. A Builder can accumulate a set of new message and enum + * descriptors and atomically register them into a pool in a way that allows for + * (co)recursive type references. + */ + @JRubyMethod + public IRubyObject initialize(ThreadContext context) { + Ruby runtime = context.runtime; + this.pendingList = runtime.newArray(); + return this; + } + + /* + * call-seq: + * Builder.add_message(name, &block) + * + * Creates a new, empty descriptor with the given name, and invokes the block in + * the context of a MessageBuilderContext on that descriptor. The block can then + * call, e.g., MessageBuilderContext#optional and MessageBuilderContext#repeated + * methods to define the message fields. + * + * This is the recommended, idiomatic way to build message definitions. + */ + @JRubyMethod(name = "add_message") + public IRubyObject addMessage(ThreadContext context, IRubyObject name, Block block) { + RubyDescriptor msgdef = (RubyDescriptor) cDescriptor.newInstance(context, Block.NULL_BLOCK); + IRubyObject ctx = cMessageBuilderContext.newInstance(context, msgdef, this, Block.NULL_BLOCK); + msgdef.setName(context, name); + if (block.isGiven()) { + if (block.arity() == Arity.ONE_ARGUMENT) { + block.yield(context, ctx); + } else { + Binding binding = block.getBinding(); + binding.setSelf(ctx); + block.yieldSpecific(context); + } + } + this.pendingList.add(msgdef); + return context.runtime.getNil(); + } + + /* + * call-seq: + * Builder.add_enum(name, &block) + * + * Creates a new, empty enum descriptor with the given name, and invokes the block in + * the context of an EnumBuilderContext on that descriptor. The block can then + * call EnumBuilderContext#add_value to define the enum values. + * + * This is the recommended, idiomatic way to build enum definitions. + */ + @JRubyMethod(name = "add_enum") + public IRubyObject addEnum(ThreadContext context, IRubyObject name, Block block) { + RubyEnumDescriptor enumDef = (RubyEnumDescriptor) cEnumDescriptor.newInstance(context, Block.NULL_BLOCK); + IRubyObject ctx = cEnumBuilderContext.newInstance(context, enumDef, Block.NULL_BLOCK); + enumDef.setName(context, name); + + if (block.isGiven()) { + if (block.arity() == Arity.ONE_ARGUMENT) { + block.yield(context, ctx); + } else { + Binding binding = block.getBinding(); + binding.setSelf(ctx); + block.yieldSpecific(context); + } + } + + this.pendingList.add(enumDef); + return context.runtime.getNil(); + } + + /* + * call-seq: + * Builder.finalize_to_pool(pool) + * + * Adds all accumulated message and enum descriptors created in this builder + * context to the given pool. The operation occurs atomically, and all + * descriptors can refer to each other (including in cycles). This is the only + * way to build (co)recursive message definitions. + * + * This method is usually called automatically by DescriptorPool#build after it + * invokes the given user block in the context of the builder. The user should + * not normally need to call this manually because a Builder is not normally + * created manually. + */ + @JRubyMethod(name = "finalize_to_pool") + public IRubyObject finalizeToPool(ThreadContext context, IRubyObject rbPool) { + RubyDescriptorPool pool = (RubyDescriptorPool) rbPool; + for (int i = 0; i < this.pendingList.size(); i++) { + IRubyObject defRb = this.pendingList.entry(i); + if (defRb instanceof RubyDescriptor) { + pool.addToSymtab(context, (RubyDescriptor) defRb); + } else { + pool.addToSymtab(context, (RubyEnumDescriptor) defRb); + } + } + this.pendingList = context.runtime.newArray(); + return context.runtime.getNil(); + } + + protected RubyArray pendingList; + private RubyClass cDescriptor, cEnumDescriptor, cMessageBuilderContext, cEnumBuilderContext; +} diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyDescriptor.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyDescriptor.java new file mode 100644 index 00000000..51c50be8 --- /dev/null +++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyDescriptor.java @@ -0,0 +1,267 @@ +/* + * Protocol Buffers - Google's data interchange format + * Copyright 2014 Google Inc. All rights reserved. + * https://developers.google.com/protocol-buffers/ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.google.protobuf.jruby; + +import com.google.protobuf.DescriptorProtos; +import com.google.protobuf.Descriptors; +import org.jruby.*; +import org.jruby.anno.JRubyClass; +import org.jruby.anno.JRubyMethod; +import org.jruby.runtime.Block; +import org.jruby.runtime.ObjectAllocator; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; + +import java.util.HashMap; +import java.util.Map; + + +@JRubyClass(name = "Descriptor", include = "Enumerable") +public class RubyDescriptor extends RubyObject { + public static void createRubyDescriptor(Ruby runtime) { + RubyModule protobuf = runtime.getClassFromPath("Google::Protobuf"); + RubyClass cDescriptor = protobuf.defineClassUnder("Descriptor", runtime.getObject(), new ObjectAllocator() { + @Override + public IRubyObject allocate(Ruby runtime, RubyClass klazz) { + return new RubyDescriptor(runtime, klazz); + } + }); + cDescriptor.includeModule(runtime.getEnumerable()); + cDescriptor.defineAnnotatedMethods(RubyDescriptor.class); + } + + public RubyDescriptor(Ruby runtime, RubyClass klazz) { + super(runtime, klazz); + } + + /* + * call-seq: + * Descriptor.new => descriptor + * + * Creates a new, empty, message type descriptor. At a minimum, its name must be + * set before it is added to a pool. It cannot be used to create messages until + * it is added to a pool, after which it becomes immutable (as part of a + * finalization process). + */ + @JRubyMethod + public IRubyObject initialize(ThreadContext context) { + this.builder = DescriptorProtos.DescriptorProto.newBuilder(); + this.fieldDefMap = new HashMap(); + this.oneofDefs = new HashMap(); + return this; + } + + /* + * call-seq: + * Descriptor.name => name + * + * Returns the name of this message type as a fully-qualfied string (e.g., + * My.Package.MessageType). + */ + @JRubyMethod(name = "name") + public IRubyObject getName(ThreadContext context) { + return this.name; + } + + /* + * call-seq: + * Descriptor.name = name + * + * Assigns a name to this message type. The descriptor must not have been added + * to a pool yet. + */ + @JRubyMethod(name = "name=") + public IRubyObject setName(ThreadContext context, IRubyObject name) { + this.name = name; + this.builder.setName(Utils.escapeIdentifier(this.name.asJavaString())); + return context.runtime.getNil(); + } + + /* + * call-seq: + * Descriptor.add_field(field) => nil + * + * Adds the given FieldDescriptor to this message type. The 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. + */ + @JRubyMethod(name = "add_field") + public IRubyObject addField(ThreadContext context, IRubyObject obj) { + RubyFieldDescriptor fieldDef = (RubyFieldDescriptor) obj; + this.fieldDefMap.put(fieldDef.getName(context).asJavaString(), fieldDef); + this.builder.addField(fieldDef.build()); + return context.runtime.getNil(); + } + + /* + * call-seq: + * Descriptor.lookup(name) => FieldDescriptor + * + * Returns the field descriptor for the field with the given name, if present, + * or nil if none. + */ + @JRubyMethod + public IRubyObject lookup(ThreadContext context, IRubyObject fieldName) { + return this.fieldDefMap.get(fieldName.asJavaString()); + } + + /* + * call-seq: + * Descriptor.msgclass => message_klass + * + * Returns the Ruby class created for this message type. Valid only once the + * message type has been added to a pool. + */ + @JRubyMethod + public IRubyObject msgclass(ThreadContext context) { + if (this.klazz == null) { + this.klazz = buildClassFromDescriptor(context); + } + return this.klazz; + } + + /* + * call-seq: + * Descriptor.each(&block) + * + * Iterates over fields in this message type, yielding to the block on each one. + */ + @JRubyMethod + public IRubyObject each(ThreadContext context, Block block) { + for (Map.Entry entry : fieldDefMap.entrySet()) { + block.yield(context, entry.getValue()); + } + return context.runtime.getNil(); + } + + /* + * 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. + */ + @JRubyMethod(name = "add_oneof") + public IRubyObject addOneof(ThreadContext context, IRubyObject obj) { + RubyOneofDescriptor def = (RubyOneofDescriptor) obj; + builder.addOneofDecl(def.build(builder.getOneofDeclCount())); + for (RubyFieldDescriptor fieldDescriptor : def.getFields()) { + addField(context, fieldDescriptor); + } + oneofDefs.put(def.getName(context), def); + return context.runtime.getNil(); + } + + /* + * call-seq: + * Descriptor.each_oneof(&block) => nil + * + * Invokes the given block for each oneof in this message type, passing the + * corresponding OneofDescriptor. + */ + @JRubyMethod(name = "each_oneof") + public IRubyObject eachOneof(ThreadContext context, Block block) { + for (RubyOneofDescriptor oneofDescriptor : oneofDefs.values()) { + block.yieldSpecific(context, oneofDescriptor); + } + return context.runtime.getNil(); + } + + /* + * call-seq: + * Descriptor.lookup_oneof(name) => OneofDescriptor + * + * Returns the oneof descriptor for the oneof with the given name, if present, + * or nil if none. + */ + @JRubyMethod(name = "lookup_oneof") + public IRubyObject lookupOneof(ThreadContext context, IRubyObject name) { + if (name instanceof RubySymbol) { + name = ((RubySymbol) name).id2name(); + } + return oneofDefs.containsKey(name) ? oneofDefs.get(name) : context.runtime.getNil(); + } + + public void setDescriptor(Descriptors.Descriptor descriptor) { + this.descriptor = descriptor; + } + + public Descriptors.Descriptor getDescriptor() { + return this.descriptor; + } + + public DescriptorProtos.DescriptorProto.Builder getBuilder() { + return builder; + } + + public void setMapEntry(boolean isMapEntry) { + this.builder.setOptions(DescriptorProtos.MessageOptions.newBuilder().setMapEntry(isMapEntry)); + } + + private RubyModule buildClassFromDescriptor(ThreadContext context) { + Ruby runtime = context.runtime; + + ObjectAllocator allocator = new ObjectAllocator() { + @Override + public IRubyObject allocate(Ruby runtime, RubyClass klazz) { + return new RubyMessage(runtime, klazz, descriptor); + } + }; + + // rb_define_class_id + RubyClass klass = RubyClass.newClass(runtime, runtime.getObject()); + klass.setAllocator(allocator); + klass.makeMetaClass(runtime.getObject().getMetaClass()); + klass.inherit(runtime.getObject()); + klass.instance_variable_set(runtime.newString(Utils.DESCRIPTOR_INSTANCE_VAR), this); + klass.defineAnnotatedMethods(RubyMessage.class); + return klass; + } + + protected RubyFieldDescriptor lookup(String fieldName) { + return fieldDefMap.get(Utils.unescapeIdentifier(fieldName)); + } + + private IRubyObject name; + private RubyModule klazz; + + private DescriptorProtos.DescriptorProto.Builder builder; + private Descriptors.Descriptor descriptor; + private Map fieldDefMap; + private Map oneofDefs; +} diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyDescriptorPool.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyDescriptorPool.java new file mode 100644 index 00000000..0345cb99 --- /dev/null +++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyDescriptorPool.java @@ -0,0 +1,169 @@ +/* + * Protocol Buffers - Google's data interchange format + * Copyright 2014 Google Inc. All rights reserved. + * https://developers.google.com/protocol-buffers/ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.google.protobuf.jruby; + +import com.google.protobuf.DescriptorProtos; +import com.google.protobuf.Descriptors; +import org.jruby.*; +import org.jruby.anno.JRubyClass; +import org.jruby.anno.JRubyMethod; +import org.jruby.runtime.*; +import org.jruby.runtime.builtin.IRubyObject; + +import java.util.HashMap; +import java.util.Map; + +@JRubyClass(name = "DescriptorPool") +public class RubyDescriptorPool extends RubyObject { + public static void createRubyDescriptorPool(Ruby runtime) { + RubyModule protobuf = runtime.getClassFromPath("Google::Protobuf"); + RubyClass cDescriptorPool = protobuf.defineClassUnder("DescriptorPool", runtime.getObject(), new ObjectAllocator() { + @Override + public IRubyObject allocate(Ruby runtime, RubyClass klazz) { + return new RubyDescriptorPool(runtime, klazz); + } + }); + + cDescriptorPool.defineAnnotatedMethods(RubyDescriptorPool.class); + descriptorPool = (RubyDescriptorPool) cDescriptorPool.newInstance(runtime.getCurrentContext(), Block.NULL_BLOCK); + } + + public RubyDescriptorPool(Ruby ruby, RubyClass klazz) { + super(ruby, klazz); + } + + @JRubyMethod + public IRubyObject initialize(ThreadContext context) { + this.symtab = new HashMap(); + this.cBuilder = (RubyClass) context.runtime.getClassFromPath("Google::Protobuf::Builder"); + this.builder = DescriptorProtos.FileDescriptorProto.newBuilder(); + return this; + } + + @JRubyMethod + public IRubyObject build(ThreadContext context, Block block) { + RubyBuilder ctx = (RubyBuilder) cBuilder.newInstance(context, Block.NULL_BLOCK); + if (block.arity() == Arity.ONE_ARGUMENT) { + block.yield(context, ctx); + } else { + Binding binding = block.getBinding(); + binding.setSelf(ctx); + block.yieldSpecific(context); + } + ctx.finalizeToPool(context, this); + buildFileDescriptor(context); + return context.runtime.getNil(); + } + + @JRubyMethod + public IRubyObject lookup(ThreadContext context, IRubyObject name) { + IRubyObject descriptor = this.symtab.get(name); + if (descriptor == null) { + return context.runtime.getNil(); + } + return descriptor; + } + + /* + * call-seq: + * DescriptorPool.generated_pool => descriptor_pool + * + * Class method that returns the global DescriptorPool. This is a singleton into + * which generated-code message and enum types are registered. The user may also + * register types in this pool for convenience so that they do not have to hold + * a reference to a private pool instance. + */ + @JRubyMethod(meta = true, name = "generated_pool") + public static IRubyObject generatedPool(ThreadContext context, IRubyObject recv) { + return descriptorPool; + } + + protected void addToSymtab(ThreadContext context, RubyDescriptor def) { + symtab.put(def.getName(context), def); + this.builder.addMessageType(def.getBuilder()); + } + + protected void addToSymtab(ThreadContext context, RubyEnumDescriptor def) { + symtab.put(def.getName(context), def); + this.builder.addEnumType(def.getBuilder()); + } + + private void buildFileDescriptor(ThreadContext context) { + Ruby runtime = context.runtime; + try { + this.builder.setSyntax("proto3"); + final Descriptors.FileDescriptor fileDescriptor = Descriptors.FileDescriptor.buildFrom( + this.builder.build(), new Descriptors.FileDescriptor[]{}); + + for (Descriptors.EnumDescriptor enumDescriptor : fileDescriptor.getEnumTypes()) { + String enumName = Utils.unescapeIdentifier(enumDescriptor.getName()); + if (enumDescriptor.findValueByNumber(0) == null) { + throw runtime.newTypeError("Enum definition " + enumName + + " does not contain a value for '0'"); + } + ((RubyEnumDescriptor) symtab.get(runtime.newString(enumName))) + .setDescriptor(enumDescriptor); + } + for (Descriptors.Descriptor descriptor : fileDescriptor.getMessageTypes()) { + RubyDescriptor rubyDescriptor = ((RubyDescriptor) + symtab.get(runtime.newString(Utils.unescapeIdentifier(descriptor.getName())))); + for (Descriptors.FieldDescriptor fieldDescriptor : descriptor.getFields()) { + if (fieldDescriptor.isRequired()) { + throw runtime.newTypeError("Required fields are unsupported in proto3"); + } + RubyFieldDescriptor rubyFieldDescriptor = rubyDescriptor.lookup(fieldDescriptor.getName()); + rubyFieldDescriptor.setFieldDef(fieldDescriptor); + if (fieldDescriptor.getType() == Descriptors.FieldDescriptor.Type.MESSAGE) { + RubyDescriptor subType = (RubyDescriptor) lookup(context, + runtime.newString(Utils.unescapeIdentifier(fieldDescriptor.getMessageType().getName()))); + rubyFieldDescriptor.setSubType(subType); + } + if (fieldDescriptor.getType() == Descriptors.FieldDescriptor.Type.ENUM) { + RubyEnumDescriptor subType = (RubyEnumDescriptor) lookup(context, + runtime.newString(Utils.unescapeIdentifier(fieldDescriptor.getEnumType().getName()))); + rubyFieldDescriptor.setSubType(subType); + } + } + rubyDescriptor.setDescriptor(descriptor); + } + } catch (Descriptors.DescriptorValidationException e) { + throw runtime.newRuntimeError(e.getMessage()); + } + } + + private static RubyDescriptorPool descriptorPool; + + private RubyClass cBuilder; + private Map symtab; + private DescriptorProtos.FileDescriptorProto.Builder builder; +} diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyEnum.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyEnum.java new file mode 100644 index 00000000..929d8699 --- /dev/null +++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyEnum.java @@ -0,0 +1,86 @@ +/* + * Protocol Buffers - Google's data interchange format + * Copyright 2014 Google Inc. All rights reserved. + * https://developers.google.com/protocol-buffers/ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.google.protobuf.jruby; + +import com.google.protobuf.Descriptors; +import org.jruby.RubyModule; +import org.jruby.RubyNumeric; +import org.jruby.anno.JRubyMethod; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; + +public class RubyEnum { + /* + * call-seq: + * Enum.lookup(number) => name + * + * This module method, provided on each generated enum module, looks up an enum + * value by number and returns its name as a Ruby symbol, or nil if not found. + */ + @JRubyMethod(meta = true) + public static IRubyObject lookup(ThreadContext context, IRubyObject recv, IRubyObject number) { + RubyEnumDescriptor rubyEnumDescriptorescriptor = (RubyEnumDescriptor) getDescriptor(context, recv); + Descriptors.EnumDescriptor descriptor = rubyEnumDescriptorescriptor.getDescriptor(); + Descriptors.EnumValueDescriptor value = descriptor.findValueByNumber(RubyNumeric.num2int(number)); + if (value == null) return context.runtime.getNil(); + return context.runtime.newSymbol(value.getName()); + } + + /* + * call-seq: + * Enum.resolve(name) => number + * + * This module method, provided on each generated enum module, looks up an enum + * value by name (as a Ruby symbol) and returns its name, or nil if not found. + */ + @JRubyMethod(meta = true) + public static IRubyObject resolve(ThreadContext context, IRubyObject recv, IRubyObject name) { + RubyEnumDescriptor rubyEnumDescriptorescriptor = (RubyEnumDescriptor) getDescriptor(context, recv); + Descriptors.EnumDescriptor descriptor = rubyEnumDescriptorescriptor.getDescriptor(); + Descriptors.EnumValueDescriptor value = descriptor.findValueByName(name.asJavaString()); + if (value == null) return context.runtime.getNil(); + return context.runtime.newFixnum(value.getNumber()); + } + + /* + * call-seq: + * Enum.descriptor + * + * This module method, provided on each generated enum module, returns the + * EnumDescriptor corresponding to this enum type. + */ + @JRubyMethod(meta = true, name = "descriptor") + public static IRubyObject getDescriptor(ThreadContext context, IRubyObject recv) { + return ((RubyModule) recv).getInstanceVariable(Utils.DESCRIPTOR_INSTANCE_VAR); + } +} diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyEnumBuilderContext.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyEnumBuilderContext.java new file mode 100644 index 00000000..e4cac345 --- /dev/null +++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyEnumBuilderContext.java @@ -0,0 +1,82 @@ +/* + * Protocol Buffers - Google's data interchange format + * Copyright 2014 Google Inc. All rights reserved. + * https://developers.google.com/protocol-buffers/ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.google.protobuf.jruby; + +import org.jruby.Ruby; +import org.jruby.RubyClass; +import org.jruby.RubyModule; +import org.jruby.RubyObject; +import org.jruby.anno.JRubyClass; +import org.jruby.anno.JRubyMethod; +import org.jruby.runtime.ObjectAllocator; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; + +@JRubyClass(name = "EnumBuilderContext") +public class RubyEnumBuilderContext extends RubyObject { + public static void createRubyEnumBuilderContext(Ruby runtime) { + RubyModule protobuf = runtime.getClassFromPath("Google::Protobuf"); + RubyClass cMessageBuilderContext = protobuf.defineClassUnder("EnumBuilderContext", runtime.getObject(), new ObjectAllocator() { + @Override + public IRubyObject allocate(Ruby runtime, RubyClass klazz) { + return new RubyEnumBuilderContext(runtime, klazz); + } + }); + cMessageBuilderContext.defineAnnotatedMethods(RubyEnumBuilderContext.class); + } + + public RubyEnumBuilderContext(Ruby ruby, RubyClass klazz) { + super(ruby, klazz); + } + + @JRubyMethod + public IRubyObject initialize(ThreadContext context, IRubyObject enumDescriptor) { + this.enumDescriptor = (RubyEnumDescriptor) enumDescriptor; + return this; + } + + /* + * call-seq: + * EnumBuilder.add_value(name, number) + * + * Adds the given name => number mapping to the enum type. Name must be a Ruby + * symbol. + */ + @JRubyMethod + public IRubyObject value(ThreadContext context, IRubyObject name, IRubyObject number) { + this.enumDescriptor.addValue(context, name, number); + return context.runtime.getNil(); + } + + private RubyEnumDescriptor enumDescriptor; +} diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyEnumDescriptor.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyEnumDescriptor.java new file mode 100644 index 00000000..4df832d0 --- /dev/null +++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyEnumDescriptor.java @@ -0,0 +1,185 @@ +/* + * Protocol Buffers - Google's data interchange format + * Copyright 2014 Google Inc. All rights reserved. + * https://developers.google.com/protocol-buffers/ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.google.protobuf.jruby; + +import com.google.protobuf.DescriptorProtos; +import com.google.protobuf.Descriptors; +import org.jruby.Ruby; +import org.jruby.RubyClass; +import org.jruby.RubyModule; +import org.jruby.RubyObject; +import org.jruby.RubyNumeric; +import org.jruby.anno.JRubyClass; +import org.jruby.anno.JRubyMethod; +import org.jruby.runtime.Block; +import org.jruby.runtime.ObjectAllocator; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; + +@JRubyClass(name = "EnumDescriptor", include = "Enumerable") +public class RubyEnumDescriptor extends RubyObject { + public static void createRubyEnumDescriptor(Ruby runtime) { + RubyModule mProtobuf = runtime.getClassFromPath("Google::Protobuf"); + RubyClass cEnumDescriptor = mProtobuf.defineClassUnder("EnumDescriptor", runtime.getObject(), new ObjectAllocator() { + @Override + public IRubyObject allocate(Ruby runtime, RubyClass klazz) { + return new RubyEnumDescriptor(runtime, klazz); + } + }); + cEnumDescriptor.includeModule(runtime.getEnumerable()); + cEnumDescriptor.defineAnnotatedMethods(RubyEnumDescriptor.class); + } + + public RubyEnumDescriptor(Ruby runtime, RubyClass klazz) { + super(runtime, klazz); + } + + /* + * call-seq: + * EnumDescriptor.new => enum_descriptor + * + * Creates a new, empty, enum descriptor. Must be added to a pool before the + * enum type can be used. The enum type may only be modified prior to adding to + * a pool. + */ + @JRubyMethod + public IRubyObject initialize(ThreadContext context) { + this.builder = DescriptorProtos.EnumDescriptorProto.newBuilder(); + return this; + } + + /* + * call-seq: + * EnumDescriptor.name => name + * + * Returns the name of this enum type. + */ + @JRubyMethod(name = "name") + public IRubyObject getName(ThreadContext context) { + return this.name; + } + + /* + * call-seq: + * EnumDescriptor.name = name + * + * Sets the name of this enum type. Cannot be called if the enum type has + * already been added to a pool. + */ + @JRubyMethod(name = "name=") + public IRubyObject setName(ThreadContext context, IRubyObject name) { + this.name = name; + this.builder.setName(Utils.escapeIdentifier(name.asJavaString())); + return context.runtime.getNil(); + } + + /* + * call-seq: + * EnumDescriptor.add_value(key, value) + * + * Adds a new key => value mapping to this enum type. Key must be given as a + * Ruby symbol. Cannot be called if the enum type has already been added to a + * pool. Will raise an exception if the key or value is already in use. + */ + @JRubyMethod(name = "add_value") + public IRubyObject addValue(ThreadContext context, IRubyObject name, IRubyObject number) { + DescriptorProtos.EnumValueDescriptorProto.Builder valueBuilder = DescriptorProtos.EnumValueDescriptorProto.newBuilder(); + valueBuilder.setName(name.asJavaString()); + valueBuilder.setNumber(RubyNumeric.num2int(number)); + this.builder.addValue(valueBuilder); + return context.runtime.getNil(); + } + + /* + * call-seq: + * EnumDescriptor.each(&block) + * + * Iterates over key => value mappings in this enum's definition, yielding to + * the block with (key, value) arguments for each one. + */ + @JRubyMethod + public IRubyObject each(ThreadContext context, Block block) { + Ruby runtime = context.runtime; + for (Descriptors.EnumValueDescriptor enumValueDescriptor : descriptor.getValues()) { + block.yield(context, runtime.newArray(runtime.newSymbol(enumValueDescriptor.getName()), + runtime.newFixnum(enumValueDescriptor.getNumber()))); + } + return runtime.getNil(); + } + + /* + * call-seq: + * EnumDescriptor.enummodule => module + * + * Returns the Ruby module corresponding to this enum type. Cannot be called + * until the enum descriptor has been added to a pool. + */ + @JRubyMethod + public IRubyObject enummodule(ThreadContext context) { + if (this.klazz == null) { + this.klazz = buildModuleFromDescriptor(context); + } + return this.klazz; + } + + public void setDescriptor(Descriptors.EnumDescriptor descriptor) { + this.descriptor = descriptor; + } + + public Descriptors.EnumDescriptor getDescriptor() { + return this.descriptor; + } + + public DescriptorProtos.EnumDescriptorProto.Builder getBuilder() { + return this.builder; + } + + private RubyModule buildModuleFromDescriptor(ThreadContext context) { + Ruby runtime = context.runtime; + Utils.checkNameAvailability(context, name.asJavaString()); + + RubyModule enumModule = RubyModule.newModule(runtime); + for (Descriptors.EnumValueDescriptor value : descriptor.getValues()) { + enumModule.defineConstant(value.getName(), runtime.newFixnum(value.getNumber())); + } + + enumModule.instance_variable_set(runtime.newString(Utils.DESCRIPTOR_INSTANCE_VAR), this); + enumModule.defineAnnotatedMethods(RubyEnum.class); + return enumModule; + } + + private IRubyObject name; + private RubyModule klazz; + private Descriptors.EnumDescriptor descriptor; + private DescriptorProtos.EnumDescriptorProto.Builder builder; +} diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyFieldDescriptor.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyFieldDescriptor.java new file mode 100644 index 00000000..38226c4e --- /dev/null +++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyFieldDescriptor.java @@ -0,0 +1,248 @@ +/* + * Protocol Buffers - Google's data interchange format + * Copyright 2014 Google Inc. All rights reserved. + * https://developers.google.com/protocol-buffers/ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.google.protobuf.jruby; + +import com.google.protobuf.DescriptorProtos; +import com.google.protobuf.Descriptors; +import org.jruby.*; +import org.jruby.anno.JRubyClass; +import org.jruby.anno.JRubyMethod; +import org.jruby.runtime.ObjectAllocator; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; + +@JRubyClass(name = "FieldDescriptor") +public class RubyFieldDescriptor extends RubyObject { + public static void createRubyFileDescriptor(Ruby runtime) { + RubyModule mProtobuf = runtime.getClassFromPath("Google::Protobuf"); + RubyClass cFieldDescriptor = mProtobuf.defineClassUnder("FieldDescriptor", runtime.getObject(), new ObjectAllocator() { + @Override + public IRubyObject allocate(Ruby runtime, RubyClass klazz) { + return new RubyFieldDescriptor(runtime, klazz); + } + }); + cFieldDescriptor.defineAnnotatedMethods(RubyFieldDescriptor.class); + } + + public RubyFieldDescriptor(Ruby runtime, RubyClass klazz) { + super(runtime, klazz); + } + + /* + * call-seq: + * FieldDescriptor.new => field + * + * Returns a new field descriptor. Its name, type, etc. must be set before it is + * added to a message type. + */ + @JRubyMethod + public IRubyObject initialize(ThreadContext context) { + builder = DescriptorProtos.FieldDescriptorProto.newBuilder(); + return this; + } + + /* + * call-seq: + * FieldDescriptor.label = label + * + * Sets the label on this field. Cannot be called if field is part of a message + * type already in a pool. + */ + @JRubyMethod(name = "label=") + public IRubyObject setLabel(ThreadContext context, IRubyObject value) { + this.builder.setLabel( + DescriptorProtos.FieldDescriptorProto.Label.valueOf("LABEL_" + value.asJavaString().toUpperCase())); + return context.runtime.getNil(); + } + + /* + * call-seq: + * FieldDescriptor.name => name + * + * Returns the name of this field. + */ + @JRubyMethod(name = "name") + public IRubyObject getName(ThreadContext context) { + return this.name; + } + + @JRubyMethod(name = "subtype") + public IRubyObject getSubType(ThreadContext context) { + return subType; + } + + /* + * call-seq: + * FieldDescriptor.name = name + * + * Sets the name of this field. Cannot be called once the containing message + * type, if any, is added to a pool. + */ + @JRubyMethod(name = "name=") + public IRubyObject setName(ThreadContext context, IRubyObject value) { + String nameStr = value.asJavaString(); + this.name = context.runtime.newString(nameStr); + this.builder.setName(Utils.escapeIdentifier(nameStr)); + return context.runtime.getNil(); + } + + /* + * call-seq: + * FieldDescriptor.type => type + * + * Returns this field's type, as a Ruby symbol, or nil if not yet set. + * + * Valid field types are: + * :int32, :int64, :uint32, :uint64, :float, :double, :bool, :string, + * :bytes, :message. + */ + @JRubyMethod(name = "type") + public IRubyObject getType(ThreadContext context) { + return Utils.fieldTypeToRuby(context, this.builder.getType()); + } + + /* + * call-seq: + * FieldDescriptor.type = type + * + * Sets this field's type. Cannot be called if field is part of a message type + * already in a pool. + */ + @JRubyMethod(name = "type=") + public IRubyObject setType(ThreadContext context, IRubyObject value) { + this.builder.setType(DescriptorProtos.FieldDescriptorProto.Type.valueOf("TYPE_" + value.asJavaString().toUpperCase())); + return context.runtime.getNil(); + } + + /* + * call-seq: + * FieldDescriptor.number = number + * + * Sets the tag number for this field. Cannot be called if field is part of a + * message type already in a pool. + */ + @JRubyMethod(name = "number=") + public IRubyObject setNumber(ThreadContext context, IRubyObject value) { + this.builder.setNumber(RubyNumeric.num2int(value)); + return context.runtime.getNil(); + } + + /* + * call-seq: + * FieldDescriptor.submsg_name = submsg_name + * + * Sets the name of the message or enum type corresponding to this field, if it + * is a message or enum field (respectively). This type name will be resolved + * within the context of the pool to which the containing message type is added. + * Cannot be called on field that are not of message or enum type, or on fields + * that are part of a message type already added to a pool. + */ + @JRubyMethod(name = "submsg_name=") + public IRubyObject setSubmsgName(ThreadContext context, IRubyObject name) { + this.builder.setTypeName("." + Utils.escapeIdentifier(name.asJavaString())); + return context.runtime.getNil(); + } + + /* + * call-seq: + * FieldDescriptor.get(message) => value + * + * Returns the value set for this field on the given message. Raises an + * exception if message is of the wrong type. + */ + @JRubyMethod(name = "get") + public IRubyObject getValue(ThreadContext context, IRubyObject msgRb) { + RubyMessage message = (RubyMessage) msgRb; + if (message.getDescriptor() != fieldDef.getContainingType()) { + throw context.runtime.newTypeError("set method called on wrong message type"); + } + return message.getField(context, fieldDef); + } + + /* + * call-seq: + * FieldDescriptor.set(message, value) + * + * Sets the value corresponding to this field to the given value on the given + * message. Raises an exception if message is of the wrong type. Performs the + * ordinary type-checks for field setting. + */ + @JRubyMethod(name = "set") + public IRubyObject setValue(ThreadContext context, IRubyObject msgRb, IRubyObject value) { + RubyMessage message = (RubyMessage) msgRb; + if (message.getDescriptor() != fieldDef.getContainingType()) { + throw context.runtime.newTypeError("set method called on wrong message type"); + } + message.setField(context, fieldDef, value); + return context.runtime.getNil(); + } + + protected void setSubType(IRubyObject rubyDescriptor) { + this.subType = rubyDescriptor; + } + + protected void setFieldDef(Descriptors.FieldDescriptor fieldDescriptor) { + this.fieldDef = fieldDescriptor; + } + + protected void setOneofName(IRubyObject name) { + oneofName = name; + } + + protected void setOneofIndex(int index) { + hasOneofIndex = true; + oneofIndex = index; + } + + protected IRubyObject getOneofName() { + return oneofName; + } + + protected Descriptors.FieldDescriptor getFieldDef() { + return fieldDef; + } + + protected DescriptorProtos.FieldDescriptorProto build() { + if (hasOneofIndex) + builder.setOneofIndex(oneofIndex); + return this.builder.build(); + } + + private DescriptorProtos.FieldDescriptorProto.Builder builder; + private IRubyObject name; + private IRubyObject subType; + private IRubyObject oneofName; + private Descriptors.FieldDescriptor fieldDef; + private int oneofIndex; + private boolean hasOneofIndex = false; +} diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyMap.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyMap.java new file mode 100644 index 00000000..b25dc6e1 --- /dev/null +++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyMap.java @@ -0,0 +1,434 @@ +/* + * Protocol Buffers - Google's data interchange format + * Copyright 2014 Google Inc. All rights reserved. + * https://developers.google.com/protocol-buffers/ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.google.protobuf.jruby; + +import com.google.protobuf.Descriptors; +import com.google.protobuf.DynamicMessage; +import com.google.protobuf.MapEntry; +import org.jruby.*; +import org.jruby.anno.JRubyClass; +import org.jruby.anno.JRubyMethod; +import org.jruby.internal.runtime.methods.DynamicMethod; +import org.jruby.runtime.Block; +import org.jruby.runtime.ObjectAllocator; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; +import org.jruby.util.ByteList; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@JRubyClass(name = "Map", include = "Enumerable") +public class RubyMap extends RubyObject { + public static void createRubyMap(Ruby runtime) { + RubyModule protobuf = runtime.getClassFromPath("Google::Protobuf"); + RubyClass cMap = protobuf.defineClassUnder("Map", runtime.getObject(), new ObjectAllocator() { + @Override + public IRubyObject allocate(Ruby ruby, RubyClass rubyClass) { + return new RubyMap(ruby, rubyClass); + } + }); + cMap.includeModule(runtime.getEnumerable()); + cMap.defineAnnotatedMethods(RubyMap.class); + } + + public RubyMap(Ruby ruby, RubyClass rubyClass) { + super(ruby, rubyClass); + } + + /* + * call-seq: + * Map.new(key_type, value_type, value_typeclass = nil, init_hashmap = {}) + * => new map + * + * Allocates a new Map container. This constructor may be called with 2, 3, or 4 + * arguments. The first two arguments are always present and are symbols (taking + * on the same values as field-type symbols in message descriptors) that + * indicate the type of the map key and value fields. + * + * The supported key types are: :int32, :int64, :uint32, :uint64, :bool, + * :string, :bytes. + * + * The supported value types are: :int32, :int64, :uint32, :uint64, :bool, + * :string, :bytes, :enum, :message. + * + * The third argument, value_typeclass, must be present if value_type is :enum + * or :message. As in RepeatedField#new, this argument must be a message class + * (for :message) or enum module (for :enum). + * + * The last argument, if present, provides initial content for map. Note that + * this may be an ordinary Ruby hashmap or another Map instance with identical + * key and value types. Also note that this argument may be present whether or + * not value_typeclass is present (and it is unambiguously separate from + * value_typeclass because value_typeclass's presence is strictly determined by + * value_type). The contents of this initial hashmap or Map instance are + * shallow-copied into the new Map: the original map is unmodified, but + * references to underlying objects will be shared if the value type is a + * message type. + */ + + @JRubyMethod(required = 2, optional = 2) + public IRubyObject initialize(ThreadContext context, IRubyObject[] args) { + this.table = new HashMap(); + this.keyType = Utils.rubyToFieldType(args[0]); + this.valueType = Utils.rubyToFieldType(args[1]); + + switch(keyType) { + case INT32: + case INT64: + case UINT32: + case UINT64: + case BOOL: + case STRING: + case BYTES: + // These are OK. + break; + default: + throw context.runtime.newArgumentError("Invalid key type for map."); + } + + int initValueArg = 2; + if (needTypeclass(this.valueType) && args.length > 2) { + this.valueTypeClass = args[2]; + Utils.validateTypeClass(context, this.valueType, this.valueTypeClass); + initValueArg = 3; + } else { + this.valueTypeClass = context.runtime.getNilClass(); + } + + // Table value type is always UINT64: this ensures enough space to store the + // native_slot value. + if (args.length > initValueArg) { + mergeIntoSelf(context, args[initValueArg]); + } + return this; + } + + /* + * call-seq: + * Map.[]=(key, value) => value + * + * Inserts or overwrites the value at the given key with the given new value. + * Throws an exception if the key type is incorrect. Returns the new value that + * was just inserted. + */ + @JRubyMethod(name = "[]=") + public IRubyObject indexSet(ThreadContext context, IRubyObject key, IRubyObject value) { + Utils.checkType(context, keyType, key, (RubyModule) valueTypeClass); + Utils.checkType(context, valueType, value, (RubyModule) valueTypeClass); + IRubyObject symbol; + if (valueType == Descriptors.FieldDescriptor.Type.ENUM && + Utils.isRubyNum(value) && + ! (symbol = RubyEnum.lookup(context, valueTypeClass, value)).isNil()) { + value = symbol; + } + this.table.put(key, value); + return value; + } + + /* + * call-seq: + * Map.[](key) => value + * + * Accesses the element at the given key. Throws an exception if the key type is + * incorrect. Returns nil when the key is not present in the map. + */ + @JRubyMethod(name = "[]") + public IRubyObject index(ThreadContext context, IRubyObject key) { + if (table.containsKey(key)) + return this.table.get(key); + return context.runtime.getNil(); + } + + /* + * call-seq: + * Map.==(other) => boolean + * + * Compares this map to another. Maps are equal if they have identical key sets, + * and for each key, the values in both maps compare equal. Elements are + * compared as per normal Ruby semantics, by calling their :== methods (or + * performing a more efficient comparison for primitive types). + * + * Maps with dissimilar key types or value types/typeclasses are never equal, + * even if value comparison (for example, between integers and floats) would + * have otherwise indicated that every element has equal value. + */ + @JRubyMethod(name = "==") + public IRubyObject eq(ThreadContext context, IRubyObject _other) { + if (_other instanceof RubyHash) + return toHash(context).op_equal(context, _other); + RubyMap other = (RubyMap) _other; + if (this == other) return context.runtime.getTrue(); + if (!typeCompatible(other) || this.table.size() != other.table.size()) + return context.runtime.getFalse(); + for (IRubyObject key : table.keySet()) { + if (! other.table.containsKey(key)) + return context.runtime.getFalse(); + if (! other.table.get(key).equals(table.get(key))) + return context.runtime.getFalse(); + } + return context.runtime.getTrue(); + } + + /* + * call-seq: + * Map.inspect => string + * + * Returns a string representing this map's elements. It will be formatted as + * "{key => value, key => value, ...}", with each key and value string + * representation computed by its own #inspect method. + */ + @JRubyMethod + public IRubyObject inspect() { + return toHash(getRuntime().getCurrentContext()).inspect(); + } + + /* + * call-seq: + * Map.hash => hash_value + * + * Returns a hash value based on this map's contents. + */ + @JRubyMethod + public IRubyObject hash(ThreadContext context) { + try { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + for (IRubyObject key : table.keySet()) { + digest.update((byte) key.hashCode()); + digest.update((byte) table.get(key).hashCode()); + } + return context.runtime.newString(new ByteList(digest.digest())); + } catch (NoSuchAlgorithmException ignore) { + return context.runtime.newFixnum(System.identityHashCode(table)); + } + } + + /* + * call-seq: + * Map.keys => [list_of_keys] + * + * Returns the list of keys contained in the map, in unspecified order. + */ + @JRubyMethod + public IRubyObject keys(ThreadContext context) { + return RubyArray.newArray(context.runtime, table.keySet()); + } + + /* + * call-seq: + * Map.values => [list_of_values] + * + * Returns the list of values contained in the map, in unspecified order. + */ + @JRubyMethod + public IRubyObject values(ThreadContext context) { + return RubyArray.newArray(context.runtime, table.values()); + } + + /* + * call-seq: + * Map.clear + * + * Removes all entries from the map. + */ + @JRubyMethod + public IRubyObject clear(ThreadContext context) { + table.clear(); + return context.runtime.getNil(); + } + + /* + * call-seq: + * Map.each(&block) + * + * Invokes &block on each |key, value| pair in the map, in unspecified order. + * Note that Map also includes Enumerable; map thus acts like a normal Ruby + * sequence. + */ + @JRubyMethod + public IRubyObject each(ThreadContext context, Block block) { + for (IRubyObject key : table.keySet()) { + block.yieldSpecific(context, key, table.get(key)); + } + return context.runtime.getNil(); + } + + /* + * call-seq: + * Map.delete(key) => old_value + * + * Deletes the value at the given key, if any, returning either the old value or + * nil if none was present. Throws an exception if the key is of the wrong type. + */ + @JRubyMethod + public IRubyObject delete(ThreadContext context, IRubyObject key) { + return table.remove(key); + } + + /* + * call-seq: + * Map.has_key?(key) => bool + * + * Returns true if the given key is present in the map. Throws an exception if + * the key has the wrong type. + */ + @JRubyMethod(name = "has_key?") + public IRubyObject hasKey(ThreadContext context, IRubyObject key) { + return this.table.containsKey(key) ? context.runtime.getTrue() : context.runtime.getFalse(); + } + + /* + * call-seq: + * Map.length + * + * Returns the number of entries (key-value pairs) in the map. + */ + @JRubyMethod + public IRubyObject length(ThreadContext context) { + return context.runtime.newFixnum(this.table.size()); + } + + /* + * call-seq: + * Map.dup => new_map + * + * Duplicates this map with a shallow copy. References to all non-primitive + * element objects (e.g., submessages) are shared. + */ + @JRubyMethod + public IRubyObject dup(ThreadContext context) { + RubyMap newMap = newThisType(context); + for (Map.Entry entry : table.entrySet()) { + newMap.table.put(entry.getKey(), entry.getValue()); + } + return newMap; + } + + @JRubyMethod(name = "to_h") + public RubyHash toHash(ThreadContext context) { + return RubyHash.newHash(context.runtime, table, context.runtime.getNil()); + } + + // Used by Google::Protobuf.deep_copy but not exposed directly. + protected IRubyObject deepCopy(ThreadContext context) { + RubyMap newMap = newThisType(context); + switch (valueType) { + case MESSAGE: + for (IRubyObject key : table.keySet()) { + RubyMessage message = (RubyMessage) table.get(key); + newMap.table.put(key.dup(), message.deepCopy(context)); + } + break; + default: + for (IRubyObject key : table.keySet()) { + newMap.table.put(key.dup(), table.get(key).dup()); + } + } + return newMap; + } + + protected List build(ThreadContext context, RubyDescriptor descriptor) { + List list = new ArrayList(); + RubyClass rubyClass = (RubyClass) descriptor.msgclass(context); + Descriptors.FieldDescriptor keyField = descriptor.lookup("key").getFieldDef(); + Descriptors.FieldDescriptor valueField = descriptor.lookup("value").getFieldDef(); + for (IRubyObject key : table.keySet()) { + RubyMessage mapMessage = (RubyMessage) rubyClass.newInstance(context, Block.NULL_BLOCK); + mapMessage.setField(context, keyField, key); + mapMessage.setField(context, valueField, table.get(key)); + list.add(mapMessage.build(context)); + } + return list; + } + + protected RubyMap mergeIntoSelf(final ThreadContext context, IRubyObject hashmap) { + if (hashmap instanceof RubyHash) { + ((RubyHash) hashmap).visitAll(new RubyHash.Visitor() { + @Override + public void visit(IRubyObject key, IRubyObject val) { + indexSet(context, key, val); + } + }); + } else if (hashmap instanceof RubyMap) { + RubyMap other = (RubyMap) hashmap; + if (!typeCompatible(other)) { + throw context.runtime.newTypeError("Attempt to merge Map with mismatching types"); + } + } else { + throw context.runtime.newTypeError("Unknown type merging into Map"); + } + return this; + } + + protected boolean typeCompatible(RubyMap other) { + return this.keyType == other.keyType && + this.valueType == other.valueType && + this.valueTypeClass == other.valueTypeClass; + } + + private RubyMap newThisType(ThreadContext context) { + RubyMap newMap; + if (needTypeclass(valueType)) { + newMap = (RubyMap) metaClass.newInstance(context, + Utils.fieldTypeToRuby(context, keyType), + Utils.fieldTypeToRuby(context, valueType), + valueTypeClass, Block.NULL_BLOCK); + } else { + newMap = (RubyMap) metaClass.newInstance(context, + Utils.fieldTypeToRuby(context, keyType), + Utils.fieldTypeToRuby(context, valueType), + Block.NULL_BLOCK); + } + newMap.table = new HashMap(); + return newMap; + } + + private boolean needTypeclass(Descriptors.FieldDescriptor.Type type) { + switch(type) { + case MESSAGE: + case ENUM: + return true; + default: + return false; + } + } + + private Descriptors.FieldDescriptor.Type keyType; + private Descriptors.FieldDescriptor.Type valueType; + private IRubyObject valueTypeClass; + private Map table; +} diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyMessage.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyMessage.java new file mode 100644 index 00000000..04bc0b76 --- /dev/null +++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyMessage.java @@ -0,0 +1,744 @@ +/* + * Protocol Buffers - Google's data interchange format + * Copyright 2014 Google Inc. All rights reserved. + * https://developers.google.com/protocol-buffers/ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.google.protobuf.jruby; + +import com.google.protobuf.*; +import org.jruby.*; +import org.jruby.anno.JRubyMethod; +import org.jruby.runtime.Block; +import org.jruby.runtime.Helpers; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; +import org.jruby.util.ByteList; + +import java.util.HashMap; +import java.util.Map; + +public class RubyMessage extends RubyObject { + public RubyMessage(Ruby ruby, RubyClass klazz, Descriptors.Descriptor descriptor) { + super(ruby, klazz); + this.descriptor = descriptor; + } + + /* + * call-seq: + * Message.new(kwargs) => new_message + * + * Creates a new instance of the given message class. Keyword arguments may be + * provided with keywords corresponding to field names. + * + * Note that no literal Message class exists. Only concrete classes per message + * type exist, as provided by the #msgclass method on Descriptors after they + * have been added to a pool. The method definitions described here on the + * Message class are provided on each concrete message class. + */ + @JRubyMethod(optional = 1) + public IRubyObject initialize(final ThreadContext context, IRubyObject[] args) { + final Ruby runtime = context.runtime; + this.cRepeatedField = (RubyClass) runtime.getClassFromPath("Google::Protobuf::RepeatedField"); + this.cMap = (RubyClass) runtime.getClassFromPath("Google::Protobuf::Map"); + this.builder = DynamicMessage.newBuilder(this.descriptor); + this.repeatedFields = new HashMap(); + this.maps = new HashMap(); + this.fields = new HashMap(); + this.oneofCases = new HashMap(); + if (args.length == 1) { + if (!(args[0] instanceof RubyHash)) { + throw runtime.newArgumentError("expected Hash arguments."); + } + RubyHash hash = args[0].convertToHash(); + hash.visitAll(new RubyHash.Visitor() { + @Override + public void visit(IRubyObject key, IRubyObject value) { + if (!(key instanceof RubySymbol)) + throw runtime.newTypeError("Expected symbols as hash keys in initialization map."); + final Descriptors.FieldDescriptor fieldDescriptor = findField(context, key); + + if (Utils.isMapEntry(fieldDescriptor)) { + if (!(value instanceof RubyHash)) + throw runtime.newArgumentError("Expected Hash object as initializer value for map field."); + + final RubyMap map = newMapForField(context, fieldDescriptor); + map.mergeIntoSelf(context, value); + maps.put(fieldDescriptor, map); + } else if (fieldDescriptor.isRepeated()) { + if (!(value instanceof RubyArray)) + throw runtime.newTypeError("Expected array as initializer var for repeated field."); + RubyRepeatedField repeatedField = rubyToRepeatedField(context, fieldDescriptor, value); + addRepeatedField(fieldDescriptor, repeatedField); + } else { + Descriptors.OneofDescriptor oneof = fieldDescriptor.getContainingOneof(); + if (oneof != null) { + oneofCases.put(oneof, fieldDescriptor); + } + fields.put(fieldDescriptor, value); + } + + } + }); + } + return this; + } + + /* + * call-seq: + * Message.[]=(index, value) + * + * Sets a field's value by field name. The provided field name should be a + * string. + */ + @JRubyMethod(name = "[]=") + public IRubyObject indexSet(ThreadContext context, IRubyObject fieldName, IRubyObject value) { + Descriptors.FieldDescriptor fieldDescriptor = findField(context, fieldName); + return setField(context, fieldDescriptor, value); + } + + /* + * call-seq: + * Message.[](index) => value + * + * Accesses a field's value by field name. The provided field name should be a + * string. + */ + @JRubyMethod(name = "[]") + public IRubyObject index(ThreadContext context, IRubyObject fieldName) { + Descriptors.FieldDescriptor fieldDescriptor = findField(context, fieldName); + return getField(context, fieldDescriptor); + } + + /* + * call-seq: + * Message.inspect => string + * + * Returns a human-readable string representing this message. It will be + * formatted as "". Each + * field's value is represented according to its own #inspect method. + */ + @JRubyMethod + public IRubyObject inspect() { + String cname = metaClass.getName(); + StringBuilder sb = new StringBuilder("<"); + sb.append(cname); + sb.append(": "); + sb.append(this.layoutInspect()); + sb.append(">"); + + return getRuntime().newString(sb.toString()); + } + + /* + * call-seq: + * Message.hash => hash_value + * + * Returns a hash value that represents this message's field values. + */ + @JRubyMethod + public IRubyObject hash(ThreadContext context) { + int hashCode = System.identityHashCode(this); + return context.runtime.newFixnum(hashCode); + } + + /* + * call-seq: + * Message.==(other) => boolean + * + * Performs a deep comparison of this message with another. Messages are equal + * if they have the same type and if each field is equal according to the :== + * method's semantics (a more efficient comparison may actually be done if the + * field is of a primitive type). + */ + @JRubyMethod(name = "==") + public IRubyObject eq(ThreadContext context, IRubyObject other) { + Ruby runtime = context.runtime; + if (!(other instanceof RubyMessage)) + return runtime.getFalse(); + RubyMessage message = (RubyMessage) other; + if (descriptor != message.descriptor) { + return runtime.getFalse(); + } + + for (Descriptors.FieldDescriptor fdef : descriptor.getFields()) { + IRubyObject thisVal = getField(context, fdef); + IRubyObject thatVal = message.getField(context, fdef); + IRubyObject ret = thisVal.callMethod(context, "==", thatVal); + if (!ret.isTrue()) { + return runtime.getFalse(); + } + } + return runtime.getTrue(); + } + + /* + * call-seq: + * Message.method_missing(*args) + * + * Provides accessors and setters for message fields according to their field + * names. For any field whose name does not conflict with a built-in method, an + * accessor is provided with the same name as the field, and a setter is + * provided with the name of the field plus the '=' suffix. Thus, given a + * message instance 'msg' with field 'foo', the following code is valid: + * + * msg.foo = 42 + * puts msg.foo + */ + @JRubyMethod(name = "method_missing", rest = true) + public IRubyObject methodMissing(ThreadContext context, IRubyObject[] args) { + if (args.length == 1) { + RubyDescriptor rubyDescriptor = (RubyDescriptor) getDescriptor(context, metaClass); + IRubyObject oneofDescriptor = rubyDescriptor.lookupOneof(context, args[0]); + if (oneofDescriptor.isNil()) { + return index(context, args[0]); + } + RubyOneofDescriptor rubyOneofDescriptor = (RubyOneofDescriptor) oneofDescriptor; + Descriptors.FieldDescriptor fieldDescriptor = + oneofCases.get(rubyOneofDescriptor.getOneofDescriptor()); + if (fieldDescriptor == null) + return context.runtime.getNil(); + + return context.runtime.newSymbol(fieldDescriptor.getName()); + } else { + // fieldName is RubySymbol + RubyString field = args[0].asString(); + RubyString equalSign = context.runtime.newString(Utils.EQUAL_SIGN); + if (field.end_with_p(context, equalSign).isTrue()) { + field.chomp_bang(context, equalSign); + } + return indexSet(context, field, args[1]); + } + } + + /** + * call-seq: + * Message.dup => new_message + * Performs a shallow copy of this message and returns the new copy. + */ + @JRubyMethod + public IRubyObject dup(ThreadContext context) { + RubyMessage dup = (RubyMessage) metaClass.newInstance(context, Block.NULL_BLOCK); + IRubyObject value; + for (Descriptors.FieldDescriptor fieldDescriptor : builder.getAllFields().keySet()) { + if (fieldDescriptor.isRepeated()) { + dup.repeatedFields.put(fieldDescriptor, getRepeatedField(context, fieldDescriptor)); + } else if (builder.hasField(fieldDescriptor)) { + dup.fields.put(fieldDescriptor, wrapField(context, fieldDescriptor, builder.getField(fieldDescriptor))); + } + } + for (Descriptors.FieldDescriptor fieldDescriptor : fields.keySet()) { + dup.fields.put(fieldDescriptor, fields.get(fieldDescriptor)); + } + for (Descriptors.FieldDescriptor fieldDescriptor : maps.keySet()) { + dup.maps.put(fieldDescriptor, maps.get(fieldDescriptor)); + } + return dup; + } + + /* + * call-seq: + * Message.descriptor => descriptor + * + * Class method that returns the Descriptor instance corresponding to this + * message class's type. + */ + @JRubyMethod(name = "descriptor", meta = true) + public static IRubyObject getDescriptor(ThreadContext context, IRubyObject recv) { + return ((RubyClass) recv).getInstanceVariable(Utils.DESCRIPTOR_INSTANCE_VAR); + } + + /* + * call-seq: + * MessageClass.encode(msg) => bytes + * + * Encodes the given message object to its serialized form in protocol buffers + * wire format. + */ + @JRubyMethod(meta = true) + public static IRubyObject encode(ThreadContext context, IRubyObject recv, IRubyObject value) { + RubyMessage message = (RubyMessage) value; + return context.runtime.newString(new ByteList(message.build(context).toByteArray())); + } + + /* + * call-seq: + * MessageClass.decode(data) => message + * + * Decodes the given data (as a string containing bytes in protocol buffers wire + * format) under the interpretration given by this message class's definition + * and returns a message object with the corresponding field values. + */ + @JRubyMethod(meta = true) + public static IRubyObject decode(ThreadContext context, IRubyObject recv, IRubyObject data) { + byte[] bin = data.convertToString().getBytes(); + RubyMessage ret = (RubyMessage) ((RubyClass) recv).newInstance(context, Block.NULL_BLOCK); + try { + ret.builder.mergeFrom(bin); + } catch (InvalidProtocolBufferException e) { + throw context.runtime.newRuntimeError(e.getMessage()); + } + return ret; + } + + /* + * call-seq: + * MessageClass.encode_json(msg) => json_string + * + * Encodes the given message object into its serialized JSON representation. + */ + @JRubyMethod(name = "encode_json", meta = true) + public static IRubyObject encodeJson(ThreadContext context, IRubyObject recv, IRubyObject msgRb) { + RubyMessage message = (RubyMessage) msgRb; + return Helpers.invoke(context, message.toHash(context), "to_json"); + } + + /* + * call-seq: + * MessageClass.decode_json(data) => message + * + * Decodes the given data (as a string containing bytes in protocol buffers wire + * format) under the interpretration given by this message class's definition + * and returns a message object with the corresponding field values. + */ + @JRubyMethod(name = "decode_json", meta = true) + public static IRubyObject decodeJson(ThreadContext context, IRubyObject recv, IRubyObject json) { + Ruby runtime = context.runtime; + RubyMessage ret = (RubyMessage) ((RubyClass) recv).newInstance(context, Block.NULL_BLOCK); + RubyModule jsonModule = runtime.getClassFromPath("JSON"); + RubyHash opts = RubyHash.newHash(runtime); + opts.fastASet(runtime.newSymbol("symbolize_names"), runtime.getTrue()); + IRubyObject[] args = new IRubyObject[] { Helpers.invoke(context, jsonModule, "parse", json, opts) }; + ret.initialize(context, args); + return ret; + } + + @JRubyMethod(name = "to_h") + public IRubyObject toHash(ThreadContext context) { + Ruby runtime = context.runtime; + RubyHash ret = RubyHash.newHash(runtime); + for (Descriptors.FieldDescriptor fdef : this.descriptor.getFields()) { + IRubyObject value = getField(context, fdef); + if (value.respondsTo("to_h")) { + value = Helpers.invoke(context, value, "to_h"); + } + ret.fastASet(runtime.newString(fdef.getName()), value); + } + return ret; + } + + protected DynamicMessage build(ThreadContext context) { + return build(context, 0); + } + + protected DynamicMessage build(ThreadContext context, int depth) { + if (depth > SINK_MAXIMUM_NESTING) { + throw context.runtime.newRuntimeError("Maximum recursion depth exceeded during encoding."); + } + for (Descriptors.FieldDescriptor fieldDescriptor : maps.keySet()) { + this.builder.clearField(fieldDescriptor); + RubyDescriptor mapDescriptor = (RubyDescriptor) getDescriptorForField(context, fieldDescriptor); + for (DynamicMessage kv : maps.get(fieldDescriptor).build(context, mapDescriptor)) { + this.builder.addRepeatedField(fieldDescriptor, kv); + } + } + for (Descriptors.FieldDescriptor fieldDescriptor : repeatedFields.keySet()) { + RubyRepeatedField repeatedField = repeatedFields.get(fieldDescriptor); + this.builder.clearField(fieldDescriptor); + for (int i = 0; i < repeatedField.size(); i++) { + Object item = convert(context, fieldDescriptor, repeatedField.get(i), depth); + this.builder.addRepeatedField(fieldDescriptor, item); + } + } + for (Descriptors.FieldDescriptor fieldDescriptor : fields.keySet()) { + IRubyObject value = fields.get(fieldDescriptor); + this.builder.setField(fieldDescriptor, convert(context, fieldDescriptor, value, depth)); + } + return this.builder.build(); + } + + protected Descriptors.Descriptor getDescriptor() { + return this.descriptor; + } + + // Internal use only, called by Google::Protobuf.deep_copy + protected IRubyObject deepCopy(ThreadContext context) { + RubyMessage copy = (RubyMessage) metaClass.newInstance(context, Block.NULL_BLOCK); + for (Descriptors.FieldDescriptor fdef : this.descriptor.getFields()) { + if (fdef.isRepeated()) { + copy.addRepeatedField(fdef, this.getRepeatedField(context, fdef).deepCopy(context)); + } else if (fields.containsKey(fdef)) { + copy.fields.put(fdef, fields.get(fdef)); + } else if (this.builder.hasField(fdef)) { + copy.fields.put(fdef, wrapField(context, fdef, this.builder.getField(fdef))); + } + } + return copy; + } + + private RubyRepeatedField getRepeatedField(ThreadContext context, Descriptors.FieldDescriptor fieldDescriptor) { + if (this.repeatedFields.containsKey(fieldDescriptor)) { + return this.repeatedFields.get(fieldDescriptor); + } + int count = this.builder.getRepeatedFieldCount(fieldDescriptor); + RubyRepeatedField ret = repeatedFieldForFieldDescriptor(context, fieldDescriptor); + for (int i = 0; i < count; i++) { + ret.push(context, wrapField(context, fieldDescriptor, this.builder.getRepeatedField(fieldDescriptor, i))); + } + return ret; + } + + private void addRepeatedField(Descriptors.FieldDescriptor fieldDescriptor, RubyRepeatedField repeatedField) { + this.repeatedFields.put(fieldDescriptor, repeatedField); + } + + private IRubyObject buildFrom(ThreadContext context, DynamicMessage dynamicMessage) { + this.builder.mergeFrom(dynamicMessage); + return this; + } + + private Descriptors.FieldDescriptor findField(ThreadContext context, IRubyObject fieldName) { + String nameStr = fieldName.asJavaString(); + Descriptors.FieldDescriptor ret = this.descriptor.findFieldByName(Utils.escapeIdentifier(nameStr)); + if (ret == null) + throw context.runtime.newArgumentError("field " + fieldName.asJavaString() + " is not found"); + return ret; + } + + private void checkRepeatedFieldType(ThreadContext context, IRubyObject value, + Descriptors.FieldDescriptor fieldDescriptor) { + Ruby runtime = context.runtime; + if (!(value instanceof RubyRepeatedField)) { + throw runtime.newTypeError("Expected repeated field array"); + } + } + + // convert a ruby object to protobuf type, with type check + private Object convert(ThreadContext context, + Descriptors.FieldDescriptor fieldDescriptor, + IRubyObject value, int depth) { + Ruby runtime = context.runtime; + Object val = null; + switch (fieldDescriptor.getType()) { + case INT32: + case INT64: + case UINT32: + case UINT64: + if (!Utils.isRubyNum(value)) { + throw runtime.newTypeError("Expected number type for integral field."); + } + Utils.checkIntTypePrecision(context, fieldDescriptor.getType(), value); + switch (fieldDescriptor.getType()) { + case INT32: + val = RubyNumeric.num2int(value); + break; + case INT64: + val = RubyNumeric.num2long(value); + break; + case UINT32: + val = Utils.num2uint(value); + break; + case UINT64: + val = Utils.num2ulong(context.runtime, value); + break; + default: + break; + } + break; + case FLOAT: + if (!Utils.isRubyNum(value)) + throw runtime.newTypeError("Expected number type for float field."); + val = (float) RubyNumeric.num2dbl(value); + break; + case DOUBLE: + if (!Utils.isRubyNum(value)) + throw runtime.newTypeError("Expected number type for double field."); + val = RubyNumeric.num2dbl(value); + break; + case BOOL: + if (!(value instanceof RubyBoolean)) + throw runtime.newTypeError("Invalid argument for boolean field."); + val = value.isTrue(); + break; + case BYTES: + case STRING: + Utils.validateStringEncoding(context.runtime, fieldDescriptor.getType(), value); + RubyString str = (RubyString) value; + switch (fieldDescriptor.getType()) { + case BYTES: + val = ByteString.copyFrom(str.getBytes()); + break; + case STRING: + val = str.asJavaString(); + break; + default: + break; + } + break; + case MESSAGE: + RubyClass typeClass = (RubyClass) ((RubyDescriptor) getDescriptorForField(context, fieldDescriptor)).msgclass(context); + if (!value.getMetaClass().equals(typeClass)) + throw runtime.newTypeError(value, "Invalid type to assign to submessage field."); + val = ((RubyMessage) value).build(context, depth + 1); + break; + case ENUM: + Descriptors.EnumDescriptor enumDescriptor = fieldDescriptor.getEnumType(); + + if (Utils.isRubyNum(value)) { + val = enumDescriptor.findValueByNumberCreatingIfUnknown(RubyNumeric.num2int(value)); + } else if (value instanceof RubySymbol) { + val = enumDescriptor.findValueByName(value.asJavaString()); + } else { + throw runtime.newTypeError("Expected number or symbol type for enum field."); + } + if (val == null) { + throw runtime.newRangeError("Enum value " + value + " is not found."); + } + break; + default: + break; + } + return val; + } + + private IRubyObject wrapField(ThreadContext context, Descriptors.FieldDescriptor fieldDescriptor, Object value) { + if (value == null) { + return context.runtime.getNil(); + } + Ruby runtime = context.runtime; + switch (fieldDescriptor.getType()) { + case INT32: + case INT64: + case UINT32: + case UINT64: + case FLOAT: + case DOUBLE: + case BOOL: + case BYTES: + case STRING: + return Utils.wrapPrimaryValue(context, fieldDescriptor.getType(), value); + case MESSAGE: + RubyClass typeClass = (RubyClass) ((RubyDescriptor) getDescriptorForField(context, fieldDescriptor)).msgclass(context); + RubyMessage msg = (RubyMessage) typeClass.newInstance(context, Block.NULL_BLOCK); + return msg.buildFrom(context, (DynamicMessage) value); + case ENUM: + Descriptors.EnumValueDescriptor enumValueDescriptor = (Descriptors.EnumValueDescriptor) value; + if (enumValueDescriptor.getIndex() == -1) { // UNKNOWN ENUM VALUE + return runtime.newFixnum(enumValueDescriptor.getNumber()); + } + return runtime.newSymbol(enumValueDescriptor.getName()); + default: + return runtime.newString(value.toString()); + } + } + + private RubyRepeatedField repeatedFieldForFieldDescriptor(ThreadContext context, + Descriptors.FieldDescriptor fieldDescriptor) { + IRubyObject typeClass = context.runtime.getNilClass(); + + IRubyObject descriptor = getDescriptorForField(context, fieldDescriptor); + Descriptors.FieldDescriptor.Type type = fieldDescriptor.getType(); + if (type == Descriptors.FieldDescriptor.Type.MESSAGE) { + typeClass = ((RubyDescriptor) descriptor).msgclass(context); + + } else if (type == Descriptors.FieldDescriptor.Type.ENUM) { + typeClass = ((RubyEnumDescriptor) descriptor).enummodule(context); + } + return new RubyRepeatedField(context.runtime, cRepeatedField, type, typeClass); + } + + protected IRubyObject getField(ThreadContext context, Descriptors.FieldDescriptor fieldDescriptor) { + Descriptors.OneofDescriptor oneofDescriptor = fieldDescriptor.getContainingOneof(); + if (oneofDescriptor != null) { + if (oneofCases.containsKey(oneofDescriptor)) { + if (oneofCases.get(oneofDescriptor) != fieldDescriptor) + return context.runtime.getNil(); + return fields.get(fieldDescriptor); + } else { + Descriptors.FieldDescriptor oneofCase = builder.getOneofFieldDescriptor(oneofDescriptor); + if (oneofCase != fieldDescriptor) return context.runtime.getNil(); + IRubyObject value = wrapField(context, oneofCase, builder.getField(oneofCase)); + fields.put(fieldDescriptor, value); + return value; + } + } + + if (Utils.isMapEntry(fieldDescriptor)) { + RubyMap map = maps.get(fieldDescriptor); + if (map == null) { + map = newMapForField(context, fieldDescriptor); + int mapSize = this.builder.getRepeatedFieldCount(fieldDescriptor); + Descriptors.FieldDescriptor keyField = fieldDescriptor.getMessageType().findFieldByNumber(1); + Descriptors.FieldDescriptor valueField = fieldDescriptor.getMessageType().findFieldByNumber(2); + RubyDescriptor kvDescriptor = (RubyDescriptor) getDescriptorForField(context, fieldDescriptor); + RubyClass kvClass = (RubyClass) kvDescriptor.msgclass(context); + for (int i = 0; i < mapSize; i++) { + RubyMessage kvMessage = (RubyMessage) kvClass.newInstance(context, Block.NULL_BLOCK); + DynamicMessage message = (DynamicMessage) this.builder.getRepeatedField(fieldDescriptor, i); + kvMessage.buildFrom(context, message); + map.indexSet(context, kvMessage.getField(context, keyField), kvMessage.getField(context, valueField)); + } + maps.put(fieldDescriptor, map); + } + return map; + } + if (fieldDescriptor.isRepeated()) { + return getRepeatedField(context, fieldDescriptor); + } + if (fieldDescriptor.getType() != Descriptors.FieldDescriptor.Type.MESSAGE || + this.builder.hasField(fieldDescriptor) || fields.containsKey(fieldDescriptor)) { + if (fields.containsKey(fieldDescriptor)) { + return fields.get(fieldDescriptor); + } else { + IRubyObject value = wrapField(context, fieldDescriptor, this.builder.getField(fieldDescriptor)); + if (this.builder.hasField(fieldDescriptor)) { + fields.put(fieldDescriptor, value); + } + return value; + } + } + return context.runtime.getNil(); + } + + protected IRubyObject setField(ThreadContext context, Descriptors.FieldDescriptor fieldDescriptor, IRubyObject value) { + if (Utils.isMapEntry(fieldDescriptor)) { + if (!(value instanceof RubyMap)) { + throw context.runtime.newTypeError("Expected Map instance"); + } + RubyMap thisMap = (RubyMap) getField(context, fieldDescriptor); + thisMap.mergeIntoSelf(context, value); + } else if (fieldDescriptor.isRepeated()) { + checkRepeatedFieldType(context, value, fieldDescriptor); + if (value instanceof RubyRepeatedField) { + addRepeatedField(fieldDescriptor, (RubyRepeatedField) value); + } else { + RubyArray ary = value.convertToArray(); + RubyRepeatedField repeatedField = rubyToRepeatedField(context, fieldDescriptor, ary); + addRepeatedField(fieldDescriptor, repeatedField); + } + } else { + Descriptors.OneofDescriptor oneofDescriptor = fieldDescriptor.getContainingOneof(); + if (oneofDescriptor != null) { + Descriptors.FieldDescriptor oneofCase = oneofCases.get(oneofDescriptor); + if (oneofCase != null && oneofCase != fieldDescriptor) { + fields.remove(oneofCase); + } + if (value.isNil()) { + oneofCases.remove(oneofDescriptor); + fields.remove(fieldDescriptor); + } else { + oneofCases.put(oneofDescriptor, fieldDescriptor); + fields.put(fieldDescriptor, value); + } + } else { + Descriptors.FieldDescriptor.Type fieldType = fieldDescriptor.getType(); + IRubyObject typeClass = context.runtime.getObject(); + if (fieldType == Descriptors.FieldDescriptor.Type.MESSAGE) { + typeClass = ((RubyDescriptor) getDescriptorForField(context, fieldDescriptor)).msgclass(context); + } else if (fieldType == Descriptors.FieldDescriptor.Type.ENUM) { + typeClass = ((RubyEnumDescriptor) getDescriptorForField(context, fieldDescriptor)).enummodule(context); + } + Utils.checkType(context, fieldType, value, (RubyModule) typeClass); + // Convert integer enum to symbol + if (fieldType == Descriptors.FieldDescriptor.Type.ENUM) { + Descriptors.EnumDescriptor enumDescriptor = fieldDescriptor.getEnumType(); + if (Utils.isRubyNum(value)) { + Descriptors.EnumValueDescriptor val = + enumDescriptor.findValueByNumberCreatingIfUnknown(RubyNumeric.num2int(value)); + if (val.getIndex() != -1) value = context.runtime.newSymbol(val.getName()); + } + } + this.fields.put(fieldDescriptor, value); + } + } + return context.runtime.getNil(); + } + + private String layoutInspect() { + ThreadContext context = getRuntime().getCurrentContext(); + StringBuilder sb = new StringBuilder(); + for (Descriptors.FieldDescriptor fdef : descriptor.getFields()) { + sb.append(Utils.unescapeIdentifier(fdef.getName())); + sb.append(": "); + sb.append(getField(context, fdef).inspect()); + sb.append(", "); + } + return sb.substring(0, sb.length() - 2); + } + + private IRubyObject getDescriptorForField(ThreadContext context, Descriptors.FieldDescriptor fieldDescriptor) { + RubyDescriptor thisRbDescriptor = (RubyDescriptor) getDescriptor(context, metaClass); + return thisRbDescriptor.lookup(fieldDescriptor.getName()).getSubType(context); + } + + private RubyRepeatedField rubyToRepeatedField(ThreadContext context, + Descriptors.FieldDescriptor fieldDescriptor, IRubyObject value) { + RubyArray arr = value.convertToArray(); + RubyRepeatedField repeatedField = repeatedFieldForFieldDescriptor(context, fieldDescriptor); + for (int i = 0; i < arr.size(); i++) { + repeatedField.push(context, arr.eltInternal(i)); + } + return repeatedField; + } + + private RubyMap newMapForField(ThreadContext context, Descriptors.FieldDescriptor fieldDescriptor) { + RubyDescriptor mapDescriptor = (RubyDescriptor) getDescriptorForField(context, fieldDescriptor); + Descriptors.FieldDescriptor keyField = fieldDescriptor.getMessageType().findFieldByNumber(1); + Descriptors.FieldDescriptor valueField = fieldDescriptor.getMessageType().findFieldByNumber(2); + IRubyObject keyType = RubySymbol.newSymbol(context.runtime, keyField.getType().name()); + IRubyObject valueType = RubySymbol.newSymbol(context.runtime, valueField.getType().name()); + if (valueField.getType() == Descriptors.FieldDescriptor.Type.MESSAGE) { + RubyFieldDescriptor rubyFieldDescriptor = (RubyFieldDescriptor) mapDescriptor.lookup(context, + context.runtime.newString("value")); + RubyDescriptor rubyDescriptor = (RubyDescriptor) rubyFieldDescriptor.getSubType(context); + return (RubyMap) cMap.newInstance(context, keyType, valueType, + rubyDescriptor.msgclass(context), Block.NULL_BLOCK); + } else { + return (RubyMap) cMap.newInstance(context, keyType, valueType, Block.NULL_BLOCK); + } + } + + private Descriptors.FieldDescriptor getOneofCase(Descriptors.OneofDescriptor oneof) { + if (oneofCases.containsKey(oneof)) { + return oneofCases.get(oneof); + } + return builder.getOneofFieldDescriptor(oneof); + } + + private Descriptors.Descriptor descriptor; + private DynamicMessage.Builder builder; + private RubyClass cRepeatedField; + private RubyClass cMap; + private Map repeatedFields; + private Map maps; + private Map fields; + private Map oneofCases; + + private static final int SINK_MAXIMUM_NESTING = 64; +} diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyMessageBuilderContext.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyMessageBuilderContext.java new file mode 100644 index 00000000..a619b803 --- /dev/null +++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyMessageBuilderContext.java @@ -0,0 +1,217 @@ +/* + * Protocol Buffers - Google's data interchange format + * Copyright 2014 Google Inc. All rights reserved. + * https://developers.google.com/protocol-buffers/ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.google.protobuf.jruby; + +import com.google.protobuf.Descriptors; +import org.jruby.*; +import org.jruby.anno.JRubyClass; +import org.jruby.anno.JRubyMethod; +import org.jruby.runtime.Binding; +import org.jruby.runtime.Block; +import org.jruby.runtime.ObjectAllocator; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; + +@JRubyClass(name = "MessageBuilderContext") +public class RubyMessageBuilderContext extends RubyObject { + public static void createRubyMessageBuilderContext(Ruby runtime) { + RubyModule protobuf = runtime.getClassFromPath("Google::Protobuf"); + RubyClass cMessageBuilderContext = protobuf.defineClassUnder("MessageBuilderContext", runtime.getObject(), new ObjectAllocator() { + @Override + public IRubyObject allocate(Ruby runtime, RubyClass klazz) { + return new RubyMessageBuilderContext(runtime, klazz); + } + }); + cMessageBuilderContext.defineAnnotatedMethods(RubyMessageBuilderContext.class); + } + + public RubyMessageBuilderContext(Ruby ruby, RubyClass klazz) { + super(ruby, klazz); + } + + @JRubyMethod + public IRubyObject initialize(ThreadContext context, IRubyObject descriptor, IRubyObject rubyBuilder) { + this.cFieldDescriptor = (RubyClass) context.runtime.getClassFromPath("Google::Protobuf::FieldDescriptor"); + this.cDescriptor = (RubyClass) context.runtime.getClassFromPath("Google::Protobuf::Descriptor"); + this.cOneofDescriptor = (RubyClass) context.runtime.getClassFromPath("Google::Protobuf::OneofDescriptor"); + this.cOneofBuilderContext = (RubyClass) context.runtime.getClassFromPath("Google::Protobuf::Internal::OneofBuilderContext"); + this.descriptor = (RubyDescriptor) descriptor; + this.builder = (RubyBuilder) rubyBuilder; + return this; + } + + /* + * call-seq: + * MessageBuilderContext.optional(name, type, number, type_class = nil) + * + * Defines a new optional field on this message type with the given type, tag + * number, and type class (for message and enum fields). The type must be a Ruby + * symbol (as accepted by FieldDescriptor#type=) and the type_class must be a + * string, if present (as accepted by FieldDescriptor#submsg_name=). + */ + @JRubyMethod(required = 3, optional = 1) + public IRubyObject optional(ThreadContext context, IRubyObject[] args) { + Ruby runtime = context.runtime; + IRubyObject typeClass = runtime.getNil(); + if (args.length > 3) typeClass = args[3]; + msgdefAddField(context, "optional", args[0], args[1], args[2], typeClass); + return context.runtime.getNil(); + } + + /* + * call-seq: + * MessageBuilderContext.required(name, type, number, type_class = nil) + * + * Defines a new required field on this message type with the given type, tag + * number, and type class (for message and enum fields). The type must be a Ruby + * symbol (as accepted by FieldDescriptor#type=) and the type_class must be a + * string, if present (as accepted by FieldDescriptor#submsg_name=). + * + * Proto3 does not have required fields, but this method exists for + * completeness. Any attempt to add a message type with required fields to a + * pool will currently result in an error. + */ + @JRubyMethod(required = 3, optional = 1) + public IRubyObject required(ThreadContext context, IRubyObject[] args) { + IRubyObject typeClass = context.runtime.getNil(); + if (args.length > 3) typeClass = args[3]; + msgdefAddField(context, "required", args[0], args[1], args[2], typeClass); + return context.runtime.getNil(); + } + + /* + * call-seq: + * MessageBuilderContext.repeated(name, type, number, type_class = nil) + * + * Defines a new repeated field on this message type with the given type, tag + * number, and type class (for message and enum fields). The type must be a Ruby + * symbol (as accepted by FieldDescriptor#type=) and the type_class must be a + * string, if present (as accepted by FieldDescriptor#submsg_name=). + */ + @JRubyMethod(required = 3, optional = 1) + public IRubyObject repeated(ThreadContext context, IRubyObject[] args) { + IRubyObject typeClass = context.runtime.getNil(); + if (args.length > 3) typeClass = args[3]; + msgdefAddField(context, "repeated", args[0], args[1], args[2], typeClass); + return context.runtime.getNil(); + } + + /* + * call-seq: + * MessageBuilderContext.map(name, key_type, value_type, number, + * value_type_class = nil) + * + * Defines a new map field on this message type with the given key and value + * types, tag number, and type class (for message and enum value types). The key + * type must be :int32/:uint32/:int64/:uint64, :bool, or :string. The value type + * type must be a Ruby symbol (as accepted by FieldDescriptor#type=) and the + * type_class must be a string, if present (as accepted by + * FieldDescriptor#submsg_name=). + */ + @JRubyMethod(required = 4, optional = 1) + public IRubyObject map(ThreadContext context, IRubyObject[] args) { + Ruby runtime = context.runtime; + IRubyObject name = args[0]; + IRubyObject keyType = args[1]; + IRubyObject valueType = args[2]; + IRubyObject number = args[3]; + IRubyObject typeClass = args.length > 4 ? args[4] : context.runtime.getNil(); + + // Validate the key type. We can't accept enums, messages, or floats/doubles + // as map keys. (We exclude these explicitly, and the field-descriptor setter + // below then ensures that the type is one of the remaining valid options.) + if (keyType.equals(RubySymbol.newSymbol(runtime, "float")) || + keyType.equals(RubySymbol.newSymbol(runtime, "double")) || + keyType.equals(RubySymbol.newSymbol(runtime, "enum")) || + keyType.equals(RubySymbol.newSymbol(runtime, "message"))) + throw runtime.newArgumentError("Cannot add a map field with a float, double, enum, or message type."); + + // Create a new message descriptor for the map entry message, and create a + // repeated submessage field here with that type. + RubyDescriptor mapentryDesc = (RubyDescriptor) cDescriptor.newInstance(context, Block.NULL_BLOCK); + IRubyObject mapentryDescName = RubySymbol.newSymbol(runtime, name).id2name(context); + mapentryDesc.setName(context, mapentryDescName); + mapentryDesc.setMapEntry(true); + + //optional key = 1; + RubyFieldDescriptor keyField = (RubyFieldDescriptor) cFieldDescriptor.newInstance(context, Block.NULL_BLOCK); + keyField.setName(context, runtime.newString("key")); + keyField.setLabel(context, RubySymbol.newSymbol(runtime, "optional")); + keyField.setNumber(context, runtime.newFixnum(1)); + keyField.setType(context, keyType); + mapentryDesc.addField(context, keyField); + + //optional value = 2; + RubyFieldDescriptor valueField = (RubyFieldDescriptor) cFieldDescriptor.newInstance(context, Block.NULL_BLOCK); + valueField.setName(context, runtime.newString("value")); + valueField.setLabel(context, RubySymbol.newSymbol(runtime, "optional")); + valueField.setNumber(context, runtime.newFixnum(2)); + valueField.setType(context, valueType); + if (! typeClass.isNil()) valueField.setSubmsgName(context, typeClass); + mapentryDesc.addField(context, valueField); + + // Add the map-entry message type to the current builder, and use the type to + // create the map field itself. + this.builder.pendingList.add(mapentryDesc); + + msgdefAddField(context, "repeated", name, runtime.newSymbol("message"), number, mapentryDescName); + return runtime.getNil(); + } + + @JRubyMethod + public IRubyObject oneof(ThreadContext context, IRubyObject name, Block block) { + RubyOneofDescriptor oneofdef = (RubyOneofDescriptor) + cOneofDescriptor.newInstance(context, Block.NULL_BLOCK); + RubyOneofBuilderContext ctx = (RubyOneofBuilderContext) + cOneofBuilderContext.newInstance(context, oneofdef, Block.NULL_BLOCK); + oneofdef.setName(context, name); + Binding binding = block.getBinding(); + binding.setSelf(ctx); + block.yieldSpecific(context); + descriptor.addOneof(context, oneofdef); + return context.runtime.getNil(); + } + + private void msgdefAddField(ThreadContext context, String label, IRubyObject name, + IRubyObject type, IRubyObject number, IRubyObject typeClass) { + descriptor.addField(context, + Utils.msgdefCreateField(context, label, name, type, number, typeClass, cFieldDescriptor)); + } + + private RubyDescriptor descriptor; + private RubyBuilder builder; + private RubyClass cFieldDescriptor; + private RubyClass cOneofDescriptor; + private RubyClass cOneofBuilderContext; + private RubyClass cDescriptor; +} diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyOneofBuilderContext.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyOneofBuilderContext.java new file mode 100644 index 00000000..c9b99e04 --- /dev/null +++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyOneofBuilderContext.java @@ -0,0 +1,84 @@ +/* + * Protocol Buffers - Google's data interchange format + * Copyright 2014 Google Inc. All rights reserved. + * https://developers.google.com/protocol-buffers/ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.google.protobuf.jruby; + +import org.jruby.Ruby; +import org.jruby.RubyClass; +import org.jruby.RubyModule; +import org.jruby.RubyObject; +import org.jruby.anno.JRubyClass; +import org.jruby.anno.JRubyMethod; +import org.jruby.runtime.ObjectAllocator; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; + +@JRubyClass(name = "OneofBuilderContext") +public class RubyOneofBuilderContext extends RubyObject { + public static void createRubyOneofBuilderContext(Ruby runtime) { + RubyModule protobuf = runtime.getClassFromPath("Google::Protobuf"); + RubyModule internal = protobuf.defineModuleUnder("Internal"); + RubyClass cRubyOneofBuidlerContext = internal.defineClassUnder("OneofBuilderContext", runtime.getObject(), new ObjectAllocator() { + @Override + public IRubyObject allocate(Ruby ruby, RubyClass rubyClass) { + return new RubyOneofBuilderContext(ruby, rubyClass); + } + }); + cRubyOneofBuidlerContext.defineAnnotatedMethods(RubyOneofBuilderContext.class); + } + + public RubyOneofBuilderContext(Ruby ruby, RubyClass rubyClass) { + super(ruby, rubyClass); + } + + @JRubyMethod + public IRubyObject initialize(ThreadContext context, IRubyObject oneofdef) { + this.descriptor = (RubyOneofDescriptor) oneofdef; + this.cFieldDescriptor = (RubyClass) context.runtime.getClassFromPath("Google::Protobuf::FieldDescriptor"); + return this; + } + + @JRubyMethod(required = 3, optional = 1) + public IRubyObject optional(ThreadContext context, IRubyObject[] args) { + IRubyObject name = args[0]; + IRubyObject type = args[1]; + IRubyObject number = args[2]; + IRubyObject typeClass = args.length > 3 ? args[3] : context.runtime.getNil(); + RubyFieldDescriptor fieldDescriptor = Utils.msgdefCreateField(context, "optional", + name, type, number, typeClass, cFieldDescriptor); + descriptor.addField(context, fieldDescriptor); + return this; + } + + private RubyOneofDescriptor descriptor; + private RubyClass cFieldDescriptor; +} diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyOneofDescriptor.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyOneofDescriptor.java new file mode 100644 index 00000000..cc4ab662 --- /dev/null +++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyOneofDescriptor.java @@ -0,0 +1,124 @@ +package com.google.protobuf.jruby; + +import com.google.protobuf.DescriptorProtos; +import com.google.protobuf.Descriptors; +import org.jruby.Ruby; +import org.jruby.RubyClass; +import org.jruby.RubyModule; +import org.jruby.RubyObject; +import org.jruby.anno.JRubyClass; +import org.jruby.anno.JRubyMethod; +import org.jruby.runtime.Block; +import org.jruby.runtime.ObjectAllocator; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; + +import java.util.*; + +@JRubyClass(name = "OneofDescriptor", include = "Enumerable") +public class RubyOneofDescriptor extends RubyObject { + + public static void createRubyOneofDescriptor(Ruby runtime) { + RubyModule protobuf = runtime.getClassFromPath("Google::Protobuf"); + RubyClass cRubyOneofDescriptor = protobuf.defineClassUnder("OneofDescriptor", runtime.getObject(), new ObjectAllocator() { + @Override + public IRubyObject allocate(Ruby ruby, RubyClass rubyClass) { + return new RubyOneofDescriptor(ruby, rubyClass); + } + }); + cRubyOneofDescriptor.defineAnnotatedMethods(RubyOneofDescriptor.class); + cRubyOneofDescriptor.includeModule(runtime.getEnumerable()); + } + + public RubyOneofDescriptor(Ruby ruby, RubyClass rubyClass) { + super(ruby, rubyClass); + } + + @JRubyMethod + public IRubyObject initialize(ThreadContext context) { + builder = DescriptorProtos.OneofDescriptorProto.newBuilder(); + fields = new ArrayList(); + return this; + } + + /* + * call-seq: + * OneofDescriptor.name => name + * + * Returns the name of this oneof. + */ + @JRubyMethod(name = "name") + public IRubyObject getName(ThreadContext context) { + return name; + } + + /* + * call-seq: + * OneofDescriptor.name = name + * + * Sets a new name for this oneof. The oneof must not have been added to a + * message descriptor yet. + */ + @JRubyMethod(name = "name=") + public IRubyObject setName(ThreadContext context, IRubyObject name) { + this.name = context.runtime.newString(name.asJavaString()); + this.builder.setName(name.asJavaString()); + return context.runtime.getNil(); + } + + /* + * 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. + */ + @JRubyMethod(name = "add_field") + public IRubyObject addField(ThreadContext context, IRubyObject obj) { + RubyFieldDescriptor fieldDescriptor = (RubyFieldDescriptor) obj; + fieldDescriptor.setOneofName(this.name); + fields.add(fieldDescriptor); + return context.runtime.getNil(); + } + + /* + * call-seq: + * OneofDescriptor.each(&block) => nil + * + * Iterates through fields in this oneof, yielding to the block on each one. + */ + @JRubyMethod + public IRubyObject each(ThreadContext context, Block block) { + for (RubyFieldDescriptor field : fields) { + block.yieldSpecific(context, field); + } + return context.runtime.getNil(); + } + + public DescriptorProtos.OneofDescriptorProto build(int index) { + for (RubyFieldDescriptor field: fields) { + field.setOneofIndex(index); + } + return this.builder.build(); + } + + protected Collection getFields() { + return fields; + } + + protected Descriptors.OneofDescriptor getOneofDescriptor() { + RubyFieldDescriptor fieldDescriptor = fields.get(0); + return fieldDescriptor.getFieldDef().getContainingOneof(); + } + + private IRubyObject name; + private DescriptorProtos.OneofDescriptorProto.Builder builder; + private List fields; +} diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyProtobuf.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyProtobuf.java new file mode 100644 index 00000000..cb3fcd48 --- /dev/null +++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyProtobuf.java @@ -0,0 +1,118 @@ +/* + * Protocol Buffers - Google's data interchange format + * Copyright 2014 Google Inc. All rights reserved. + * https://developers.google.com/protocol-buffers/ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.google.protobuf.jruby; + +import org.jruby.Ruby; +import org.jruby.RubyModule; +import org.jruby.anno.JRubyMethod; +import org.jruby.anno.JRubyModule; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; + +@JRubyModule(name = "Protobuf") +public class RubyProtobuf { + + public static void createProtobuf(Ruby runtime) { + RubyModule mGoogle = runtime.getModule("Google"); + RubyModule mProtobuf = mGoogle.defineModuleUnder("Protobuf"); + mProtobuf.defineAnnotatedMethods(RubyProtobuf.class); + } + + /* + * call-seq: + * Google::Protobuf.encode(msg) => bytes + * + * Encodes the given message object to protocol buffers wire format. This is an + * alternative to the #encode method on msg's class. + */ + @JRubyMethod(meta = true) + public static IRubyObject encode(ThreadContext context, IRubyObject self, IRubyObject message) { + return RubyMessage.encode(context, message.getMetaClass(), message); + } + + /* + * call-seq: + * Google::Protobuf.decode(class, bytes) => msg + * + * Decodes the given bytes as protocol buffers wire format under the + * interpretation given by the given class's message definition. This is an + * alternative to the #decode method on the given class. + */ + @JRubyMethod(meta = true) + public static IRubyObject decode(ThreadContext context, IRubyObject self, IRubyObject klazz, IRubyObject message) { + return RubyMessage.decode(context, klazz, message); + } + + /* + * call-seq: + * Google::Protobuf.encode_json(msg) => json_string + * + * Encodes the given message object to its JSON representation. This is an + * alternative to the #encode_json method on msg's class. + */ + @JRubyMethod(name = "encode_json", meta = true) + public static IRubyObject encodeJson(ThreadContext context, IRubyObject self, IRubyObject message) { + return RubyMessage.encodeJson(context, message.getMetaClass(), message); + } + + /* + * call-seq: + * Google::Protobuf.decode_json(class, json_string) => msg + * + * Decodes the given JSON string under the interpretation given by the given + * class's message definition. This is an alternative to the #decode_json method + * on the given class. + */ + @JRubyMethod(name = "decode_json", meta = true) + public static IRubyObject decodeJson(ThreadContext context, IRubyObject self, IRubyObject klazz, IRubyObject message) { + return RubyMessage.decodeJson(context, klazz, message); + } + + /* + * call-seq: + * Google::Protobuf.deep_copy(obj) => copy_of_obj + * + * Performs a deep copy of either a RepeatedField instance or a message object, + * recursively copying its members. + */ + @JRubyMethod(name = "deep_copy", meta = true) + public static IRubyObject deepCopy(ThreadContext context, IRubyObject self, IRubyObject message) { + if (message instanceof RubyMessage) { + return ((RubyMessage) message).deepCopy(context); + } else if (message instanceof RubyRepeatedField) { + return ((RubyRepeatedField) message).deepCopy(context); + } else { + return ((RubyMap) message).deepCopy(context); + } + } +} diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyRepeatedField.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyRepeatedField.java new file mode 100644 index 00000000..9788317a --- /dev/null +++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyRepeatedField.java @@ -0,0 +1,391 @@ +/* + * Protocol Buffers - Google's data interchange format + * Copyright 2014 Google Inc. All rights reserved. + * https://developers.google.com/protocol-buffers/ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.google.protobuf.jruby; + +import com.google.protobuf.Descriptors; +import org.jruby.*; +import org.jruby.anno.JRubyClass; +import org.jruby.anno.JRubyMethod; +import org.jruby.runtime.Block; +import org.jruby.runtime.ObjectAllocator; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; + +@JRubyClass(name = "RepeatedClass", include = "Enumerable") +public class RubyRepeatedField extends RubyObject { + public static void createRubyRepeatedField(Ruby runtime) { + RubyModule mProtobuf = runtime.getClassFromPath("Google::Protobuf"); + RubyClass cRepeatedField = mProtobuf.defineClassUnder("RepeatedField", runtime.getObject(), + new ObjectAllocator() { + @Override + public IRubyObject allocate(Ruby runtime, RubyClass klazz) { + return new RubyRepeatedField(runtime, klazz); + } + }); + cRepeatedField.defineAnnotatedMethods(RubyRepeatedField.class); + cRepeatedField.includeModule(runtime.getEnumerable()); + } + + public RubyRepeatedField(Ruby runtime, RubyClass klazz) { + super(runtime, klazz); + } + + public RubyRepeatedField(Ruby runtime, RubyClass klazz, Descriptors.FieldDescriptor.Type fieldType, IRubyObject typeClass) { + this(runtime, klazz); + this.fieldType = fieldType; + this.storage = runtime.newArray(); + this.typeClass = typeClass; + } + + @JRubyMethod(required = 1, optional = 2) + public IRubyObject initialize(ThreadContext context, IRubyObject[] args) { + Ruby runtime = context.runtime; + this.storage = runtime.newArray(); + IRubyObject ary = null; + if (!(args[0] instanceof RubySymbol)) { + throw runtime.newArgumentError("Expected Symbol for type name"); + } + this.fieldType = Utils.rubyToFieldType(args[0]); + if (fieldType == Descriptors.FieldDescriptor.Type.MESSAGE + || fieldType == Descriptors.FieldDescriptor.Type.ENUM) { + if (args.length < 2) + throw runtime.newArgumentError("Expected at least 2 arguments for message/enum"); + typeClass = args[1]; + if (args.length > 2) + ary = args[2]; + Utils.validateTypeClass(context, fieldType, typeClass); + } else { + if (args.length > 2) + throw runtime.newArgumentError("Too many arguments: expected 1 or 2"); + if (args.length > 1) + ary = args[1]; + } + if (ary != null) { + RubyArray arr = ary.convertToArray(); + for (int i = 0; i < arr.size(); i++) { + this.storage.add(arr.eltInternal(i)); + } + } + return this; + } + + /* + * call-seq: + * RepeatedField.[]=(index, value) + * + * Sets the element at the given index. On out-of-bounds assignments, extends + * the array and fills the hole (if any) with default values. + */ + @JRubyMethod(name = "[]=") + public IRubyObject indexSet(ThreadContext context, IRubyObject index, IRubyObject value) { + Utils.checkType(context, fieldType, value, (RubyModule) typeClass); + this.storage.set(RubyNumeric.num2int(index), value); + return context.runtime.getNil(); + } + + /* + * call-seq: + * RepeatedField.[](index) => value + * + * Accesses the element at the given index. Throws an exception on out-of-bounds + * errors. + */ + @JRubyMethod(name = "[]") + public IRubyObject index(ThreadContext context, IRubyObject index) { + return this.storage.eltInternal(RubyNumeric.num2int(index)); + } + + /* + * call-seq: + * RepeatedField.insert(*args) + * + * Pushes each arg in turn onto the end of the repeated field. + */ + @JRubyMethod(rest = true) + public IRubyObject insert(ThreadContext context, IRubyObject[] args) { + for (int i = 0; i < args.length; i++) { + Utils.checkType(context, fieldType, args[i], (RubyModule) typeClass); + this.storage.add(args[i]); + } + return context.runtime.getNil(); + } + + /* + * call-seq: + * RepeatedField.push(value) + * + * Adds a new element to the repeated field. + */ + @JRubyMethod(name = {"push", "<<"}) + public IRubyObject push(ThreadContext context, IRubyObject value) { + Utils.checkType(context, fieldType, value, (RubyModule) typeClass); + this.storage.add(value); + return this; + } + + /* + * call-seq: + * RepeatedField.pop => value + * + * Removes the last element and returns it. Throws an exception if the repeated + * field is empty. + */ + @JRubyMethod + public IRubyObject pop(ThreadContext context) { + IRubyObject ret = this.storage.last(); + this.storage.remove(ret); + return ret; + } + + /* + * call-seq: + * RepeatedField.replace(list) + * + * Replaces the contents of the repeated field with the given list of elements. + */ + @JRubyMethod + public IRubyObject replace(ThreadContext context, IRubyObject list) { + RubyArray arr = (RubyArray) list; + checkArrayElementType(context, arr); + this.storage = arr; + return context.runtime.getNil(); + } + + /* + * call-seq: + * RepeatedField.clear + * + * Clears (removes all elements from) this repeated field. + */ + @JRubyMethod + public IRubyObject clear(ThreadContext context) { + this.storage.clear(); + return context.runtime.getNil(); + } + + /* + * call-seq: + * RepeatedField.length + * + * Returns the length of this repeated field. + */ + @JRubyMethod(name = {"count", "length"}) + public IRubyObject length(ThreadContext context) { + return context.runtime.newFixnum(this.storage.size()); + } + + /* + * call-seq: + * RepeatedField.+(other) => repeated field + * + * Returns a new repeated field that contains the concatenated list of this + * repeated field's elements and other's elements. The other (second) list may + * be either another repeated field or a Ruby array. + */ + @JRubyMethod(name = "+") + public IRubyObject plus(ThreadContext context, IRubyObject list) { + RubyRepeatedField dup = (RubyRepeatedField) dup(context); + if (list instanceof RubyArray) { + checkArrayElementType(context, (RubyArray) list); + dup.storage.addAll((RubyArray) list); + } else { + RubyRepeatedField repeatedField = (RubyRepeatedField) list; + if (! fieldType.equals(repeatedField.fieldType) || (typeClass != null && ! + typeClass.equals(repeatedField.typeClass))) + throw context.runtime.newArgumentError("Attempt to append RepeatedField with different element type."); + dup.storage.addAll((RubyArray) repeatedField.toArray(context)); + } + return dup; + } + + /* + * call-seq: + * RepeatedField.hash => hash_value + * + * Returns a hash value computed from this repeated field's elements. + */ + @JRubyMethod + public IRubyObject hash(ThreadContext context) { + int hashCode = System.identityHashCode(this.storage); + return context.runtime.newFixnum(hashCode); + } + + /* + * call-seq: + * RepeatedField.==(other) => boolean + * + * Compares this repeated field to another. Repeated fields are equal if their + * element types are equal, their lengths are equal, and each element is equal. + * Elements are compared as per normal Ruby semantics, by calling their :== + * methods (or performing a more efficient comparison for primitive types). + */ + @JRubyMethod(name = "==") + public IRubyObject eq(ThreadContext context, IRubyObject other) { + return this.toArray(context).op_equal(context, other); + } + + /* + * call-seq: + * RepeatedField.each(&block) + * + * Invokes the block once for each element of the repeated field. RepeatedField + * also includes Enumerable; combined with this method, the repeated field thus + * acts like an ordinary Ruby sequence. + */ + @JRubyMethod + public IRubyObject each(ThreadContext context, Block block) { + this.storage.each(context, block); + return context.runtime.getNil(); + } + + @JRubyMethod(name = {"to_ary", "to_a"}) + public IRubyObject toArray(ThreadContext context) { + for (int i = 0; i < this.storage.size(); i++) { + IRubyObject defaultValue = defaultValue(context); + if (storage.eltInternal(i).isNil()) { + storage.set(i, defaultValue); + } + } + return this.storage; + } + + /* + * call-seq: + * RepeatedField.dup => repeated_field + * + * Duplicates this repeated field with a shallow copy. References to all + * non-primitive element objects (e.g., submessages) are shared. + */ + @JRubyMethod + public IRubyObject dup(ThreadContext context) { + RubyRepeatedField dup = new RubyRepeatedField(context.runtime, metaClass, fieldType, typeClass); + for (int i = 0; i < this.storage.size(); i++) { + dup.push(context, this.storage.eltInternal(i)); + } + return dup; + } + + /* + * call-seq: + * RepeatedField.inspect => string + * + * Returns a string representing this repeated field's elements. It will be + * formated as "[, , ...]", with each element's string + * representation computed by its own #inspect method. + */ + @JRubyMethod + public IRubyObject inspect() { + StringBuilder str = new StringBuilder("["); + for (int i = 0; i < this.storage.size(); i++) { + str.append(storage.eltInternal(i).inspect()); + str.append(", "); + } + + if (str.length() > 1) { + str.replace(str.length() - 2, str.length(), "]"); + } else { + str.append("]"); + } + + return getRuntime().newString(str.toString()); + } + + // Java API + protected IRubyObject get(int index) { + return this.storage.eltInternal(index); + } + + protected RubyRepeatedField deepCopy(ThreadContext context) { + RubyRepeatedField copy = new RubyRepeatedField(context.runtime, metaClass, fieldType, typeClass); + for (int i = 0; i < size(); i++) { + IRubyObject value = storage.eltInternal(i); + if (fieldType == Descriptors.FieldDescriptor.Type.MESSAGE) { + copy.storage.add(((RubyMessage) value).deepCopy(context)); + } else { + copy.storage.add(value); + } + } + return copy; + } + + protected int size() { + return this.storage.size(); + } + + private IRubyObject defaultValue(ThreadContext context) { + SentinelOuterClass.Sentinel sentinel = SentinelOuterClass.Sentinel.getDefaultInstance(); + Object value; + switch (fieldType) { + case INT32: + value = sentinel.getDefaultInt32(); + break; + case INT64: + value = sentinel.getDefaultInt64(); + break; + case UINT32: + value = sentinel.getDefaultUnit32(); + break; + case UINT64: + value = sentinel.getDefaultUint64(); + break; + case FLOAT: + value = sentinel.getDefaultFloat(); + break; + case DOUBLE: + value = sentinel.getDefaultDouble(); + break; + case BOOL: + value = sentinel.getDefaultBool(); + break; + case BYTES: + value = sentinel.getDefaultBytes(); + break; + case STRING: + value = sentinel.getDefaultString(); + break; + default: + return context.runtime.getNil(); + } + return Utils.wrapPrimaryValue(context, fieldType, value); + } + + private void checkArrayElementType(ThreadContext context, RubyArray arr) { + for (int i = 0; i < arr.getLength(); i++) { + Utils.checkType(context, fieldType, arr.eltInternal(i), (RubyModule) typeClass); + } + } + + private RubyArray storage; + private Descriptors.FieldDescriptor.Type fieldType; + private IRubyObject typeClass; +} diff --git a/ruby/src/main/java/com/google/protobuf/jruby/SentinelOuterClass.java b/ruby/src/main/java/com/google/protobuf/jruby/SentinelOuterClass.java new file mode 100644 index 00000000..54f2c729 --- /dev/null +++ b/ruby/src/main/java/com/google/protobuf/jruby/SentinelOuterClass.java @@ -0,0 +1,776 @@ +/* + * Protocol Buffers - Google's data interchange format + * Copyright 2014 Google Inc. All rights reserved. + * https://developers.google.com/protocol-buffers/ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: sentinel.proto + +package com.google.protobuf.jruby; + +public final class SentinelOuterClass { + private SentinelOuterClass() {} + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + } + public interface SentinelOrBuilder extends + // @@protoc_insertion_point(interface_extends:com.google.protobuf.jruby.Sentinel) + com.google.protobuf.MessageOrBuilder { + + /** + * optional int32 default_int32 = 1; + */ + int getDefaultInt32(); + + /** + * optional int64 default_int64 = 2; + */ + long getDefaultInt64(); + + /** + * optional uint32 default_unit32 = 3; + */ + int getDefaultUnit32(); + + /** + * optional uint64 default_uint64 = 4; + */ + long getDefaultUint64(); + + /** + * optional string default_string = 5; + */ + java.lang.String getDefaultString(); + /** + * optional string default_string = 5; + */ + com.google.protobuf.ByteString + getDefaultStringBytes(); + + /** + * optional bool default_bool = 6; + */ + boolean getDefaultBool(); + + /** + * optional float default_float = 7; + */ + float getDefaultFloat(); + + /** + * optional double default_double = 8; + */ + double getDefaultDouble(); + + /** + * optional bytes default_bytes = 9; + */ + com.google.protobuf.ByteString getDefaultBytes(); + } + /** + * Protobuf type {@code com.google.protobuf.jruby.Sentinel} + */ + public static final class Sentinel extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:com.google.protobuf.jruby.Sentinel) + SentinelOrBuilder { + // Use Sentinel.newBuilder() to construct. + private Sentinel(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + } + private Sentinel() { + defaultInt32_ = 0; + defaultInt64_ = 0L; + defaultUnit32_ = 0; + defaultUint64_ = 0L; + defaultString_ = ""; + defaultBool_ = false; + defaultFloat_ = 0F; + defaultDouble_ = 0D; + defaultBytes_ = com.google.protobuf.ByteString.EMPTY; + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return com.google.protobuf.UnknownFieldSet.getDefaultInstance(); + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return com.google.protobuf.jruby.SentinelOuterClass.internal_static_com_google_protobuf_jruby_Sentinel_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return com.google.protobuf.jruby.SentinelOuterClass.internal_static_com_google_protobuf_jruby_Sentinel_fieldAccessorTable + .ensureFieldAccessorsInitialized( + com.google.protobuf.jruby.SentinelOuterClass.Sentinel.class, com.google.protobuf.jruby.SentinelOuterClass.Sentinel.Builder.class); + } + + public static final com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public Sentinel parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + public static final int DEFAULT_INT32_FIELD_NUMBER = 1; + private int defaultInt32_; + /** + * optional int32 default_int32 = 1; + */ + public int getDefaultInt32() { + return defaultInt32_; + } + + public static final int DEFAULT_INT64_FIELD_NUMBER = 2; + private long defaultInt64_; + /** + * optional int64 default_int64 = 2; + */ + public long getDefaultInt64() { + return defaultInt64_; + } + + public static final int DEFAULT_UNIT32_FIELD_NUMBER = 3; + private int defaultUnit32_; + /** + * optional uint32 default_unit32 = 3; + */ + public int getDefaultUnit32() { + return defaultUnit32_; + } + + public static final int DEFAULT_UINT64_FIELD_NUMBER = 4; + private long defaultUint64_; + /** + * optional uint64 default_uint64 = 4; + */ + public long getDefaultUint64() { + return defaultUint64_; + } + + public static final int DEFAULT_STRING_FIELD_NUMBER = 5; + private java.lang.Object defaultString_; + /** + * optional string default_string = 5; + */ + public java.lang.String getDefaultString() { + java.lang.Object ref = defaultString_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + defaultString_ = s; + } + return s; + } + } + /** + * optional string default_string = 5; + */ + public com.google.protobuf.ByteString + getDefaultStringBytes() { + java.lang.Object ref = defaultString_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + defaultString_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int DEFAULT_BOOL_FIELD_NUMBER = 6; + private boolean defaultBool_; + /** + * optional bool default_bool = 6; + */ + public boolean getDefaultBool() { + return defaultBool_; + } + + public static final int DEFAULT_FLOAT_FIELD_NUMBER = 7; + private float defaultFloat_; + /** + * optional float default_float = 7; + */ + public float getDefaultFloat() { + return defaultFloat_; + } + + public static final int DEFAULT_DOUBLE_FIELD_NUMBER = 8; + private double defaultDouble_; + /** + * optional double default_double = 8; + */ + public double getDefaultDouble() { + return defaultDouble_; + } + + public static final int DEFAULT_BYTES_FIELD_NUMBER = 9; + private com.google.protobuf.ByteString defaultBytes_; + /** + * optional bytes default_bytes = 9; + */ + public com.google.protobuf.ByteString getDefaultBytes() { + return defaultBytes_; + } + + public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return new Builder(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(com.google.protobuf.jruby.SentinelOuterClass.Sentinel prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code com.google.protobuf.jruby.Sentinel} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:com.google.protobuf.jruby.Sentinel) + com.google.protobuf.jruby.SentinelOuterClass.SentinelOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return com.google.protobuf.jruby.SentinelOuterClass.internal_static_com_google_protobuf_jruby_Sentinel_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return com.google.protobuf.jruby.SentinelOuterClass.internal_static_com_google_protobuf_jruby_Sentinel_fieldAccessorTable + .ensureFieldAccessorsInitialized( + com.google.protobuf.jruby.SentinelOuterClass.Sentinel.class, com.google.protobuf.jruby.SentinelOuterClass.Sentinel.Builder.class); + } + + // Construct using com.google.protobuf.jruby.SentinelOuterClass.Sentinel.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + public Builder clear() { + super.clear(); + defaultInt32_ = 0; + + defaultInt64_ = 0L; + + defaultUnit32_ = 0; + + defaultUint64_ = 0L; + + defaultString_ = ""; + + defaultBool_ = false; + + defaultFloat_ = 0F; + + defaultDouble_ = 0D; + + defaultBytes_ = com.google.protobuf.ByteString.EMPTY; + + return this; + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return com.google.protobuf.jruby.SentinelOuterClass.internal_static_com_google_protobuf_jruby_Sentinel_descriptor; + } + + public com.google.protobuf.jruby.SentinelOuterClass.Sentinel getDefaultInstanceForType() { + return com.google.protobuf.jruby.SentinelOuterClass.Sentinel.getDefaultInstance(); + } + + public com.google.protobuf.jruby.SentinelOuterClass.Sentinel build() { + com.google.protobuf.jruby.SentinelOuterClass.Sentinel result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public com.google.protobuf.jruby.SentinelOuterClass.Sentinel buildPartial() { + com.google.protobuf.jruby.SentinelOuterClass.Sentinel result = new com.google.protobuf.jruby.SentinelOuterClass.Sentinel(this); + result.defaultInt32_ = defaultInt32_; + result.defaultInt64_ = defaultInt64_; + result.defaultUnit32_ = defaultUnit32_; + result.defaultUint64_ = defaultUint64_; + result.defaultString_ = defaultString_; + result.defaultBool_ = defaultBool_; + result.defaultFloat_ = defaultFloat_; + result.defaultDouble_ = defaultDouble_; + result.defaultBytes_ = defaultBytes_; + onBuilt(); + return result; + } + + + private int defaultInt32_ ; + /** + * optional int32 default_int32 = 1; + */ + public int getDefaultInt32() { + return defaultInt32_; + } + /** + * optional int32 default_int32 = 1; + */ + public Builder setDefaultInt32(int value) { + + defaultInt32_ = value; + onChanged(); + return this; + } + /** + * optional int32 default_int32 = 1; + */ + public Builder clearDefaultInt32() { + + defaultInt32_ = 0; + onChanged(); + return this; + } + + private long defaultInt64_ ; + /** + * optional int64 default_int64 = 2; + */ + public long getDefaultInt64() { + return defaultInt64_; + } + /** + * optional int64 default_int64 = 2; + */ + public Builder setDefaultInt64(long value) { + + defaultInt64_ = value; + onChanged(); + return this; + } + /** + * optional int64 default_int64 = 2; + */ + public Builder clearDefaultInt64() { + + defaultInt64_ = 0L; + onChanged(); + return this; + } + + private int defaultUnit32_ ; + /** + * optional uint32 default_unit32 = 3; + */ + public int getDefaultUnit32() { + return defaultUnit32_; + } + /** + * optional uint32 default_unit32 = 3; + */ + public Builder setDefaultUnit32(int value) { + + defaultUnit32_ = value; + onChanged(); + return this; + } + /** + * optional uint32 default_unit32 = 3; + */ + public Builder clearDefaultUnit32() { + + defaultUnit32_ = 0; + onChanged(); + return this; + } + + private long defaultUint64_ ; + /** + * optional uint64 default_uint64 = 4; + */ + public long getDefaultUint64() { + return defaultUint64_; + } + /** + * optional uint64 default_uint64 = 4; + */ + public Builder setDefaultUint64(long value) { + + defaultUint64_ = value; + onChanged(); + return this; + } + /** + * optional uint64 default_uint64 = 4; + */ + public Builder clearDefaultUint64() { + + defaultUint64_ = 0L; + onChanged(); + return this; + } + + private java.lang.Object defaultString_ = ""; + /** + * optional string default_string = 5; + */ + public java.lang.String getDefaultString() { + java.lang.Object ref = defaultString_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + defaultString_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * optional string default_string = 5; + */ + public com.google.protobuf.ByteString + getDefaultStringBytes() { + java.lang.Object ref = defaultString_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + defaultString_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string default_string = 5; + */ + public Builder setDefaultString( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + + defaultString_ = value; + onChanged(); + return this; + } + /** + * optional string default_string = 5; + */ + public Builder clearDefaultString() { + + defaultString_ = getDefaultInstance().getDefaultString(); + onChanged(); + return this; + } + /** + * optional string default_string = 5; + */ + public Builder setDefaultStringBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + + defaultString_ = value; + onChanged(); + return this; + } + + private boolean defaultBool_ ; + /** + * optional bool default_bool = 6; + */ + public boolean getDefaultBool() { + return defaultBool_; + } + /** + * optional bool default_bool = 6; + */ + public Builder setDefaultBool(boolean value) { + + defaultBool_ = value; + onChanged(); + return this; + } + /** + * optional bool default_bool = 6; + */ + public Builder clearDefaultBool() { + + defaultBool_ = false; + onChanged(); + return this; + } + + private float defaultFloat_ ; + /** + * optional float default_float = 7; + */ + public float getDefaultFloat() { + return defaultFloat_; + } + /** + * optional float default_float = 7; + */ + public Builder setDefaultFloat(float value) { + + defaultFloat_ = value; + onChanged(); + return this; + } + /** + * optional float default_float = 7; + */ + public Builder clearDefaultFloat() { + + defaultFloat_ = 0F; + onChanged(); + return this; + } + + private double defaultDouble_ ; + /** + * optional double default_double = 8; + */ + public double getDefaultDouble() { + return defaultDouble_; + } + /** + * optional double default_double = 8; + */ + public Builder setDefaultDouble(double value) { + + defaultDouble_ = value; + onChanged(); + return this; + } + /** + * optional double default_double = 8; + */ + public Builder clearDefaultDouble() { + + defaultDouble_ = 0D; + onChanged(); + return this; + } + + private com.google.protobuf.ByteString defaultBytes_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes default_bytes = 9; + */ + public com.google.protobuf.ByteString getDefaultBytes() { + return defaultBytes_; + } + /** + * optional bytes default_bytes = 9; + */ + public Builder setDefaultBytes(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + + defaultBytes_ = value; + onChanged(); + return this; + } + /** + * optional bytes default_bytes = 9; + */ + public Builder clearDefaultBytes() { + + defaultBytes_ = getDefaultInstance().getDefaultBytes(); + onChanged(); + return this; + } + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return this; + } + + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return this; + } + + + // @@protoc_insertion_point(builder_scope:com.google.protobuf.jruby.Sentinel) + } + + // @@protoc_insertion_point(class_scope:com.google.protobuf.jruby.Sentinel) + private static final com.google.protobuf.jruby.SentinelOuterClass.Sentinel defaultInstance;static { + defaultInstance = new com.google.protobuf.jruby.SentinelOuterClass.Sentinel(); + } + + public static com.google.protobuf.jruby.SentinelOuterClass.Sentinel getDefaultInstance() { + return defaultInstance; + } + + public com.google.protobuf.jruby.SentinelOuterClass.Sentinel getDefaultInstanceForType() { + return defaultInstance; + } + + } + + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_com_google_protobuf_jruby_Sentinel_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_com_google_protobuf_jruby_Sentinel_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + java.lang.String[] descriptorData = { + "\n\016sentinel.proto\022\031com.google.protobuf.jr" + + "uby\"\334\001\n\010Sentinel\022\025\n\rdefault_int32\030\001 \001(\005\022" + + "\025\n\rdefault_int64\030\002 \001(\003\022\026\n\016default_unit32" + + "\030\003 \001(\r\022\026\n\016default_uint64\030\004 \001(\004\022\026\n\016defaul" + + "t_string\030\005 \001(\t\022\024\n\014default_bool\030\006 \001(\010\022\025\n\r" + + "default_float\030\007 \001(\002\022\026\n\016default_double\030\010 " + + "\001(\001\022\025\n\rdefault_bytes\030\t \001(\014B\002H\002b\006proto3" + }; + com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = + new com.google.protobuf.Descriptors.FileDescriptor. InternalDescriptorAssigner() { + public com.google.protobuf.ExtensionRegistry assignDescriptors( + com.google.protobuf.Descriptors.FileDescriptor root) { + descriptor = root; + return null; + } + }; + com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + }, assigner); + internal_static_com_google_protobuf_jruby_Sentinel_descriptor = + getDescriptor().getMessageTypes().get(0); + internal_static_com_google_protobuf_jruby_Sentinel_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_com_google_protobuf_jruby_Sentinel_descriptor, + new java.lang.String[] { "DefaultInt32", "DefaultInt64", "DefaultUnit32", "DefaultUint64", "DefaultString", "DefaultBool", "DefaultFloat", "DefaultDouble", "DefaultBytes", }); + } + + // @@protoc_insertion_point(outer_class_scope) +} diff --git a/ruby/src/main/java/com/google/protobuf/jruby/Utils.java b/ruby/src/main/java/com/google/protobuf/jruby/Utils.java new file mode 100644 index 00000000..596a0979 --- /dev/null +++ b/ruby/src/main/java/com/google/protobuf/jruby/Utils.java @@ -0,0 +1,300 @@ +/* + * Protocol Buffers - Google's data interchange format + * Copyright 2014 Google Inc. All rights reserved. + * https://developers.google.com/protocol-buffers/ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.google.protobuf.jruby; + +import com.google.protobuf.ByteString; +import com.google.protobuf.DescriptorProtos; +import com.google.protobuf.Descriptors; +import org.jcodings.Encoding; +import org.jcodings.specific.ASCIIEncoding; +import org.jcodings.specific.USASCIIEncoding; +import org.jcodings.specific.UTF8Encoding; +import org.jruby.*; +import org.jruby.runtime.Block; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; + +import java.math.BigInteger; + +public class Utils { + public static Descriptors.FieldDescriptor.Type rubyToFieldType(IRubyObject typeClass) { + return Descriptors.FieldDescriptor.Type.valueOf(typeClass.asJavaString().toUpperCase()); + } + + public static IRubyObject fieldTypeToRuby(ThreadContext context, Descriptors.FieldDescriptor.Type type) { + return fieldTypeToRuby(context, type.name()); + } + + public static IRubyObject fieldTypeToRuby(ThreadContext context, DescriptorProtos.FieldDescriptorProto.Type type) { + return fieldTypeToRuby(context, type.name()); + } + + private static IRubyObject fieldTypeToRuby(ThreadContext context, String typeName) { + + return context.runtime.newSymbol(typeName.replace("TYPE_", "").toLowerCase()); + } + + public static void checkType(ThreadContext context, Descriptors.FieldDescriptor.Type fieldType, + IRubyObject value, RubyModule typeClass) { + Ruby runtime = context.runtime; + Object val; + switch(fieldType) { + case INT32: + case INT64: + case UINT32: + case UINT64: + if (!isRubyNum(value)) { + throw runtime.newTypeError("Expected number type for integral field."); + } + switch(fieldType) { + case INT32: + RubyNumeric.num2int(value); + break; + case INT64: + RubyNumeric.num2long(value); + break; + case UINT32: + num2uint(value); + break; + default: + num2ulong(context.runtime, value); + break; + } + checkIntTypePrecision(context, fieldType, value); + break; + case FLOAT: + if (!isRubyNum(value)) + throw runtime.newTypeError("Expected number type for float field."); + break; + case DOUBLE: + if (!isRubyNum(value)) + throw runtime.newTypeError("Expected number type for double field."); + break; + case BOOL: + if (!(value instanceof RubyBoolean)) + throw runtime.newTypeError("Invalid argument for boolean field."); + break; + case BYTES: + case STRING: + validateStringEncoding(context.runtime, fieldType, value); + break; + case MESSAGE: + if (value.getMetaClass() != typeClass) { + throw runtime.newTypeError(value, typeClass); + } + break; + case ENUM: + if (value instanceof RubySymbol) { + Descriptors.EnumDescriptor enumDescriptor = + ((RubyEnumDescriptor) typeClass.getInstanceVariable(DESCRIPTOR_INSTANCE_VAR)).getDescriptor(); + val = enumDescriptor.findValueByName(value.asJavaString()); + if (val == null) + throw runtime.newRangeError("Enum value " + value + " is not found."); + } else if(!isRubyNum(value)) { + throw runtime.newTypeError("Expected number or symbol type for enum field."); + } + break; + default: + break; + } + } + + public static IRubyObject wrapPrimaryValue(ThreadContext context, Descriptors.FieldDescriptor.Type fieldType, Object value) { + Ruby runtime = context.runtime; + switch (fieldType) { + case INT32: + return runtime.newFixnum((Integer) value); + case INT64: + return runtime.newFixnum((Long) value); + case UINT32: + return runtime.newFixnum(((Integer) value) & (-1l >>> 32)); + case UINT64: + long ret = (Long) value; + return ret >= 0 ? runtime.newFixnum(ret) : + RubyBignum.newBignum(runtime, UINT64_COMPLEMENTARY.add(new BigInteger(ret + ""))); + case FLOAT: + return runtime.newFloat((Float) value); + case DOUBLE: + return runtime.newFloat((Double) value); + case BOOL: + return (Boolean) value ? runtime.getTrue() : runtime.getFalse(); + case BYTES: + return runtime.newString(((ByteString) value).toStringUtf8()); + case STRING: + return runtime.newString(value.toString()); + default: + return runtime.getNil(); + } + } + + public static int num2uint(IRubyObject value) { + long longVal = RubyNumeric.num2long(value); + if (longVal > UINT_MAX) + throw value.getRuntime().newRangeError("Integer " + longVal + " too big to convert to 'unsigned int'"); + long num = longVal; + if (num > Integer.MAX_VALUE || num < Integer.MIN_VALUE) + // encode to UINT32 + num = (-longVal ^ (-1l >>> 32) ) + 1; + RubyNumeric.checkInt(value, num); + return (int) num; + } + + public static long num2ulong(Ruby runtime, IRubyObject value) { + if (value instanceof RubyFloat) { + RubyBignum bignum = RubyBignum.newBignum(runtime, ((RubyFloat) value).getDoubleValue()); + return RubyBignum.big2ulong(bignum); + } else if (value instanceof RubyBignum) { + return RubyBignum.big2ulong((RubyBignum) value); + } else { + return RubyNumeric.num2long(value); + } + } + + public static void validateStringEncoding(Ruby runtime, Descriptors.FieldDescriptor.Type type, IRubyObject value) { + if (!(value instanceof RubyString)) + throw runtime.newTypeError("Invalid argument for string field."); + Encoding encoding = ((RubyString) value).getEncoding(); + switch(type) { + case BYTES: + if (encoding != ASCIIEncoding.INSTANCE) + throw runtime.newTypeError("Encoding for bytes fields" + + " must be \"ASCII-8BIT\", but was " + encoding); + break; + case STRING: + if (encoding != UTF8Encoding.INSTANCE + && encoding != USASCIIEncoding.INSTANCE) + throw runtime.newTypeError("Encoding for string fields" + + " must be \"UTF-8\" or \"ASCII\", but was " + encoding); + break; + default: + break; + } + } + + public static void checkNameAvailability(ThreadContext context, String name) { + if (context.runtime.getObject().getConstantAt(name) != null) + throw context.runtime.newNameError(name + " is already defined", name); + } + + /** + * Replace invalid "." in descriptor with __DOT__ + * @param name + * @return + */ + public static String escapeIdentifier(String name) { + return name.replace(".", BADNAME_REPLACEMENT); + } + + /** + * Replace __DOT__ in descriptor name with "." + * @param name + * @return + */ + public static String unescapeIdentifier(String name) { + return name.replace(BADNAME_REPLACEMENT, "."); + } + + public static boolean isMapEntry(Descriptors.FieldDescriptor fieldDescriptor) { + return fieldDescriptor.getType() == Descriptors.FieldDescriptor.Type.MESSAGE && + fieldDescriptor.isRepeated() && + fieldDescriptor.getMessageType().getOptions().getMapEntry(); + } + + public static RubyFieldDescriptor msgdefCreateField(ThreadContext context, String label, IRubyObject name, + IRubyObject type, IRubyObject number, IRubyObject typeClass, RubyClass cFieldDescriptor) { + Ruby runtime = context.runtime; + RubyFieldDescriptor fieldDef = (RubyFieldDescriptor) cFieldDescriptor.newInstance(context, Block.NULL_BLOCK); + fieldDef.setLabel(context, runtime.newString(label)); + fieldDef.setName(context, name); + fieldDef.setType(context, type); + fieldDef.setNumber(context, number); + + if (!typeClass.isNil()) { + if (!(typeClass instanceof RubyString)) { + throw runtime.newArgumentError("expected string for type class"); + } + fieldDef.setSubmsgName(context, typeClass); + } + return fieldDef; + } + + protected static void checkIntTypePrecision(ThreadContext context, Descriptors.FieldDescriptor.Type type, IRubyObject value) { + if (value instanceof RubyFloat) { + double doubleVal = RubyNumeric.num2dbl(value); + if (Math.floor(doubleVal) != doubleVal) { + throw context.runtime.newRangeError("Non-integral floating point value assigned to integer field."); + } + } + if (type == Descriptors.FieldDescriptor.Type.UINT32 || type == Descriptors.FieldDescriptor.Type.UINT64) { + if (RubyNumeric.num2dbl(value) < 0) { + throw context.runtime.newRangeError("Assigning negative value to unsigned integer field."); + } + } + } + + protected static boolean isRubyNum(Object value) { + return value instanceof RubyFixnum || value instanceof RubyFloat || value instanceof RubyBignum; + } + + protected static void validateTypeClass(ThreadContext context, Descriptors.FieldDescriptor.Type type, IRubyObject value) { + Ruby runtime = context.runtime; + if (!(value instanceof RubyModule)) { + throw runtime.newArgumentError("TypeClass has incorrect type"); + } + RubyModule klass = (RubyModule) value; + IRubyObject descriptor = klass.getInstanceVariable(DESCRIPTOR_INSTANCE_VAR); + if (descriptor.isNil()) { + throw runtime.newArgumentError("Type class has no descriptor. Please pass a " + + "class or enum as returned by the DescriptorPool."); + } + if (type == Descriptors.FieldDescriptor.Type.MESSAGE) { + if (! (descriptor instanceof RubyDescriptor)) { + throw runtime.newArgumentError("Descriptor has an incorrect type"); + } + } else if (type == Descriptors.FieldDescriptor.Type.ENUM) { + if (! (descriptor instanceof RubyEnumDescriptor)) { + throw runtime.newArgumentError("Descriptor has an incorrect type"); + } + } + } + + public static String BADNAME_REPLACEMENT = "__DOT__"; + + public static String DESCRIPTOR_INSTANCE_VAR = "@descriptor"; + + public static String EQUAL_SIGN = "="; + + private static BigInteger UINT64_COMPLEMENTARY = new BigInteger("18446744073709551616"); //Math.pow(2, 64) + + private static long UINT_MAX = 0xffffffffl; +} diff --git a/ruby/src/main/java/google/ProtobufJavaService.java b/ruby/src/main/java/google/ProtobufJavaService.java new file mode 100644 index 00000000..bffb492a --- /dev/null +++ b/ruby/src/main/java/google/ProtobufJavaService.java @@ -0,0 +1,60 @@ +/* + * Protocol Buffers - Google's data interchange format + * Copyright 2014 Google Inc. All rights reserved. + * https://developers.google.com/protocol-buffers/ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package google; + +import com.google.protobuf.jruby.*; +import org.jruby.Ruby; +import org.jruby.runtime.load.BasicLibraryService; + +import java.io.IOException; + +public class ProtobufJavaService implements BasicLibraryService { + @Override + public boolean basicLoad(Ruby ruby) throws IOException { + ruby.defineModule("Google"); + RubyProtobuf.createProtobuf(ruby); + RubyDescriptor.createRubyDescriptor(ruby); + RubyBuilder.createRubyBuilder(ruby); + RubyFieldDescriptor.createRubyFileDescriptor(ruby); + RubyMessageBuilderContext.createRubyMessageBuilderContext(ruby); + RubyEnumDescriptor.createRubyEnumDescriptor(ruby); + RubyEnumBuilderContext.createRubyEnumBuilderContext(ruby); + RubyDescriptorPool.createRubyDescriptorPool(ruby); + RubyRepeatedField.createRubyRepeatedField(ruby); + RubyFieldDescriptor.createRubyFileDescriptor(ruby); + RubyMap.createRubyMap(ruby); + RubyOneofDescriptor.createRubyOneofDescriptor(ruby); + RubyOneofBuilderContext.createRubyOneofBuilderContext(ruby); + return true; + } +} diff --git a/ruby/src/main/sentinel.proto b/ruby/src/main/sentinel.proto new file mode 100644 index 00000000..89a1ae19 --- /dev/null +++ b/ruby/src/main/sentinel.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; +package com.google.protobuf.jruby; +option optimize_for = CODE_SIZE; + +message Sentinel { + optional int32 default_int32 = 1; + optional int64 default_int64 = 2; + optional uint32 default_unit32 = 3; + optional uint64 default_uint64 = 4; + optional string default_string = 5; + optional bool default_bool = 6; + optional float default_float = 7; + optional double default_double = 8; + optional bytes default_bytes = 9; +} diff --git a/ruby/tests/basic.rb b/ruby/tests/basic.rb index a78cc394..9d0f0a98 100644 --- a/ruby/tests/basic.rb +++ b/ruby/tests/basic.rb @@ -972,6 +972,7 @@ module BasicTest end def test_json + skip("Unimplemented") if RUBY_PLATFORM == "java" m = TestMessage.new(:optional_int32 => 1234, :optional_int64 => -0x1_0000_0000, :optional_uint32 => 0x8000_0000, @@ -994,6 +995,7 @@ module BasicTest end def test_json_maps + skip("Unimplemented") if RUBY_PLATFORM == "java" m = MapMessage.new(:map_string_int32 => {"a" => 1}) expected = '{"map_string_int32":{"a":1},"map_string_msg":{}}' assert MapMessage.encode_json(m) == expected diff --git a/ruby/tests/stress.rb b/ruby/tests/stress.rb index 1bd768ed..082d5e22 100644 --- a/ruby/tests/stress.rb +++ b/ruby/tests/stress.rb @@ -30,7 +30,7 @@ module StressTest 100_000.times do mnew = TestMessage.decode(data) mnew = mnew.dup - assert mnew.inspect == m.inspect + assert_equal mnew.inspect, m.inspect assert TestMessage.encode(mnew) == data end end -- cgit v1.2.3 From e9abbd23fbb60da882833e7212345a4250665c92 Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Mon, 13 Apr 2015 14:01:54 -0700 Subject: Fixed issue #283: crash in JSON handler cleanup. Includes repro test case from @wfarr. --- ruby/ext/google/protobuf_c/defs.c | 2 +- ruby/tests/basic.rb | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) (limited to 'ruby') diff --git a/ruby/ext/google/protobuf_c/defs.c b/ruby/ext/google/protobuf_c/defs.c index b39c27f4..9b590a55 100644 --- a/ruby/ext/google/protobuf_c/defs.c +++ b/ruby/ext/google/protobuf_c/defs.c @@ -250,7 +250,7 @@ void Descriptor_free(void* _self) { &self->pb_serialize_handlers); } if (self->json_serialize_handlers) { - upb_handlers_unref(self->pb_serialize_handlers, + upb_handlers_unref(self->json_serialize_handlers, &self->json_serialize_handlers); } xfree(self); diff --git a/ruby/tests/basic.rb b/ruby/tests/basic.rb index 9d0f0a98..1c3fb62c 100644 --- a/ruby/tests/basic.rb +++ b/ruby/tests/basic.rb @@ -8,6 +8,19 @@ require 'test/unit' module BasicTest pool = Google::Protobuf::DescriptorPool.new pool.build do + add_message "Foo" do + optional :bar, :message, 1, "Bar" + repeated :baz, :message, 2, "Baz" + end + + add_message "Bar" do + optional :msg, :string, 1 + end + + add_message "Baz" do + optional :msg, :string, 1 + end + add_message "TestMessage" do optional :optional_int32, :int32, 1 optional :optional_int64, :int64, 2 @@ -84,6 +97,9 @@ module BasicTest end end + Foo = pool.lookup("Foo").msgclass + Bar = pool.lookup("Bar").msgclass + Baz = pool.lookup("Baz").msgclass TestMessage = pool.lookup("TestMessage").msgclass TestMessage2 = pool.lookup("TestMessage2").msgclass Recursive1 = pool.lookup("Recursive1").msgclass @@ -992,6 +1008,14 @@ module BasicTest json_text = TestMessage.encode_json(m) m2 = TestMessage.decode_json(json_text) assert m == m2 + + # Crash case from GitHub issue 283. + bar = Bar.new(msg: "bar") + baz1 = Baz.new(msg: "baz") + baz2 = Baz.new(msg: "quux") + Foo.encode_json(Foo.new) + Foo.encode_json(Foo.new(bar: bar)) + Foo.encode_json(Foo.new(bar: bar, baz: [baz1, baz2])) end def test_json_maps -- cgit v1.2.3 From 14fd96224c585a365f3859b9c1b1eb87b9ebc230 Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Mon, 13 Apr 2015 14:04:12 -0700 Subject: Bump gem version to release bugfix. --- ruby/google-protobuf.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'ruby') diff --git a/ruby/google-protobuf.gemspec b/ruby/google-protobuf.gemspec index 4c9449d2..abbbde35 100644 --- a/ruby/google-protobuf.gemspec +++ b/ruby/google-protobuf.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |s| s.name = "google-protobuf" - s.version = "3.0.0.alpha.3.0.pre" + s.version = "3.0.0.alpha.3.1.pre" s.licenses = ["BSD"] s.summary = "Protocol Buffers" s.description = "Protocol Buffers are Google's data interchange format." -- cgit v1.2.3 From 761cfa08e6cc60039a0bf490d75078d6738724ae Mon Sep 17 00:00:00 2001 From: Adam Greene Date: Fri, 1 May 2015 08:48:56 -0700 Subject: build cleanups * update docs to simplify build steps * Gemfile.lock seemed to have an older version specified * do not check in the pkg dir --- ruby/.gitignore | 1 + ruby/Gemfile.lock | 2 +- ruby/README.md | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) (limited to 'ruby') diff --git a/ruby/.gitignore b/ruby/.gitignore index 80c978f2..93e24502 100644 --- a/ruby/.gitignore +++ b/ruby/.gitignore @@ -4,3 +4,4 @@ tags lib/google/protobuf_java.jar protobuf-jruby.iml target/ +pkg/ diff --git a/ruby/Gemfile.lock b/ruby/Gemfile.lock index 89deb47d..6f349276 100644 --- a/ruby/Gemfile.lock +++ b/ruby/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - google-protobuf (3.0.0.alpha.2) + google-protobuf (3.0.0.alpha.3.1.pre) GEM remote: https://rubygems.org/ diff --git a/ruby/README.md b/ruby/README.md index d2fa76ab..9ae3ac36 100644 --- a/ruby/README.md +++ b/ruby/README.md @@ -76,7 +76,7 @@ Then install the required Ruby gems: Then build the Gem: $ rake gem - $ gem install pkg/protobuf-$VERSION.gem + $ gem install `ls pkg/google-protobuf-*.gem` To run the specs: -- cgit v1.2.3 From c70b6058eaae4fa5b1af577c548e6809a53dfd98 Mon Sep 17 00:00:00 2001 From: Adam Greene Date: Fri, 1 May 2015 08:54:18 -0700 Subject: add size alias for length starting to make `RepeatedField` quack like an array additional changes: * make sure gemspec gets all ruby code files * add homepage in gem spec removes one of the warnings, and the gem spec authors are pushing everyone to include a homepage in the gem * remove excess whitespace in test suite to bring formatting inline with the rest of the file --- ruby/.gitignore | 1 + ruby/google-protobuf.gemspec | 3 ++- ruby/lib/google/protobuf.rb | 2 ++ ruby/lib/google/protobuf/repeated_field.rb | 40 ++++++++++++++++++++++++++++++ ruby/tests/basic.rb | 14 +++++++++-- 5 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 ruby/lib/google/protobuf/repeated_field.rb (limited to 'ruby') diff --git a/ruby/.gitignore b/ruby/.gitignore index 93e24502..bd8745dd 100644 --- a/ruby/.gitignore +++ b/ruby/.gitignore @@ -5,3 +5,4 @@ lib/google/protobuf_java.jar protobuf-jruby.iml target/ pkg/ +tmp/ diff --git a/ruby/google-protobuf.gemspec b/ruby/google-protobuf.gemspec index abbbde35..28cdebf5 100644 --- a/ruby/google-protobuf.gemspec +++ b/ruby/google-protobuf.gemspec @@ -4,10 +4,11 @@ Gem::Specification.new do |s| s.licenses = ["BSD"] s.summary = "Protocol Buffers" s.description = "Protocol Buffers are Google's data interchange format." + s.homepage = "https://developers.google.com/protocol-buffers" s.authors = ["Protobuf Authors"] s.email = "protobuf@googlegroups.com" s.require_paths = ["lib"] - s.files = ["lib/google/protobuf.rb"] + s.files = `git ls-files -z`.split("\x0").find_all{|f| f =~ /lib\/.+\.rb/} unless RUBY_PLATFORM == "java" s.files += `git ls-files "*.c" "*.h" extconf.rb Makefile`.split s.extensions= ["ext/google/protobuf_c/extconf.rb"] diff --git a/ruby/lib/google/protobuf.rb b/ruby/lib/google/protobuf.rb index 75869dd8..72797245 100644 --- a/ruby/lib/google/protobuf.rb +++ b/ruby/lib/google/protobuf.rb @@ -34,3 +34,5 @@ if RUBY_PLATFORM == "java" else require 'google/protobuf_c' end + +require 'google/protobuf/repeated_field' diff --git a/ruby/lib/google/protobuf/repeated_field.rb b/ruby/lib/google/protobuf/repeated_field.rb new file mode 100644 index 00000000..5b934e56 --- /dev/null +++ b/ruby/lib/google/protobuf/repeated_field.rb @@ -0,0 +1,40 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# https://developers.google.com/protocol-buffers/ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# add syntatic sugar on top of the core library +module Google + module Protobuf + class RepeatedField + + alias_method :size, :length + + end + end +end diff --git a/ruby/tests/basic.rb b/ruby/tests/basic.rb index 1c3fb62c..307374e3 100644 --- a/ruby/tests/basic.rb +++ b/ruby/tests/basic.rb @@ -377,6 +377,18 @@ module BasicTest end end + def test_rptfield_array_ducktyping + l = Google::Protobuf::RepeatedField.new(:int32) + length_methods = %w(count length size) + length_methods.each do |lm| + assert l.send(lm) == 0 + end + l.push 4 + length_methods.each do |lm| + assert l.send(lm) == 1 + end + end + def test_map_basic # allowed key types: # :int32, :int64, :uint32, :uint64, :bool, :string, :bytes. @@ -827,7 +839,6 @@ module BasicTest assert m['a.b'] == 4 end - def test_int_ranges m = TestMessage.new @@ -933,7 +944,6 @@ module BasicTest assert_raise RangeError do m.optional_uint64 = 1.5 end - end def test_stress_test -- cgit v1.2.3 From d55733c76ee1db702529f38f602548ffe48a4ab1 Mon Sep 17 00:00:00 2001 From: Adam Greene Date: Fri, 1 May 2015 11:54:29 -0700 Subject: return nil if array index indicie is out of bounds ruby arrays don't throw an exception; they return nil. Lets do the same! this fix also includes the ability to use negative array indicies --- ruby/README.md | 5 ++- ruby/Rakefile | 3 ++ ruby/ext/google/protobuf_c/repeated_field.c | 20 +++++++--- ruby/pom.xml | 2 +- .../com/google/protobuf/jruby/RubyMessage.java | 14 +++---- .../google/protobuf/jruby/RubyRepeatedField.java | 24 +++++++++--- ruby/tests/basic.rb | 43 ++++++++++++++++++++-- 7 files changed, 85 insertions(+), 26 deletions(-) (limited to 'ruby') diff --git a/ruby/README.md b/ruby/README.md index 9ae3ac36..16474322 100644 --- a/ruby/README.md +++ b/ruby/README.md @@ -63,7 +63,7 @@ To build this Ruby extension, you will need: To Build the JRuby extension, you will need: * Maven -* The latest version of the protobuf java library +* The latest version of the protobuf java library (see ../java/README.md) * Install JRuby via rbenv or RVM First switch to the desired platform with rbenv or RVM. @@ -75,7 +75,8 @@ Then install the required Ruby gems: Then build the Gem: - $ rake gem + $ rake + $ rake clobber_package gem $ gem install `ls pkg/google-protobuf-*.gem` To run the specs: diff --git a/ruby/Rakefile b/ruby/Rakefile index 7c1d8495..c25103d8 100644 --- a/ruby/Rakefile +++ b/ruby/Rakefile @@ -6,6 +6,9 @@ require "rake/testtask" spec = Gem::Specification.load("google-protobuf.gemspec") if RUBY_PLATFORM == "java" + if `which mvn` == '' + raise ArgumentError, "maven needs to be installed" + end task :clean do system("mvn clean") end diff --git a/ruby/ext/google/protobuf_c/repeated_field.c b/ruby/ext/google/protobuf_c/repeated_field.c index 8cf2e29b..5148ee87 100644 --- a/ruby/ext/google/protobuf_c/repeated_field.c +++ b/ruby/ext/google/protobuf_c/repeated_field.c @@ -47,6 +47,15 @@ RepeatedField* ruby_to_RepeatedField(VALUE _self) { return self; } +static int index_position(VALUE _index, RepeatedField* repeated_field) { + int index = NUM2INT(_index); + if (index < 0 && repeated_field->size > 0) { + index = repeated_field->size + index; + } + return index; +} + + /* * call-seq: * RepeatedField.each(&block) @@ -74,8 +83,7 @@ VALUE RepeatedField_each(VALUE _self) { * call-seq: * RepeatedField.[](index) => value * - * Accesses the element at the given index. Throws an exception on out-of-bounds - * errors. + * Accesses the element at the given index. Returns nil on out-of-bounds */ VALUE RepeatedField_index(VALUE _self, VALUE _index) { RepeatedField* self = ruby_to_RepeatedField(_self); @@ -83,9 +91,9 @@ VALUE RepeatedField_index(VALUE _self, VALUE _index) { upb_fieldtype_t field_type = self->field_type; VALUE field_type_class = self->field_type_class; - int index = NUM2INT(_index); + int index = index_position(_index, self); if (index < 0 || index >= self->size) { - rb_raise(rb_eRangeError, "Index out of range"); + return Qnil; } void* memory = (void *) (((uint8_t *)self->elements) + index * element_size); @@ -105,9 +113,9 @@ VALUE RepeatedField_index_set(VALUE _self, VALUE _index, VALUE val) { VALUE field_type_class = self->field_type_class; int element_size = native_slot_size(field_type); - int index = NUM2INT(_index); + int index = index_position(_index, self); if (index < 0 || index >= (INT_MAX - 1)) { - rb_raise(rb_eRangeError, "Index out of range"); + return Qnil; } if (index >= self->size) { RepeatedField_reserve(self, index + 1); diff --git a/ruby/pom.xml b/ruby/pom.xml index 1630fe84..01f0e16b 100644 --- a/ruby/pom.xml +++ b/ruby/pom.xml @@ -78,7 +78,7 @@ com.google.protobuf protobuf-java - 3.0.0-pre + 3.0.0-alpha-3-pre diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyMessage.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyMessage.java index 04bc0b76..20e825e2 100644 --- a/ruby/src/main/java/com/google/protobuf/jruby/RubyMessage.java +++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyMessage.java @@ -246,16 +246,15 @@ public class RubyMessage extends RubyObject { public IRubyObject dup(ThreadContext context) { RubyMessage dup = (RubyMessage) metaClass.newInstance(context, Block.NULL_BLOCK); IRubyObject value; - for (Descriptors.FieldDescriptor fieldDescriptor : builder.getAllFields().keySet()) { + for (Descriptors.FieldDescriptor fieldDescriptor : this.descriptor.getFields()) { if (fieldDescriptor.isRepeated()) { - dup.repeatedFields.put(fieldDescriptor, getRepeatedField(context, fieldDescriptor)); - } else if (builder.hasField(fieldDescriptor)) { - dup.fields.put(fieldDescriptor, wrapField(context, fieldDescriptor, builder.getField(fieldDescriptor))); + dup.addRepeatedField(fieldDescriptor, this.getRepeatedField(context, fieldDescriptor)); + } else if (fields.containsKey(fieldDescriptor)) { + dup.fields.put(fieldDescriptor, fields.get(fieldDescriptor)); + } else if (this.builder.hasField(fieldDescriptor)) { + dup.fields.put(fieldDescriptor, wrapField(context, fieldDescriptor, this.builder.getField(fieldDescriptor))); } } - for (Descriptors.FieldDescriptor fieldDescriptor : fields.keySet()) { - dup.fields.put(fieldDescriptor, fields.get(fieldDescriptor)); - } for (Descriptors.FieldDescriptor fieldDescriptor : maps.keySet()) { dup.maps.put(fieldDescriptor, maps.get(fieldDescriptor)); } @@ -411,6 +410,7 @@ public class RubyMessage extends RubyObject { for (int i = 0; i < count; i++) { ret.push(context, wrapField(context, fieldDescriptor, this.builder.getRepeatedField(fieldDescriptor, i))); } + addRepeatedField(fieldDescriptor, ret); return ret; } diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyRepeatedField.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyRepeatedField.java index 9788317a..84bf8956 100644 --- a/ruby/src/main/java/com/google/protobuf/jruby/RubyRepeatedField.java +++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyRepeatedField.java @@ -108,8 +108,9 @@ public class RubyRepeatedField extends RubyObject { */ @JRubyMethod(name = "[]=") public IRubyObject indexSet(ThreadContext context, IRubyObject index, IRubyObject value) { + int arrIndex = normalizeArrayIndex(index); Utils.checkType(context, fieldType, value, (RubyModule) typeClass); - this.storage.set(RubyNumeric.num2int(index), value); + this.storage.set(arrIndex, value); return context.runtime.getNil(); } @@ -117,12 +118,15 @@ public class RubyRepeatedField extends RubyObject { * call-seq: * RepeatedField.[](index) => value * - * Accesses the element at the given index. Throws an exception on out-of-bounds - * errors. + * Accesses the element at the given index. Returns nil on out-of-bounds */ @JRubyMethod(name = "[]") public IRubyObject index(ThreadContext context, IRubyObject index) { - return this.storage.eltInternal(RubyNumeric.num2int(index)); + int arrIndex = normalizeArrayIndex(index); + if (arrIndex < 0 || arrIndex >= this.storage.size()) { + return context.runtime.getNil(); + } + return this.storage.eltInternal(arrIndex); } /* @@ -134,8 +138,7 @@ public class RubyRepeatedField extends RubyObject { @JRubyMethod(rest = true) public IRubyObject insert(ThreadContext context, IRubyObject[] args) { for (int i = 0; i < args.length; i++) { - Utils.checkType(context, fieldType, args[i], (RubyModule) typeClass); - this.storage.add(args[i]); + push(context, args[i]); } return context.runtime.getNil(); } @@ -385,6 +388,15 @@ public class RubyRepeatedField extends RubyObject { } } + private int normalizeArrayIndex(IRubyObject index) { + int arrIndex = RubyNumeric.num2int(index); + int arrSize = this.storage.size(); + if (arrIndex < 0 && arrSize > 0) { + arrIndex = arrSize + arrIndex; + } + return arrIndex; + } + private RubyArray storage; private Descriptors.FieldDescriptor.Type fieldType; private IRubyObject typeClass; diff --git a/ruby/tests/basic.rb b/ruby/tests/basic.rb index 307374e3..141ce7c5 100644 --- a/ruby/tests/basic.rb +++ b/ruby/tests/basic.rb @@ -314,6 +314,17 @@ module BasicTest assert l4 == [0, 0, 0, 0, 0, 42, 100, 101, 102] end + def test_parent_rptfield + #make sure we set the RepeatedField and can add to it + m = TestMessage.new + assert m.repeated_string == [] + m.repeated_string << 'ok' + m.repeated_string.push('ok2') + assert m.repeated_string == ['ok', 'ok2'] + m.repeated_string += ['ok3'] + assert m.repeated_string == ['ok', 'ok2', 'ok3'] + end + def test_rptfield_msg l = Google::Protobuf::RepeatedField.new(:message, TestMessage) l.push TestMessage.new @@ -383,10 +394,31 @@ module BasicTest length_methods.each do |lm| assert l.send(lm) == 0 end + # out of bounds returns a nil + assert l[0] == nil + assert l[1] == nil + assert l[-1] == nil l.push 4 length_methods.each do |lm| - assert l.send(lm) == 1 + assert l.send(lm) == 1 + end + assert l[0] == 4 + assert l[1] == nil + assert l[-1] == 4 + assert l[-2] == nil + + l.push 2 + length_methods.each do |lm| + assert l.send(lm) == 2 end + assert l[0] == 4 + assert l[1] == 2 + assert l[2] == nil + assert l[-1] == 2 + assert l[-2] == 4 + assert l[-3] == nil + + #adding out of scope will backfill with empty objects end def test_map_basic @@ -724,9 +756,12 @@ module BasicTest m = TestMessage.new m.optional_string = "hello" m.optional_int32 = 42 - m.repeated_msg.push TestMessage2.new(:foo => 100) - m.repeated_msg.push TestMessage2.new(:foo => 200) - + tm1 = TestMessage2.new(:foo => 100) + tm2 = TestMessage2.new(:foo => 200) + m.repeated_msg.push tm1 + assert m.repeated_msg[-1] == tm1 + m.repeated_msg.push tm2 + assert m.repeated_msg[-1] == tm2 m2 = m.dup assert m == m2 m.optional_int32 += 1 -- cgit v1.2.3 From 64678265c5ae28998d031900c2de52419a8ed7e4 Mon Sep 17 00:00:00 2001 From: Adam Greene Date: Sat, 2 May 2015 13:48:23 -0700 Subject: allow a message field to be unset --- ruby/ext/google/protobuf_c/storage.c | 4 +++- ruby/pom.xml | 2 +- .../main/java/com/google/protobuf/jruby/RubyMessage.java | 15 ++++++++++----- ruby/tests/basic.rb | 2 ++ 4 files changed, 16 insertions(+), 7 deletions(-) (limited to 'ruby') diff --git a/ruby/ext/google/protobuf_c/storage.c b/ruby/ext/google/protobuf_c/storage.c index 5b1549d2..2ad8bd74 100644 --- a/ruby/ext/google/protobuf_c/storage.c +++ b/ruby/ext/google/protobuf_c/storage.c @@ -155,7 +155,9 @@ void native_slot_set_value_and_case(upb_fieldtype_t type, VALUE type_class, break; } case UPB_TYPE_MESSAGE: { - if (CLASS_OF(value) != type_class) { + if (CLASS_OF(value) == CLASS_OF(Qnil)) { + value = Qnil; + } else if (CLASS_OF(value) != type_class) { rb_raise(rb_eTypeError, "Invalid type %s to assign to submessage field.", rb_class2name(CLASS_OF(value))); diff --git a/ruby/pom.xml b/ruby/pom.xml index 1630fe84..01f0e16b 100644 --- a/ruby/pom.xml +++ b/ruby/pom.xml @@ -78,7 +78,7 @@ com.google.protobuf protobuf-java - 3.0.0-pre + 3.0.0-alpha-3-pre diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyMessage.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyMessage.java index 04bc0b76..9ddadfcd 100644 --- a/ruby/src/main/java/com/google/protobuf/jruby/RubyMessage.java +++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyMessage.java @@ -659,14 +659,14 @@ public class RubyMessage extends RubyObject { } else { Descriptors.FieldDescriptor.Type fieldType = fieldDescriptor.getType(); IRubyObject typeClass = context.runtime.getObject(); + boolean addValue = true; if (fieldType == Descriptors.FieldDescriptor.Type.MESSAGE) { typeClass = ((RubyDescriptor) getDescriptorForField(context, fieldDescriptor)).msgclass(context); + if (value.isNil()){ + addValue = false; + } } else if (fieldType == Descriptors.FieldDescriptor.Type.ENUM) { typeClass = ((RubyEnumDescriptor) getDescriptorForField(context, fieldDescriptor)).enummodule(context); - } - Utils.checkType(context, fieldType, value, (RubyModule) typeClass); - // Convert integer enum to symbol - if (fieldType == Descriptors.FieldDescriptor.Type.ENUM) { Descriptors.EnumDescriptor enumDescriptor = fieldDescriptor.getEnumType(); if (Utils.isRubyNum(value)) { Descriptors.EnumValueDescriptor val = @@ -674,7 +674,12 @@ public class RubyMessage extends RubyObject { if (val.getIndex() != -1) value = context.runtime.newSymbol(val.getName()); } } - this.fields.put(fieldDescriptor, value); + if (addValue) { + Utils.checkType(context, fieldType, value, (RubyModule) typeClass); + this.fields.put(fieldDescriptor, value); + } else { + this.fields.remove(fieldDescriptor); + } } } return context.runtime.getNil(); diff --git a/ruby/tests/basic.rb b/ruby/tests/basic.rb index 307374e3..0eb5cefc 100644 --- a/ruby/tests/basic.rb +++ b/ruby/tests/basic.rb @@ -154,6 +154,8 @@ module BasicTest assert m.optional_bytes == "world" m.optional_msg = TestMessage2.new(:foo => 42) assert m.optional_msg == TestMessage2.new(:foo => 42) + m.optional_msg = nil + assert m.optional_msg == nil end def test_ctor_args -- cgit v1.2.3 From d1b52a00e002fd7a4adbcdeafa0634de6088a88f Mon Sep 17 00:00:00 2001 From: Adam Greene Date: Sun, 3 May 2015 10:55:10 -0700 Subject: adding and simplifying encoders/decoders * make consistent between mri and jruby * create a #to_h and have it use symbols for keys * add #to_json and #to_proto helpers on the Google::Protobuf message classes --- ruby/ext/google/protobuf_c/encode_decode.c | 47 ----------------- ruby/ext/google/protobuf_c/message.c | 31 +++++++++++ ruby/ext/google/protobuf_c/protobuf.c | 7 --- ruby/ext/google/protobuf_c/protobuf.h | 6 +-- ruby/lib/google/protobuf.rb | 25 +++++++++ ruby/lib/google/protobuf/message_exts.rb | 53 +++++++++++++++++++ .../com/google/protobuf/jruby/RubyDescriptor.java | 2 + .../java/com/google/protobuf/jruby/RubyMap.java | 2 +- .../com/google/protobuf/jruby/RubyMessage.java | 12 +++-- .../com/google/protobuf/jruby/RubyProtobuf.java | 50 ------------------ ruby/tests/basic.rb | 61 ++++++++++++++++++++++ 11 files changed, 182 insertions(+), 114 deletions(-) create mode 100644 ruby/lib/google/protobuf/message_exts.rb (limited to 'ruby') diff --git a/ruby/ext/google/protobuf_c/encode_decode.c b/ruby/ext/google/protobuf_c/encode_decode.c index 5730504d..f9a046cb 100644 --- a/ruby/ext/google/protobuf_c/encode_decode.c +++ b/ruby/ext/google/protobuf_c/encode_decode.c @@ -1118,50 +1118,3 @@ VALUE Message_encode_json(VALUE klass, VALUE msg_rb) { return ret; } -/* - * call-seq: - * Google::Protobuf.encode(msg) => bytes - * - * Encodes the given message object to protocol buffers wire format. This is an - * alternative to the #encode method on msg's class. - */ -VALUE Google_Protobuf_encode(VALUE self, VALUE msg_rb) { - VALUE klass = CLASS_OF(msg_rb); - return Message_encode(klass, msg_rb); -} - -/* - * call-seq: - * Google::Protobuf.encode_json(msg) => json_string - * - * Encodes the given message object to its JSON representation. This is an - * alternative to the #encode_json method on msg's class. - */ -VALUE Google_Protobuf_encode_json(VALUE self, VALUE msg_rb) { - VALUE klass = CLASS_OF(msg_rb); - return Message_encode_json(klass, msg_rb); -} - -/* - * call-seq: - * Google::Protobuf.decode(class, bytes) => msg - * - * Decodes the given bytes as protocol buffers wire format under the - * interpretation given by the given class's message definition. This is an - * alternative to the #decode method on the given class. - */ -VALUE Google_Protobuf_decode(VALUE self, VALUE klass, VALUE msg_rb) { - return Message_decode(klass, msg_rb); -} - -/* - * call-seq: - * Google::Protobuf.decode_json(class, json_string) => msg - * - * Decodes the given JSON string under the interpretation given by the given - * class's message definition. This is an alternative to the #decode_json method - * on the given class. - */ -VALUE Google_Protobuf_decode_json(VALUE self, VALUE klass, VALUE msg_rb) { - return Message_decode_json(klass, msg_rb); -} diff --git a/ruby/ext/google/protobuf_c/message.c b/ruby/ext/google/protobuf_c/message.c index 7e58a617..6e850427 100644 --- a/ruby/ext/google/protobuf_c/message.c +++ b/ruby/ext/google/protobuf_c/message.c @@ -329,6 +329,30 @@ VALUE Message_inspect(VALUE _self) { return str; } + +VALUE Message_to_h(VALUE _self) { + MessageHeader* self; + TypedData_Get_Struct(_self, MessageHeader, &Message_type, self); + + VALUE hash = rb_hash_new(); + + upb_msg_field_iter it; + for (upb_msg_field_begin(&it, self->descriptor->msgdef); + !upb_msg_field_done(&it); + upb_msg_field_next(&it)) { + const upb_fielddef* field = upb_msg_iter_field(&it); + VALUE msg_value = layout_get(self->descriptor->layout, Message_data(self), field); + VALUE msg_key = ID2SYM(rb_intern(upb_fielddef_name(field))); + if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { + msg_value = RepeatedField_to_ary(msg_value); + } + rb_hash_aset(hash, msg_key, msg_value); + } + return hash; +} + + + /* * call-seq: * Message.[](index) => value @@ -399,6 +423,10 @@ VALUE build_class_from_descriptor(Descriptor* desc) { rb_cObject); rb_iv_set(klass, kDescriptorInstanceVar, get_def_obj(desc->msgdef)); rb_define_alloc_func(klass, Message_alloc); + rb_require("google/protobuf/message_exts"); + rb_include_module(klass, rb_eval_string("Google::Protobuf::MessageExts")); + rb_extend_object(klass, rb_eval_string("Google::Protobuf::MessageExts::ClassMethods")); + rb_define_method(klass, "method_missing", Message_method_missing, -1); rb_define_method(klass, "initialize", Message_initialize, -1); @@ -407,6 +435,8 @@ VALUE build_class_from_descriptor(Descriptor* desc) { rb_define_method(klass, "clone", Message_dup, 0); rb_define_method(klass, "==", Message_eq, 1); rb_define_method(klass, "hash", Message_hash, 0); + rb_define_method(klass, "to_h", Message_to_h, 0); + rb_define_method(klass, "to_hash", Message_to_h, 0); rb_define_method(klass, "inspect", Message_inspect, 0); rb_define_method(klass, "[]", Message_index, 1); rb_define_method(klass, "[]=", Message_index_set, 2); @@ -415,6 +445,7 @@ VALUE build_class_from_descriptor(Descriptor* desc) { rb_define_singleton_method(klass, "decode_json", Message_decode_json, 1); rb_define_singleton_method(klass, "encode_json", Message_encode_json, 1); rb_define_singleton_method(klass, "descriptor", Message_descriptor, 0); + return klass; } diff --git a/ruby/ext/google/protobuf_c/protobuf.c b/ruby/ext/google/protobuf_c/protobuf.c index d2d35033..8ab518a5 100644 --- a/ruby/ext/google/protobuf_c/protobuf.c +++ b/ruby/ext/google/protobuf_c/protobuf.c @@ -86,13 +86,6 @@ void Init_protobuf_c() { RepeatedField_register(protobuf); Map_register(protobuf); - rb_define_singleton_method(protobuf, "encode", Google_Protobuf_encode, 1); - rb_define_singleton_method(protobuf, "decode", Google_Protobuf_decode, 2); - rb_define_singleton_method(protobuf, "encode_json", - Google_Protobuf_encode_json, 1); - rb_define_singleton_method(protobuf, "decode_json", - Google_Protobuf_decode_json, 2); - rb_define_singleton_method(protobuf, "deep_copy", Google_Protobuf_deep_copy, 1); diff --git a/ruby/ext/google/protobuf_c/protobuf.h b/ruby/ext/google/protobuf_c/protobuf.h index d8a327aa..ef00b5fa 100644 --- a/ruby/ext/google/protobuf_c/protobuf.h +++ b/ruby/ext/google/protobuf_c/protobuf.h @@ -374,6 +374,7 @@ VALUE RepeatedField_clear(VALUE _self); VALUE RepeatedField_length(VALUE _self); VALUE RepeatedField_dup(VALUE _self); VALUE RepeatedField_deep_copy(VALUE _self); +VALUE RepeatedField_to_ary(VALUE _self); VALUE RepeatedField_eq(VALUE _self, VALUE _other); VALUE RepeatedField_hash(VALUE _self); VALUE RepeatedField_inspect(VALUE _self); @@ -497,11 +498,6 @@ VALUE Message_encode(VALUE klass, VALUE msg_rb); VALUE Message_decode_json(VALUE klass, VALUE data); VALUE Message_encode_json(VALUE klass, VALUE msg_rb); -VALUE Google_Protobuf_encode(VALUE self, VALUE msg_rb); -VALUE Google_Protobuf_decode(VALUE self, VALUE klass, VALUE msg_rb); -VALUE Google_Protobuf_encode_json(VALUE self, VALUE msg_rb); -VALUE Google_Protobuf_decode_json(VALUE self, VALUE klass, VALUE msg_rb); - VALUE Google_Protobuf_deep_copy(VALUE self, VALUE obj); VALUE build_module_from_enumdesc(EnumDescriptor* enumdef); diff --git a/ruby/lib/google/protobuf.rb b/ruby/lib/google/protobuf.rb index 72797245..99b17929 100644 --- a/ruby/lib/google/protobuf.rb +++ b/ruby/lib/google/protobuf.rb @@ -28,6 +28,9 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# require mixins before we hook them into the java & c code +require 'google/protobuf/message_exts' + if RUBY_PLATFORM == "java" require 'json' require 'google/protobuf_java' @@ -36,3 +39,25 @@ else end require 'google/protobuf/repeated_field' + +module Google + module Protobuf + + def self.encode(msg) + msg.to_proto + end + + def self.encode_json(msg) + msg.to_json + end + + def self.decode(klass, proto) + klass.decode(proto) + end + + def self.decode_json(klass, json) + klass.decode_json(json) + end + + end +end diff --git a/ruby/lib/google/protobuf/message_exts.rb b/ruby/lib/google/protobuf/message_exts.rb new file mode 100644 index 00000000..e10266ba --- /dev/null +++ b/ruby/lib/google/protobuf/message_exts.rb @@ -0,0 +1,53 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# https://developers.google.com/protocol-buffers/ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +module Google + module Protobuf + module MessageExts + + #this is only called in jruby; mri loades the ClassMethods differently + def self.included(klass) + klass.extend(ClassMethods) + end + + module ClassMethods + end + + def to_json + self.class.encode_json(self) + end + + def to_proto + self.class.encode(self) + end + + end + end +end diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyDescriptor.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyDescriptor.java index 51c50be8..dd9179b0 100644 --- a/ruby/src/main/java/com/google/protobuf/jruby/RubyDescriptor.java +++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyDescriptor.java @@ -248,6 +248,8 @@ public class RubyDescriptor extends RubyObject { klass.setAllocator(allocator); klass.makeMetaClass(runtime.getObject().getMetaClass()); klass.inherit(runtime.getObject()); + RubyModule messageExts = runtime.getClassFromPath("Google::Protobuf::MessageExts"); + klass.include(new IRubyObject[] {messageExts}); klass.instance_variable_set(runtime.newString(Utils.DESCRIPTOR_INSTANCE_VAR), this); klass.defineAnnotatedMethods(RubyMessage.class); return klass; diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyMap.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyMap.java index b25dc6e1..2d4c03b5 100644 --- a/ruby/src/main/java/com/google/protobuf/jruby/RubyMap.java +++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyMap.java @@ -338,7 +338,7 @@ public class RubyMap extends RubyObject { return newMap; } - @JRubyMethod(name = "to_h") + @JRubyMethod(name = {"to_h", "to_hash"}) public RubyHash toHash(ThreadContext context) { return RubyHash.newHash(context.runtime, table, context.runtime.getNil()); } diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyMessage.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyMessage.java index c7fd7aa7..547ab22c 100644 --- a/ruby/src/main/java/com/google/protobuf/jruby/RubyMessage.java +++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyMessage.java @@ -338,16 +338,20 @@ public class RubyMessage extends RubyObject { return ret; } - @JRubyMethod(name = "to_h") + @JRubyMethod(name = {"to_h", "to_hash"}) public IRubyObject toHash(ThreadContext context) { Ruby runtime = context.runtime; RubyHash ret = RubyHash.newHash(runtime); for (Descriptors.FieldDescriptor fdef : this.descriptor.getFields()) { IRubyObject value = getField(context, fdef); - if (value.respondsTo("to_h")) { - value = Helpers.invoke(context, value, "to_h"); + if (!value.isNil()) { + if (value.respondsTo("to_h")) { + value = Helpers.invoke(context, value, "to_h"); + } else if (value.respondsTo("to_a")) { + value = Helpers.invoke(context, value, "to_a"); + } } - ret.fastASet(runtime.newString(fdef.getName()), value); + ret.fastASet(runtime.newSymbol(fdef.getName()), value); } return ret; } diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyProtobuf.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyProtobuf.java index cb3fcd48..2cf210d2 100644 --- a/ruby/src/main/java/com/google/protobuf/jruby/RubyProtobuf.java +++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyProtobuf.java @@ -48,56 +48,6 @@ public class RubyProtobuf { mProtobuf.defineAnnotatedMethods(RubyProtobuf.class); } - /* - * call-seq: - * Google::Protobuf.encode(msg) => bytes - * - * Encodes the given message object to protocol buffers wire format. This is an - * alternative to the #encode method on msg's class. - */ - @JRubyMethod(meta = true) - public static IRubyObject encode(ThreadContext context, IRubyObject self, IRubyObject message) { - return RubyMessage.encode(context, message.getMetaClass(), message); - } - - /* - * call-seq: - * Google::Protobuf.decode(class, bytes) => msg - * - * Decodes the given bytes as protocol buffers wire format under the - * interpretation given by the given class's message definition. This is an - * alternative to the #decode method on the given class. - */ - @JRubyMethod(meta = true) - public static IRubyObject decode(ThreadContext context, IRubyObject self, IRubyObject klazz, IRubyObject message) { - return RubyMessage.decode(context, klazz, message); - } - - /* - * call-seq: - * Google::Protobuf.encode_json(msg) => json_string - * - * Encodes the given message object to its JSON representation. This is an - * alternative to the #encode_json method on msg's class. - */ - @JRubyMethod(name = "encode_json", meta = true) - public static IRubyObject encodeJson(ThreadContext context, IRubyObject self, IRubyObject message) { - return RubyMessage.encodeJson(context, message.getMetaClass(), message); - } - - /* - * call-seq: - * Google::Protobuf.decode_json(class, json_string) => msg - * - * Decodes the given JSON string under the interpretation given by the given - * class's message definition. This is an alternative to the #decode_json method - * on the given class. - */ - @JRubyMethod(name = "decode_json", meta = true) - public static IRubyObject decodeJson(ThreadContext context, IRubyObject self, IRubyObject klazz, IRubyObject message) { - return RubyMessage.decodeJson(context, klazz, message); - } - /* * call-seq: * Google::Protobuf.deep_copy(obj) => copy_of_obj diff --git a/ruby/tests/basic.rb b/ruby/tests/basic.rb index 1c2a03dc..3d977c08 100644 --- a/ruby/tests/basic.rb +++ b/ruby/tests/basic.rb @@ -822,6 +822,67 @@ module BasicTest assert m == m2 end + def test_encode_decode_helpers + m = TestMessage.new(:optional_string => 'foo', :repeated_string => ['bar1', 'bar2']) + json = m.to_json + m2 = TestMessage.decode_json(json) + assert m2.optional_string == 'foo' + assert m2.repeated_string == ['bar1', 'bar2'] + + proto = m.to_proto + m2 = TestMessage.decode(proto) + assert m2.optional_string == 'foo' + assert m2.repeated_string == ['bar1', 'bar2'] + end + + def test_protobuf_encode_decode_helpers + m = TestMessage.new(:optional_string => 'foo', :repeated_string => ['bar1', 'bar2']) + encoded_msg = Google::Protobuf.encode(m) + assert_equal m.to_proto, encoded_msg + + decoded_msg = Google::Protobuf.decode(TestMessage, encoded_msg) + assert_equal TestMessage.decode(m.to_proto), decoded_msg + end + + def test_protobuf_encode_decode_json_helpers + m = TestMessage.new(:optional_string => 'foo', :repeated_string => ['bar1', 'bar2']) + encoded_msg = Google::Protobuf.encode_json(m) + assert_equal m.to_json, encoded_msg + + decoded_msg = Google::Protobuf.decode_json(TestMessage, encoded_msg) + assert_equal TestMessage.decode_json(m.to_json), decoded_msg + end + + def test_to_h + m = TestMessage.new(:optional_bool => true, :optional_double => -10.100001, :optional_string => 'foo', :repeated_string => ['bar1', 'bar2']) + expected_result = { + :optional_bool=>true, + :optional_bytes=>"", + :optional_double=>-10.100001, + :optional_enum=>:Default, + :optional_float=>0.0, + :optional_int32=>0, + :optional_int64=>0, + :optional_msg=>nil, + :optional_string=>"foo", + :optional_uint32=>0, + :optional_uint64=>0, + :repeated_bool=>[], + :repeated_bytes=>[], + :repeated_double=>[], + :repeated_enum=>[], + :repeated_float=>[], + :repeated_int32=>[], + :repeated_int64=>[], + :repeated_msg=>[], + :repeated_string=>["bar1", "bar2"], + :repeated_uint32=>[], + :repeated_uint64=>[] + } + assert_equal expected_result, m.to_h + end + + def test_def_errors s = Google::Protobuf::DescriptorPool.new assert_raise TypeError do -- cgit v1.2.3 From eb37551ae4fbb491f7670bf4175cb43bbc649fe0 Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Wed, 13 May 2015 14:58:48 -0700 Subject: Added Ruby to Travis testing. - Added RVM-based Ruby test driver that tests MRI and JRuby. - Fixed JRuby compilation (at least in my current setup): force source version to 1.6 (Java 6) to allow generics and annotations. - Modify the skipped JRuby JSON tests so that the exit code is 0 (skip() results in a failing exit code from `rake test`). An upcoming PR should fix JSON under JRuby in general soon. --- .travis.yml | 1 + ruby/pom.xml | 10 +++++++++- ruby/tests/basic.rb | 6 ++++-- ruby/travis-test.sh | 19 +++++++++++++++++++ 4 files changed, 33 insertions(+), 3 deletions(-) create mode 100755 ruby/travis-test.sh (limited to 'ruby') diff --git a/.travis.yml b/.travis.yml index 354be896..4f513653 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,6 +14,7 @@ script: - cd python && python setup.py build && python setup.py test && cd .. - export LD_LIBRARY_PATH=../src/.libs - cd python && python setup.py build --cpp_implementation && python setup.py test --cpp_implementation && cd .. + - cd ruby && sh travis-test.sh && cd .. - cd conformance && make test_java && cd .. - make distcheck -j2 notifications: diff --git a/ruby/pom.xml b/ruby/pom.xml index 01f0e16b..247b243a 100644 --- a/ruby/pom.xml +++ b/ruby/pom.xml @@ -61,6 +61,14 @@ + + org.apache.maven.plugins + maven-compiler-plugin + + 1.6 + 1.6 + + @@ -78,7 +86,7 @@ com.google.protobuf protobuf-java - 3.0.0-alpha-3-pre + 3.0.0-alpha-2 diff --git a/ruby/tests/basic.rb b/ruby/tests/basic.rb index 1c2a03dc..a00f0b05 100644 --- a/ruby/tests/basic.rb +++ b/ruby/tests/basic.rb @@ -1035,7 +1035,8 @@ module BasicTest end def test_json - skip("Unimplemented") if RUBY_PLATFORM == "java" + # TODO: Fix JSON in JRuby version. + return if RUBY_PLATFORM == "java" m = TestMessage.new(:optional_int32 => 1234, :optional_int64 => -0x1_0000_0000, :optional_uint32 => 0x8000_0000, @@ -1066,7 +1067,8 @@ module BasicTest end def test_json_maps - skip("Unimplemented") if RUBY_PLATFORM == "java" + # TODO: Fix JSON in JRuby version. + return if RUBY_PLATFORM == "java" m = MapMessage.new(:map_string_int32 => {"a" => 1}) expected = '{"map_string_int32":{"a":1},"map_string_msg":{}}' assert MapMessage.encode_json(m) == expected diff --git a/ruby/travis-test.sh b/ruby/travis-test.sh new file mode 100755 index 00000000..2ccb490a --- /dev/null +++ b/ruby/travis-test.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# Exit on any error. +set -e + +test_version() { + version=$1 + bash --login -c \ + "rvm install $version && rvm use $version && \ + which ruby && \ + gem install bundler && bundle && \ + rake test" +} + +test_version ruby-1.9 +test_version ruby-2.0 +test_version ruby-2.1 +test_version ruby-2.2 +test_version jruby -- cgit v1.2.3 From cd7ebbe54f16999b74c2c32a64336bad131ec5f3 Mon Sep 17 00:00:00 2001 From: Adam Greene Date: Sun, 3 May 2015 20:42:11 -0700 Subject: make repeated_field quack like an array --- ruby/ext/google/protobuf_c/protobuf.h | 4 +- ruby/ext/google/protobuf_c/repeated_field.c | 125 ++-- ruby/lib/google/protobuf/repeated_field.rb | 152 ++++- .../google/protobuf/jruby/RubyFieldDescriptor.java | 43 +- .../google/protobuf/jruby/RubyRepeatedField.java | 136 ++--- ruby/tests/basic.rb | 4 +- ruby/tests/repeated_field_test.rb | 640 +++++++++++++++++++++ 7 files changed, 979 insertions(+), 125 deletions(-) create mode 100644 ruby/tests/repeated_field_test.rb (limited to 'ruby') diff --git a/ruby/ext/google/protobuf_c/protobuf.h b/ruby/ext/google/protobuf_c/protobuf.h index d8a327aa..985b7f3d 100644 --- a/ruby/ext/google/protobuf_c/protobuf.h +++ b/ruby/ext/google/protobuf_c/protobuf.h @@ -361,13 +361,13 @@ extern VALUE cRepeatedField; RepeatedField* ruby_to_RepeatedField(VALUE value); VALUE RepeatedField_each(VALUE _self); -VALUE RepeatedField_index(VALUE _self, VALUE _index); +VALUE RepeatedField_index(int argc, VALUE* argv, VALUE _self); void* RepeatedField_index_native(VALUE _self, int index); VALUE RepeatedField_index_set(VALUE _self, VALUE _index, VALUE val); void RepeatedField_reserve(RepeatedField* self, int new_size); VALUE RepeatedField_push(VALUE _self, VALUE val); void RepeatedField_push_native(VALUE _self, void* data); -VALUE RepeatedField_pop(VALUE _self); +VALUE RepeatedField_pop_one(VALUE _self); VALUE RepeatedField_insert(int argc, VALUE* argv, VALUE _self); VALUE RepeatedField_replace(VALUE _self, VALUE list); VALUE RepeatedField_clear(VALUE _self); diff --git a/ruby/ext/google/protobuf_c/repeated_field.c b/ruby/ext/google/protobuf_c/repeated_field.c index 5148ee87..dc1d0355 100644 --- a/ruby/ext/google/protobuf_c/repeated_field.c +++ b/ruby/ext/google/protobuf_c/repeated_field.c @@ -55,6 +55,21 @@ static int index_position(VALUE _index, RepeatedField* repeated_field) { return index; } +VALUE RepeatedField_subarray(VALUE _self, long beg, long len) { + RepeatedField* self = ruby_to_RepeatedField(_self); + int element_size = native_slot_size(self->field_type); + upb_fieldtype_t field_type = self->field_type; + VALUE field_type_class = self->field_type_class; + + size_t off = beg * element_size; + VALUE ary = rb_ary_new2(len); + for (int i = beg; i < beg + len; i++, off += element_size) { + void* mem = ((uint8_t *)self->elements) + off; + VALUE elem = native_slot_get(field_type, field_type_class, mem); + rb_ary_push(ary, elem); + } + return ary; +} /* * call-seq: @@ -76,28 +91,57 @@ VALUE RepeatedField_each(VALUE _self) { VALUE val = native_slot_get(field_type, field_type_class, memory); rb_yield(val); } - return Qnil; + return _self; } + /* * call-seq: * RepeatedField.[](index) => value * * Accesses the element at the given index. Returns nil on out-of-bounds */ -VALUE RepeatedField_index(VALUE _self, VALUE _index) { +VALUE RepeatedField_index(int argc, VALUE* argv, VALUE _self) { RepeatedField* self = ruby_to_RepeatedField(_self); int element_size = native_slot_size(self->field_type); upb_fieldtype_t field_type = self->field_type; VALUE field_type_class = self->field_type_class; - int index = index_position(_index, self); - if (index < 0 || index >= self->size) { + VALUE arg = argv[0]; + long beg, len; + + if (argc == 1){ + if (FIXNUM_P(arg)) { + /* standard case */ + int index = index_position(argv[0], self); + if (index < 0 || index >= self->size) { + return Qnil; + } + void* memory = (void *) (((uint8_t *)self->elements) + index * element_size); + return native_slot_get(field_type, field_type_class, memory); + }else{ + /* check if idx is Range */ + size_t off; + switch (rb_range_beg_len(arg, &beg, &len, self->size, 0)) { + case Qfalse: + break; + case Qnil: + return Qnil; + default: + return RepeatedField_subarray(_self, beg, len); + } + } + } + /* assume 2 arguments */ + beg = NUM2LONG(argv[0]); + len = NUM2LONG(argv[1]); + if (beg < 0) { + beg += self->size; + } + if (beg >= self->size) { return Qnil; } - - void* memory = (void *) (((uint8_t *)self->elements) + index * element_size); - return native_slot_get(field_type, field_type_class, memory); + return RepeatedField_subarray(_self, beg, len); } /* @@ -173,6 +217,7 @@ VALUE RepeatedField_push(VALUE _self, VALUE val) { return _self; } + // Used by parsing handlers. void RepeatedField_push_native(VALUE _self, void* data) { RepeatedField* self = ruby_to_RepeatedField(_self); @@ -193,19 +238,15 @@ void* RepeatedField_index_native(VALUE _self, int index) { } /* - * call-seq: - * RepeatedField.pop => value - * - * Removes the last element and returns it. Throws an exception if the repeated - * field is empty. + * Private ruby method, used by RepeatedField.pop */ -VALUE RepeatedField_pop(VALUE _self) { +VALUE RepeatedField_pop_one(VALUE _self) { RepeatedField* self = ruby_to_RepeatedField(_self); upb_fieldtype_t field_type = self->field_type; VALUE field_type_class = self->field_type_class; int element_size = native_slot_size(field_type); if (self->size == 0) { - rb_raise(rb_eRangeError, "Pop from empty repeated field is not allowed."); + return Qnil; } int index = self->size - 1; void* memory = (void *) (((uint8_t *)self->elements) + index * element_size); @@ -214,19 +255,6 @@ VALUE RepeatedField_pop(VALUE _self) { return ret; } -/* - * call-seq: - * RepeatedField.insert(*args) - * - * Pushes each arg in turn onto the end of the repeated field. - */ -VALUE RepeatedField_insert(int argc, VALUE* argv, VALUE _self) { - for (int i = 0; i < argc; i++) { - RepeatedField_push(_self, argv[i]); - } - return Qnil; -} - /* * call-seq: * RepeatedField.replace(list) @@ -240,7 +268,7 @@ VALUE RepeatedField_replace(VALUE _self, VALUE list) { for (int i = 0; i < RARRAY_LEN(list); i++) { RepeatedField_push(_self, rb_ary_entry(list, i)); } - return Qnil; + return list; } /* @@ -252,7 +280,7 @@ VALUE RepeatedField_replace(VALUE _self, VALUE list) { VALUE RepeatedField_clear(VALUE _self) { RepeatedField* self = ruby_to_RepeatedField(_self); self->size = 0; - return Qnil; + return _self; } /* @@ -341,7 +369,6 @@ VALUE RepeatedField_to_ary(VALUE _self) { 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; @@ -417,19 +444,6 @@ VALUE RepeatedField_hash(VALUE _self) { return hash; } -/* - * call-seq: - * RepeatedField.inspect => string - * - * Returns a string representing this repeated field's elements. It will be - * formated as "[, , ...]", with each element's string - * representation computed by its own #inspect method. - */ -VALUE RepeatedField_inspect(VALUE _self) { - VALUE self_ary = RepeatedField_to_ary(_self); - return rb_funcall(self_ary, rb_intern("inspect"), 0); -} - /* * call-seq: * RepeatedField.+(other) => repeated field @@ -466,6 +480,22 @@ VALUE RepeatedField_plus(VALUE _self, VALUE list) { return dupped; } +/* + * call-seq: + * RepeatedField.concat(other) => self + * + * concats the passed in array to self. Returns a Ruby array. + */ +VALUE RepeatedField_concat(VALUE _self, VALUE list) { + RepeatedField* self = ruby_to_RepeatedField(_self); + Check_Type(list, T_ARRAY); + for (int i = 0; i < RARRAY_LEN(list); i++) { + RepeatedField_push(_self, rb_ary_entry(list, i)); + } + return _self; +} + + void validate_type_class(upb_fieldtype_t type, VALUE klass) { if (rb_iv_get(klass, kDescriptorInstanceVar) == Qnil) { rb_raise(rb_eArgError, @@ -585,22 +615,23 @@ void RepeatedField_register(VALUE module) { rb_define_method(klass, "initialize", RepeatedField_init, -1); rb_define_method(klass, "each", RepeatedField_each, 0); - rb_define_method(klass, "[]", RepeatedField_index, 1); + rb_define_method(klass, "[]", RepeatedField_index, -1); + rb_define_method(klass, "at", RepeatedField_index, -1); rb_define_method(klass, "[]=", RepeatedField_index_set, 2); rb_define_method(klass, "push", RepeatedField_push, 1); rb_define_method(klass, "<<", RepeatedField_push, 1); - rb_define_method(klass, "pop", RepeatedField_pop, 0); - rb_define_method(klass, "insert", RepeatedField_insert, -1); + rb_define_private_method(klass, "pop_one", RepeatedField_pop_one, 0); rb_define_method(klass, "replace", RepeatedField_replace, 1); rb_define_method(klass, "clear", RepeatedField_clear, 0); rb_define_method(klass, "length", RepeatedField_length, 0); + rb_define_method(klass, "size", RepeatedField_length, 0); rb_define_method(klass, "dup", RepeatedField_dup, 0); // 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); + rb_define_method(klass, "concat", RepeatedField_concat, 1); rb_include_module(klass, rb_mEnumerable); } diff --git a/ruby/lib/google/protobuf/repeated_field.rb b/ruby/lib/google/protobuf/repeated_field.rb index 5b934e56..16c843c0 100644 --- a/ruby/lib/google/protobuf/repeated_field.rb +++ b/ruby/lib/google/protobuf/repeated_field.rb @@ -28,12 +28,160 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# add syntatic sugar on top of the core library +require 'forwardable' + +# +# This class makes RepeatedField act (almost-) like a Ruby Array. +# It has convenience methods that extend the core C or Java based +# methods. +# +# This is a best-effort to mirror Array behavior. Two comments: +# 1) patches always welcome :) +# 2) if performance is an issue, feel free to rewrite the method +# in jruby and C. The source code has plenty of examples +# +# KNOWN ISSUES +# - #[]= doesn't allow less used approaches such as `arr[1, 2] = 'fizz'` +# - #concat should return the orig array +# - #push should accept multiple arguments and push them all at the same time +# module Google module Protobuf class RepeatedField + extend Forwardable + + # methods defined in C or Java: + # + + # [], at + # []= + # concat + # clear + # dup, clone + # each + # push, << + # replace + # length, size + # == + # to_ary, to_a + # also all enumerable + # + # NOTE: using delegators rather than method_missing to make the + # relationship explicit instead of implicit + def_delegators :to_ary, + :&, :*, :-, :'<=>', + :assoc, :bsearch, :combination, :compact, :count, :cycle, + :drop, :drop_while, :eql?, :fetch, :find_index, :flatten, + :include?, :index, :inspect, :join, + :pack, :permutation, :product, :pretty_print, :pretty_print_cycle, + :rassoc, :repeated_combination, :repeated_permutation, :reverse, + :rindex, :rotate, :sample, :shuffle, :shelljoin, :slice, + :to_s, :transpose, :uniq, :| + + + def first(n=nil) + n ? self[0..n] : self[0] + end + + + def last(n=nil) + n ? self[(self.size-n-1)..-1] : self[-1] + end + + + def pop(n=nil) + if n + results = [] + n.times{ results << pop_one } + return results + else + return pop_one + end + end + + + def empty? + self.size == 0 + end + + # array aliases into enumerable + alias_method :each_index, :each_with_index + alias_method :slice, :[] + alias_method :values_at, :select + alias_method :map, :collect + + + class << self + def define_array_wrapper_method(method_name) + define_method(method_name) do |*args, &block| + arr = self.to_a + result = arr.send(method_name, *args) + self.replace(arr) + return result if result + return block ? block.call : result + end + end + private :define_array_wrapper_method + + + def define_array_wrapper_with_result_method(method_name) + define_method(method_name) do |*args, &block| + # result can be an Enumerator, Array, or nil + # Enumerator can sometimes be returned if a block is an optional argument and it is not passed in + # nil usually specifies that no change was made + result = self.to_a.send(method_name, *args, &block) + if result + new_arr = result.to_a + self.replace(new_arr) + if result.is_a?(Enumerator) + # generate a fresh enum; rewinding the exiting one, in Ruby 2.2, will + # reset the enum with the same length, but all the #next calls will + # return nil + result = new_arr.to_enum + # generate a wrapper enum so any changes which occur by a chained + # enum can be captured + ie = ProxyingEnumerator.new(self, result) + result = ie.to_enum + end + end + result + end + end + private :define_array_wrapper_with_result_method + end + + + %w(delete delete_at delete_if shift slice! unshift).each do |method_name| + define_array_wrapper_method(method_name) + end + + + %w(collect! compact! fill flatten! insert reverse! + rotate! select! shuffle! sort! sort_by! uniq!).each do |method_name| + define_array_wrapper_with_result_method(method_name) + end + alias_method :keep_if, :select! + alias_method :map!, :collect! + alias_method :reject!, :delete_if + + + # propagates changes made by user of enumerator back to the original repeated field. + # This only applies in cases where the calling function which created the enumerator, + # such as #sort!, modifies itself rather than a new array, such as #sort + class ProxyingEnumerator < Struct.new(:repeated_field, :external_enumerator) + def each(*args, &block) + results = [] + external_enumerator.each_with_index do |val, i| + result = yield(val) + results << result + #nil means no change occured from yield; usually occurs when #to_a is called + if result + repeated_field[i] = result if result != val + end + end + results + end + end - alias_method :size, :length end end diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyFieldDescriptor.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyFieldDescriptor.java index 38226c4e..f3c488bc 100644 --- a/ruby/src/main/java/com/google/protobuf/jruby/RubyFieldDescriptor.java +++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyFieldDescriptor.java @@ -71,6 +71,17 @@ public class RubyFieldDescriptor extends RubyObject { return this; } + /* + * call-seq: + * FieldDescriptor.label + * + * Return the label of this field. + */ + @JRubyMethod(name = "label") + public IRubyObject getLabel(ThreadContext context) { + return this.label; + } + /* * call-seq: * FieldDescriptor.label = label @@ -80,8 +91,10 @@ public class RubyFieldDescriptor extends RubyObject { */ @JRubyMethod(name = "label=") public IRubyObject setLabel(ThreadContext context, IRubyObject value) { + String labelName = value.asJavaString(); + this.label = context.runtime.newSymbol(labelName.toLowerCase()); this.builder.setLabel( - DescriptorProtos.FieldDescriptorProto.Label.valueOf("LABEL_" + value.asJavaString().toUpperCase())); + DescriptorProtos.FieldDescriptorProto.Label.valueOf("LABEL_" + labelName.toUpperCase())); return context.runtime.getNil(); } @@ -89,18 +102,13 @@ public class RubyFieldDescriptor extends RubyObject { * call-seq: * FieldDescriptor.name => name * - * Returns the name of this field. + * Returns the name of this field as a Ruby String, or nil if it is not set. */ @JRubyMethod(name = "name") public IRubyObject getName(ThreadContext context) { return this.name; } - @JRubyMethod(name = "subtype") - public IRubyObject getSubType(ThreadContext context) { - return subType; - } - /* * call-seq: * FieldDescriptor.name = name @@ -116,6 +124,12 @@ public class RubyFieldDescriptor extends RubyObject { return context.runtime.getNil(); } + + @JRubyMethod(name = "subtype") + public IRubyObject getSubType(ThreadContext context) { + return subType; + } + /* * call-seq: * FieldDescriptor.type => type @@ -144,6 +158,18 @@ public class RubyFieldDescriptor extends RubyObject { return context.runtime.getNil(); } + /* + * call-seq: + * FieldDescriptor.number => number + * + * Returns this field's number, as a Ruby Integer, or nil if not yet set. + * + */ + @JRubyMethod(name = "number") + public IRubyObject getnumber(ThreadContext context) { + return this.number; + } + /* * call-seq: * FieldDescriptor.number = number @@ -153,6 +179,7 @@ public class RubyFieldDescriptor extends RubyObject { */ @JRubyMethod(name = "number=") public IRubyObject setNumber(ThreadContext context, IRubyObject value) { + this.number = value; this.builder.setNumber(RubyNumeric.num2int(value)); return context.runtime.getNil(); } @@ -240,6 +267,8 @@ public class RubyFieldDescriptor extends RubyObject { private DescriptorProtos.FieldDescriptorProto.Builder builder; private IRubyObject name; + private IRubyObject label; + private IRubyObject number; private IRubyObject subType; private IRubyObject oneofName; private Descriptors.FieldDescriptor fieldDef; diff --git a/ruby/src/main/java/com/google/protobuf/jruby/RubyRepeatedField.java b/ruby/src/main/java/com/google/protobuf/jruby/RubyRepeatedField.java index 84bf8956..946f9e74 100644 --- a/ruby/src/main/java/com/google/protobuf/jruby/RubyRepeatedField.java +++ b/ruby/src/main/java/com/google/protobuf/jruby/RubyRepeatedField.java @@ -40,6 +40,7 @@ import org.jruby.runtime.Block; import org.jruby.runtime.ObjectAllocator; import org.jruby.runtime.ThreadContext; import org.jruby.runtime.builtin.IRubyObject; +import java.util.Arrays; @JRubyClass(name = "RepeatedClass", include = "Enumerable") public class RubyRepeatedField extends RubyObject { @@ -110,6 +111,10 @@ public class RubyRepeatedField extends RubyObject { public IRubyObject indexSet(ThreadContext context, IRubyObject index, IRubyObject value) { int arrIndex = normalizeArrayIndex(index); Utils.checkType(context, fieldType, value, (RubyModule) typeClass); + IRubyObject defaultValue = defaultValue(context); + for (int i = this.storage.size(); i < arrIndex; i++) { + this.storage.set(i, defaultValue); + } this.storage.set(arrIndex, value); return context.runtime.getNil(); } @@ -120,27 +125,35 @@ public class RubyRepeatedField extends RubyObject { * * Accesses the element at the given index. Returns nil on out-of-bounds */ - @JRubyMethod(name = "[]") - public IRubyObject index(ThreadContext context, IRubyObject index) { - int arrIndex = normalizeArrayIndex(index); - if (arrIndex < 0 || arrIndex >= this.storage.size()) { - return context.runtime.getNil(); + @JRubyMethod(required=1, optional=1, name = {"at", "[]"}) + public IRubyObject index(ThreadContext context, IRubyObject[] args) { + if (args.length == 1){ + IRubyObject arg = args[0]; + if (Utils.isRubyNum(arg)) { + /* standard case */ + int arrIndex = normalizeArrayIndex(arg); + if (arrIndex < 0 || arrIndex >= this.storage.size()) { + return context.runtime.getNil(); + } + return this.storage.eltInternal(arrIndex); + } else if (arg instanceof RubyRange) { + RubyRange range = ((RubyRange) arg); + int beg = RubyNumeric.num2int(range.first(context)); + int to = RubyNumeric.num2int(range.last(context)); + int len = to - beg + 1; + return this.storage.subseq(beg, len); + } } - return this.storage.eltInternal(arrIndex); - } - - /* - * call-seq: - * RepeatedField.insert(*args) - * - * Pushes each arg in turn onto the end of the repeated field. - */ - @JRubyMethod(rest = true) - public IRubyObject insert(ThreadContext context, IRubyObject[] args) { - for (int i = 0; i < args.length; i++) { - push(context, args[i]); + /* assume 2 arguments */ + int beg = RubyNumeric.num2int(args[0]); + int len = RubyNumeric.num2int(args[1]); + if (beg < 0) { + beg += this.storage.size(); } - return context.runtime.getNil(); + if (beg >= this.storage.size()) { + return context.runtime.getNil(); + } + return this.storage.subseq(beg, len); } /* @@ -151,20 +164,19 @@ public class RubyRepeatedField extends RubyObject { */ @JRubyMethod(name = {"push", "<<"}) public IRubyObject push(ThreadContext context, IRubyObject value) { - Utils.checkType(context, fieldType, value, (RubyModule) typeClass); + if (!(fieldType == Descriptors.FieldDescriptor.Type.MESSAGE && + value == context.runtime.getNil())) { + Utils.checkType(context, fieldType, value, (RubyModule) typeClass); + } this.storage.add(value); - return this; + return this.storage; } /* - * call-seq: - * RepeatedField.pop => value - * - * Removes the last element and returns it. Throws an exception if the repeated - * field is empty. + * private Ruby method used by RepeatedField.pop */ - @JRubyMethod - public IRubyObject pop(ThreadContext context) { + @JRubyMethod(visibility = org.jruby.runtime.Visibility.PRIVATE) + public IRubyObject pop_one(ThreadContext context) { IRubyObject ret = this.storage.last(); this.storage.remove(ret); return ret; @@ -181,7 +193,7 @@ public class RubyRepeatedField extends RubyObject { RubyArray arr = (RubyArray) list; checkArrayElementType(context, arr); this.storage = arr; - return context.runtime.getNil(); + return this.storage; } /* @@ -193,7 +205,7 @@ public class RubyRepeatedField extends RubyObject { @JRubyMethod public IRubyObject clear(ThreadContext context) { this.storage.clear(); - return context.runtime.getNil(); + return this.storage; } /* @@ -202,7 +214,7 @@ public class RubyRepeatedField extends RubyObject { * * Returns the length of this repeated field. */ - @JRubyMethod(name = {"count", "length"}) + @JRubyMethod(name = {"length", "size"}) public IRubyObject length(ThreadContext context) { return context.runtime.newFixnum(this.storage.size()); } @@ -215,7 +227,7 @@ public class RubyRepeatedField extends RubyObject { * repeated field's elements and other's elements. The other (second) list may * be either another repeated field or a Ruby array. */ - @JRubyMethod(name = "+") + @JRubyMethod(name = {"+"}) public IRubyObject plus(ThreadContext context, IRubyObject list) { RubyRepeatedField dup = (RubyRepeatedField) dup(context); if (list instanceof RubyArray) { @@ -231,6 +243,27 @@ public class RubyRepeatedField extends RubyObject { return dup; } + /* + * call-seq: + * RepeatedField.concat(other) => self + * + * concats the passed in array to self. Returns a Ruby array. + */ + @JRubyMethod + public IRubyObject concat(ThreadContext context, IRubyObject list) { + if (list instanceof RubyArray) { + checkArrayElementType(context, (RubyArray) list); + this.storage.addAll((RubyArray) list); + } else { + RubyRepeatedField repeatedField = (RubyRepeatedField) list; + if (! fieldType.equals(repeatedField.fieldType) || (typeClass != null && ! + typeClass.equals(repeatedField.typeClass))) + throw context.runtime.newArgumentError("Attempt to append RepeatedField with different element type."); + this.storage.addAll((RubyArray) repeatedField.toArray(context)); + } + return this.storage; + } + /* * call-seq: * RepeatedField.hash => hash_value @@ -239,7 +272,7 @@ public class RubyRepeatedField extends RubyObject { */ @JRubyMethod public IRubyObject hash(ThreadContext context) { - int hashCode = System.identityHashCode(this.storage); + int hashCode = this.storage.hashCode(); return context.runtime.newFixnum(hashCode); } @@ -268,17 +301,12 @@ public class RubyRepeatedField extends RubyObject { @JRubyMethod public IRubyObject each(ThreadContext context, Block block) { this.storage.each(context, block); - return context.runtime.getNil(); + return this.storage; } + @JRubyMethod(name = {"to_ary", "to_a"}) public IRubyObject toArray(ThreadContext context) { - for (int i = 0; i < this.storage.size(); i++) { - IRubyObject defaultValue = defaultValue(context); - if (storage.eltInternal(i).isNil()) { - storage.set(i, defaultValue); - } - } return this.storage; } @@ -298,31 +326,6 @@ public class RubyRepeatedField extends RubyObject { return dup; } - /* - * call-seq: - * RepeatedField.inspect => string - * - * Returns a string representing this repeated field's elements. It will be - * formated as "[, , ...]", with each element's string - * representation computed by its own #inspect method. - */ - @JRubyMethod - public IRubyObject inspect() { - StringBuilder str = new StringBuilder("["); - for (int i = 0; i < this.storage.size(); i++) { - str.append(storage.eltInternal(i).inspect()); - str.append(", "); - } - - if (str.length() > 1) { - str.replace(str.length() - 2, str.length(), "]"); - } else { - str.append("]"); - } - - return getRuntime().newString(str.toString()); - } - // Java API protected IRubyObject get(int index) { return this.storage.eltInternal(index); @@ -376,6 +379,9 @@ public class RubyRepeatedField extends RubyObject { case STRING: value = sentinel.getDefaultString(); break; + case ENUM: + IRubyObject defaultEnumLoc = context.runtime.newFixnum(0); + return RubyEnum.lookup(context, typeClass, defaultEnumLoc); default: return context.runtime.getNil(); } diff --git a/ruby/tests/basic.rb b/ruby/tests/basic.rb index a00f0b05..b6e26108 100644 --- a/ruby/tests/basic.rb +++ b/ruby/tests/basic.rb @@ -178,7 +178,7 @@ module BasicTest :optional_msg => TestMessage2.new, :repeated_string => ["hello", "there", "world"]) expected = ', optional_enum: :A, repeated_int32: [], repeated_int64: [], repeated_uint32: [], repeated_uint64: [], repeated_bool: [], repeated_float: [], repeated_double: [], repeated_string: ["hello", "there", "world"], repeated_bytes: [], repeated_msg: [], repeated_enum: []>' - assert m.inspect == expected + assert_equal expected, m.inspect end def test_hash @@ -276,7 +276,7 @@ module BasicTest assert l.inspect == '[5, 2, 3, 4]' - l.insert(7, 8, 9) + l.concat([7, 8, 9]) assert l == [5, 2, 3, 4, 7, 8, 9] assert l.pop == 9 assert l == [5, 2, 3, 4, 7, 8] diff --git a/ruby/tests/repeated_field_test.rb b/ruby/tests/repeated_field_test.rb new file mode 100644 index 00000000..25727b7b --- /dev/null +++ b/ruby/tests/repeated_field_test.rb @@ -0,0 +1,640 @@ +#!/usr/bin/ruby + +require 'google/protobuf' +require 'test/unit' + +class RepeatedFieldTest < Test::Unit::TestCase + + def test_acts_like_enumerator + m = TestMessage.new + (Enumerable.instance_methods - TestMessage.new.repeated_string.methods).each do |method_name| + assert m.repeated_string.respond_to?(method_name) == true, "does not respond to #{method_name}" + end + end + + def test_acts_like_an_array + m = TestMessage.new + arr_methods = ([].methods - TestMessage.new.repeated_string.methods) + # jRuby additions to the Array class that we can ignore + arr_methods -= [ :indices, :iter_for_each, :iter_for_each_index, + :iter_for_each_with_index, :dimensions, :copy_data, :copy_data_simple, + :nitems, :iter_for_reverse_each, :indexes] + arr_methods.each do |method_name| + assert m.repeated_string.respond_to?(method_name) == true, "does not respond to #{method_name}" + end + end + + def test_first + m = TestMessage.new + repeated_field_names(TestMessage).each do |field_name| + assert_nil m.send(field_name).first + end + fill_test_msg(m) + assert_equal -10, m.repeated_int32.first + assert_equal -1_000_000, m.repeated_int64.first + assert_equal 10, m.repeated_uint32.first + assert_equal 1_000_000, m.repeated_uint64.first + assert_equal true, m.repeated_bool.first + assert_equal -1.01, m.repeated_float.first.round(2) + assert_equal -1.0000000000001, m.repeated_double.first + assert_equal 'foo', m.repeated_string.first + assert_equal "bar".encode!('ASCII-8BIT'), m.repeated_bytes.first + assert_equal TestMessage2.new(:foo => 1), m.repeated_msg.first + assert_equal :A, m.repeated_enum.first + end + + + def test_last + m = TestMessage.new + repeated_field_names(TestMessage).each do |field_name| + assert_nil m.send(field_name).first + end + fill_test_msg(m) + assert_equal -11, m.repeated_int32.last + assert_equal -1_000_001, m.repeated_int64.last + assert_equal 11, m.repeated_uint32.last + assert_equal 1_000_001, m.repeated_uint64.last + assert_equal false, m.repeated_bool.last + assert_equal -1.02, m.repeated_float.last.round(2) + assert_equal -1.0000000000002, m.repeated_double.last + assert_equal 'bar', m.repeated_string.last + assert_equal "foo".encode!('ASCII-8BIT'), m.repeated_bytes.last + assert_equal TestMessage2.new(:foo => 2), m.repeated_msg.last + assert_equal :B, m.repeated_enum.last + end + + + def test_pop + m = TestMessage.new + repeated_field_names(TestMessage).each do |field_name| + assert_nil m.send(field_name).pop + end + fill_test_msg(m) + + assert_equal -11, m.repeated_int32.pop + assert_equal -10, m.repeated_int32.pop + assert_equal -1_000_001, m.repeated_int64.pop + assert_equal -1_000_000, m.repeated_int64.pop + assert_equal 11, m.repeated_uint32.pop + assert_equal 10, m.repeated_uint32.pop + assert_equal 1_000_001, m.repeated_uint64.pop + assert_equal 1_000_000, m.repeated_uint64.pop + assert_equal false, m.repeated_bool.pop + assert_equal true, m.repeated_bool.pop + assert_equal -1.02, m.repeated_float.pop.round(2) + assert_equal -1.01, m.repeated_float.pop.round(2) + assert_equal -1.0000000000002, m.repeated_double.pop + assert_equal -1.0000000000001, m.repeated_double.pop + assert_equal 'bar', m.repeated_string.pop + assert_equal 'foo', m.repeated_string.pop + assert_equal "foo".encode!('ASCII-8BIT'), m.repeated_bytes.pop + assert_equal "bar".encode!('ASCII-8BIT'), m.repeated_bytes.pop + assert_equal TestMessage2.new(:foo => 2), m.repeated_msg.pop + assert_equal TestMessage2.new(:foo => 1), m.repeated_msg.pop + assert_equal :B, m.repeated_enum.pop + assert_equal :A, m.repeated_enum.pop + repeated_field_names(TestMessage).each do |field_name| + assert_nil m.send(field_name).pop + end + + fill_test_msg(m) + assert_equal ['bar', 'foo'], m.repeated_string.pop(2) + assert_nil m.repeated_string.pop + end + + + def test_each + m = TestMessage.new + 5.times{|i| m.repeated_string << 'string' } + count = 0 + m.repeated_string.each do |val| + assert_equal 'string', val + count += 1 + end + assert_equal 5, count + result = m.repeated_string.each{|val| val + '_junk'} + assert_equal ['string'] * 5, result + end + + + def test_empty? + m = TestMessage.new + assert_equal true, m.repeated_string.empty? + m.repeated_string << 'foo' + assert_equal false, m.repeated_string.empty? + m.repeated_string << 'bar' + assert_equal false, m.repeated_string.empty? + end + + def test_array_accessor + m = TestMessage.new + reference_arr = %w(foo bar baz) + m.repeated_string += reference_arr.clone + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr[1] + end + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr[-2] + end + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr[20] + end + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr[1, 2] + end + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr[0..2] + end + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr[-1, 1] + end + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr[10, 12] + end + end + + def test_array_settor + m = TestMessage.new + reference_arr = %w(foo bar baz) + m.repeated_string += reference_arr.clone + + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr[1] = 'junk' + end + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr[-2] = 'snappy' + end + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr[3] = '' + end + # slight deviation; we are strongly typed, and nil is not allowed + # for string types; + m.repeated_string[5] = 'spacious' + assert_equal ["foo", "snappy", "baz", "", "", "spacious"], m.repeated_string + + #make sure it sests the default types for other fields besides strings + %w(repeated_int32 repeated_int64 repeated_uint32 repeated_uint64).each do |field_name| + m.send(field_name)[3] = 10 + assert_equal [0,0,0,10], m.send(field_name) + end + m.repeated_float[3] = 10.1 + #wonky mri float handling + assert_equal [0,0,0], m.repeated_float.to_a[0..2] + assert_equal 10.1, m.repeated_float[3].round(1) + m.repeated_double[3] = 10.1 + assert_equal [0,0,0,10.1], m.repeated_double + m.repeated_bool[3] = true + assert_equal [false, false, false, true], m.repeated_bool + m.repeated_bytes[3] = "bar".encode!('ASCII-8BIT') + assert_equal ['', '', '', "bar".encode!('ASCII-8BIT')], m.repeated_bytes + m.repeated_msg[3] = TestMessage2.new(:foo => 1) + assert_equal [nil, nil, nil, TestMessage2.new(:foo => 1)], m.repeated_msg + m.repeated_enum[3] = :A + assert_equal [:Default, :Default, :Default, :A], m.repeated_enum + + # check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + # arr[20] = 'spacious' + # end + # TODO: accessor doesn't allow other ruby-like methods + # check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + # arr[1, 2] = 'fizz' + # end + # check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + # arr[0..2] = 'buzz' + # end + end + + def test_push + m = TestMessage.new + reference_arr = %w(foo bar baz) + m.repeated_string += reference_arr.clone + + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.push('fizz') + end + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr << 'fizz' + end + #TODO: push should support multiple + # check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + # arr.push('fizz', 'buzz') + # end + end + + def test_clear + m = TestMessage.new + reference_arr = %w(foo bar baz) + m.repeated_string += reference_arr.clone + + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.clear + end + end + + def test_concat + m = TestMessage.new + reference_arr = %w(foo bar baz) + m.repeated_string += reference_arr.clone + m.repeated_string.concat(['fizz', 'buzz']) + assert_equal %w(foo bar baz fizz buzz), m.repeated_string + #TODO: concat should return the orig array + # check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + # arr.concat(['fizz', 'buzz']) + # end + end + + def test_equal + m = TestMessage.new + reference_arr = %w(foo bar baz) + m.repeated_string += reference_arr.clone + assert_equal reference_arr, m.repeated_string + reference_arr << 'fizz' + assert_not_equal reference_arr, m.repeated_string + m.repeated_string << 'fizz' + assert_equal reference_arr, m.repeated_string + end + + def test_hash + # just a sanity check + m = TestMessage.new + reference_arr = %w(foo bar baz) + m.repeated_string += reference_arr.clone + assert m.repeated_string.hash.is_a?(Integer) + hash = m.repeated_string.hash + assert_equal hash, m.repeated_string.hash + m.repeated_string << 'j' + assert_not_equal hash, m.repeated_string.hash + end + + def test_plus + m = TestMessage.new + reference_arr = %w(foo bar baz) + m.repeated_string += reference_arr.clone + + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr + ['fizz', 'buzz'] + end + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr += ['fizz', 'buzz'] + end + end + + def test_replace + m = TestMessage.new + reference_arr = %w(foo bar baz) + m.repeated_string += reference_arr.clone + + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.replace(['fizz', 'buzz']) + end + end + + def test_to_a + m = TestMessage.new + reference_arr = %w(foo bar baz) + m.repeated_string += reference_arr.clone + + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.to_a + end + end + + def test_to_ary + m = TestMessage.new + reference_arr = %w(foo bar baz) + m.repeated_string += reference_arr.clone + + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.to_ary + end + end + + # emulate Array behavior + ########################## + + def test_collect! + m = TestMessage.new + reference_arr = %w(foo bar baz) + m.repeated_string += reference_arr.clone + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.collect!{|x| x + "!" } + end + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.collect!.with_index{|x, i| x[0...i] } + end + end + + def test_compact! + m = TestMessage.new + m.repeated_msg << TestMessage2.new(:foo => 1) + m.repeated_msg << nil + m.repeated_msg << TestMessage2.new(:foo => 2) + reference_arr = m.repeated_string.to_a + + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.compact! + end + end + + def test_delete + m = TestMessage.new + reference_arr = %w(foo bar baz) + m.repeated_string += reference_arr.clone + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.delete('bar') + end + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.delete('nope') + end + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.delete('nope'){'within'} + end + end + + def test_delete_at + m = TestMessage.new + reference_arr = %w(foo bar baz) + m.repeated_string += reference_arr.clone + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.delete_at(2) + end + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.delete_at(10) + end + end + + def test_fill + m = TestMessage.new + reference_arr = %w(foo bar baz) + m.repeated_string += reference_arr.clone + + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.fill("x") + end + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.fill("z", 2, 2) + end + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.fill("y", 0..1) + end + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.fill { |i| (i*i).to_s } + end + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.fill(-2) { |i| (i*i*i).to_s } + end + end + + def test_flatten! + m = TestMessage.new + reference_arr = %w(foo bar baz) + m.repeated_string += reference_arr.clone + + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.flatten! + end + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.flatten!(1) + end + end + + def test_insert + m = TestMessage.new + reference_arr = %w(foo bar baz) + m.repeated_string += reference_arr.clone + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.insert(2, 'fizz') + end + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.insert(3, 'fizz', 'buzz', 'bazz') + end + end + + def test_inspect + m = TestMessage.new + assert_equal '[]', m.repeated_string.inspect + m.repeated_string << 'foo' + assert_equal m.repeated_string.to_a.inspect, m.repeated_string.inspect + m.repeated_string << 'bar' + assert_equal m.repeated_string.to_a.inspect, m.repeated_string.inspect + end + + def test_reverse! + m = TestMessage.new + reference_arr = %w(foo bar baz) + m.repeated_string += reference_arr.clone + + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.reverse! + end + end + + def test_rotate! + m = TestMessage.new + reference_arr = %w(foo bar baz) + m.repeated_string += reference_arr.clone + + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.rotate! + end + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.rotate!(2) + end + end + + def test_select! + m = TestMessage.new + reference_arr = %w(foo bar baz) + m.repeated_string += reference_arr.clone + + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.select! { |v| v =~ /[aeiou]/ } + end + end + + def test_shift + m = TestMessage.new + reference_arr = %w(foo bar baz) + m.repeated_string += reference_arr.clone + + # should return an element + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.shift + end + # should return an array + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.shift(2) + end + # should return nil + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.shift + end + end + + def test_shuffle! + m = TestMessage.new + m.repeated_string += %w(foo bar baz) + orig_repeated_string = m.repeated_string.clone + result = m.repeated_string.shuffle! + assert_equal m.repeated_string, result + # NOTE: sometimes it doesn't change the order... + # assert_not_equal m.repeated_string.to_a, orig_repeated_string.to_a + end + + def test_slice! + m = TestMessage.new + reference_arr = %w(foo bar baz bar fizz buzz) + m.repeated_string += reference_arr.clone + + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.slice!(2) + end + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.slice!(1,2) + end + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.slice!(0..1) + end + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.slice!(10) + end + end + + def test_sort! + m = TestMessage.new + reference_arr = %w(foo bar baz) + m.repeated_string += reference_arr.clone + + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.sort! + end + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.sort! { |x,y| y <=> x } + end + end + + def test_sort_by! + m = TestMessage.new + reference_arr = %w(foo bar baz) + m.repeated_string += reference_arr.clone + + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.sort_by! + end + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.sort_by!(&:hash) + end + end + + def test_uniq! + m = TestMessage.new + reference_arr = %w(foo bar baz) + m.repeated_string += reference_arr.clone + + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.uniq! + end + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.uniq!{|s| s[0] } + end + end + + def test_unshift + m = TestMessage.new + reference_arr = %w(foo bar baz) + m.repeated_string += reference_arr.clone + + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.unshift('1') + end + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.unshift('a', 'b') + end + check_self_modifying_method(m.repeated_string, reference_arr) do |arr| + arr.unshift('') + end + end + + + ##### HELPER METHODS + + def check_self_modifying_method(repeated_field, ref_array) + expected_result = yield(ref_array) + actual_result = yield(repeated_field) + if expected_result.is_a?(Enumerator) + assert_equal expected_result.to_a, actual_result.to_a + else + assert_equal expected_result, actual_result + end + assert_equal ref_array, repeated_field + end + + + def repeated_field_names(klass) + klass.descriptor.find_all{|f| f.label == :repeated}.map(&:name) + end + + + def fill_test_msg(test_msg) + test_msg.repeated_int32 += [-10, -11] + test_msg.repeated_int64 += [-1_000_000, -1_000_001] + test_msg.repeated_uint32 += [10, 11] + test_msg.repeated_uint64 += [1_000_000, 1_000_001] + test_msg.repeated_bool += [true, false] + test_msg.repeated_float += [-1.01, -1.02] + test_msg.repeated_double += [-1.0000000000001, -1.0000000000002] + test_msg.repeated_string += %w(foo bar) + test_msg.repeated_bytes += ["bar".encode!('ASCII-8BIT'), "foo".encode!('ASCII-8BIT')] + test_msg.repeated_msg << TestMessage2.new(:foo => 1) + test_msg.repeated_msg << TestMessage2.new(:foo => 2) + test_msg.repeated_enum << :A + test_msg.repeated_enum << :B + end + + + pool = Google::Protobuf::DescriptorPool.new + pool.build do + + add_message "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_float, :float, 6 + optional :optional_double, :double, 7 + optional :optional_string, :string, 8 + optional :optional_bytes, :bytes, 9 + optional :optional_msg, :message, 10, "TestMessage2" + optional :optional_enum, :enum, 11, "TestEnum" + + repeated :repeated_int32, :int32, 12 + repeated :repeated_int64, :int64, 13 + repeated :repeated_uint32, :uint32, 14 + repeated :repeated_uint64, :uint64, 15 + repeated :repeated_bool, :bool, 16 + repeated :repeated_float, :float, 17 + repeated :repeated_double, :double, 18 + repeated :repeated_string, :string, 19 + repeated :repeated_bytes, :bytes, 20 + repeated :repeated_msg, :message, 21, "TestMessage2" + repeated :repeated_enum, :enum, 22, "TestEnum" + end + add_message "TestMessage2" do + optional :foo, :int32, 1 + end + + add_enum "TestEnum" do + value :Default, 0 + value :A, 1 + value :B, 2 + value :C, 3 + end + end + + TestMessage = pool.lookup("TestMessage").msgclass + TestMessage2 = pool.lookup("TestMessage2").msgclass + TestEnum = pool.lookup("TestEnum").enummodule + + +end -- cgit v1.2.3 From 20e94b24ddada6c0bbb05b6e81ed1342ec4dc5b1 Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Wed, 13 May 2015 16:43:48 -0700 Subject: Refactor Travis tests: split configs and run in parallel. --- .travis.yml | 34 ++++++------ ruby/travis-test.sh | 6 +-- travis.sh | 152 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 172 insertions(+), 20 deletions(-) create mode 100755 travis.sh (limited to 'ruby') diff --git a/.travis.yml b/.travis.yml index 4f513653..bfe63d1d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,21 +1,25 @@ -sudo: false -language: java -jdk: - - openjdk6 - - openjdk7 - - oraclejdk7 +sudo: required +language: cpp os: - linux - osx script: - - ./autogen.sh && ./configure && make -j2 - - cd java && mvn test && cd .. - - cd javanano && mvn test && cd .. - - cd python && python setup.py build && python setup.py test && cd .. - - export LD_LIBRARY_PATH=../src/.libs - - cd python && python setup.py build --cpp_implementation && python setup.py test --cpp_implementation && cd .. - - cd ruby && sh travis-test.sh && cd .. - - cd conformance && make test_java && cd .. - - make distcheck -j2 + - ./travis.sh $CONFIG +env: + - CONFIG=cpp + - CONFIG=cpp_distcheck + - CONFIG=java_jdk6 + - CONFIG=java_jdk7 + - CONFIG=java_oracle7 + - CONFIG=javanano_jdk6 + - CONFIG=javanano_jdk7 + - CONFIG=javanano_oracle7 + - CONFIG=python + - CONFIG=python_cpp + - CONFIG=ruby19 + - CONFIG=ruby20 + - CONFIG=ruby21 + - CONFIG=ruby22 + - CONFIG=jruby notifications: email: false diff --git a/ruby/travis-test.sh b/ruby/travis-test.sh index 2ccb490a..e94af80a 100755 --- a/ruby/travis-test.sh +++ b/ruby/travis-test.sh @@ -12,8 +12,4 @@ test_version() { rake test" } -test_version ruby-1.9 -test_version ruby-2.0 -test_version ruby-2.1 -test_version ruby-2.2 -test_version jruby +test_version $1 diff --git a/travis.sh b/travis.sh new file mode 100755 index 00000000..556bc91c --- /dev/null +++ b/travis.sh @@ -0,0 +1,152 @@ +#!/bin/bash + +build_cpp() { + ./autogen.sh + ./configure + make -j2 + make check -j2 + cd conformance && make test_cpp && cd .. +} + +build_cpp_distcheck() { + ./autogen.sh + ./configure + make distcheck -j2 +} + +use_java() { + if [ `uname` != "Linux" ]; then + # It's nontrivial to programmatically install a new JDK from the command + # line on OS X, so we rely on testing on Linux for Java code. + echo "Java not tested on OS X." + exit 0 # success + fi + version=$1 + case "$version" in + jdk6) + sudo apt-get install openjdk-6-jdk + export PATH=/usr/lib/jvm/java-6-openjdk-amd64/bin:$PATH + ;; + jdk7) + sudo apt-get install openjdk-7-jdk + export PATH=/usr/lib/jvm/java-7-openjdk-amd64/bin:$PATH + ;; + oracle7) + sudo apt-get install python-software-properties # for apt-add-repository + echo "oracle-java7-installer shared/accepted-oracle-license-v1-1 select true" | \ + sudo debconf-set-selections + yes | sudo apt-add-repository ppa:webupd8team/java + yes | sudo apt-get install oracle-java7-installer + export PATH=/usr/lib/jvm/java-7-oracle/bin:$PATH + ;; + esac + + which java + java -version +} + +build_java() { + # Java build needs `protoc`. + ./autogen.sh + ./configure + make -j2 + cd java && mvn test && cd .. + cd conformance && make test_java && cd .. +} + +build_javanano() { + # Java build needs `protoc`. + ./autogen.sh + ./configure + make -j2 + cd javanano && mvn test && cd .. +} + +build_java_jdk6() { + use_java jdk6 + build_java +} +build_java_jdk7() { + use_java jdk7 + build_java +} +build_java_oracle7() { + use_java oracle7 + build_java +} + +build_javanano_jdk6() { + use_java jdk6 + build_javanano +} +build_javanano_jdk7() { + use_java jdk7 + build_javanano +} +build_javanano_oracle7() { + use_java oracle7 + build_javanano +} + +build_python() { + ./autogen.sh + ./configure + make -j2 + cd python + python setup.py build + python setup.py test + cd .. +} + +build_python_cpp() { + ./autogen.sh + ./configure + make -j2 + export LD_LIBRARY_PATH=../src/.libs + cd python + python setup.py build --cpp_implementation + python setup.py test --cpp_implementation + cd .. +} + +build_ruby19() { + cd ruby && bash travis-test.sh ruby-1.9 && cd .. +} +build_ruby20() { + cd ruby && bash travis-test.sh ruby-2.0 && cd .. +} +build_ruby21() { + cd ruby && bash travis-test.sh ruby-2.1 && cd .. +} +build_ruby22() { + cd ruby && bash travis-test.sh ruby-2.2 && cd .. +} +build_jruby() { + cd ruby && bash travis-test.sh jruby && cd .. +} + +# -------- main -------- + +if [ "$#" -ne 1 ]; then + echo " +Usage: $0 { cpp | + java_jdk6 | + java_jdk7 | + java_oracle7 | + javanano_jdk6 | + javanano_jdk7 | + javanano_oracle7 | + python | + python_cpp | + ruby_19 | + ruby_20 | + ruby_21 | + ruby_22 | + jruby } +" + exit 1 +fi + +set -e # exit immediately on error +set -x # display all commands +eval "build_$1" -- cgit v1.2.3 From d326277397e345b5bda4a8afbd0a9d54f01b9a06 Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Thu, 14 May 2015 18:24:26 -0700 Subject: Update MRI C Ruby extension to use new version of upb. - Alter encode/decode paths to use the `upb_env` (environment) abstraction. - Update upb amalgamation to upstream `93791bfe`. - Fix a compilation warning (void*->char* cast). - Modify build flags so that upb doesn't produce warnings -- the Travis build logs were pretty cluttered previously. --- ruby/ext/google/protobuf_c/encode_decode.c | 99 ++- ruby/ext/google/protobuf_c/extconf.rb | 4 +- ruby/ext/google/protobuf_c/message.c | 2 +- ruby/ext/google/protobuf_c/protobuf.h | 4 + ruby/ext/google/protobuf_c/upb.c | 1070 +++++++++++++++++++++------- ruby/ext/google/protobuf_c/upb.h | 978 +++++++++++++------------ 6 files changed, 1408 insertions(+), 749 deletions(-) (limited to 'ruby') diff --git a/ruby/ext/google/protobuf_c/encode_decode.c b/ruby/ext/google/protobuf_c/encode_decode.c index 5730504d..ba555048 100644 --- a/ruby/ext/google/protobuf_c/encode_decode.c +++ b/ruby/ext/google/protobuf_c/encode_decode.c @@ -622,6 +622,48 @@ static const upb_pbdecodermethod *msgdef_decodermethod(Descriptor* desc) { return desc->fill_method; } + +// Stack-allocated context during an encode/decode operation. Contains the upb +// environment and its stack-based allocator, an initial buffer for allocations +// to avoid malloc() when possible, and a template for Ruby exception messages +// if any error occurs. +#define STACK_ENV_STACKBYTES 4096 +typedef struct { + upb_env env; + upb_seededalloc alloc; + const char* ruby_error_template; + char allocbuf[STACK_ENV_STACKBYTES]; +} stackenv; + +static void stackenv_init(stackenv* se, const char* errmsg); +static void stackenv_uninit(stackenv* se); + +// Callback invoked by upb if any error occurs during parsing or serialization. +static bool env_error_func(void* ud, const upb_status* status) { + stackenv* se = ud; + // Free the env -- rb_raise will longjmp up the stack past the encode/decode + // function so it would not otherwise have been freed. + stackenv_uninit(se); + rb_raise(rb_eRuntimeError, se->ruby_error_template, upb_status_errmsg(status)); + // Never reached: rb_raise() always longjmp()s up the stack, past all of our + // code, back to Ruby. + return false; +} + +static void stackenv_init(stackenv* se, const char* errmsg) { + se->ruby_error_template = errmsg; + upb_env_init(&se->env); + upb_seededalloc_init(&se->alloc, &se->allocbuf, STACK_ENV_STACKBYTES); + upb_env_setallocfunc( + &se->env, upb_seededalloc_getallocfunc(&se->alloc), &se->alloc); + upb_env_seterrorfunc(&se->env, env_error_func, se); +} + +static void stackenv_uninit(stackenv* se) { + upb_env_uninit(&se->env); + upb_seededalloc_uninit(&se->alloc); +} + /* * call-seq: * MessageClass.decode(data) => message @@ -645,21 +687,17 @@ VALUE Message_decode(VALUE klass, VALUE data) { const upb_pbdecodermethod* method = msgdef_decodermethod(desc); const upb_handlers* h = upb_pbdecodermethod_desthandlers(method); - upb_pbdecoder decoder; - upb_sink sink; - upb_status status = UPB_STATUS_INIT; + stackenv se; + stackenv_init(&se, "Error occurred during parsing: %s"); - upb_pbdecoder_init(&decoder, method, &status); + upb_sink sink; upb_sink_reset(&sink, h, msg); - upb_pbdecoder_resetoutput(&decoder, &sink); + upb_pbdecoder* decoder = + upb_pbdecoder_create(&se.env, method, &sink); upb_bufsrc_putbuf(RSTRING_PTR(data), RSTRING_LEN(data), - upb_pbdecoder_input(&decoder)); + upb_pbdecoder_input(decoder)); - upb_pbdecoder_uninit(&decoder); - if (!upb_ok(&status)) { - rb_raise(rb_eRuntimeError, "Error occurred during parsing: %s.", - upb_status_errmsg(&status)); - } + stackenv_uninit(&se); return msg_rb; } @@ -688,21 +726,16 @@ VALUE Message_decode_json(VALUE klass, VALUE data) { MessageHeader* msg; TypedData_Get_Struct(msg_rb, MessageHeader, &Message_type, msg); - upb_status status = UPB_STATUS_INIT; - upb_json_parser parser; - upb_json_parser_init(&parser, &status); + stackenv se; + stackenv_init(&se, "Error occurred during parsing: %s"); upb_sink sink; upb_sink_reset(&sink, get_fill_handlers(desc), msg); - upb_json_parser_resetoutput(&parser, &sink); + upb_json_parser* parser = upb_json_parser_create(&se.env, &sink); upb_bufsrc_putbuf(RSTRING_PTR(data), RSTRING_LEN(data), - upb_json_parser_input(&parser)); + upb_json_parser_input(parser)); - upb_json_parser_uninit(&parser); - if (!upb_ok(&status)) { - rb_raise(rb_eRuntimeError, "Error occurred during parsing: %s.", - upb_status_errmsg(&status)); - } + stackenv_uninit(&se); return msg_rb; } @@ -956,7 +989,7 @@ static void putmsg(VALUE msg_rb, const Descriptor* desc, // Protect against cycles (possible because users may freely reassign message // and repeated fields) by imposing a maximum recursion depth. - if (depth > UPB_SINK_MAX_NESTING) { + if (depth > ENCODE_MAX_NESTING) { rb_raise(rb_eRuntimeError, "Maximum recursion depth exceeded during encoding."); } @@ -1074,15 +1107,16 @@ VALUE Message_encode(VALUE klass, VALUE msg_rb) { const upb_handlers* serialize_handlers = msgdef_pb_serialize_handlers(desc); - upb_pb_encoder encoder; - upb_pb_encoder_init(&encoder, serialize_handlers); - upb_pb_encoder_resetoutput(&encoder, &sink.sink); + stackenv se; + stackenv_init(&se, "Error occurred during encoding: %s"); + upb_pb_encoder* encoder = + upb_pb_encoder_create(&se.env, serialize_handlers, &sink.sink); - putmsg(msg_rb, desc, upb_pb_encoder_input(&encoder), 0); + putmsg(msg_rb, desc, upb_pb_encoder_input(encoder), 0); VALUE ret = rb_str_new(sink.ptr, sink.len); - upb_pb_encoder_uninit(&encoder); + stackenv_uninit(&se); stringsink_uninit(&sink); return ret; @@ -1104,15 +1138,16 @@ VALUE Message_encode_json(VALUE klass, VALUE msg_rb) { const upb_handlers* serialize_handlers = msgdef_json_serialize_handlers(desc); - upb_json_printer printer; - upb_json_printer_init(&printer, serialize_handlers); - upb_json_printer_resetoutput(&printer, &sink.sink); + stackenv se; + stackenv_init(&se, "Error occurred during encoding: %s"); + upb_json_printer* printer = + upb_json_printer_create(&se.env, serialize_handlers, &sink.sink); - putmsg(msg_rb, desc, upb_json_printer_input(&printer), 0); + putmsg(msg_rb, desc, upb_json_printer_input(printer), 0); VALUE ret = rb_str_new(sink.ptr, sink.len); - upb_json_printer_uninit(&printer); + stackenv_uninit(&se); stringsink_uninit(&sink); return ret; diff --git a/ruby/ext/google/protobuf_c/extconf.rb b/ruby/ext/google/protobuf_c/extconf.rb index 8d60392c..74203b07 100644 --- a/ruby/ext/google/protobuf_c/extconf.rb +++ b/ruby/ext/google/protobuf_c/extconf.rb @@ -2,7 +2,9 @@ require 'mkmf' -$CFLAGS += " -O3 -std=c99 -Wno-unused-function -DNDEBUG " +$CFLAGS += " -O3 -std=c99 -Wno-unused-function " + + "-Wno-declaration-after-statement -Wno-unused-variable " + + "-Wno-sign-compare -DNDEBUG " $objs = ["protobuf.o", "defs.o", "storage.o", "message.o", "repeated_field.o", "map.o", "encode_decode.o", "upb.o"] diff --git a/ruby/ext/google/protobuf_c/message.c b/ruby/ext/google/protobuf_c/message.c index 7e58a617..ee5f23a5 100644 --- a/ruby/ext/google/protobuf_c/message.c +++ b/ruby/ext/google/protobuf_c/message.c @@ -86,7 +86,7 @@ static VALUE which_oneof_field(MessageHeader* self, const upb_oneofdef* o) { 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)); + uint32_t oneof_case = *((uint32_t*)((char*)Message_data(self) + case_ofs)); if (oneof_case == ONEOF_CASE_NONE) { return Qnil; diff --git a/ruby/ext/google/protobuf_c/protobuf.h b/ruby/ext/google/protobuf_c/protobuf.h index 985b7f3d..400909fe 100644 --- a/ruby/ext/google/protobuf_c/protobuf.h +++ b/ruby/ext/google/protobuf_c/protobuf.h @@ -511,6 +511,10 @@ VALUE enum_resolve(VALUE self, VALUE sym); const upb_pbdecodermethod *new_fillmsg_decodermethod( Descriptor* descriptor, const void *owner); +// Maximum depth allowed during encoding, to avoid stack overflows due to +// cycles. +#define ENCODE_MAX_NESTING 63 + // ----------------------------------------------------------------------------- // Global map from upb {msg,enum}defs to wrapper Descriptor/EnumDescriptor // instances. diff --git a/ruby/ext/google/protobuf_c/upb.c b/ruby/ext/google/protobuf_c/upb.c index 20bd76bc..55a99cfe 100644 --- a/ruby/ext/google/protobuf_c/upb.c +++ b/ruby/ext/google/protobuf_c/upb.c @@ -1779,6 +1779,275 @@ upb_fielddef *upb_oneof_iter_field(const upb_oneof_iter *iter) { void upb_oneof_iter_setdone(upb_oneof_iter *iter) { upb_inttable_iter_setdone(iter); } +/* + * upb - a minimalist implementation of protocol buffers. + * + * Copyright (c) 2014 Google Inc. See LICENSE for details. + * Author: Josh Haberman + */ + + +#include +#include +#include + +typedef struct cleanup_ent { + upb_cleanup_func *cleanup; + void *ud; + struct cleanup_ent *next; +} cleanup_ent; + +static void *seeded_alloc(void *ud, void *ptr, size_t oldsize, size_t size); + +/* Default allocator **********************************************************/ + +// Just use realloc, keeping all allocated blocks in a linked list to destroy at +// the end. + +typedef struct mem_block { + // List is doubly-linked, because in cases where realloc() moves an existing + // block, we need to be able to remove the old pointer from the list + // efficiently. + struct mem_block *prev, *next; +#ifndef NDEBUG + size_t size; // Doesn't include mem_block structure. +#endif + char data[]; +} mem_block; + +typedef struct { + mem_block *head; +} default_alloc_ud; + +static void *default_alloc(void *_ud, void *ptr, size_t oldsize, size_t size) { + UPB_UNUSED(oldsize); + default_alloc_ud *ud = _ud; + + mem_block *from = ptr ? (void*)((char*)ptr - sizeof(mem_block)) : NULL; + +#ifndef NDEBUG + if (from) { + assert(oldsize <= from->size); + } +#endif + + mem_block *block = realloc(from, size + sizeof(mem_block)); + if (!block) return NULL; + +#ifndef NDEBUG + block->size = size; +#endif + + if (from) { + if (block != from) { + // The block was moved, so pointers in next and prev blocks must be + // updated to its new location. + if (block->next) block->next->prev = block; + if (block->prev) block->prev->next = block; + } + } else { + // Insert at head of linked list. + block->prev = NULL; + block->next = ud->head; + if (block->next) block->next->prev = block; + ud->head = block; + } + + return &block->data; +} + +static void default_alloc_cleanup(void *_ud) { + default_alloc_ud *ud = _ud; + mem_block *block = ud->head; + + while (block) { + void *to_free = block; + block = block->next; + free(to_free); + } +} + + +/* Standard error functions ***************************************************/ + +static bool default_err(void *ud, const upb_status *status) { + UPB_UNUSED(ud); + fprintf(stderr, "upb error: %s\n", upb_status_errmsg(status)); + return false; +} + +static bool write_err_to(void *ud, const upb_status *status) { + upb_status *copy_to = ud; + upb_status_copy(copy_to, status); + return false; +} + + +/* upb_env ********************************************************************/ + +void upb_env_init(upb_env *e) { + e->ok_ = true; + e->bytes_allocated = 0; + e->cleanup_head = NULL; + + default_alloc_ud *ud = (default_alloc_ud*)&e->default_alloc_ud; + ud->head = NULL; + + // Set default functions. + upb_env_setallocfunc(e, default_alloc, ud); + upb_env_seterrorfunc(e, default_err, NULL); +} + +void upb_env_uninit(upb_env *e) { + cleanup_ent *ent = e->cleanup_head; + + while (ent) { + ent->cleanup(ent->ud); + ent = ent->next; + } + + // Must do this after running cleanup functions, because this will delete + // the memory we store our cleanup entries in! + if (e->alloc == default_alloc) { + default_alloc_cleanup(e->alloc_ud); + } +} + +UPB_FORCEINLINE void upb_env_setallocfunc(upb_env *e, upb_alloc_func *alloc, + void *ud) { + e->alloc = alloc; + e->alloc_ud = ud; +} + +UPB_FORCEINLINE void upb_env_seterrorfunc(upb_env *e, upb_error_func *func, + void *ud) { + e->err = func; + e->err_ud = ud; +} + +void upb_env_reporterrorsto(upb_env *e, upb_status *status) { + e->err = write_err_to; + e->err_ud = status; +} + +bool upb_env_ok(const upb_env *e) { + return e->ok_; +} + +bool upb_env_reporterror(upb_env *e, const upb_status *status) { + e->ok_ = false; + return e->err(e->err_ud, status); +} + +bool upb_env_addcleanup(upb_env *e, upb_cleanup_func *func, void *ud) { + cleanup_ent *ent = upb_env_malloc(e, sizeof(cleanup_ent)); + if (!ent) return false; + + ent->cleanup = func; + ent->ud = ud; + ent->next = e->cleanup_head; + e->cleanup_head = ent; + + return true; +} + +void *upb_env_malloc(upb_env *e, size_t size) { + e->bytes_allocated += size; + if (e->alloc == seeded_alloc) { + // This is equivalent to the next branch, but allows inlining for a + // measurable perf benefit. + return seeded_alloc(e->alloc_ud, NULL, 0, size); + } else { + return e->alloc(e->alloc_ud, NULL, 0, size); + } +} + +void *upb_env_realloc(upb_env *e, void *ptr, size_t oldsize, size_t size) { + assert(oldsize <= size); + char *ret = e->alloc(e->alloc_ud, ptr, oldsize, size); + +#ifndef NDEBUG + // Overwrite non-preserved memory to ensure callers are passing the oldsize + // that they truly require. + memset(ret + oldsize, 0xff, size - oldsize); +#endif + + return ret; +} + +size_t upb_env_bytesallocated(const upb_env *e) { + return e->bytes_allocated; +} + + +/* upb_seededalloc ************************************************************/ + +// Be conservative and choose 16 in case anyone is using SSE. +static const size_t maxalign = 16; + +static size_t align_up(size_t size) { + return ((size + maxalign - 1) / maxalign) * maxalign; +} + +UPB_FORCEINLINE static void *seeded_alloc(void *ud, void *ptr, size_t oldsize, + size_t size) { + UPB_UNUSED(ptr); + + upb_seededalloc *a = ud; + size = align_up(size); + + assert(a->mem_limit >= a->mem_ptr); + + if (oldsize == 0 && size <= (size_t)(a->mem_limit - a->mem_ptr)) { + // Fast path: we can satisfy from the initial allocation. + void *ret = a->mem_ptr; + a->mem_ptr += size; + return ret; + } else { + // Slow path: fallback to other allocator. + a->need_cleanup = true; + // Is `ptr` part of the user-provided initial block? Don't pass it to the + // default allocator if so; otherwise, it may try to realloc() the block. + char *chptr = ptr; + if (chptr >= a->mem_base && chptr < a->mem_limit) { + return a->alloc(a->alloc_ud, NULL, 0, size); + } else { + return a->alloc(a->alloc_ud, ptr, oldsize, size); + } + } +} + +void upb_seededalloc_init(upb_seededalloc *a, void *mem, size_t len) { + a->mem_base = mem; + a->mem_ptr = mem; + a->mem_limit = (char*)mem + len; + a->need_cleanup = false; + a->returned_allocfunc = false; + + default_alloc_ud *ud = (default_alloc_ud*)&a->default_alloc_ud; + ud->head = NULL; + + upb_seededalloc_setfallbackalloc(a, default_alloc, ud); +} + +void upb_seededalloc_uninit(upb_seededalloc *a) { + if (a->alloc == default_alloc && a->need_cleanup) { + default_alloc_cleanup(a->alloc_ud); + } +} + +UPB_FORCEINLINE void upb_seededalloc_setfallbackalloc(upb_seededalloc *a, + upb_alloc_func *alloc, + void *ud) { + assert(!a->returned_allocfunc); + a->alloc = alloc; + a->alloc_ud = ud; +} + +upb_alloc_func *upb_seededalloc_getallocfunc(upb_seededalloc *a) { + a->returned_allocfunc = true; + return seeded_alloc; +} /* * upb - a minimalist implementation of protocol buffers. * @@ -1955,7 +2224,14 @@ static bool doset(upb_handlers *h, int32_t sel, const upb_fielddef *f, if (closure_type && *context_closure_type && closure_type != *context_closure_type) { // TODO(haberman): better message for debugging. - upb_status_seterrmsg(&h->status_, "closure type does not match"); + if (f) { + upb_status_seterrf(&h->status_, + "closure type does not match for field %s", + upb_fielddef_name(f)); + } else { + upb_status_seterrmsg( + &h->status_, "closure type does not match for message-level handler"); + } return false; } @@ -2353,7 +2629,7 @@ bool upb_handlers_getselector(const upb_fielddef *f, upb_handlertype_t type, *s = f->selector_base; break; } - assert(*s < upb_fielddef_containingtype(f)->selector_count); + assert((size_t)*s < upb_fielddef_containingtype(f)->selector_count); return true; } @@ -4295,7 +4571,7 @@ void upb_inttable_compact(upb_inttable *t) { counts[log2ceil(key)]++; } - int arr_size; + size_t arr_size = 1; int arr_count = upb_inttable_count(t); if (upb_inttable_count(t) >= max_key * MIN_DENSITY) { @@ -5522,6 +5798,54 @@ static upb_inttable reftables[212] = { #include #include +// upb_deflist is an internal-only dynamic array for storing a growing list of +// upb_defs. +typedef struct { + upb_def **defs; + size_t len; + size_t size; + bool owned; +} upb_deflist; + +// We keep a stack of all the messages scopes we are currently in, as well as +// the top-level file scope. This is necessary to correctly qualify the +// definitions that are contained inside. "name" tracks the name of the +// message or package (a bare name -- not qualified by any enclosing scopes). +typedef struct { + char *name; + // Index of the first def that is under this scope. For msgdefs, the + // msgdef itself is at start-1. + int start; +} upb_descreader_frame; + +// The maximum number of nested declarations that are allowed, ie. +// message Foo { +// message Bar { +// message Baz { +// } +// } +// } +// +// This is a resource limit that affects how big our runtime stack can grow. +// TODO: make this a runtime-settable property of the Reader instance. +#define UPB_MAX_MESSAGE_NESTING 64 + +struct upb_descreader { + upb_sink sink; + upb_deflist defs; + upb_descreader_frame stack[UPB_MAX_MESSAGE_NESTING]; + int stack_len; + + uint32_t number; + char *name; + bool saw_number; + bool saw_name; + + char *default_string; + + upb_fielddef *f; +}; + static char *upb_strndup(const char *buf, size_t n) { char *ret = malloc(n + 1); if (!ret) return NULL; @@ -5601,36 +5925,6 @@ static void upb_deflist_qualify(upb_deflist *l, char *str, int32_t start) { /* upb_descreader ************************************************************/ -void upb_descreader_init(upb_descreader *r, const upb_handlers *handlers, - upb_status *status) { - UPB_UNUSED(status); - upb_deflist_init(&r->defs); - upb_sink_reset(upb_descreader_input(r), handlers, r); - r->stack_len = 0; - r->name = NULL; - r->default_string = NULL; -} - -void upb_descreader_uninit(upb_descreader *r) { - free(r->name); - upb_deflist_uninit(&r->defs); - free(r->default_string); - while (r->stack_len > 0) { - upb_descreader_frame *f = &r->stack[--r->stack_len]; - free(f->name); - } -} - -upb_def **upb_descreader_getdefs(upb_descreader *r, void *owner, int *n) { - *n = r->defs.len; - upb_deflist_donaterefs(&r->defs, owner); - return r->defs.defs; -} - -upb_sink *upb_descreader_input(upb_descreader *r) { - return &r->sink; -} - static upb_msgdef *upb_descreader_top(upb_descreader *r) { assert(r->stack_len > 1); int index = r->stack[r->stack_len-1].start - 1; @@ -5803,7 +6097,7 @@ static bool parse_default(char *str, upb_fielddef *f) { break; } case UPB_TYPE_UINT32: { - long val = strtoul(str, &end, 0); + unsigned long val = strtoul(str, &end, 0); if (val > UINT32_MAX || errno == ERANGE || *end) success = false; else @@ -6070,6 +6364,45 @@ static void reghandlers(const void *closure, upb_handlers *h) { #undef D +void descreader_cleanup(void *_r) { + upb_descreader *r = _r; + free(r->name); + upb_deflist_uninit(&r->defs); + free(r->default_string); + while (r->stack_len > 0) { + upb_descreader_frame *f = &r->stack[--r->stack_len]; + free(f->name); + } +} + + +/* Public API ****************************************************************/ + +upb_descreader *upb_descreader_create(upb_env *e, const upb_handlers *h) { + upb_descreader *r = upb_env_malloc(e, sizeof(upb_descreader)); + if (!r || !upb_env_addcleanup(e, descreader_cleanup, r)) { + return NULL; + } + + upb_deflist_init(&r->defs); + upb_sink_reset(upb_descreader_input(r), h, r); + r->stack_len = 0; + r->name = NULL; + r->default_string = NULL; + + return r; +} + +upb_def **upb_descreader_getdefs(upb_descreader *r, void *owner, int *n) { + *n = r->defs.len; + upb_deflist_donaterefs(&r->defs, owner); + return r->defs.defs; +} + +upb_sink *upb_descreader_input(upb_descreader *r) { + return &r->sink; +} + const upb_handlers *upb_descreader_newhandlers(const void *owner) { const upb_symtab *s = upbdefs_google_protobuf_descriptor(&s); const upb_handlers *h = upb_handlers_newfrozen( @@ -6141,7 +6474,6 @@ mgroup *newgroup(const void *owner) { static void freemethod(upb_refcounted *r) { upb_pbdecodermethod *method = (upb_pbdecodermethod*)r; - upb_byteshandler_uninit(&method->input_handler_); if (method->dest_handlers_) { upb_handlers_unref(method->dest_handlers_, method); @@ -7073,10 +7405,7 @@ void upb_pbdecodermethodopts_setlazy(upb_pbdecodermethodopts *opts, bool lazy) { */ #include -#include -#include #include -#include #ifdef UPB_DUMP_BYTECODE #include @@ -7122,18 +7451,17 @@ static bool consumes_input(opcode op) { static bool in_residual_buf(const upb_pbdecoder *d, const char *p); -// It's unfortunate that we have to micro-manage the compiler this way, -// especially since this tuning is necessarily specific to one hardware -// configuration. But emperically on a Core i7, performance increases 30-50% -// with these annotations. Every instance where these appear, gcc 4.2.1 made -// the wrong decision and degraded performance in benchmarks. -#define FORCEINLINE static inline __attribute__((always_inline)) -#define NOINLINE __attribute__((noinline)) +// It's unfortunate that we have to micro-manage the compiler with +// UPB_FORCEINLINE and UPB_NOINLINE, especially since this tuning is necessarily +// specific to one hardware configuration. But empirically on a Core i7, +// performance increases 30-50% with these annotations. Every instance where +// these appear, gcc 4.2.1 made the wrong decision and degraded performance in +// benchmarks. static void seterr(upb_pbdecoder *d, const char *msg) { - // TODO(haberman): encapsulate this access to pipeline->status, but not sure - // exactly what that interface should look like. - upb_status_seterrmsg(d->status, msg); + upb_status status = UPB_STATUS_INIT; + upb_status_seterrmsg(&status, msg); + upb_env_reporterror(d->env, &status); } void upb_pbdecoder_seterr(upb_pbdecoder *d, const char *msg) { @@ -7176,7 +7504,7 @@ static bool in_residual_buf(const upb_pbdecoder *d, const char *p) { // and the parsing stack, so must be called whenever either is updated. static void set_delim_end(upb_pbdecoder *d) { size_t delim_ofs = d->top->end_ofs - d->bufstart_ofs; - if (delim_ofs <= (d->end - d->buf)) { + if (delim_ofs <= (size_t)(d->end - d->buf)) { d->delim_end = d->buf + delim_ofs; d->data_end = d->delim_end; } else { @@ -7301,7 +7629,8 @@ static int32_t skip(upb_pbdecoder *d, size_t bytes) { // Copies the next "bytes" bytes into "buf" and advances the stream. // Requires that this many bytes are available in the current buffer. -FORCEINLINE void consumebytes(upb_pbdecoder *d, void *buf, size_t bytes) { +UPB_FORCEINLINE static void consumebytes(upb_pbdecoder *d, void *buf, + size_t bytes) { assert(bytes <= curbufleft(d)); memcpy(buf, d->ptr, bytes); advance(d, bytes); @@ -7310,8 +7639,8 @@ FORCEINLINE void consumebytes(upb_pbdecoder *d, void *buf, size_t bytes) { // Slow path for getting the next "bytes" bytes, regardless of whether they are // available in the current buffer or not. Returns a status code as described // in decoder.int.h. -static NOINLINE int32_t getbytes_slow(upb_pbdecoder *d, void *buf, - size_t bytes) { +UPB_NOINLINE static int32_t getbytes_slow(upb_pbdecoder *d, void *buf, + size_t bytes) { const size_t avail = curbufleft(d); consumebytes(d, buf, avail); bytes -= avail; @@ -7320,7 +7649,7 @@ static NOINLINE int32_t getbytes_slow(upb_pbdecoder *d, void *buf, advancetobuf(d, d->buf_param, d->size_param); } if (curbufleft(d) >= bytes) { - consumebytes(d, buf + avail, bytes); + consumebytes(d, (char *)buf + avail, bytes); return DECODE_OK; } else if (d->data_end == d->delim_end) { seterr(d, "Submessage ended in the middle of a value or group"); @@ -7332,7 +7661,8 @@ static NOINLINE int32_t getbytes_slow(upb_pbdecoder *d, void *buf, // Gets the next "bytes" bytes, regardless of whether they are available in the // current buffer or not. Returns a status code as described in decoder.int.h. -FORCEINLINE int32_t getbytes(upb_pbdecoder *d, void *buf, size_t bytes) { +UPB_FORCEINLINE static int32_t getbytes(upb_pbdecoder *d, void *buf, + size_t bytes) { if (curbufleft(d) >= bytes) { // Buffer has enough data to satisfy. consumebytes(d, buf, bytes); @@ -7342,19 +7672,20 @@ FORCEINLINE int32_t getbytes(upb_pbdecoder *d, void *buf, size_t bytes) { } } -static NOINLINE size_t peekbytes_slow(upb_pbdecoder *d, void *buf, - size_t bytes) { +UPB_NOINLINE static size_t peekbytes_slow(upb_pbdecoder *d, void *buf, + size_t bytes) { size_t ret = curbufleft(d); memcpy(buf, d->ptr, ret); if (in_residual_buf(d, d->ptr)) { size_t copy = UPB_MIN(bytes - ret, d->size_param); - memcpy(buf + ret, d->buf_param, copy); + memcpy((char *)buf + ret, d->buf_param, copy); ret += copy; } return ret; } -FORCEINLINE size_t peekbytes(upb_pbdecoder *d, void *buf, size_t bytes) { +UPB_FORCEINLINE static size_t peekbytes(upb_pbdecoder *d, void *buf, + size_t bytes) { if (curbufleft(d) >= bytes) { memcpy(buf, d->ptr, bytes); return bytes; @@ -7368,8 +7699,8 @@ FORCEINLINE size_t peekbytes(upb_pbdecoder *d, void *buf, size_t bytes) { // Slow path for decoding a varint from the current buffer position. // Returns a status code as described in decoder.int.h. -NOINLINE int32_t upb_pbdecoder_decode_varint_slow(upb_pbdecoder *d, - uint64_t *u64) { +UPB_NOINLINE int32_t upb_pbdecoder_decode_varint_slow(upb_pbdecoder *d, + uint64_t *u64) { *u64 = 0; uint8_t byte = 0x80; int bitpos; @@ -7387,7 +7718,7 @@ NOINLINE int32_t upb_pbdecoder_decode_varint_slow(upb_pbdecoder *d, // Decodes a varint from the current buffer position. // Returns a status code as described in decoder.int.h. -FORCEINLINE int32_t decode_varint(upb_pbdecoder *d, uint64_t *u64) { +UPB_FORCEINLINE static int32_t decode_varint(upb_pbdecoder *d, uint64_t *u64) { if (curbufleft(d) > 0 && !(*d->ptr & 0x80)) { *u64 = *d->ptr; advance(d, 1); @@ -7410,7 +7741,7 @@ FORCEINLINE int32_t decode_varint(upb_pbdecoder *d, uint64_t *u64) { // Decodes a 32-bit varint from the current buffer position. // Returns a status code as described in decoder.int.h. -FORCEINLINE int32_t decode_v32(upb_pbdecoder *d, uint32_t *u32) { +UPB_FORCEINLINE static int32_t decode_v32(upb_pbdecoder *d, uint32_t *u32) { uint64_t u64; int32_t ret = decode_varint(d, &u64); if (ret >= 0) return ret; @@ -7429,14 +7760,14 @@ FORCEINLINE int32_t decode_v32(upb_pbdecoder *d, uint32_t *u32) { // Decodes a fixed32 from the current buffer position. // Returns a status code as described in decoder.int.h. // TODO: proper byte swapping for big-endian machines. -FORCEINLINE int32_t decode_fixed32(upb_pbdecoder *d, uint32_t *u32) { +UPB_FORCEINLINE static int32_t decode_fixed32(upb_pbdecoder *d, uint32_t *u32) { return getbytes(d, u32, 4); } // Decodes a fixed64 from the current buffer position. // Returns a status code as described in decoder.int.h. // TODO: proper byte swapping for big-endian machines. -FORCEINLINE int32_t decode_fixed64(upb_pbdecoder *d, uint64_t *u64) { +UPB_FORCEINLINE static int32_t decode_fixed64(upb_pbdecoder *d, uint64_t *u64) { return getbytes(d, u64, 8); } @@ -7460,7 +7791,7 @@ static bool decoder_push(upb_pbdecoder *d, uint64_t end) { if (end > fr->end_ofs) { seterr(d, "Submessage end extends past enclosing submessage."); return false; - } else if ((fr + 1) == d->limit) { + } else if (fr == d->limit) { seterr(d, kPbDecoderStackOverflow); return false; } @@ -7487,8 +7818,8 @@ static bool pushtagdelim(upb_pbdecoder *d, uint32_t arg) { // Pops a frame from the decoder stack. static void decoder_pop(upb_pbdecoder *d) { d->top--; } -NOINLINE int32_t upb_pbdecoder_checktag_slow(upb_pbdecoder *d, - uint64_t expected) { +UPB_NOINLINE int32_t upb_pbdecoder_checktag_slow(upb_pbdecoder *d, + uint64_t expected) { uint64_t data = 0; size_t bytes = upb_value_size(expected); size_t read = peekbytes(d, &data, bytes); @@ -7640,10 +7971,17 @@ static int32_t dispatch(upb_pbdecoder *d) { if (ret == DECODE_ENDGROUP) { goto_endmsg(d); return DECODE_OK; - } else { - d->pc = d->last - 1; // Rewind to CHECKDELIM. - return ret; + } else if (ret == DECODE_OK) { + // We just consumed some input, so we might now have consumed all the data + // in the delmited region. Since every opcode that can trigger dispatch is + // directly preceded by OP_CHECKDELIM, rewind to it now to re-check the + // delimited end. + d->pc = d->last - 1; + assert(getop(*d->pc) == OP_CHECKDELIM); + return DECODE_OK; } + + return ret; } // Callers know that the stack is more than one deep because the opcodes that @@ -7866,7 +8204,10 @@ size_t upb_pbdecoder_decode(void *closure, const void *hd, const char *buf, void *upb_pbdecoder_startbc(void *closure, const void *pc, size_t size_hint) { upb_pbdecoder *d = closure; UPB_UNUSED(size_hint); + d->top->end_ofs = UINT64_MAX; + d->bufstart_ofs = 0; d->call_len = 1; + d->callstack[0] = &halt; d->pc = pc; return d; } @@ -7875,6 +8216,8 @@ void *upb_pbdecoder_startjit(void *closure, const void *hd, size_t size_hint) { UPB_UNUSED(hd); UPB_UNUSED(size_hint); upb_pbdecoder *d = closure; + d->top->end_ofs = UINT64_MAX; + d->bufstart_ofs = 0; d->call_len = 0; return d; } @@ -7931,58 +8274,120 @@ bool upb_pbdecoder_end(void *closure, const void *handler_data) { return true; } -void upb_pbdecoder_init(upb_pbdecoder *d, const upb_pbdecodermethod *m, - upb_status *s) { - d->limit = &d->stack[UPB_DECODER_MAX_NESTING]; - upb_bytessink_reset(&d->input_, &m->input_handler_, d); - d->method_ = m; - d->callstack[0] = &halt; - d->status = s; - upb_pbdecoder_reset(d); -} - void upb_pbdecoder_reset(upb_pbdecoder *d) { d->top = d->stack; - d->top->end_ofs = UINT64_MAX; d->top->groupnum = 0; - d->bufstart_ofs = 0; d->ptr = d->residual; d->buf = d->residual; d->end = d->residual; d->residual_end = d->residual; - d->call_len = 1; } -uint64_t upb_pbdecoder_bytesparsed(const upb_pbdecoder *d) { - return offset(d); +static size_t stacksize(upb_pbdecoder *d, size_t entries) { + UPB_UNUSED(d); + return entries * sizeof(upb_pbdecoder_frame); } -// Not currently required, but to support outgrowing the static stack we need -// this. -void upb_pbdecoder_uninit(upb_pbdecoder *d) { +static size_t callstacksize(upb_pbdecoder *d, size_t entries) { UPB_UNUSED(d); -} -const upb_pbdecodermethod *upb_pbdecoder_method(const upb_pbdecoder *d) { - return d->method_; +#ifdef UPB_USE_JIT_X64 + if (d->method_->is_native_) { + // Each native stack frame needs two pointers, plus we need a few frames for + // the enter/exit trampolines. + size_t ret = entries * sizeof(void*) * 2; + ret += sizeof(void*) * 10; + return ret; + } +#endif + + return entries * sizeof(uint32_t*); } -bool upb_pbdecoder_resetoutput(upb_pbdecoder *d, upb_sink* sink) { - // TODO(haberman): do we need to test whether the decoder is already on the - // stack (like calling this from within a callback)? Should we support - // rebinding the output at all? +upb_pbdecoder *upb_pbdecoder_create(upb_env *e, const upb_pbdecodermethod *m, + upb_sink *sink) { + const size_t default_max_nesting = 64; +#ifndef NDEBUG + size_t size_before = upb_env_bytesallocated(e); +#endif + + upb_pbdecoder *d = upb_env_malloc(e, sizeof(upb_pbdecoder)); + if (!d) return NULL; + + d->method_ = m; + d->callstack = upb_env_malloc(e, callstacksize(d, default_max_nesting)); + d->stack = upb_env_malloc(e, stacksize(d, default_max_nesting)); + if (!d->stack || !d->callstack) { + return NULL; + } + + d->env = e; + d->limit = d->stack + default_max_nesting - 1; + d->stack_size = default_max_nesting; + + upb_pbdecoder_reset(d); + upb_bytessink_reset(&d->input_, &m->input_handler_, d); + assert(sink); if (d->method_->dest_handlers_) { if (sink->handlers != d->method_->dest_handlers_) - return false; + return NULL; } upb_sink_reset(&d->top->sink, sink->handlers, sink->closure); - return true; + + // If this fails, increase the value in decoder.h. + assert(upb_env_bytesallocated(e) - size_before <= UPB_PB_DECODER_SIZE); + return d; +} + +uint64_t upb_pbdecoder_bytesparsed(const upb_pbdecoder *d) { + return offset(d); +} + +const upb_pbdecodermethod *upb_pbdecoder_method(const upb_pbdecoder *d) { + return d->method_; } upb_bytessink *upb_pbdecoder_input(upb_pbdecoder *d) { return &d->input_; } + +size_t upb_pbdecoder_maxnesting(const upb_pbdecoder *d) { + return d->stack_size; +} + +bool upb_pbdecoder_setmaxnesting(upb_pbdecoder *d, size_t max) { + assert(d->top >= d->stack); + + if (max < (size_t)(d->top - d->stack)) { + // Can't set a limit smaller than what we are currently at. + return false; + } + + if (max > d->stack_size) { + // Need to reallocate stack and callstack to accommodate. + size_t old_size = stacksize(d, d->stack_size); + size_t new_size = stacksize(d, max); + void *p = upb_env_realloc(d->env, d->stack, old_size, new_size); + if (!p) { + return false; + } + d->stack = p; + + old_size = callstacksize(d, d->stack_size); + new_size = callstacksize(d, max); + p = upb_env_realloc(d->env, d->callstack, old_size, new_size); + if (!p) { + return false; + } + d->callstack = p; + + d->stack_size = max; + } + + d->limit = d->stack + max - 1; + return true; +} /* * upb - a minimalist implementation of protocol buffers. * @@ -8045,6 +8450,68 @@ upb_bytessink *upb_pbdecoder_input(upb_pbdecoder *d) { #include +// The output buffer is divided into segments; a segment is a string of data +// that is "ready to go" -- it does not need any varint lengths inserted into +// the middle. The seams between segments are where varints will be inserted +// once they are known. +// +// We also use the concept of a "run", which is a range of encoded bytes that +// occur at a single submessage level. Every segment contains one or more runs. +// +// A segment can span messages. Consider: +// +// .--Submessage lengths---------. +// | | | +// | V V +// V | |--------------- | |----------------- +// Submessages: | |----------------------------------------------- +// Top-level msg: ------------------------------------------------------------ +// +// Segments: ----- ------------------- ----------------- +// Runs: *---- *--------------*--- *---------------- +// (* marks the start) +// +// Note that the top-level menssage is not in any segment because it does not +// have any length preceding it. +// +// A segment is only interrupted when another length needs to be inserted. So +// observe how the second segment spans both the inner submessage and part of +// the next enclosing message. +typedef struct { + uint32_t msglen; // The length to varint-encode before this segment. + uint32_t seglen; // Length of the segment. +} upb_pb_encoder_segment; + +struct upb_pb_encoder { + upb_env *env; + + // Our input and output. + upb_sink input_; + upb_bytessink *output_; + + // The "subclosure" -- used as the inner closure as part of the bytessink + // protocol. + void *subc; + + // The output buffer and limit, and our current write position. "buf" + // initially points to "initbuf", but is dynamically allocated if we need to + // grow beyond the initial size. + char *buf, *ptr, *limit; + + // The beginning of the current run, or undefined if we are at the top level. + char *runbegin; + + // The list of segments we are accumulating. + upb_pb_encoder_segment *segbuf, *segptr, *seglimit; + + // The stack of enclosing submessages. Each entry in the stack points to the + // segment where this submessage's length is being accumulated. + int *stack, *top, *stacklimit; + + // Depth of startmsg/endmsg calls. + int depth; +}; + /* low-level buffering ********************************************************/ // Low-level functions for interacting with the output buffer. @@ -8062,25 +8529,23 @@ static upb_pb_encoder_segment *top(upb_pb_encoder *e) { // Call to ensure that at least "bytes" bytes are available for writing at // e->ptr. Returns false if the bytes could not be allocated. static bool reserve(upb_pb_encoder *e, size_t bytes) { - if ((e->limit - e->ptr) < bytes) { + if ((size_t)(e->limit - e->ptr) < bytes) { + // Grow buffer. size_t needed = bytes + (e->ptr - e->buf); size_t old_size = e->limit - e->buf; + size_t new_size = old_size; + while (new_size < needed) { new_size *= 2; } - char *realloc_from = (e->buf == e->initbuf) ? NULL : e->buf; - char *new_buf = realloc(realloc_from, new_size); + char *new_buf = upb_env_realloc(e->env, e->buf, old_size, new_size); if (new_buf == NULL) { return false; } - if (realloc_from == NULL) { - memcpy(new_buf, e->initbuf, old_size); - } - e->ptr = new_buf + (e->ptr - e->buf); e->runbegin = new_buf + (e->runbegin - e->buf); e->limit = new_buf + new_size; @@ -8093,7 +8558,7 @@ static bool reserve(upb_pb_encoder *e, size_t bytes) { // Call when "bytes" bytes have been writte at e->ptr. The caller *must* have // previously called reserve() with at least this many bytes. static void encoder_advance(upb_pb_encoder *e, size_t bytes) { - assert((e->limit - e->ptr) >= bytes); + assert((size_t)(e->limit - e->ptr) >= bytes); e->ptr += bytes; } @@ -8149,21 +8614,17 @@ static bool start_delim(upb_pb_encoder *e) { } if (++e->segptr == e->seglimit) { - upb_pb_encoder_segment *realloc_from = - (e->segbuf == e->seginitbuf) ? NULL : e->segbuf; + // Grow segment buffer. size_t old_size = (e->seglimit - e->segbuf) * sizeof(upb_pb_encoder_segment); size_t new_size = old_size * 2; - upb_pb_encoder_segment *new_buf = realloc(realloc_from, new_size); + upb_pb_encoder_segment *new_buf = + upb_env_realloc(e->env, e->segbuf, old_size, new_size); if (new_buf == NULL) { return false; } - if (realloc_from == NULL) { - memcpy(new_buf, e->seginitbuf, old_size); - } - e->segptr = new_buf + (e->segptr - e->segbuf); e->seglimit = new_buf + (new_size / sizeof(upb_pb_encoder_segment)); e->segbuf = new_buf; @@ -8434,6 +8895,12 @@ static void newhandlers_callback(const void *closure, upb_handlers *h) { } } +void upb_pb_encoder_reset(upb_pb_encoder *e) { + e->segptr = NULL; + e->top = NULL; + e->depth = 0; +} + /* public API *****************************************************************/ @@ -8442,40 +8909,42 @@ const upb_handlers *upb_pb_encoder_newhandlers(const upb_msgdef *m, return upb_handlers_newfrozen(m, owner, newhandlers_callback, NULL); } -#define ARRAYSIZE(x) (sizeof(x) / sizeof(x[0])) +upb_pb_encoder *upb_pb_encoder_create(upb_env *env, const upb_handlers *h, + upb_bytessink *output) { + const size_t initial_bufsize = 256; + const size_t initial_segbufsize = 16; + // TODO(haberman): make this configurable. + const size_t stack_size = 64; +#ifndef NDEBUG + const size_t size_before = upb_env_bytesallocated(env); +#endif -void upb_pb_encoder_init(upb_pb_encoder *e, const upb_handlers *h) { - e->output_ = NULL; - e->subc = NULL; - e->buf = e->initbuf; - e->ptr = e->buf; - e->limit = e->buf + ARRAYSIZE(e->initbuf); - e->segbuf = e->seginitbuf; - e->seglimit = e->segbuf + ARRAYSIZE(e->seginitbuf); - e->stacklimit = e->stack + ARRAYSIZE(e->stack); - upb_sink_reset(&e->input_, h, e); -} + upb_pb_encoder *e = upb_env_malloc(env, sizeof(upb_pb_encoder)); + if (!e) return NULL; -void upb_pb_encoder_uninit(upb_pb_encoder *e) { - if (e->buf != e->initbuf) { - free(e->buf); - } + e->buf = upb_env_malloc(env, initial_bufsize); + e->segbuf = upb_env_malloc(env, initial_segbufsize * sizeof(*e->segbuf)); + e->stack = upb_env_malloc(env, stack_size * sizeof(*e->stack)); - if (e->segbuf != e->seginitbuf) { - free(e->segbuf); + if (!e->buf || !e->segbuf || !e->stack) { + return NULL; } -} -void upb_pb_encoder_resetoutput(upb_pb_encoder *e, upb_bytessink *output) { + e->limit = e->buf + initial_bufsize; + e->seglimit = e->segbuf + initial_segbufsize; + e->stacklimit = e->stack + stack_size; + upb_pb_encoder_reset(e); + upb_sink_reset(&e->input_, h, e); + + e->env = env; e->output_ = output; e->subc = output->closure; -} + e->ptr = e->buf; -void upb_pb_encoder_reset(upb_pb_encoder *e) { - e->segptr = NULL; - e->top = NULL; - e->depth = 0; + // If this fails, increase the value in encoder.h. + assert(upb_env_bytesallocated(env) - size_before <= UPB_PB_ENCODER_SIZE); + return e; } upb_sink *upb_pb_encoder_input(upb_pb_encoder *e) { return &e->input_; } @@ -8500,26 +8969,26 @@ upb_def **upb_load_defs_from_descriptor(const char *str, size_t len, int *n, const upb_pbdecodermethod *decoder_m = upb_pbdecodermethod_new(&opts, &decoder_m); - upb_pbdecoder decoder; - upb_descreader reader; + upb_env env; + upb_env_init(&env); + upb_env_reporterrorsto(&env, status); - upb_pbdecoder_init(&decoder, decoder_m, status); - upb_descreader_init(&reader, reader_h, status); - upb_pbdecoder_resetoutput(&decoder, upb_descreader_input(&reader)); + upb_descreader *reader = upb_descreader_create(&env, reader_h); + upb_pbdecoder *decoder = + upb_pbdecoder_create(&env, decoder_m, upb_descreader_input(reader)); // Push input data. - bool ok = upb_bufsrc_putbuf(str, len, upb_pbdecoder_input(&decoder)); + bool ok = upb_bufsrc_putbuf(str, len, upb_pbdecoder_input(decoder)); upb_def **ret = NULL; if (!ok) goto cleanup; - upb_def **defs = upb_descreader_getdefs(&reader, owner, n); + upb_def **defs = upb_descreader_getdefs(reader, owner, n); ret = malloc(sizeof(upb_def*) * (*n)); memcpy(ret, defs, sizeof(upb_def*) * (*n)); cleanup: - upb_pbdecoder_uninit(&decoder); - upb_descreader_uninit(&reader); + upb_env_uninit(&env); upb_handlers_unref(reader_h, &reader_h); upb_pbdecodermethod_unref(decoder_m, &decoder_m); return ret; @@ -8584,6 +9053,14 @@ bool upb_load_descriptor_file_into_symtab(upb_symtab *symtab, const char *fname, #include +struct upb_textprinter { + upb_sink input_; + upb_bytessink *output_; + int indent_depth_; + bool single_line_; + void *subc; +}; + #define CHECK(x) if ((x) < 0) goto err; static const char *shortname(const char *longname) { @@ -8801,24 +9278,6 @@ err: return false; } - -/* Public API *****************************************************************/ - -void upb_textprinter_init(upb_textprinter *p, const upb_handlers *h) { - p->single_line_ = false; - p->indent_depth_ = 0; - upb_sink_reset(&p->input_, h, p); -} - -void upb_textprinter_uninit(upb_textprinter *p) { - UPB_UNUSED(p); -} - -void upb_textprinter_reset(upb_textprinter *p, bool single_line) { - p->single_line_ = single_line; - p->indent_depth_ = 0; -} - static void onmreg(const void *c, upb_handlers *h) { UPB_UNUSED(c); const upb_msgdef *m = upb_handlers_msgdef(h); @@ -8878,6 +9337,26 @@ static void onmreg(const void *c, upb_handlers *h) { } } +static void textprinter_reset(upb_textprinter *p, bool single_line) { + p->single_line_ = single_line; + p->indent_depth_ = 0; +} + + +/* Public API *****************************************************************/ + +upb_textprinter *upb_textprinter_create(upb_env *env, const upb_handlers *h, + upb_bytessink *output) { + upb_textprinter *p = upb_env_malloc(env, sizeof(upb_textprinter)); + if (!p) return NULL; + + p->output_ = output; + upb_sink_reset(&p->input_, h, p); + textprinter_reset(p, false); + + return p; +} + const upb_handlers *upb_textprinter_newhandlers(const upb_msgdef *m, const void *owner) { return upb_handlers_newfrozen(m, owner, &onmreg, NULL); @@ -8885,11 +9364,6 @@ const upb_handlers *upb_textprinter_newhandlers(const upb_msgdef *m, upb_sink *upb_textprinter_input(upb_textprinter *p) { return &p->input_; } -bool upb_textprinter_resetoutput(upb_textprinter *p, upb_bytessink *output) { - p->output_ = output; - return true; -} - void upb_textprinter_setsingleline(upb_textprinter *p, bool single_line) { p->single_line_ = single_line; } @@ -9052,6 +9526,71 @@ upb_decoderet upb_vdecode_max8_wright(upb_decoderet r) { #include +#define UPB_JSON_MAX_DEPTH 64 + +typedef struct { + 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; + +struct upb_json_parser { + upb_env *env; + upb_byteshandler input_handler_; + upb_bytessink input_; + + // Stack to track the JSON scopes we are in. + upb_jsonparser_frame stack[UPB_JSON_MAX_DEPTH]; + upb_jsonparser_frame *top; + upb_jsonparser_frame *limit; + + upb_status *status; + + // Ragel's internal parsing stack for the parsing state machine. + int current_state; + int parser_stack[UPB_JSON_MAX_DEPTH]; + int parser_top; + + // The handle for the current buffer. + const upb_bufhandle *handle; + + // Accumulate buffer. See details in parser.rl. + const char *accumulated; + size_t accumulated_len; + char *accumulate_buf; + size_t accumulate_buf_size; + + // Multi-part text data. See details in parser.rl. + int multipart_state; + upb_selector_t string_selector; + + // Input capture. See details in parser.rl. + const char *capture; + + // Intermediate result of parsing a unicode escape sequence. + uint32_t digit; +}; + #define PARSER_CHECK_RETURN(x) if (!(x)) return false // Used to signal that a capture has been suspended. @@ -9254,12 +9793,13 @@ static void accumulate_clear(upb_json_parser *p) { // Used internally by accumulate_append(). static bool accumulate_realloc(upb_json_parser *p, size_t need) { - size_t new_size = UPB_MAX(p->accumulate_buf_size, 128); + size_t old_size = p->accumulate_buf_size; + size_t new_size = UPB_MAX(old_size, 128); while (new_size < need) { new_size = saturating_multiply(new_size, 2); } - void *mem = realloc(p->accumulate_buf, new_size); + void *mem = upb_env_realloc(p->env, p->accumulate_buf, old_size, new_size); if (!mem) { upb_status_seterrmsg(p->status, "Out of memory allocating buffer."); return false; @@ -10008,11 +10548,11 @@ static void end_object(upb_json_parser *p) { // final state once, when the closing '"' is seen. -#line 1085 "upb/json/parser.rl" +#line 1151 "upb/json/parser.rl" -#line 997 "upb/json/parser.c" +#line 1063 "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, @@ -10154,8 +10694,6 @@ static const char _json_trans_actions[] = { }; static const int json_start = 1; -static const int json_first_final = 56; -static const int json_error = 0; static const int json_en_number_machine = 10; static const int json_en_string_machine = 19; @@ -10163,7 +10701,7 @@ static const int json_en_value_machine = 27; static const int json_en_main = 1; -#line 1088 "upb/json/parser.rl" +#line 1154 "upb/json/parser.rl" size_t parse(void *closure, const void *hd, const char *buf, size_t size, const upb_bufhandle *handle) { @@ -10183,7 +10721,7 @@ size_t parse(void *closure, const void *hd, const char *buf, size_t size, capture_resume(parser, buf); -#line 1168 "upb/json/parser.c" +#line 1232 "upb/json/parser.c" { int _klen; unsigned int _trans; @@ -10258,118 +10796,118 @@ _match: switch ( *_acts++ ) { case 0: -#line 1000 "upb/json/parser.rl" +#line 1066 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; case 1: -#line 1001 "upb/json/parser.rl" +#line 1067 "upb/json/parser.rl" { p--; {stack[top++] = cs; cs = 10; goto _again;} } break; case 2: -#line 1005 "upb/json/parser.rl" +#line 1071 "upb/json/parser.rl" { start_text(parser, p); } break; case 3: -#line 1006 "upb/json/parser.rl" +#line 1072 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_text(parser, p)); } break; case 4: -#line 1012 "upb/json/parser.rl" +#line 1078 "upb/json/parser.rl" { start_hex(parser); } break; case 5: -#line 1013 "upb/json/parser.rl" +#line 1079 "upb/json/parser.rl" { hexdigit(parser, p); } break; case 6: -#line 1014 "upb/json/parser.rl" +#line 1080 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_hex(parser)); } break; case 7: -#line 1020 "upb/json/parser.rl" +#line 1086 "upb/json/parser.rl" { CHECK_RETURN_TOP(escape(parser, p)); } break; case 8: -#line 1026 "upb/json/parser.rl" +#line 1092 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; case 9: -#line 1029 "upb/json/parser.rl" +#line 1095 "upb/json/parser.rl" { {stack[top++] = cs; cs = 19; goto _again;} } break; case 10: -#line 1031 "upb/json/parser.rl" +#line 1097 "upb/json/parser.rl" { p--; {stack[top++] = cs; cs = 27; goto _again;} } break; case 11: -#line 1036 "upb/json/parser.rl" +#line 1102 "upb/json/parser.rl" { start_member(parser); } break; case 12: -#line 1037 "upb/json/parser.rl" +#line 1103 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_membername(parser)); } break; case 13: -#line 1040 "upb/json/parser.rl" +#line 1106 "upb/json/parser.rl" { end_member(parser); } break; case 14: -#line 1046 "upb/json/parser.rl" +#line 1112 "upb/json/parser.rl" { start_object(parser); } break; case 15: -#line 1049 "upb/json/parser.rl" +#line 1115 "upb/json/parser.rl" { end_object(parser); } break; case 16: -#line 1055 "upb/json/parser.rl" +#line 1121 "upb/json/parser.rl" { CHECK_RETURN_TOP(start_array(parser)); } break; case 17: -#line 1059 "upb/json/parser.rl" +#line 1125 "upb/json/parser.rl" { end_array(parser); } break; case 18: -#line 1064 "upb/json/parser.rl" +#line 1130 "upb/json/parser.rl" { start_number(parser, p); } break; case 19: -#line 1065 "upb/json/parser.rl" +#line 1131 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_number(parser, p)); } break; case 20: -#line 1067 "upb/json/parser.rl" +#line 1133 "upb/json/parser.rl" { CHECK_RETURN_TOP(start_stringval(parser)); } break; case 21: -#line 1068 "upb/json/parser.rl" +#line 1134 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_stringval(parser)); } break; case 22: -#line 1070 "upb/json/parser.rl" +#line 1136 "upb/json/parser.rl" { CHECK_RETURN_TOP(parser_putbool(parser, true)); } break; case 23: -#line 1072 "upb/json/parser.rl" +#line 1138 "upb/json/parser.rl" { CHECK_RETURN_TOP(parser_putbool(parser, false)); } break; case 24: -#line 1074 "upb/json/parser.rl" +#line 1140 "upb/json/parser.rl" { /* null value */ } break; case 25: -#line 1076 "upb/json/parser.rl" +#line 1142 "upb/json/parser.rl" { CHECK_RETURN_TOP(start_subobject(parser)); } break; case 26: -#line 1077 "upb/json/parser.rl" +#line 1143 "upb/json/parser.rl" { end_subobject(parser); } break; case 27: -#line 1082 "upb/json/parser.rl" +#line 1148 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; -#line 1354 "upb/json/parser.c" +#line 1418 "upb/json/parser.c" } } @@ -10382,7 +10920,7 @@ _again: _out: {} } -#line 1107 "upb/json/parser.rl" +#line 1173 "upb/json/parser.rl" if (p != pe) { upb_status_seterrf(parser->status, "Parse error at %s\n", p); @@ -10401,29 +10939,17 @@ error: bool end(void *closure, const void *hd) { UPB_UNUSED(closure); UPB_UNUSED(hd); - return true; -} - -/* Public API *****************************************************************/ - -void upb_json_parser_init(upb_json_parser *p, upb_status *status) { - p->limit = p->stack + UPB_JSON_MAX_DEPTH; - p->accumulate_buf = NULL; - p->accumulate_buf_size = 0; - upb_byteshandler_init(&p->input_handler_); - upb_byteshandler_setstring(&p->input_handler_, parse, NULL); - upb_byteshandler_setendstr(&p->input_handler_, end, NULL); - upb_bytessink_reset(&p->input_, &p->input_handler_, p); - p->status = status; -} - -void upb_json_parser_uninit(upb_json_parser *p) { - upb_byteshandler_uninit(&p->input_handler_); - free(p->accumulate_buf); + // Prevent compile warning on unused static constants. + UPB_UNUSED(json_start); + UPB_UNUSED(json_en_number_machine); + UPB_UNUSED(json_en_string_machine); + UPB_UNUSED(json_en_value_machine); + UPB_UNUSED(json_en_main); + return true; } -void upb_json_parser_reset(upb_json_parser *p) { +static void json_parser_reset(upb_json_parser *p) { p->top = p->stack; p->top->f = NULL; p->top->is_map = false; @@ -10433,25 +10959,48 @@ void upb_json_parser_reset(upb_json_parser *p) { int top; // Emit Ragel initialization of the parser. -#line 1418 "upb/json/parser.c" +#line 1470 "upb/json/parser.c" { cs = json_start; top = 0; } -#line 1157 "upb/json/parser.rl" +#line 1211 "upb/json/parser.rl" p->current_state = cs; p->parser_top = top; accumulate_clear(p); p->multipart_state = MULTIPART_INACTIVE; p->capture = NULL; + p->accumulated = NULL; } -void upb_json_parser_resetoutput(upb_json_parser *p, upb_sink *sink) { - upb_json_parser_reset(p); - upb_sink_reset(&p->top->sink, sink->handlers, sink->closure); - p->top->m = upb_handlers_msgdef(sink->handlers); - p->accumulated = NULL; + +/* Public API *****************************************************************/ + +upb_json_parser *upb_json_parser_create(upb_env *env, upb_sink *output) { +#ifndef NDEBUG + const size_t size_before = upb_env_bytesallocated(env); +#endif + upb_json_parser *p = upb_env_malloc(env, sizeof(upb_json_parser)); + if (!p) return false; + + p->env = env; + p->limit = p->stack + UPB_JSON_MAX_DEPTH; + p->accumulate_buf = NULL; + p->accumulate_buf_size = 0; + upb_byteshandler_init(&p->input_handler_); + upb_byteshandler_setstring(&p->input_handler_, parse, NULL); + upb_byteshandler_setendstr(&p->input_handler_, end, NULL); + upb_bytessink_reset(&p->input_, &p->input_handler_, p); + + json_parser_reset(p); + upb_sink_reset(&p->top->sink, output->handlers, output->closure); + p->top->m = upb_handlers_msgdef(output->handlers); + + // If this fails, uncomment and increase the value in parser.h. + // fprintf(stderr, "%zd\n", upb_env_bytesallocated(env) - size_before); + assert(upb_env_bytesallocated(env) - size_before <= UPB_JSON_PARSER_SIZE); + return p; } upb_bytessink *upb_json_parser_input(upb_json_parser *p) { @@ -10473,6 +11022,27 @@ upb_bytessink *upb_json_parser_input(upb_json_parser *p) { #include #include +struct upb_json_printer { + upb_sink input_; + // BytesSink closure. + void *subc_; + upb_bytessink *output_; + + // We track the depth so that we know when to emit startstr/endstr on the + // output. + int depth_; + + // Have we emitted the first element? This state is necessary to emit commas + // without leaving a trailing comma in arrays/maps. We keep this state per + // frame depth. + // + // Why max_depth * 2? UPB_MAX_HANDLER_DEPTH counts depth as nested messages. + // We count frames (contexts in which we separate elements by commas) as both + // repeated fields and messages (maps), and the worst case is a + // message->repeated field->submessage->repeated field->... nesting. + bool first_elem_[UPB_MAX_HANDLER_DEPTH * 2]; +}; + // StringPiece; a pointer plus a length. typedef struct { const char *ptr; @@ -10620,7 +11190,7 @@ static bool putkey(void *closure, const void *handler_data) { return true; } -#define CHKFMT(val) if ((val) == -1) return false; +#define CHKFMT(val) if ((val) == (size_t)-1) return false; #define CHK(val) if (!(val)) return false; #define TYPE_HANDLERS(type, fmt_func) \ @@ -11189,25 +11759,29 @@ void printer_sethandlers(const void *closure, upb_handlers *h) { #undef TYPE } -/* Public API *****************************************************************/ - -void upb_json_printer_init(upb_json_printer *p, const upb_handlers *h) { - p->output_ = NULL; +static void json_printer_reset(upb_json_printer *p) { p->depth_ = 0; - upb_sink_reset(&p->input_, h, p); } -void upb_json_printer_uninit(upb_json_printer *p) { - UPB_UNUSED(p); -} -void upb_json_printer_reset(upb_json_printer *p) { - p->depth_ = 0; -} +/* Public API *****************************************************************/ + +upb_json_printer *upb_json_printer_create(upb_env *e, const upb_handlers *h, + upb_bytessink *output) { +#ifndef NDEBUG + size_t size_before = upb_env_bytesallocated(e); +#endif + + upb_json_printer *p = upb_env_malloc(e, sizeof(upb_json_printer)); + if (!p) return NULL; -void upb_json_printer_resetoutput(upb_json_printer *p, upb_bytessink *output) { - upb_json_printer_reset(p); p->output_ = output; + json_printer_reset(p); + upb_sink_reset(&p->input_, h, p); + + // If this fails, increase the value in printer.h. + assert(upb_env_bytesallocated(e) - size_before <= UPB_JSON_PRINTER_SIZE); + return p; } upb_sink *upb_json_printer_input(upb_json_printer *p) { diff --git a/ruby/ext/google/protobuf_c/upb.h b/ruby/ext/google/protobuf_c/upb.h index 8f6d3643..97df943a 100644 --- a/ruby/ext/google/protobuf_c/upb.h +++ b/ruby/ext/google/protobuf_c/upb.h @@ -99,6 +99,15 @@ #define UPB_INLINE static inline #endif +// For use in C/C++ source files (not headers), forces inlining within the file. +#ifdef __GNUC__ +#define UPB_FORCEINLINE inline __attribute__((always_inline)) +#define UPB_NOINLINE __attribute__((noinline)) +#else +#define UPB_FORCEINLINE +#define UPB_NOINLINE +#endif + #if __STDC_VERSION__ >= 199901L #define UPB_C99 #endif @@ -4805,10 +4814,8 @@ UPB_DEFINE_STRUCT0(upb_byteshandler, )); void upb_byteshandler_init(upb_byteshandler *h); -void upb_byteshandler_uninit(upb_byteshandler *h); // Caller must ensure that "d" outlives the handlers. -// TODO(haberman): support handlerfree function for the data. // TODO(haberman): should this have a "freeze" operation? It's not necessary // for memory management, but could be useful to force immutability and provide // a convenient moment to verify that all registration succeeded. @@ -4983,12 +4990,17 @@ template struct disable_if_same {}; template void DeletePointer(void *p) { delete static_cast(p); } template -struct FirstUnlessVoid { +struct FirstUnlessVoidOrBool { typedef T1 value; }; template -struct FirstUnlessVoid { +struct FirstUnlessVoidOrBool { + typedef T2 value; +}; + +template +struct FirstUnlessVoidOrBool { typedef T2 value; }; @@ -5370,10 +5382,14 @@ inline MethodSig4 MatchFunc(R (C::*f)(P1, P2, P3, P4)) { // // 1. If the function returns void, make it return the expected type and with // a value that always indicates success. -// 2. If the function is expected to return void* but doesn't, wrap it so it -// does (either by returning the closure param if the wrapped function -// returns void or by casting a different pointer type to void* for -// return). +// 2. If the function returns bool, make it return the expected type with a +// value that indicates success or failure. +// +// The "expected type" for return is: +// 1. void* for start handlers. If the closure parameter has a different type +// we will cast it to void* for the return in the success case. +// 2. size_t for string buffer handlers. +// 3. bool for everything else. // Template parameters are FuncN type and desired return type. template @@ -5762,10 +5778,13 @@ inline Handler::Handler(F func) attr_.SetClosureType(UniquePtrForType()); // We use the closure type (from the first parameter) if the return type is - // void. This is all nonsense for non START* handlers, but it doesn't matter - // because in that case the value will be ignored. - typedef typename FirstUnlessVoid::value + // void or bool, since these are the two cases we wrap to return the closure's + // type anyway. + // + // This is all nonsense for non START* handlers, but it doesn't matter because + // in that case the value will be ignored. + typedef typename FirstUnlessVoidOrBool::value EffectiveReturn; attr_.SetReturnClosureType(UniquePtrForType()); } @@ -5960,9 +5979,7 @@ inline BytesHandler::BytesHandler() { upb_byteshandler_init(this); } -inline BytesHandler::~BytesHandler() { - upb_byteshandler_uninit(this); -} +inline BytesHandler::~BytesHandler() {} } // namespace upb @@ -5983,6 +6000,261 @@ inline BytesHandler::~BytesHandler() { #endif // UPB_HANDLERS_INL_H_ #endif // UPB_HANDLERS_H +/* + * upb - a minimalist implementation of protocol buffers. + * + * Copyright (c) 2014 Google Inc. See LICENSE for details. + * Author: Josh Haberman + * + * A upb::Environment provides a means for injecting malloc and an + * error-reporting callback into encoders/decoders. This allows them to be + * independent of nearly all assumptions about their actual environment. + * + * It is also a container for allocating the encoders/decoders themselves that + * insulates clients from knowing their actual size. This provides ABI + * compatibility even if the size of the objects change. And this allows the + * structure definitions to be in the .c files instead of the .h files, making + * the .h files smaller and more readable. + */ + + +#ifndef UPB_ENV_H_ +#define UPB_ENV_H_ + +#ifdef __cplusplus +namespace upb { +class Environment; +class SeededAllocator; +} +#endif + +UPB_DECLARE_TYPE(upb::Environment, upb_env); +UPB_DECLARE_TYPE(upb::SeededAllocator, upb_seededalloc); + +typedef void *upb_alloc_func(void *ud, void *ptr, size_t oldsize, size_t size); +typedef void upb_cleanup_func(void *ud); +typedef bool upb_error_func(void *ud, const upb_status *status); + +// An environment is *not* thread-safe. +UPB_DEFINE_CLASS0(upb::Environment, + public: + Environment(); + ~Environment(); + + // Set a custom memory allocation function for the environment. May ONLY + // be called before any calls to Malloc()/Realloc()/AddCleanup() below. + // If this is not called, the system realloc() function will be used. + // The given user pointer "ud" will be passed to the allocation function. + // + // The allocation function will not receive corresponding "free" calls. it + // must ensure that the memory is valid for the lifetime of the Environment, + // but it may be reclaimed any time thereafter. The likely usage is that + // "ud" points to a stateful allocator, and that the allocator frees all + // memory, arena-style, when it is destroyed. In this case the allocator must + // outlive the Environment. Another possibility is that the allocation + // function returns GC-able memory that is guaranteed to be GC-rooted for the + // life of the Environment. + void SetAllocationFunction(upb_alloc_func* alloc, void* ud); + + template + void SetAllocator(T* allocator) { + SetAllocationFunction(allocator->GetAllocationFunction(), allocator); + } + + // Set a custom error reporting function. + void SetErrorFunction(upb_error_func* func, void* ud); + + // Set the error reporting function to simply copy the status to the given + // status and abort. + void ReportErrorsTo(Status* status); + + // Returns true if all allocations and AddCleanup() calls have succeeded, + // and no errors were reported with ReportError() (except ones that recovered + // successfully). + bool ok() const; + + ////////////////////////////////////////////////////////////////////////////// + // Functions for use by encoders/decoders. + + // Reports an error to this environment's callback, returning true if + // the caller should try to recover. + bool ReportError(const Status* status); + + // Allocate memory. Uses the environment's allocation function. + // + // There is no need to free(). All memory will be freed automatically, but is + // guaranteed to outlive the Environment. + void* Malloc(size_t size); + + // Reallocate memory. Preserves "oldsize" bytes from the existing buffer + // Requires: oldsize <= existing_size. + // + // TODO(haberman): should we also enforce that oldsize <= size? + void* Realloc(void* ptr, size_t oldsize, size_t size); + + // Add a cleanup function to run when the environment is destroyed. + // Returns false on out-of-memory. + // + // The first call to AddCleanup() after SetAllocationFunction() is guaranteed + // to return true -- this makes it possible to robustly set a cleanup handler + // for a custom allocation function. + bool AddCleanup(upb_cleanup_func* func, void* ud); + + // Total number of bytes that have been allocated. It is undefined what + // Realloc() does to this counter. + size_t BytesAllocated() const; + + private: + UPB_DISALLOW_COPY_AND_ASSIGN(Environment); +, +UPB_DEFINE_STRUCT0(upb_env, + bool ok_; + size_t bytes_allocated; + + // Alloc function. + upb_alloc_func *alloc; + void *alloc_ud; + + // Error-reporting function. + upb_error_func *err; + void *err_ud; + + // Userdata for default alloc func. + void *default_alloc_ud; + + // Cleanup entries. Pointer to a cleanup_ent, defined in env.c + void *cleanup_head; + + // For future expansion, since the size of this struct is exposed to users. + void *future1; + void *future2; +)); + +UPB_BEGIN_EXTERN_C + +void upb_env_init(upb_env *e); +void upb_env_uninit(upb_env *e); +void upb_env_setallocfunc(upb_env *e, upb_alloc_func *func, void *ud); +void upb_env_seterrorfunc(upb_env *e, upb_error_func *func, void *ud); +void upb_env_reporterrorsto(upb_env *e, upb_status *status); +bool upb_env_ok(const upb_env *e); +bool upb_env_reporterror(upb_env *e, const upb_status *status); +void *upb_env_malloc(upb_env *e, size_t size); +void *upb_env_realloc(upb_env *e, void *ptr, size_t oldsize, size_t size); +bool upb_env_addcleanup(upb_env *e, upb_cleanup_func *func, void *ud); +size_t upb_env_bytesallocated(const upb_env *e); + +UPB_END_EXTERN_C + +// An allocator that allocates from an initial memory region (likely the stack) +// before falling back to another allocator. +UPB_DEFINE_CLASS0(upb::SeededAllocator, + public: + SeededAllocator(void *mem, size_t len); + ~SeededAllocator(); + + // Set a custom fallback memory allocation function for the allocator, to use + // once the initial region runs out. + // + // May ONLY be called before GetAllocationFunction(). If this is not + // called, the system realloc() will be the fallback allocator. + void SetFallbackAllocator(upb_alloc_func *alloc, void *ud); + + // Gets the allocation function for this allocator. + upb_alloc_func* GetAllocationFunction(); + + private: + UPB_DISALLOW_COPY_AND_ASSIGN(SeededAllocator); +, +UPB_DEFINE_STRUCT0(upb_seededalloc, + // Fallback alloc function. + upb_alloc_func *alloc; + upb_cleanup_func *alloc_cleanup; + void *alloc_ud; + bool need_cleanup; + bool returned_allocfunc; + + // Userdata for default alloc func. + void *default_alloc_ud; + + // Pointers for the initial memory region. + char *mem_base; + char *mem_ptr; + char *mem_limit; + + // For future expansion, since the size of this struct is exposed to users. + void *future1; + void *future2; +)); + +UPB_BEGIN_EXTERN_C + +void upb_seededalloc_init(upb_seededalloc *a, void *mem, size_t len); +void upb_seededalloc_uninit(upb_seededalloc *a); +void upb_seededalloc_setfallbackalloc(upb_seededalloc *a, upb_alloc_func *func, + void *ud); +upb_alloc_func *upb_seededalloc_getallocfunc(upb_seededalloc *a); + +UPB_END_EXTERN_C + +#ifdef __cplusplus + +namespace upb { + +inline Environment::Environment() { + upb_env_init(this); +} +inline Environment::~Environment() { + upb_env_uninit(this); +} +inline void Environment::SetAllocationFunction(upb_alloc_func *alloc, + void *ud) { + upb_env_setallocfunc(this, alloc, ud); +} +inline void Environment::SetErrorFunction(upb_error_func *func, void *ud) { + upb_env_seterrorfunc(this, func, ud); +} +inline void Environment::ReportErrorsTo(Status* status) { + upb_env_reporterrorsto(this, status); +} +inline bool Environment::ok() const { + return upb_env_ok(this); +} +inline bool Environment::ReportError(const Status* status) { + return upb_env_reporterror(this, status); +} +inline void *Environment::Malloc(size_t size) { + return upb_env_malloc(this, size); +} +inline void *Environment::Realloc(void *ptr, size_t oldsize, size_t size) { + return upb_env_realloc(this, ptr, oldsize, size); +} +inline bool Environment::AddCleanup(upb_cleanup_func *func, void *ud) { + return upb_env_addcleanup(this, func, ud); +} +inline size_t Environment::BytesAllocated() const { + return upb_env_bytesallocated(this); +} + +inline SeededAllocator::SeededAllocator(void *mem, size_t len) { + upb_seededalloc_init(this, mem, len); +} +inline SeededAllocator::~SeededAllocator() { + upb_seededalloc_uninit(this); +} +inline void SeededAllocator::SetFallbackAllocator(upb_alloc_func *alloc, + void *ud) { + upb_seededalloc_setfallbackalloc(this, alloc, ud); +} +inline upb_alloc_func *SeededAllocator::GetAllocationFunction() { + return upb_seededalloc_getallocfunc(this); +} + +} // namespace upb + +#endif // __cplusplus + +#endif // UPB_ENV_H_ /* * upb - a minimalist implementation of protocol buffers. * @@ -6018,27 +6290,6 @@ UPB_DECLARE_TYPE(upb::BufferSource, upb_bufsrc); UPB_DECLARE_TYPE(upb::BytesSink, upb_bytessink); UPB_DECLARE_TYPE(upb::Sink, upb_sink); -// Internal-only struct for the sink. -struct upb_sinkframe { - UPB_PRIVATE_FOR_CPP - const upb_handlers *h; - void *closure; - - // For any frames besides the top, this is the END* callback that will run - // when the subframe is popped (for example, for a "sequence" frame the frame - // above it will be a UPB_HANDLER_ENDSEQ handler). But this is only - // necessary for assertion checking inside upb_sink and can be omitted if the - // sink has only one caller. - // - // TODO(haberman): have a mechanism for ensuring that a sink only has one - // caller. - upb_selector_t selector; -}; - -// The maximum nesting depth that upb::Sink will allow. Matches proto2's limit. -// TODO: make this a runtime-settable property of Sink. -#define UPB_SINK_MAX_NESTING 64 - // A upb::Sink is an object that binds a upb::Handlers object to some runtime // state. It represents an endpoint to which data can be sent. // @@ -6598,45 +6849,11 @@ class Reader; UPB_DECLARE_TYPE(upb::descriptor::Reader, upb_descreader); -// Internal-only structs used by Reader. - -// upb_deflist is an internal-only dynamic array for storing a growing list of -// upb_defs. -typedef struct { - UPB_PRIVATE_FOR_CPP - upb_def **defs; - size_t len; - size_t size; - bool owned; -} upb_deflist; - -// We keep a stack of all the messages scopes we are currently in, as well as -// the top-level file scope. This is necessary to correctly qualify the -// definitions that are contained inside. "name" tracks the name of the -// message or package (a bare name -- not qualified by any enclosing scopes). -typedef struct { - UPB_PRIVATE_FOR_CPP - char *name; - // Index of the first def that is under this scope. For msgdefs, the - // msgdef itself is at start-1. - int start; -} upb_descreader_frame; - -// The maximum number of nested declarations that are allowed, ie. -// message Foo { -// message Bar { -// message Baz { -// } -// } -// } -// -// This is a resource limit that affects how big our runtime stack can grow. -// TODO: make this a runtime-settable property of the Reader instance. -#define UPB_MAX_MESSAGE_NESTING 64 +#ifdef __cplusplus // Class that receives descriptor data according to the descriptor.proto schema // and use it to build upb::Defs corresponding to that schema. -UPB_DEFINE_CLASS0(upb::descriptor::Reader, +class upb::descriptor::Reader { public: // These handlers must have come from NewHandlers() and must outlive the // Reader. @@ -6646,11 +6863,7 @@ UPB_DEFINE_CLASS0(upb::descriptor::Reader, // to build/memory-manage the handlers at runtime at all). Unfortunately this // is a bit tricky to implement for Handlers, but necessary to simplify this // interface. - Reader(const Handlers* handlers, Status* status); - ~Reader(); - - // Resets the reader's state and discards any defs it may have built. - void Reset(); + static Reader* Create(Environment* env, const Handlers* handlers); // The reader's input; this is where descriptor.proto data should be sent. Sink* input(); @@ -6666,45 +6879,30 @@ UPB_DEFINE_CLASS0(upb::descriptor::Reader, // Builds and returns handlers for the reader, owned by "owner." static Handlers* NewHandlers(const void* owner); -, -UPB_DEFINE_STRUCT0(upb_descreader, - upb_sink sink; - upb_deflist defs; - upb_descreader_frame stack[UPB_MAX_MESSAGE_NESTING]; - int stack_len; - - uint32_t number; - char *name; - bool saw_number; - bool saw_name; - char *default_string; + private: + UPB_DISALLOW_POD_OPS(Reader, upb::descriptor::Reader); +}; - upb_fielddef *f; -)); +#endif -UPB_BEGIN_EXTERN_C // { +UPB_BEGIN_EXTERN_C // C API. -void upb_descreader_init(upb_descreader *r, const upb_handlers *handlers, - upb_status *status); -void upb_descreader_uninit(upb_descreader *r); -void upb_descreader_reset(upb_descreader *r); +upb_descreader *upb_descreader_create(upb_env *e, const upb_handlers *h); upb_sink *upb_descreader_input(upb_descreader *r); upb_def **upb_descreader_getdefs(upb_descreader *r, void *owner, int *n); const upb_handlers *upb_descreader_newhandlers(const void *owner); -UPB_END_EXTERN_C // } +UPB_END_EXTERN_C #ifdef __cplusplus // C++ implementation details. ///////////////////////////////////////////////// namespace upb { namespace descriptor { -inline Reader::Reader(const Handlers *h, Status *s) { - upb_descreader_init(this, h, s); +inline Reader* Reader::Create(Environment* e, const Handlers *h) { + return upb_descreader_create(e, h); } -inline Reader::~Reader() { upb_descreader_uninit(this); } -inline void Reader::Reset() { upb_descreader_reset(this); } inline Sink* Reader::input() { return upb_descreader_input(this); } inline upb::Def** Reader::GetDefs(void* owner, int* n) { return upb_descreader_getdefs(this, owner, n); @@ -6764,44 +6962,6 @@ UPB_DECLARE_TYPE(upb::pb::Decoder, upb_pbdecoder); UPB_DECLARE_TYPE(upb::pb::DecoderMethod, upb_pbdecodermethod); UPB_DECLARE_TYPE(upb::pb::DecoderMethodOptions, upb_pbdecodermethodopts); -// The maximum that any submessages can be nested. Matches proto2's limit. -// This specifies the size of the decoder's statically-sized array and therefore -// setting it high will cause the upb::pb::Decoder object to be larger. -// -// If necessary we can add a runtime-settable property to Decoder that allow -// this to be larger than the compile-time setting, but this would add -// complexity, particularly since we would have to decide how/if to give users -// the ability to set a custom memory allocation function. -#define UPB_DECODER_MAX_NESTING 64 - -// Internal-only struct used by the decoder. -typedef struct { - UPB_PRIVATE_FOR_CPP - // Space optimization note: we store two pointers here that the JIT - // doesn't need at all; the upb_handlers* inside the sink and - // the dispatch table pointer. We can optimze so that the JIT uses - // smaller stack frames than the interpreter. The only thing we need - // to guarantee is that the fallback routines can find end_ofs. - upb_sink sink; - - // The absolute stream offset of the end-of-frame delimiter. - // Non-delimited frames (groups and non-packed repeated fields) reuse the - // delimiter of their parent, even though the frame may not end there. - // - // NOTE: the JIT stores a slightly different value here for non-top frames. - // It stores the value relative to the end of the enclosed message. But the - // top frame is still stored the same way, which is important for ensuring - // that calls from the JIT into C work correctly. - uint64_t end_ofs; - const uint32_t *base; - - // 0 indicates a length-delimited field. - // A positive number indicates a known group. - // A negative number indicates an unknown group. - int32_t groupnum; - upb_inttable *dispatch; // Not used by the JIT. -} upb_pbdecoder_frame; - // The parameters one uses to construct a DecoderMethod. // TODO(haberman): move allowjit here? Seems more convenient for users. UPB_DEFINE_CLASS0(upb::pb::DecoderMethodOptions, @@ -6879,22 +7039,31 @@ UPB_DEFINE_STRUCT(upb_pbdecodermethod, upb_refcounted, upb_inttable dispatch; )); +// Preallocation hint: decoder won't allocate more bytes than this when first +// constructed. This hint may be an overestimate for some build configurations. +// But if the decoder library is upgraded without recompiling the application, +// it may be an underestimate. +#define UPB_PB_DECODER_SIZE 4400 + +#ifdef __cplusplus + // A Decoder receives binary protobuf data on its input sink and pushes the // decoded data to its output sink. -UPB_DEFINE_CLASS0(upb::pb::Decoder, +class upb::pb::Decoder { public: // Constructs a decoder instance for the given method, which must outlive this // decoder. Any errors during parsing will be set on the given status, which // must also outlive this decoder. - Decoder(const DecoderMethod* method, Status* status); - ~Decoder(); + // + // The sink must match the given method. + static Decoder* Create(Environment* env, const DecoderMethod* method, + Sink* output); // Returns the DecoderMethod this decoder is parsing from. - // TODO(haberman): Do users need to be able to rebind this? const DecoderMethod* method() const; - // Resets the state of the decoder. - void Reset(); + // The sink on which this decoder receives input. + BytesSink* input(); // Returns number of bytes successfully parsed. // @@ -6905,76 +7074,25 @@ UPB_DEFINE_CLASS0(upb::pb::Decoder, // callback. uint64_t BytesParsed() const; - // Resets the output sink of the Decoder. - // The given sink must match method()->dest_handlers(). + // Gets/sets the parsing nexting limit. If the total number of nested + // submessages and repeated fields hits this limit, parsing will fail. This + // is a resource limit that controls the amount of memory used by the parsing + // stack. // - // This must be called at least once before the decoder can be used. It may - // only be called with the decoder is in a state where it was just created or - // reset with pipeline.Reset(). The given sink must be from the same pipeline - // as this decoder. - bool ResetOutput(Sink* sink); - - // The sink on which this decoder receives input. - BytesSink* input(); - - private: - UPB_DISALLOW_COPY_AND_ASSIGN(Decoder); -, -UPB_DEFINE_STRUCT0(upb_pbdecoder, UPB_QUOTE( - // Our input sink. - upb_bytessink input_; - - // The decoder method we are parsing with (owned). - const upb_pbdecodermethod *method_; - - size_t call_len; - const uint32_t *pc, *last; - - // Current input buffer and its stream offset. - const char *buf, *ptr, *end, *checkpoint; - - // End of the delimited region, relative to ptr, or NULL if not in this buf. - const char *delim_end; - - // End of the delimited region, relative to ptr, or end if not in this buf. - const char *data_end; - - // Overall stream offset of "buf." - uint64_t bufstart_ofs; - - // Buffer for residual bytes not parsed from the previous buffer. - // The maximum number of residual bytes we require is 12; a five-byte - // unknown tag plus an eight-byte value, less one because the value - // is only a partial value. - char residual[12]; - char *residual_end; + // Setting the limit will fail if the parser is currently suspended at a depth + // greater than this, or if memory allocation of the stack fails. + size_t max_nesting() const; + bool set_max_nesting(size_t max); - // Stores the user buffer passed to our decode function. - const char *buf_param; - size_t size_param; - const upb_bufhandle *handle; - -#ifdef UPB_USE_JIT_X64 - // Used momentarily by the generated code to store a value while a user - // function is called. - uint32_t tmp_len; + void Reset(); - const void *saved_rsp; -#endif + static const size_t kSize = UPB_PB_DECODER_SIZE; - upb_status *status; + private: + UPB_DISALLOW_POD_OPS(Decoder, upb::pb::Decoder); +}; - // Our internal stack. - upb_pbdecoder_frame *top, *limit; - upb_pbdecoder_frame stack[UPB_DECODER_MAX_NESTING]; -#ifdef UPB_USE_JIT_X64 - // Each native stack frame needs two pointers, plus we need a few frames for - // the enter/exit trampolines. - const uint32_t *callstack[(UPB_DECODER_MAX_NESTING * 2) + 10]; -#else - const uint32_t *callstack[UPB_DECODER_MAX_NESTING]; -#endif -))); +#endif // __cplusplus // A class for caching protobuf processing code, whether bytecode for the // interpreted decoder or machine code for the JIT. @@ -7023,14 +7141,15 @@ UPB_DEFINE_STRUCT0(upb_pbcodecache, UPB_BEGIN_EXTERN_C // { -void upb_pbdecoder_init(upb_pbdecoder *d, const upb_pbdecodermethod *method, - upb_status *status); -void upb_pbdecoder_uninit(upb_pbdecoder *d); -void upb_pbdecoder_reset(upb_pbdecoder *d); +upb_pbdecoder *upb_pbdecoder_create(upb_env *e, + const upb_pbdecodermethod *method, + upb_sink *output); const upb_pbdecodermethod *upb_pbdecoder_method(const upb_pbdecoder *d); -bool upb_pbdecoder_resetoutput(upb_pbdecoder *d, upb_sink *sink); upb_bytessink *upb_pbdecoder_input(upb_pbdecoder *d); uint64_t upb_pbdecoder_bytesparsed(const upb_pbdecoder *d); +size_t upb_pbdecoder_maxnesting(const upb_pbdecoder *d); +bool upb_pbdecoder_setmaxnesting(upb_pbdecoder *d, size_t max); +void upb_pbdecoder_reset(upb_pbdecoder *d); void upb_pbdecodermethodopts_init(upb_pbdecodermethodopts *opts, const upb_handlers *h); @@ -7065,27 +7184,27 @@ namespace upb { namespace pb { -inline Decoder::Decoder(const DecoderMethod* m, Status* s) { - upb_pbdecoder_init(this, m, s); -} -inline Decoder::~Decoder() { - upb_pbdecoder_uninit(this); +// static +inline Decoder* Decoder::Create(Environment* env, const DecoderMethod* m, + Sink* sink) { + return upb_pbdecoder_create(env, m, sink); } inline const DecoderMethod* Decoder::method() const { return upb_pbdecoder_method(this); } -inline void Decoder::Reset() { - upb_pbdecoder_reset(this); +inline BytesSink* Decoder::input() { + return upb_pbdecoder_input(this); } inline uint64_t Decoder::BytesParsed() const { return upb_pbdecoder_bytesparsed(this); } -inline bool Decoder::ResetOutput(Sink* sink) { - return upb_pbdecoder_resetoutput(this, sink); +inline size_t Decoder::max_nesting() const { + return upb_pbdecoder_maxnesting(this); } -inline BytesSink* Decoder::input() { - return upb_pbdecoder_input(this); +inline bool Decoder::set_max_nesting(size_t max) { + return upb_pbdecoder_setmaxnesting(this, max); } +inline void Decoder::Reset() { upb_pbdecoder_reset(this); } inline DecoderMethodOptions::DecoderMethodOptions(const Handlers* h) { upb_pbdecodermethodopts_init(this, h); @@ -7242,6 +7361,95 @@ typedef struct { #endif } mgroup; +// The maximum that any submessages can be nested. Matches proto2's limit. +// This specifies the size of the decoder's statically-sized array and therefore +// setting it high will cause the upb::pb::Decoder object to be larger. +// +// If necessary we can add a runtime-settable property to Decoder that allow +// this to be larger than the compile-time setting, but this would add +// complexity, particularly since we would have to decide how/if to give users +// the ability to set a custom memory allocation function. +#define UPB_DECODER_MAX_NESTING 64 + +// Internal-only struct used by the decoder. +typedef struct { + // Space optimization note: we store two pointers here that the JIT + // doesn't need at all; the upb_handlers* inside the sink and + // the dispatch table pointer. We can optimze so that the JIT uses + // smaller stack frames than the interpreter. The only thing we need + // to guarantee is that the fallback routines can find end_ofs. + upb_sink sink; + + // The absolute stream offset of the end-of-frame delimiter. + // Non-delimited frames (groups and non-packed repeated fields) reuse the + // delimiter of their parent, even though the frame may not end there. + // + // NOTE: the JIT stores a slightly different value here for non-top frames. + // It stores the value relative to the end of the enclosed message. But the + // top frame is still stored the same way, which is important for ensuring + // that calls from the JIT into C work correctly. + uint64_t end_ofs; + const uint32_t *base; + + // 0 indicates a length-delimited field. + // A positive number indicates a known group. + // A negative number indicates an unknown group. + int32_t groupnum; + upb_inttable *dispatch; // Not used by the JIT. +} upb_pbdecoder_frame; + +struct upb_pbdecoder { + upb_env *env; + + // Our input sink. + upb_bytessink input_; + + // The decoder method we are parsing with (owned). + const upb_pbdecodermethod *method_; + + size_t call_len; + const uint32_t *pc, *last; + + // Current input buffer and its stream offset. + const char *buf, *ptr, *end, *checkpoint; + + // End of the delimited region, relative to ptr, or NULL if not in this buf. + const char *delim_end; + + // End of the delimited region, relative to ptr, or end if not in this buf. + const char *data_end; + + // Overall stream offset of "buf." + uint64_t bufstart_ofs; + + // Buffer for residual bytes not parsed from the previous buffer. + // The maximum number of residual bytes we require is 12; a five-byte + // unknown tag plus an eight-byte value, less one because the value + // is only a partial value. + char residual[12]; + char *residual_end; + + // Stores the user buffer passed to our decode function. + const char *buf_param; + size_t size_param; + const upb_bufhandle *handle; + + // Our internal stack. + upb_pbdecoder_frame *stack, *top, *limit; + const uint32_t **callstack; + size_t stack_size; + + upb_status *status; + +#ifdef UPB_USE_JIT_X64 + // Used momentarily by the generated code to store a value while a user + // function is called. + uint32_t tmp_len; + + const void *saved_rsp; +#endif +}; + // Decoder entry points; used as handlers. void *upb_pbdecoder_startbc(void *closure, const void *pc, size_t size_hint); void *upb_pbdecoder_startjit(void *closure, const void *hd, size_t size_hint); @@ -7509,101 +7717,42 @@ UPB_DECLARE_TYPE(upb::pb::Encoder, upb_pb_encoder); /* upb::pb::Encoder ***********************************************************/ -// The output buffer is divided into segments; a segment is a string of data -// that is "ready to go" -- it does not need any varint lengths inserted into -// the middle. The seams between segments are where varints will be inserted -// once they are known. -// -// We also use the concept of a "run", which is a range of encoded bytes that -// occur at a single submessage level. Every segment contains one or more runs. -// -// A segment can span messages. Consider: -// -// .--Submessage lengths---------. -// | | | -// | V V -// V | |--------------- | |----------------- -// Submessages: | |----------------------------------------------- -// Top-level msg: ------------------------------------------------------------ -// -// Segments: ----- ------------------- ----------------- -// Runs: *---- *--------------*--- *---------------- -// (* marks the start) -// -// Note that the top-level menssage is not in any segment because it does not -// have any length preceding it. -// -// A segment is only interrupted when another length needs to be inserted. So -// observe how the second segment spans both the inner submessage and part of -// the next enclosing message. -typedef struct { - UPB_PRIVATE_FOR_CPP - uint32_t msglen; // The length to varint-encode before this segment. - uint32_t seglen; // Length of the segment. -} upb_pb_encoder_segment; - -UPB_DEFINE_CLASS0(upb::pb::Encoder, - public: - Encoder(const upb::Handlers* handlers); - ~Encoder(); - - static reffed_ptr NewHandlers(const upb::MessageDef* msg); +// Preallocation hint: decoder won't allocate more bytes than this when first +// constructed. This hint may be an overestimate for some build configurations. +// But if the decoder library is upgraded without recompiling the application, +// it may be an underestimate. +#define UPB_PB_ENCODER_SIZE 768 - // Resets the state of the printer, so that it will expect to begin a new - // document. - void Reset(); +#ifdef __cplusplus - // Resets the output pointer which will serve as our closure. - void ResetOutput(BytesSink* output); +class upb::pb::Encoder { + public: + // Creates a new encoder in the given environment. The Handlers must have + // come from NewHandlers() below. + static Encoder* Create(Environment* env, const Handlers* handlers, + BytesSink* output); // The input to the encoder. Sink* input(); - private: - UPB_DISALLOW_COPY_AND_ASSIGN(Encoder); -, -UPB_DEFINE_STRUCT0(upb_pb_encoder, UPB_QUOTE( - // Our input and output. - upb_sink input_; - upb_bytessink *output_; + // Creates a new set of handlers for this MessageDef. + static reffed_ptr NewHandlers(const MessageDef* msg); - // The "subclosure" -- used as the inner closure as part of the bytessink - // protocol. - void *subc; + static const size_t kSize = UPB_PB_ENCODER_SIZE; - // The output buffer and limit, and our current write position. "buf" - // initially points to "initbuf", but is dynamically allocated if we need to - // grow beyond the initial size. - char *buf, *ptr, *limit; - - // The beginning of the current run, or undefined if we are at the top level. - char *runbegin; - - // The list of segments we are accumulating. - upb_pb_encoder_segment *segbuf, *segptr, *seglimit; - - // The stack of enclosing submessages. Each entry in the stack points to the - // segment where this submessage's length is being accumulated. - int stack[UPB_PBENCODER_MAX_NESTING], *top, *stacklimit; - - // Depth of startmsg/endmsg calls. - int depth; + private: + UPB_DISALLOW_POD_OPS(Encoder, upb::pb::Encoder); +}; - // Initial buffers for the output buffer and segment buffer. If we outgrow - // these we will dynamically allocate bigger ones. - char initbuf[256]; - upb_pb_encoder_segment seginitbuf[32]; -))); +#endif UPB_BEGIN_EXTERN_C const upb_handlers *upb_pb_encoder_newhandlers(const upb_msgdef *m, const void *owner); -void upb_pb_encoder_reset(upb_pb_encoder *e); upb_sink *upb_pb_encoder_input(upb_pb_encoder *p); -void upb_pb_encoder_init(upb_pb_encoder *e, const upb_handlers *h); -void upb_pb_encoder_resetoutput(upb_pb_encoder *e, upb_bytessink *output); -void upb_pb_encoder_uninit(upb_pb_encoder *e); +upb_pb_encoder* upb_pb_encoder_create(upb_env* e, const upb_handlers* h, + upb_bytessink* output); UPB_END_EXTERN_C @@ -7611,17 +7760,9 @@ UPB_END_EXTERN_C namespace upb { namespace pb { -inline Encoder::Encoder(const upb::Handlers* handlers) { - upb_pb_encoder_init(this, handlers); -} -inline Encoder::~Encoder() { - upb_pb_encoder_uninit(this); -} -inline void Encoder::Reset() { - upb_pb_encoder_reset(this); -} -inline void Encoder::ResetOutput(BytesSink* output) { - upb_pb_encoder_resetoutput(this, output); +inline Encoder* Encoder::Create(Environment* env, const Handlers* handlers, + BytesSink* output) { + return upb_pb_encoder_create(env, handlers, output); } inline Sink* Encoder::input() { return upb_pb_encoder_input(this); @@ -7739,58 +7880,51 @@ class TextPrinter; UPB_DECLARE_TYPE(upb::pb::TextPrinter, upb_textprinter); -UPB_DEFINE_CLASS0(upb::pb::TextPrinter, +#ifdef __cplusplus + +class upb::pb::TextPrinter { public: // The given handlers must have come from NewHandlers(). It must outlive the // TextPrinter. - explicit TextPrinter(const upb::Handlers* handlers); + static TextPrinter *Create(Environment *env, const upb::Handlers *handlers, + BytesSink *output); void SetSingleLineMode(bool single_line); - bool ResetOutput(BytesSink* output); Sink* input(); // If handler caching becomes a requirement we can add a code cache as in // decoder.h static reffed_ptr NewHandlers(const MessageDef* md); +}; - private: -, -UPB_DEFINE_STRUCT0(upb_textprinter, - upb_sink input_; - upb_bytessink *output_; - int indent_depth_; - bool single_line_; - void *subc; -)); +#endif -UPB_BEGIN_EXTERN_C // { +UPB_BEGIN_EXTERN_C // C API. -void upb_textprinter_init(upb_textprinter *p, const upb_handlers *h); -void upb_textprinter_uninit(upb_textprinter *p); -bool upb_textprinter_resetoutput(upb_textprinter *p, upb_bytessink *output); +upb_textprinter *upb_textprinter_create(upb_env *env, const upb_handlers *h, + upb_bytessink *output); void upb_textprinter_setsingleline(upb_textprinter *p, bool single_line); upb_sink *upb_textprinter_input(upb_textprinter *p); const upb_handlers *upb_textprinter_newhandlers(const upb_msgdef *m, const void *owner); -UPB_END_EXTERN_C // } +UPB_END_EXTERN_C #ifdef __cplusplus namespace upb { namespace pb { -inline TextPrinter::TextPrinter(const upb::Handlers* handlers) { - upb_textprinter_init(this, handlers); +inline TextPrinter *TextPrinter::Create(Environment *env, + const upb::Handlers *handlers, + BytesSink *output) { + return upb_textprinter_create(env, handlers, output); } inline void TextPrinter::SetSingleLineMode(bool single_line) { upb_textprinter_setsingleline(this, single_line); } -inline bool TextPrinter::ResetOutput(BytesSink* output) { - return upb_textprinter_resetoutput(this, output); -} inline Sink* TextPrinter::input() { return upb_textprinter_input(this); } @@ -7829,96 +7963,32 @@ class Parser; UPB_DECLARE_TYPE(upb::json::Parser, upb_json_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; - - /* upb::json::Parser **********************************************************/ -#define UPB_JSON_MAX_DEPTH 64 +// Preallocation hint: parser won't allocate more bytes than this when first +// constructed. This hint may be an overestimate for some build configurations. +// But if the parser library is upgraded without recompiling the application, +// it may be an underestimate. +#define UPB_JSON_PARSER_SIZE 3568 + +#ifdef __cplusplus // Parses an incoming BytesStream, pushing the results to the destination sink. -UPB_DEFINE_CLASS0(upb::json::Parser, +class upb::json::Parser { public: - Parser(Status* status); - ~Parser(); - - // Resets the state of the printer, so that it will expect to begin a new - // document. - void Reset(); + static Parser* Create(Environment* env, Sink* output); - // Resets the output pointer which will serve as our closure. Implies - // Reset(). - void ResetOutput(Sink* output); - - // The input to the printer. BytesSink* input(); -, -UPB_DEFINE_STRUCT0(upb_json_parser, - upb_byteshandler input_handler_; - upb_bytessink input_; - - // Stack to track the JSON scopes we are in. - upb_jsonparser_frame stack[UPB_JSON_MAX_DEPTH]; - upb_jsonparser_frame *top; - upb_jsonparser_frame *limit; - - upb_status *status; - - // Ragel's internal parsing stack for the parsing state machine. - int current_state; - int parser_stack[UPB_JSON_MAX_DEPTH]; - int parser_top; - - // The handle for the current buffer. - const upb_bufhandle *handle; - - // Accumulate buffer. See details in parser.rl. - const char *accumulated; - size_t accumulated_len; - char *accumulate_buf; - size_t accumulate_buf_size; - - // Multi-part text data. See details in parser.rl. - int multipart_state; - upb_selector_t string_selector; - // Input capture. See details in parser.rl. - const char *capture; + private: + UPB_DISALLOW_POD_OPS(Parser, upb::json::Parser); +}; - // Intermediate result of parsing a unicode escape sequence. - uint32_t digit; -)); +#endif UPB_BEGIN_EXTERN_C -void upb_json_parser_init(upb_json_parser *p, upb_status *status); -void upb_json_parser_uninit(upb_json_parser *p); -void upb_json_parser_reset(upb_json_parser *p); -void upb_json_parser_resetoutput(upb_json_parser *p, upb_sink *output); +upb_json_parser *upb_json_parser_create(upb_env *e, upb_sink *output); upb_bytessink *upb_json_parser_input(upb_json_parser *p); UPB_END_EXTERN_C @@ -7927,11 +7997,8 @@ UPB_END_EXTERN_C namespace upb { namespace json { -inline Parser::Parser(Status* status) { upb_json_parser_init(this, status); } -inline Parser::~Parser() { upb_json_parser_uninit(this); } -inline void Parser::Reset() { upb_json_parser_reset(this); } -inline void Parser::ResetOutput(Sink* output) { - upb_json_parser_resetoutput(this, output); +inline Parser* Parser::Create(Environment* env, Sink* output) { + return upb_json_parser_create(env, output); } inline BytesSink* Parser::input() { return upb_json_parser_input(this); @@ -7970,71 +8037,48 @@ UPB_DECLARE_TYPE(upb::json::Printer, upb_json_printer); /* upb::json::Printer *********************************************************/ -// Prints an incoming stream of data to a BytesSink in JSON format. -UPB_DEFINE_CLASS0(upb::json::Printer, - public: - Printer(const upb::Handlers* handlers); - ~Printer(); +#define UPB_JSON_PRINTER_SIZE 168 - // Resets the state of the printer, so that it will expect to begin a new - // document. - void Reset(); +#ifdef __cplusplus - // Resets the output pointer which will serve as our closure. Implies - // Reset(). - void ResetOutput(BytesSink* output); +// Prints an incoming stream of data to a BytesSink in JSON format. +class upb::json::Printer { + public: + static Printer* Create(Environment* env, const upb::Handlers* handlers, + BytesSink* output); // The input to the printer. Sink* input(); // Returns handlers for printing according to the specified schema. static reffed_ptr NewHandlers(const upb::MessageDef* md); -, -UPB_DEFINE_STRUCT0(upb_json_printer, - upb_sink input_; - // BytesSink closure. - void *subc_; - upb_bytessink *output_; - - // We track the depth so that we know when to emit startstr/endstr on the - // output. - int depth_; - // Have we emitted the first element? This state is necessary to emit commas - // without leaving a trailing comma in arrays/maps. We keep this state per - // frame depth. - // - // Why max_depth * 2? UPB_MAX_HANDLER_DEPTH counts depth as nested messages. - // We count frames (contexts in which we separate elements by commas) as both - // repeated fields and messages (maps), and the worst case is a - // message->repeated field->submessage->repeated field->... nesting. - bool first_elem_[UPB_MAX_HANDLER_DEPTH * 2]; -)); -UPB_BEGIN_EXTERN_C // { + static const size_t kSize = UPB_JSON_PRINTER_SIZE; -// Native C API. + private: + UPB_DISALLOW_POD_OPS(Printer, upb::json::Printer); +}; + +#endif -void upb_json_printer_init(upb_json_printer *p, const upb_handlers *h); -void upb_json_printer_uninit(upb_json_printer *p); -void upb_json_printer_reset(upb_json_printer *p); -void upb_json_printer_resetoutput(upb_json_printer *p, upb_bytessink *output); +UPB_BEGIN_EXTERN_C + +// Native C API. +upb_json_printer *upb_json_printer_create(upb_env *e, const upb_handlers *h, + upb_bytessink *output); upb_sink *upb_json_printer_input(upb_json_printer *p); const upb_handlers *upb_json_printer_newhandlers(const upb_msgdef *md, const void *owner); -UPB_END_EXTERN_C // } +UPB_END_EXTERN_C #ifdef __cplusplus namespace upb { namespace json { -inline Printer::Printer(const upb::Handlers* handlers) { - upb_json_printer_init(this, handlers); -} -inline Printer::~Printer() { upb_json_printer_uninit(this); } -inline void Printer::Reset() { upb_json_printer_reset(this); } -inline void Printer::ResetOutput(BytesSink* output) { - upb_json_printer_resetoutput(this, output); +inline Printer* Printer::Create(Environment* env, const upb::Handlers* handlers, + BytesSink* output) { + return upb_json_printer_create(env, handlers, output); } inline Sink* Printer::input() { return upb_json_printer_input(this); } inline reffed_ptr Printer::NewHandlers( -- cgit v1.2.3 From e54c14552f3bee973f515546b946169ec432295a Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Wed, 6 May 2015 20:24:58 -0400 Subject: Don't hardcode bash --- ruby/travis-test.sh | 2 +- travis.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'ruby') diff --git a/ruby/travis-test.sh b/ruby/travis-test.sh index e94af80a..a240dd65 100755 --- a/ruby/travis-test.sh +++ b/ruby/travis-test.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Exit on any error. set -e diff --git a/travis.sh b/travis.sh index 556bc91c..0917904b 100755 --- a/travis.sh +++ b/travis.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash build_cpp() { ./autogen.sh -- cgit v1.2.3 From 231886f6327b7cad480b96d08595f45b3526cb4a Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Tue, 19 May 2015 15:33:48 -0700 Subject: Ruby C extension speedup: don't re-intern constant string needlessly. Also fixed lines with > 80 char length. --- ruby/ext/google/protobuf_c/defs.c | 8 +++----- ruby/ext/google/protobuf_c/encode_decode.c | 16 +++++++++------- ruby/ext/google/protobuf_c/message.c | 24 ++++++++++++++---------- ruby/ext/google/protobuf_c/protobuf.c | 10 ++++++++++ ruby/ext/google/protobuf_c/protobuf.h | 4 ++-- ruby/ext/google/protobuf_c/repeated_field.c | 9 +++++---- ruby/ext/google/protobuf_c/storage.c | 3 ++- 7 files changed, 45 insertions(+), 29 deletions(-) (limited to 'ruby') diff --git a/ruby/ext/google/protobuf_c/defs.c b/ruby/ext/google/protobuf_c/defs.c index 9b590a55..e3341cca 100644 --- a/ruby/ext/google/protobuf_c/defs.c +++ b/ruby/ext/google/protobuf_c/defs.c @@ -34,8 +34,6 @@ // Common utilities. // ----------------------------------------------------------------------------- -const char* kDescriptorInstanceVar = "descriptor"; - static const char* get_str(VALUE str) { Check_Type(str, T_STRING); return RSTRING_PTR(str); @@ -1590,9 +1588,9 @@ VALUE Builder_add_message(VALUE _self, VALUE name) { * call-seq: * Builder.add_enum(name, &block) * - * Creates a new, empty enum descriptor with the given name, and invokes the block in - * the context of an EnumBuilderContext on that descriptor. The block can then - * call EnumBuilderContext#add_value to define the enum values. + * Creates a new, empty enum descriptor with the given name, and invokes the + * block in the context of an EnumBuilderContext on that descriptor. The block + * can then call EnumBuilderContext#add_value to define the enum values. * * This is the recommended, idiomatic way to build enum definitions. */ diff --git a/ruby/ext/google/protobuf_c/encode_decode.c b/ruby/ext/google/protobuf_c/encode_decode.c index 736f573e..fe249d45 100644 --- a/ruby/ext/google/protobuf_c/encode_decode.c +++ b/ruby/ext/google/protobuf_c/encode_decode.c @@ -644,7 +644,8 @@ static bool env_error_func(void* ud, const upb_status* status) { // Free the env -- rb_raise will longjmp up the stack past the encode/decode // function so it would not otherwise have been freed. stackenv_uninit(se); - rb_raise(rb_eRuntimeError, se->ruby_error_template, upb_status_errmsg(status)); + rb_raise(rb_eRuntimeError, se->ruby_error_template, + upb_status_errmsg(status)); // Never reached: rb_raise() always longjmp()s up the stack, past all of our // code, back to Ruby. return false; @@ -673,7 +674,7 @@ static void stackenv_uninit(stackenv* se) { * and returns a message object with the corresponding field values. */ VALUE Message_decode(VALUE klass, VALUE data) { - VALUE descriptor = rb_iv_get(klass, kDescriptorInstanceVar); + VALUE descriptor = rb_ivar_get(klass, descriptor_instancevar_interned); Descriptor* desc = ruby_to_Descriptor(descriptor); VALUE msgklass = Descriptor_msgclass(descriptor); @@ -711,7 +712,7 @@ VALUE Message_decode(VALUE klass, VALUE data) { * and returns a message object with the corresponding field values. */ VALUE Message_decode_json(VALUE klass, VALUE data) { - VALUE descriptor = rb_iv_get(klass, kDescriptorInstanceVar); + VALUE descriptor = rb_ivar_get(klass, descriptor_instancevar_interned); Descriptor* desc = ruby_to_Descriptor(descriptor); VALUE msgklass = Descriptor_msgclass(descriptor); @@ -846,7 +847,7 @@ static void putsubmsg(VALUE submsg, const upb_fielddef *f, upb_sink *sink, if (submsg == Qnil) return; upb_sink subsink; - VALUE descriptor = rb_iv_get(submsg, kDescriptorInstanceVar); + VALUE descriptor = rb_ivar_get(submsg, descriptor_instancevar_interned); Descriptor* subdesc = ruby_to_Descriptor(descriptor); upb_sink_startsubmsg(sink, getsel(f, UPB_HANDLER_STARTSUBMSG), &subsink); @@ -968,7 +969,8 @@ static void putmap(VALUE map, const upb_fielddef *f, upb_sink *sink, VALUE value = Map_iter_value(&it); upb_sink entry_sink; - upb_sink_startsubmsg(&subsink, getsel(f, UPB_HANDLER_STARTSUBMSG), &entry_sink); + upb_sink_startsubmsg(&subsink, getsel(f, UPB_HANDLER_STARTSUBMSG), + &entry_sink); upb_sink_startmsg(&entry_sink); put_ruby_value(key, key_field, Qnil, depth + 1, &entry_sink); @@ -1098,7 +1100,7 @@ static const upb_handlers* msgdef_json_serialize_handlers(Descriptor* desc) { * wire format. */ VALUE Message_encode(VALUE klass, VALUE msg_rb) { - VALUE descriptor = rb_iv_get(klass, kDescriptorInstanceVar); + VALUE descriptor = rb_ivar_get(klass, descriptor_instancevar_interned); Descriptor* desc = ruby_to_Descriptor(descriptor); stringsink sink; @@ -1129,7 +1131,7 @@ VALUE Message_encode(VALUE klass, VALUE msg_rb) { * Encodes the given message object into its serialized JSON representation. */ VALUE Message_encode_json(VALUE klass, VALUE msg_rb) { - VALUE descriptor = rb_iv_get(klass, kDescriptorInstanceVar); + VALUE descriptor = rb_ivar_get(klass, descriptor_instancevar_interned); Descriptor* desc = ruby_to_Descriptor(descriptor); stringsink sink; diff --git a/ruby/ext/google/protobuf_c/message.c b/ruby/ext/google/protobuf_c/message.c index 55a674cf..4be1c8c8 100644 --- a/ruby/ext/google/protobuf_c/message.c +++ b/ruby/ext/google/protobuf_c/message.c @@ -53,7 +53,7 @@ rb_data_type_t Message_type = { }; VALUE Message_alloc(VALUE klass) { - VALUE descriptor = rb_iv_get(klass, kDescriptorInstanceVar); + VALUE descriptor = rb_ivar_get(klass, descriptor_instancevar_interned); Descriptor* desc = ruby_to_Descriptor(descriptor); MessageHeader* msg = (MessageHeader*)ALLOC_N( uint8_t, sizeof(MessageHeader) + desc->layout->size); @@ -63,7 +63,7 @@ VALUE Message_alloc(VALUE klass) { // a collection happens during object creation in layout_init(). VALUE ret = TypedData_Wrap_Struct(klass, &Message_type, msg); msg->descriptor = desc; - rb_iv_set(ret, kDescriptorInstanceVar, descriptor); + rb_ivar_set(ret, descriptor_instancevar_interned, descriptor); layout_init(desc->layout, Message_data(msg)); @@ -341,7 +341,8 @@ VALUE Message_to_h(VALUE _self) { !upb_msg_field_done(&it); upb_msg_field_next(&it)) { const upb_fielddef* field = upb_msg_iter_field(&it); - VALUE msg_value = layout_get(self->descriptor->layout, Message_data(self), field); + VALUE msg_value = layout_get(self->descriptor->layout, Message_data(self), + field); VALUE msg_key = ID2SYM(rb_intern(upb_fielddef_name(field))); if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { msg_value = RepeatedField_to_ary(msg_value); @@ -400,7 +401,7 @@ VALUE Message_index_set(VALUE _self, VALUE field_name, VALUE value) { * message class's type. */ VALUE Message_descriptor(VALUE klass) { - return rb_iv_get(klass, kDescriptorInstanceVar); + return rb_ivar_get(klass, descriptor_instancevar_interned); } VALUE build_class_from_descriptor(Descriptor* desc) { @@ -421,11 +422,13 @@ VALUE build_class_from_descriptor(Descriptor* desc) { // their own toplevel constant class name. rb_intern("Message"), rb_cObject); - rb_iv_set(klass, kDescriptorInstanceVar, get_def_obj(desc->msgdef)); + rb_ivar_set(klass, descriptor_instancevar_interned, + get_def_obj(desc->msgdef)); rb_define_alloc_func(klass, Message_alloc); rb_require("google/protobuf/message_exts"); rb_include_module(klass, rb_eval_string("Google::Protobuf::MessageExts")); - rb_extend_object(klass, rb_eval_string("Google::Protobuf::MessageExts::ClassMethods")); + rb_extend_object( + klass, rb_eval_string("Google::Protobuf::MessageExts::ClassMethods")); rb_define_method(klass, "method_missing", Message_method_missing, -1); @@ -458,7 +461,7 @@ VALUE build_class_from_descriptor(Descriptor* desc) { */ VALUE enum_lookup(VALUE self, VALUE number) { int32_t num = NUM2INT(number); - VALUE desc = rb_iv_get(self, kDescriptorInstanceVar); + VALUE desc = rb_ivar_get(self, descriptor_instancevar_interned); EnumDescriptor* enumdesc = ruby_to_EnumDescriptor(desc); const char* name = upb_enumdef_iton(enumdesc->enumdef, num); @@ -478,7 +481,7 @@ VALUE enum_lookup(VALUE self, VALUE number) { */ VALUE enum_resolve(VALUE self, VALUE sym) { const char* name = rb_id2name(SYM2ID(sym)); - VALUE desc = rb_iv_get(self, kDescriptorInstanceVar); + VALUE desc = rb_ivar_get(self, descriptor_instancevar_interned); EnumDescriptor* enumdesc = ruby_to_EnumDescriptor(desc); int32_t num = 0; @@ -498,7 +501,7 @@ VALUE enum_resolve(VALUE self, VALUE sym) { * EnumDescriptor corresponding to this enum type. */ VALUE enum_descriptor(VALUE self) { - return rb_iv_get(self, kDescriptorInstanceVar); + return rb_ivar_get(self, descriptor_instancevar_interned); } VALUE build_module_from_enumdesc(EnumDescriptor* enumdesc) { @@ -523,7 +526,8 @@ VALUE build_module_from_enumdesc(EnumDescriptor* enumdesc) { rb_define_singleton_method(mod, "lookup", enum_lookup, 1); rb_define_singleton_method(mod, "resolve", enum_resolve, 1); rb_define_singleton_method(mod, "descriptor", enum_descriptor, 0); - rb_iv_set(mod, kDescriptorInstanceVar, get_def_obj(enumdesc->enumdef)); + rb_ivar_set(mod, descriptor_instancevar_interned, + get_def_obj(enumdesc->enumdef)); return mod; } diff --git a/ruby/ext/google/protobuf_c/protobuf.c b/ruby/ext/google/protobuf_c/protobuf.c index 8ab518a5..d0625a10 100644 --- a/ruby/ext/google/protobuf_c/protobuf.c +++ b/ruby/ext/google/protobuf_c/protobuf.c @@ -64,6 +64,15 @@ rb_encoding* kRubyStringUtf8Encoding; rb_encoding* kRubyStringASCIIEncoding; rb_encoding* kRubyString8bitEncoding; +// Ruby-interned string: "descriptor". We use this identifier to store an +// instance variable on message classes we create in order to link them back to +// their descriptors. +// +// We intern this once at module load time then use the interned identifier at +// runtime in order to avoid the cost of repeatedly interning in hot paths. +const char* kDescriptorInstanceVar = "descriptor"; +ID descriptor_instancevar_interned; + // ----------------------------------------------------------------------------- // Initialization/entry point. // ----------------------------------------------------------------------------- @@ -71,6 +80,7 @@ rb_encoding* kRubyString8bitEncoding; // This must be named "Init_protobuf_c" because the Ruby module is named // "protobuf_c" -- the VM looks for this symbol in our .so. void Init_protobuf_c() { + descriptor_instancevar_interned = rb_intern(kDescriptorInstanceVar); VALUE google = rb_define_module("Google"); VALUE protobuf = rb_define_module_under(google, "Protobuf"); VALUE internal = rb_define_module_under(protobuf, "Internal"); diff --git a/ruby/ext/google/protobuf_c/protobuf.h b/ruby/ext/google/protobuf_c/protobuf.h index 3d619617..f8667486 100644 --- a/ruby/ext/google/protobuf_c/protobuf.h +++ b/ruby/ext/google/protobuf_c/protobuf.h @@ -161,8 +161,6 @@ extern VALUE cOneofBuilderContext; extern VALUE cEnumBuilderContext; extern VALUE cBuilder; -extern const char* kDescriptorInstanceVar; - // We forward-declare all of the Ruby method implementations here because we // sometimes call the methods directly across .c files, rather than going // through Ruby's method dispatching (e.g. during message parse). It's cleaner @@ -530,4 +528,6 @@ void check_upb_status(const upb_status* status, const char* msg); check_upb_status(&status, msg); \ } while (0) +extern ID descriptor_instancevar_interned; + #endif // __GOOGLE_PROTOBUF_RUBY_PROTOBUF_H__ diff --git a/ruby/ext/google/protobuf_c/repeated_field.c b/ruby/ext/google/protobuf_c/repeated_field.c index dc1d0355..ffa60c15 100644 --- a/ruby/ext/google/protobuf_c/repeated_field.c +++ b/ruby/ext/google/protobuf_c/repeated_field.c @@ -117,7 +117,8 @@ VALUE RepeatedField_index(int argc, VALUE* argv, VALUE _self) { if (index < 0 || index >= self->size) { return Qnil; } - void* memory = (void *) (((uint8_t *)self->elements) + index * element_size); + void* memory = (void *) (((uint8_t *)self->elements) + + index * element_size); return native_slot_get(field_type, field_type_class, memory); }else{ /* check if idx is Range */ @@ -497,13 +498,13 @@ VALUE RepeatedField_concat(VALUE _self, VALUE list) { void validate_type_class(upb_fieldtype_t type, VALUE klass) { - if (rb_iv_get(klass, kDescriptorInstanceVar) == Qnil) { + if (rb_ivar_get(klass, descriptor_instancevar_interned) == Qnil) { rb_raise(rb_eArgError, "Type class has no descriptor. Please pass a " "class or enum as returned by the DescriptorPool."); } if (type == UPB_TYPE_MESSAGE) { - VALUE desc = rb_iv_get(klass, kDescriptorInstanceVar); + VALUE desc = rb_ivar_get(klass, descriptor_instancevar_interned); if (!RB_TYPE_P(desc, T_DATA) || !RTYPEDDATA_P(desc) || RTYPEDDATA_TYPE(desc) != &_Descriptor_type) { rb_raise(rb_eArgError, "Descriptor has an incorrect type."); @@ -513,7 +514,7 @@ void validate_type_class(upb_fieldtype_t type, VALUE klass) { "Message class was not returned by the DescriptorPool."); } } else if (type == UPB_TYPE_ENUM) { - VALUE enumdesc = rb_iv_get(klass, kDescriptorInstanceVar); + VALUE enumdesc = rb_ivar_get(klass, descriptor_instancevar_interned); if (!RB_TYPE_P(enumdesc, T_DATA) || !RTYPEDDATA_P(enumdesc) || RTYPEDDATA_TYPE(enumdesc) != &_EnumDescriptor_type) { rb_raise(rb_eArgError, "Descriptor has an incorrect type."); diff --git a/ruby/ext/google/protobuf_c/storage.c b/ruby/ext/google/protobuf_c/storage.c index 2ad8bd74..c4cdc9cc 100644 --- a/ruby/ext/google/protobuf_c/storage.c +++ b/ruby/ext/google/protobuf_c/storage.c @@ -415,7 +415,8 @@ MessageLayout* create_layout(const upb_msgdef* msgdef) { // 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; + layout->fields[upb_fielddef_index(field)].case_offset = + MESSAGE_FIELD_NO_CASE; off += field_size; } -- cgit v1.2.3 From 5db217305f37a79eeccd70f000088a06ec82fcec Mon Sep 17 00:00:00 2001 From: Bo Yang Date: Thu, 21 May 2015 14:28:59 -0700 Subject: down-integrate internal changes --- .gitignore | 1 + Android.mk | 6 + Makefile.am | 14 + conformance/conformance.proto | 68 +- conformance/conformance_test.cc | 1 + editors/proto.vim | 2 +- editors/protobuf-mode.el | 2 +- generate_descriptor_proto.sh | 5 + java/pom.xml | 15 + .../com/google/protobuf/AbstractMessageLite.java | 16 +- .../com/google/protobuf/AbstractProtobufList.java | 136 ++ .../java/com/google/protobuf/BooleanArrayList.java | 244 +++ .../main/java/com/google/protobuf/Descriptors.java | 42 +- .../java/com/google/protobuf/DoubleArrayList.java | 243 +++ .../java/com/google/protobuf/FloatArrayList.java | 242 +++ .../com/google/protobuf/GeneratedMessageLite.java | 396 +++-- .../java/com/google/protobuf/IntArrayList.java | 242 +++ .../main/java/com/google/protobuf/Internal.java | 129 ++ .../com/google/protobuf/LazyStringArrayList.java | 87 +- .../java/com/google/protobuf/LongArrayList.java | 242 +++ .../main/java/com/google/protobuf/MapField.java | 63 +- .../java/com/google/protobuf/MapFieldLite.java | 376 +++- .../java/com/google/protobuf/MutabilityOracle.java | 48 + .../com/google/protobuf/ProtobufArrayList.java | 95 + .../main/java/com/google/protobuf/TextFormat.java | 4 +- .../com/google/protobuf/BooleanArrayListTest.java | 473 +++++ .../java/com/google/protobuf/DescriptorsTest.java | 17 + .../com/google/protobuf/DoubleArrayListTest.java | 473 +++++ .../com/google/protobuf/FloatArrayListTest.java | 473 +++++ .../java/com/google/protobuf/IntArrayListTest.java | 473 +++++ .../google/protobuf/LazyStringArrayListTest.java | 188 ++ .../test/java/com/google/protobuf/LiteTest.java | 1314 +++++++++++++- .../com/google/protobuf/LiteralByteStringTest.java | 3 +- .../com/google/protobuf/LongArrayListTest.java | 473 +++++ .../com/google/protobuf/MapForProto2LiteTest.java | 161 ++ .../java/com/google/protobuf/MapForProto2Test.java | 148 ++ .../src/test/java/com/google/protobuf/MapTest.java | 133 ++ .../com/google/protobuf/ProtobufArrayListTest.java | 303 ++++ .../com/google/protobuf/field_presence_test.proto | 26 +- .../protobuf/map_initialization_order_test.proto | 61 + .../test/java/com/google/protobuf/map_test.proto | 4 +- python/google/protobuf/descriptor.py | 46 +- python/google/protobuf/descriptor_pool.py | 39 +- python/google/protobuf/internal/containers.py | 297 ++++ python/google/protobuf/internal/decoder.py | 44 + .../protobuf/internal/descriptor_database_test.py | 1 - .../protobuf/internal/descriptor_pool_test.py | 45 + python/google/protobuf/internal/descriptor_test.py | 2 +- python/google/protobuf/internal/encoder.py | 55 +- python/google/protobuf/internal/generator_test.py | 1 - .../protobuf/internal/message_factory_test.py | 1 - python/google/protobuf/internal/message_test.py | 521 +++++- .../google/protobuf/internal/proto_builder_test.py | 21 +- python/google/protobuf/internal/python_message.py | 159 +- python/google/protobuf/internal/reflection_test.py | 6 +- .../protobuf/internal/service_reflection_test.py | 3 +- .../protobuf/internal/symbol_database_test.py | 1 - python/google/protobuf/internal/test_util.py | 24 +- .../google/protobuf/internal/text_encoding_test.py | 1 - .../google/protobuf/internal/text_format_test.py | 141 +- python/google/protobuf/internal/type_checkers.py | 9 + .../protobuf/internal/unknown_fields_test.py | 1 - .../google/protobuf/internal/wire_format_test.py | 1 - python/google/protobuf/proto_builder.py | 20 +- python/google/protobuf/pyext/descriptor.cc | 287 +-- python/google/protobuf/pyext/descriptor.h | 21 +- .../google/protobuf/pyext/descriptor_containers.cc | 26 +- .../google/protobuf/pyext/descriptor_containers.h | 5 + python/google/protobuf/pyext/descriptor_pool.cc | 185 +- python/google/protobuf/pyext/descriptor_pool.h | 8 +- python/google/protobuf/pyext/extension_dict.cc | 17 +- python/google/protobuf/pyext/message.cc | 432 ++++- python/google/protobuf/pyext/message.h | 13 +- .../google/protobuf/pyext/message_map_container.cc | 540 ++++++ .../google/protobuf/pyext/message_map_container.h | 117 ++ .../protobuf/pyext/repeated_composite_container.cc | 56 +- .../protobuf/pyext/repeated_composite_container.h | 10 +- .../protobuf/pyext/repeated_scalar_container.cc | 8 +- .../google/protobuf/pyext/scalar_map_container.cc | 514 ++++++ .../google/protobuf/pyext/scalar_map_container.h | 110 ++ python/google/protobuf/reflection.py | 1 - python/google/protobuf/text_format.py | 36 +- python/setup.py | 1 + ruby/tests/generated_code.proto | 26 +- src/Makefile.am | 27 + src/google/protobuf/any.cc | 100 ++ src/google/protobuf/any.h | 90 + src/google/protobuf/any.pb.cc | 23 +- src/google/protobuf/any.pb.h | 10 + src/google/protobuf/any_test.cc | 89 + src/google/protobuf/any_test.proto | 41 + src/google/protobuf/api.pb.cc | 74 +- src/google/protobuf/api.pb.h | 4 +- src/google/protobuf/arena.cc | 9 +- src/google/protobuf/arena.h | 346 +++- src/google/protobuf/arena_nc_test.py | 1 + src/google/protobuf/arena_test_util.h | 1 + src/google/protobuf/arena_unittest.cc | 75 +- src/google/protobuf/compiler/code_generator.h | 31 + .../protobuf/compiler/command_line_interface.cc | 46 +- .../compiler/command_line_interface_unittest.cc | 10 + src/google/protobuf/compiler/cpp/cpp_enum.cc | 14 +- src/google/protobuf/compiler/cpp/cpp_enum.h | 6 + src/google/protobuf/compiler/cpp/cpp_enum_field.cc | 14 +- src/google/protobuf/compiler/cpp/cpp_field.h | 26 + src/google/protobuf/compiler/cpp/cpp_file.cc | 505 +++--- src/google/protobuf/compiler/cpp/cpp_file.h | 29 + src/google/protobuf/compiler/cpp/cpp_generator.cc | 1 + src/google/protobuf/compiler/cpp/cpp_helpers.cc | 59 +- src/google/protobuf/compiler/cpp/cpp_helpers.h | 21 + src/google/protobuf/compiler/cpp/cpp_map_field.cc | 9 +- src/google/protobuf/compiler/cpp/cpp_message.cc | 688 ++++++-- src/google/protobuf/compiler/cpp/cpp_message.h | 25 +- .../protobuf/compiler/cpp/cpp_message_field.cc | 33 +- .../protobuf/compiler/cpp/cpp_message_field.h | 6 + src/google/protobuf/compiler/cpp/cpp_options.h | 5 +- .../protobuf/compiler/cpp/cpp_primitive_field.cc | 14 +- .../protobuf/compiler/cpp/cpp_string_field.cc | 1 - .../compiler/cpp/cpp_test_bad_identifiers.proto | 34 +- src/google/protobuf/compiler/cpp/cpp_unittest.cc | 16 + .../compiler/cpp/test_large_enum_value.proto | 43 + .../protobuf/compiler/java/java_enum_field.cc | 16 +- .../protobuf/compiler/java/java_enum_field_lite.cc | 967 ++++++++++ .../protobuf/compiler/java/java_enum_field_lite.h | 159 ++ src/google/protobuf/compiler/java/java_field.cc | 108 ++ src/google/protobuf/compiler/java/java_field.h | 32 +- src/google/protobuf/compiler/java/java_file.cc | 10 +- .../compiler/java/java_generator_factory.cc | 8 +- src/google/protobuf/compiler/java/java_helpers.h | 10 +- .../compiler/java/java_lazy_message_field.cc | 226 ++- .../compiler/java/java_lazy_message_field_lite.cc | 708 ++++++++ .../compiler/java/java_lazy_message_field_lite.h | 118 ++ .../protobuf/compiler/java/java_map_field.cc | 51 +- src/google/protobuf/compiler/java/java_map_field.h | 1 - .../protobuf/compiler/java/java_map_field_lite.cc | 461 +++++ .../protobuf/compiler/java/java_map_field_lite.h | 81 + src/google/protobuf/compiler/java/java_message.cc | 1184 +++---------- src/google/protobuf/compiler/java/java_message.h | 11 +- .../protobuf/compiler/java/java_message_builder.cc | 661 +++++++ .../protobuf/compiler/java/java_message_builder.h | 86 + .../compiler/java/java_message_builder_lite.cc | 192 ++ .../compiler/java/java_message_builder_lite.h | 83 + .../protobuf/compiler/java/java_message_field.cc | 411 ++--- .../compiler/java/java_message_field_lite.cc | 944 ++++++++++ .../compiler/java/java_message_field_lite.h | 157 ++ .../protobuf/compiler/java/java_message_lite.cc | 1174 ++++++++++++ .../protobuf/compiler/java/java_message_lite.h | 91 + .../protobuf/compiler/java/java_primitive_field.cc | 15 +- .../compiler/java/java_primitive_field_lite.cc | 892 ++++++++++ .../compiler/java/java_primitive_field_lite.h | 163 ++ .../protobuf/compiler/java/java_string_field.cc | 2 +- .../compiler/java/java_string_field_lite.cc | 1013 +++++++++++ .../compiler/java/java_string_field_lite.h | 158 ++ src/google/protobuf/compiler/parser.cc | 80 + src/google/protobuf/compiler/parser.h | 8 + src/google/protobuf/compiler/parser_unittest.cc | 75 +- src/google/protobuf/compiler/plugin.cc | 28 +- src/google/protobuf/compiler/plugin.pb.cc | 90 +- src/google/protobuf/compiler/plugin.pb.h | 4 + src/google/protobuf/descriptor.cc | 183 +- src/google/protobuf/descriptor.h | 67 +- src/google/protobuf/descriptor.pb.cc | 1863 ++++++++++++++------ src/google/protobuf/descriptor.pb.h | 414 ++++- src/google/protobuf/descriptor.proto | 45 +- src/google/protobuf/descriptor_database.h | 4 +- src/google/protobuf/descriptor_unittest.cc | 246 ++- src/google/protobuf/duration.pb.cc | 12 +- src/google/protobuf/empty.pb.cc | 8 +- src/google/protobuf/extension_set.cc | 61 +- src/google/protobuf/extension_set.h | 6 +- src/google/protobuf/extension_set_heavy.cc | 2 +- src/google/protobuf/field_mask.pb.cc | 12 +- src/google/protobuf/generated_message_reflection.h | 37 + src/google/protobuf/generated_message_util.h | 13 + src/google/protobuf/io/coded_stream.cc | 104 +- src/google/protobuf/io/coded_stream.h | 84 +- src/google/protobuf/io/coded_stream_inl.h | 17 + src/google/protobuf/io/printer.cc | 72 + src/google/protobuf/io/printer.h | 32 +- src/google/protobuf/lite_unittest.cc | 378 ++++ src/google/protobuf/map.h | 1 + src/google/protobuf/map_entry_lite.h | 18 +- src/google/protobuf/map_field.cc | 18 +- src/google/protobuf/map_field.h | 1 + src/google/protobuf/map_field_inl.h | 2 +- src/google/protobuf/map_field_lite.h | 2 +- src/google/protobuf/map_field_test.cc | 13 +- src/google/protobuf/map_test.cc | 21 + src/google/protobuf/map_type_handler.h | 4 +- src/google/protobuf/map_unittest.proto | 16 +- src/google/protobuf/message.cc | 19 +- src/google/protobuf/message.h | 15 +- src/google/protobuf/message_lite.cc | 2 +- src/google/protobuf/message_lite.h | 3 + src/google/protobuf/message_unittest.cc | 31 +- src/google/protobuf/proto3_arena_unittest.cc | 10 + src/google/protobuf/reflection.h | 1 + src/google/protobuf/repeated_field.h | 60 +- src/google/protobuf/repeated_field_unittest.cc | 22 + src/google/protobuf/source_context.pb.cc | 10 +- src/google/protobuf/struct.pb.cc | 95 +- src/google/protobuf/struct.pb.h | 4 + src/google/protobuf/stubs/common.h | 8 + src/google/protobuf/stubs/strutil.cc | 18 - src/google/protobuf/stubs/strutil.h | 6 - src/google/protobuf/testdata/golden_message_proto3 | Bin 398 -> 248 bytes src/google/protobuf/testdata/map_test_data.txt | 140 ++ src/google/protobuf/text_format.cc | 267 ++- src/google/protobuf/text_format.h | 15 + src/google/protobuf/text_format_unittest.cc | 21 +- src/google/protobuf/timestamp.pb.cc | 12 +- src/google/protobuf/type.pb.cc | 161 +- src/google/protobuf/type.pb.h | 14 +- src/google/protobuf/unittest.proto | 6 + .../protobuf/unittest_drop_unknown_fields.proto | 10 +- .../protobuf/unittest_no_field_presence.proto | 56 +- .../protobuf/unittest_preserve_unknown_enum.proto | 4 +- src/google/protobuf/unittest_proto3_arena.proto | 90 +- src/google/protobuf/unknown_field_set.cc | 8 +- src/google/protobuf/unknown_field_set.h | 2 +- src/google/protobuf/wire_format.cc | 4 +- src/google/protobuf/wire_format_lite.h | 11 + src/google/protobuf/wire_format_lite_inl.h | 21 +- src/google/protobuf/wire_format_unittest.cc | 132 ++ src/google/protobuf/wrappers.pb.cc | 90 +- src/google/protobuf/wrappers.pb.h | 16 + vsprojects/libprotoc.vcproj | 64 + 227 files changed, 25713 insertions(+), 3789 deletions(-) create mode 100644 java/src/main/java/com/google/protobuf/AbstractProtobufList.java create mode 100644 java/src/main/java/com/google/protobuf/BooleanArrayList.java create mode 100644 java/src/main/java/com/google/protobuf/DoubleArrayList.java create mode 100644 java/src/main/java/com/google/protobuf/FloatArrayList.java create mode 100644 java/src/main/java/com/google/protobuf/IntArrayList.java create mode 100644 java/src/main/java/com/google/protobuf/LongArrayList.java create mode 100644 java/src/main/java/com/google/protobuf/MutabilityOracle.java create mode 100644 java/src/main/java/com/google/protobuf/ProtobufArrayList.java create mode 100644 java/src/test/java/com/google/protobuf/BooleanArrayListTest.java create mode 100644 java/src/test/java/com/google/protobuf/DoubleArrayListTest.java create mode 100644 java/src/test/java/com/google/protobuf/FloatArrayListTest.java create mode 100644 java/src/test/java/com/google/protobuf/IntArrayListTest.java create mode 100644 java/src/test/java/com/google/protobuf/LongArrayListTest.java create mode 100644 java/src/test/java/com/google/protobuf/ProtobufArrayListTest.java create mode 100644 java/src/test/java/com/google/protobuf/map_initialization_order_test.proto create mode 100644 python/google/protobuf/pyext/message_map_container.cc create mode 100644 python/google/protobuf/pyext/message_map_container.h create mode 100644 python/google/protobuf/pyext/scalar_map_container.cc create mode 100644 python/google/protobuf/pyext/scalar_map_container.h create mode 100644 src/google/protobuf/any.cc create mode 100644 src/google/protobuf/any.h create mode 100644 src/google/protobuf/any_test.cc create mode 100644 src/google/protobuf/any_test.proto create mode 100644 src/google/protobuf/compiler/cpp/test_large_enum_value.proto create mode 100644 src/google/protobuf/compiler/java/java_enum_field_lite.cc create mode 100644 src/google/protobuf/compiler/java/java_enum_field_lite.h create mode 100644 src/google/protobuf/compiler/java/java_lazy_message_field_lite.cc create mode 100644 src/google/protobuf/compiler/java/java_lazy_message_field_lite.h create mode 100644 src/google/protobuf/compiler/java/java_map_field_lite.cc create mode 100644 src/google/protobuf/compiler/java/java_map_field_lite.h create mode 100644 src/google/protobuf/compiler/java/java_message_builder.cc create mode 100644 src/google/protobuf/compiler/java/java_message_builder.h create mode 100644 src/google/protobuf/compiler/java/java_message_builder_lite.cc create mode 100644 src/google/protobuf/compiler/java/java_message_builder_lite.h create mode 100644 src/google/protobuf/compiler/java/java_message_field_lite.cc create mode 100644 src/google/protobuf/compiler/java/java_message_field_lite.h create mode 100644 src/google/protobuf/compiler/java/java_message_lite.cc create mode 100644 src/google/protobuf/compiler/java/java_message_lite.h create mode 100644 src/google/protobuf/compiler/java/java_primitive_field_lite.cc create mode 100644 src/google/protobuf/compiler/java/java_primitive_field_lite.h create mode 100644 src/google/protobuf/compiler/java/java_string_field_lite.cc create mode 100644 src/google/protobuf/compiler/java/java_string_field_lite.h create mode 100644 src/google/protobuf/testdata/map_test_data.txt (limited to 'ruby') diff --git a/.gitignore b/.gitignore index 6508eccb..38755c96 100644 --- a/.gitignore +++ b/.gitignore @@ -44,6 +44,7 @@ src/.libs .dirstamp +any_test.pb.* map*unittest.pb.* unittest*.pb.* cpp_test*.pb.* diff --git a/Android.mk b/Android.mk index 18bdd091..bbd15740 100644 --- a/Android.mk +++ b/Android.mk @@ -90,14 +90,20 @@ COMPILER_SRC_FILES := \ src/google/protobuf/compiler/cpp/cpp_string_field.cc \ src/google/protobuf/compiler/java/java_enum.cc \ src/google/protobuf/compiler/java/java_enum_field.cc \ + src/google/protobuf/compiler/java/java_enum_field_lite.cc \ src/google/protobuf/compiler/java/java_extension.cc \ src/google/protobuf/compiler/java/java_field.cc \ src/google/protobuf/compiler/java/java_file.cc \ src/google/protobuf/compiler/java/java_generator.cc \ src/google/protobuf/compiler/java/java_helpers.cc \ src/google/protobuf/compiler/java/java_message.cc \ + src/google/protobuf/compiler/java/java_message_lite.cc \ + src/google/protobuf/compiler/java/java_message_builder.cc \ + src/google/protobuf/compiler/java/java_message_builder_lite.cc \ src/google/protobuf/compiler/java/java_message_field.cc \ + src/google/protobuf/compiler/java/java_message_field_lite.cc \ src/google/protobuf/compiler/java/java_primitive_field.cc \ + src/google/protobuf/compiler/java/java_primitive_field_lite.cc \ src/google/protobuf/compiler/java/java_service.cc \ src/google/protobuf/compiler/javamicro/javamicro_enum.cc \ src/google/protobuf/compiler/javamicro/javamicro_enum_field.cc \ diff --git a/Makefile.am b/Makefile.am index 45d44f5c..645c924b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -43,28 +43,34 @@ java_EXTRA_DIST= \ java/src/main/java/com/google/protobuf/AbstractMessage.java \ java/src/main/java/com/google/protobuf/AbstractMessageLite.java \ java/src/main/java/com/google/protobuf/AbstractParser.java \ + java/src/main/java/com/google/protobuf/AbstractProtobufList.java \ java/src/main/java/com/google/protobuf/BlockingRpcChannel.java \ java/src/main/java/com/google/protobuf/BlockingService.java \ java/src/main/java/com/google/protobuf/BoundedByteString.java \ + java/src/main/java/com/google/protobuf/BooleanArrayList.java \ java/src/main/java/com/google/protobuf/ByteString.java \ java/src/main/java/com/google/protobuf/CodedInputStream.java \ java/src/main/java/com/google/protobuf/CodedOutputStream.java \ java/src/main/java/com/google/protobuf/Descriptors.java \ + java/src/main/java/com/google/protobuf/DoubleArrayList.java \ java/src/main/java/com/google/protobuf/DynamicMessage.java \ java/src/main/java/com/google/protobuf/Extension.java \ java/src/main/java/com/google/protobuf/ExtensionLite.java \ java/src/main/java/com/google/protobuf/ExtensionRegistry.java \ java/src/main/java/com/google/protobuf/ExtensionRegistryLite.java \ java/src/main/java/com/google/protobuf/FieldSet.java \ + java/src/main/java/com/google/protobuf/FloatArrayList.java \ java/src/main/java/com/google/protobuf/GeneratedMessage.java \ java/src/main/java/com/google/protobuf/GeneratedMessageLite.java \ java/src/main/java/com/google/protobuf/Internal.java \ + java/src/main/java/com/google/protobuf/IntArrayList.java \ java/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java \ java/src/main/java/com/google/protobuf/LazyField.java \ java/src/main/java/com/google/protobuf/LazyFieldLite.java \ java/src/main/java/com/google/protobuf/LazyStringArrayList.java \ java/src/main/java/com/google/protobuf/LazyStringList.java \ java/src/main/java/com/google/protobuf/LiteralByteString.java \ + java/src/main/java/com/google/protobuf/LongArrayList.java \ java/src/main/java/com/google/protobuf/MapEntry.java \ java/src/main/java/com/google/protobuf/MapEntryLite.java \ java/src/main/java/com/google/protobuf/MapField.java \ @@ -74,7 +80,9 @@ java_EXTRA_DIST= \ java/src/main/java/com/google/protobuf/MessageLiteOrBuilder.java \ java/src/main/java/com/google/protobuf/MessageOrBuilder.java \ java/src/main/java/com/google/protobuf/MessageReflection.java \ + java/src/main/java/com/google/protobuf/MutabilityOracle.java \ java/src/main/java/com/google/protobuf/Parser.java \ + java/src/main/java/com/google/protobuf/ProtobufArrayList.java \ java/src/main/java/com/google/protobuf/ProtocolMessageEnum.java \ java/src/main/java/com/google/protobuf/ProtocolStringList.java \ java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java \ @@ -96,16 +104,20 @@ java_EXTRA_DIST= \ java/src/main/java/com/google/protobuf/WireFormat.java \ java/src/test/java/com/google/protobuf/AbstractMessageTest.java \ java/src/test/java/com/google/protobuf/BoundedByteStringTest.java \ + java/src/test/java/com/google/protobuf/BooleanArrayListTest.java \ java/src/test/java/com/google/protobuf/ByteStringTest.java \ java/src/test/java/com/google/protobuf/CheckUtf8Test.java \ java/src/test/java/com/google/protobuf/CodedInputStreamTest.java \ java/src/test/java/com/google/protobuf/CodedOutputStreamTest.java \ java/src/test/java/com/google/protobuf/DeprecatedFieldTest.java \ java/src/test/java/com/google/protobuf/DescriptorsTest.java \ + java/src/test/java/com/google/protobuf/DoubleArrayListTest.java \ java/src/test/java/com/google/protobuf/DynamicMessageTest.java \ java/src/test/java/com/google/protobuf/FieldPresenceTest.java \ + java/src/test/java/com/google/protobuf/FloatArrayListTest.java \ java/src/test/java/com/google/protobuf/ForceFieldBuildersPreRun.java \ java/src/test/java/com/google/protobuf/GeneratedMessageTest.java \ + java/src/test/java/com/google/protobuf/IntArrayListTest.java \ java/src/test/java/com/google/protobuf/IsValidUtf8Test.java \ java/src/test/java/com/google/protobuf/IsValidUtf8TestUtil.java \ java/src/test/java/com/google/protobuf/LazyFieldLiteTest.java \ @@ -116,12 +128,14 @@ java_EXTRA_DIST= \ java/src/test/java/com/google/protobuf/LiteEqualsAndHashTest.java \ java/src/test/java/com/google/protobuf/LiteralByteStringTest.java \ java/src/test/java/com/google/protobuf/LiteTest.java \ + java/src/test/java/com/google/protobuf/LongArrayListTest.java \ java/src/test/java/com/google/protobuf/MapForProto2LiteTest.java \ java/src/test/java/com/google/protobuf/MapForProto2Test.java \ java/src/test/java/com/google/protobuf/MapTest.java \ java/src/test/java/com/google/protobuf/MessageTest.java \ java/src/test/java/com/google/protobuf/NestedBuildersTest.java \ java/src/test/java/com/google/protobuf/ParserTest.java \ + java/src/test/java/com/google/protobuf/ProtobufArrayListTest.java \ java/src/test/java/com/google/protobuf/RepeatedFieldBuilderTest.java \ java/src/test/java/com/google/protobuf/RopeByteStringSubstringTest.java \ java/src/test/java/com/google/protobuf/RopeByteStringTest.java \ diff --git a/conformance/conformance.proto b/conformance/conformance.proto index 7b676898..39eafdbb 100644 --- a/conformance/conformance.proto +++ b/conformance/conformance.proto @@ -71,7 +71,7 @@ message ConformanceRequest { } // Which format should the testee serialize its message to? - optional RequestedOutput requested_output = 3; + RequestedOutput requested_output = 3; } // Represents a single test case's output. @@ -103,8 +103,8 @@ message ConformanceResponse { // forms. message TestAllTypes { message NestedMessage { - optional int32 a = 1; - optional TestAllTypes corecursive = 2; + int32 a = 1; + TestAllTypes corecursive = 2; } enum NestedEnum { @@ -115,36 +115,32 @@ message TestAllTypes { } // Singular - optional int32 optional_int32 = 1; - optional int64 optional_int64 = 2; - optional uint32 optional_uint32 = 3; - optional uint64 optional_uint64 = 4; - optional sint32 optional_sint32 = 5; - optional sint64 optional_sint64 = 6; - optional fixed32 optional_fixed32 = 7; - optional fixed64 optional_fixed64 = 8; - optional sfixed32 optional_sfixed32 = 9; - optional sfixed64 optional_sfixed64 = 10; - optional float optional_float = 11; - optional double optional_double = 12; - optional bool optional_bool = 13; - optional string optional_string = 14; - optional bytes optional_bytes = 15; - - optional group OptionalGroup = 16 { - optional int32 a = 17; - } - - optional NestedMessage optional_nested_message = 18; - optional ForeignMessage optional_foreign_message = 19; - - optional NestedEnum optional_nested_enum = 21; - optional ForeignEnum optional_foreign_enum = 22; - - optional string optional_string_piece = 24 [ctype=STRING_PIECE]; - optional string optional_cord = 25 [ctype=CORD]; - - optional TestAllTypes recursive_message = 27; + int32 optional_int32 = 1; + int64 optional_int64 = 2; + uint32 optional_uint32 = 3; + uint64 optional_uint64 = 4; + sint32 optional_sint32 = 5; + sint64 optional_sint64 = 6; + fixed32 optional_fixed32 = 7; + fixed64 optional_fixed64 = 8; + sfixed32 optional_sfixed32 = 9; + sfixed64 optional_sfixed64 = 10; + float optional_float = 11; + double optional_double = 12; + bool optional_bool = 13; + string optional_string = 14; + bytes optional_bytes = 15; + + NestedMessage optional_nested_message = 18; + ForeignMessage optional_foreign_message = 19; + + NestedEnum optional_nested_enum = 21; + ForeignEnum optional_foreign_enum = 22; + + string optional_string_piece = 24 [ctype=STRING_PIECE]; + string optional_cord = 25 [ctype=CORD]; + + TestAllTypes recursive_message = 27; // Repeated repeated int32 repeated_int32 = 31; @@ -163,10 +159,6 @@ message TestAllTypes { repeated string repeated_string = 44; repeated bytes repeated_bytes = 45; - repeated group RepeatedGroup = 46 { - optional int32 a = 47; - } - repeated NestedMessage repeated_nested_message = 48; repeated ForeignMessage repeated_foreign_message = 49; @@ -206,7 +198,7 @@ message TestAllTypes { } message ForeignMessage { - optional int32 c = 1; + int32 c = 1; } enum ForeignEnum { diff --git a/conformance/conformance_test.cc b/conformance/conformance_test.cc index 61029e44..857f2152 100644 --- a/conformance/conformance_test.cc +++ b/conformance/conformance_test.cc @@ -295,6 +295,7 @@ void ConformanceTestSuite::RunSuite(ConformanceTestRunner* runner, failures_ = 0; for (int i = 1; i <= FieldDescriptor::MAX_TYPE; i++) { + if (i == FieldDescriptor::TYPE_GROUP) continue; TestPrematureEOFForType(static_cast(i)); } diff --git a/editors/proto.vim b/editors/proto.vim index 23085a28..7f1aeb73 100644 --- a/editors/proto.vim +++ b/editors/proto.vim @@ -57,7 +57,7 @@ syn keyword pbSyntax syntax import option syn keyword pbStructure package message group oneof syn keyword pbRepeat optional required repeated syn keyword pbDefault default -syn keyword pbExtend extend extensions to max +syn keyword pbExtend extend extensions to max reserved syn keyword pbRPC service rpc returns syn keyword pbType int32 int64 uint32 uint64 sint32 sint64 diff --git a/editors/protobuf-mode.el b/editors/protobuf-mode.el index 09aecc93..f615a0af 100644 --- a/editors/protobuf-mode.el +++ b/editors/protobuf-mode.el @@ -106,7 +106,7 @@ ;; cc-mode. So, we approximate as best we can. (c-lang-defconst c-type-list-kwds - protobuf '("extensions" "to")) + protobuf '("extensions" "to" "reserved")) (c-lang-defconst c-typeless-decl-kwds protobuf '("extend" "rpc" "option" "returns")) diff --git a/generate_descriptor_proto.sh b/generate_descriptor_proto.sh index 27d293a2..89449e10 100755 --- a/generate_descriptor_proto.sh +++ b/generate_descriptor_proto.sh @@ -43,8 +43,11 @@ declare -a RUNTIME_PROTO_FILES=(\ google/protobuf/wrappers.proto) CORE_PROTO_IS_CORRECT=0 +PROCESS_ROUND=1 +echo "Updating descriptor protos..." while [ $CORE_PROTO_IS_CORRECT -ne 1 ] do + echo "Round $PROCESS_ROUND" CORE_PROTO_IS_CORRECT=1 for PROTO_FILE in ${RUNTIME_PROTO_FILES[@]}; do BASE_NAME=${PROTO_FILE%.*} @@ -86,5 +89,7 @@ do done rm google/protobuf/compiler/plugin.pb.h.tmp rm google/protobuf/compiler/plugin.pb.cc.tmp + + PROCESS_ROUND=$((PROCESS_ROUND + 1)) done cd .. diff --git a/java/pom.xml b/java/pom.xml index 3eb7a703..9f270113 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -134,6 +134,7 @@ + target/generated-test-sources @@ -227,25 +228,33 @@ **/AbstractMessageLite.java **/AbstractParser.java + **/AbstractProtobufList.java **/BoundedByteString.java + **/BooleanArrayList.java **/ByteString.java **/CodedInputStream.java **/CodedOutputStream.java + **/DoublerrayList.java **/ExtensionLite.java **/ExtensionRegistryLite.java **/FieldSet.java + **/FloatArrayList.java **/GeneratedMessageLite.java + **/IntArrayList.java **/Internal.java **/InvalidProtocolBufferException.java **/LazyFieldLite.java **/LazyStringArrayList.java **/LazyStringList.java **/LiteralByteString.java + **/LongArrayList.java **/MapEntryLite.java **/MapFieldLite.java **/MessageLite.java **/MessageLiteOrBuilder.java + **/MutabilityOracle.java **/Parser.java + **/ProtobufArrayList.java **/ProtocolStringList.java **/RopeByteString.java **/SmallSortedMap.java @@ -257,8 +266,14 @@ **/*Lite.java + **/BooleanArrayListTest.java + **/DoubleArrayListTest.java + **/FloatArrayListTest.java + **/IntArrayListTest.java **/LazyMessageLiteTest.java **/LiteTest.java + **/LongArrayListTest.java + **/ProtobufArrayListTest.java **/UnknownFieldSetLiteTest.java diff --git a/java/src/main/java/com/google/protobuf/AbstractMessageLite.java b/java/src/main/java/com/google/protobuf/AbstractMessageLite.java index aac4fa77..12384983 100644 --- a/java/src/main/java/com/google/protobuf/AbstractMessageLite.java +++ b/java/src/main/java/com/google/protobuf/AbstractMessageLite.java @@ -31,8 +31,8 @@ package com.google.protobuf; import java.io.FilterInputStream; -import java.io.InputStream; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; import java.util.Collection; @@ -109,6 +109,11 @@ public abstract class AbstractMessageLite implements MessageLite { } } + protected static void addAll(final Iterable values, + final Collection list) { + Builder.addAll(values, list); + } + /** * A partial implementation of the {@link Message.Builder} interface which * implements as many methods of that interface as possible in terms of @@ -320,12 +325,15 @@ public abstract class AbstractMessageLite implements MessageLite { * Adds the {@code values} to the {@code list}. This is a helper method * used by generated code. Users should ignore it. * - * @throws NullPointerException if any of the elements of {@code values} is - * null. When that happens, some elements of {@code values} may have already - * been added to the result {@code list}. + * @throws NullPointerException if {@code values} or any of the elements of + * {@code values} is null. When that happens, some elements of + * {@code values} may have already been added to the result {@code list}. */ protected static void addAll(final Iterable values, final Collection list) { + if (values == null) { + throw new NullPointerException(); + } if (values instanceof LazyStringList) { // For StringOrByteStringLists, check the underlying elements to avoid // forcing conversions of ByteStrings to Strings. diff --git a/java/src/main/java/com/google/protobuf/AbstractProtobufList.java b/java/src/main/java/com/google/protobuf/AbstractProtobufList.java new file mode 100644 index 00000000..bb6446b2 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/AbstractProtobufList.java @@ -0,0 +1,136 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import com.google.protobuf.Internal.ProtobufList; + +import java.util.AbstractList; +import java.util.Collection; + +/** + * An abstract implementation of {@link ProtobufList} which manages mutability semantics. All mutate + * methods are check if the list is mutable before proceeding. Subclasses must invoke + * {@link #ensureIsMutable()} manually when overriding those methods. + */ +abstract class AbstractProtobufList extends AbstractList implements ProtobufList { + + /** + * Whether or not this list is modifiable. + */ + private boolean isMutable; + + /** + * Constructs a mutable list by default. + */ + AbstractProtobufList() { + isMutable = true; + } + + @Override + public boolean add(E e) { + ensureIsMutable(); + return super.add(e); + } + + @Override + public void add(int index, E element) { + ensureIsMutable(); + super.add(index, element); + } + + @Override + public boolean addAll(Collection c) { + ensureIsMutable(); + return super.addAll(c); + } + + @Override + public boolean addAll(int index, Collection c) { + ensureIsMutable(); + return super.addAll(index, c); + } + + @Override + public void clear() { + ensureIsMutable(); + super.clear(); + } + + @Override + public boolean isModifiable() { + return isMutable; + } + + @Override + public final void makeImmutable() { + isMutable = false; + } + + @Override + public E remove(int index) { + ensureIsMutable(); + return super.remove(index); + } + + @Override + public boolean remove(Object o) { + ensureIsMutable(); + return super.remove(o); + } + + @Override + public boolean removeAll(Collection c) { + ensureIsMutable(); + return super.removeAll(c); + } + + @Override + public boolean retainAll(Collection c) { + ensureIsMutable(); + return super.retainAll(c); + } + + @Override + public E set(int index, E element) { + ensureIsMutable(); + return super.set(index, element); + } + + /** + * Throws an {@link UnsupportedOperationException} if the list is immutable. Subclasses are + * responsible for invoking this method on mutate operations. + */ + protected void ensureIsMutable() { + if (!isMutable) { + throw new UnsupportedOperationException(); + } + } +} diff --git a/java/src/main/java/com/google/protobuf/BooleanArrayList.java b/java/src/main/java/com/google/protobuf/BooleanArrayList.java new file mode 100644 index 00000000..45492d2f --- /dev/null +++ b/java/src/main/java/com/google/protobuf/BooleanArrayList.java @@ -0,0 +1,244 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import com.google.protobuf.Internal.BooleanList; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.RandomAccess; + +/** + * An implementation of {@link BooleanList} on top of a primitive array. + * + * @author dweis@google.com (Daniel Weis) + */ +final class BooleanArrayList + extends AbstractProtobufList implements BooleanList, RandomAccess { + + private static final int DEFAULT_CAPACITY = 10; + + private static final BooleanArrayList EMPTY_LIST = new BooleanArrayList(); + static { + EMPTY_LIST.makeImmutable(); + } + + public static BooleanArrayList emptyList() { + return EMPTY_LIST; + } + + /** + * The backing store for the list. + */ + private boolean[] array; + + /** + * The size of the list distinct from the length of the array. That is, it is the number of + * elements set in the list. + */ + private int size; + + /** + * Constructs a new mutable {@code BooleanArrayList}. + */ + BooleanArrayList() { + array = new boolean[DEFAULT_CAPACITY]; + size = 0; + } + + /** + * Constructs a new mutable {@code BooleanArrayList} containing the same elements as + * {@code other}. + */ + BooleanArrayList(List other) { + if (other instanceof BooleanArrayList) { + BooleanArrayList list = (BooleanArrayList) other; + array = list.array.clone(); + size = list.size; + } else { + size = other.size(); + array = new boolean[size]; + for (int i = 0; i < size; i++) { + array[i] = other.get(i); + } + } + } + + @Override + public Boolean get(int index) { + return getBoolean(index); + } + + @Override + public boolean getBoolean(int index) { + ensureIndexInRange(index); + return array[index]; + } + + @Override + public int size() { + return size; + } + + @Override + public Boolean set(int index, Boolean element) { + return setBoolean(index, element); + } + + @Override + public boolean setBoolean(int index, boolean element) { + ensureIsMutable(); + ensureIndexInRange(index); + boolean previousValue = array[index]; + array[index] = element; + return previousValue; + } + + @Override + public void add(int index, Boolean element) { + addBoolean(index, element); + } + + /** + * Like {@link #add(Boolean)} but more efficient in that it doesn't box the element. + */ + @Override + public void addBoolean(boolean element) { + addBoolean(size, element); + } + + /** + * Like {@link #add(int, Boolean)} but more efficient in that it doesn't box the element. + */ + private void addBoolean(int index, boolean element) { + ensureIsMutable(); + if (index < 0 || index > size) { + throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index)); + } + + if (size < array.length) { + // Shift everything over to make room + System.arraycopy(array, index, array, index + 1, size - index); + } else { + // Resize to 1.5x the size + int length = ((size * 3) / 2) + 1; + boolean[] newArray = new boolean[length]; + + // Copy the first part directly + System.arraycopy(array, 0, newArray, 0, index); + + // Copy the rest shifted over by one to make room + System.arraycopy(array, index, newArray, index + 1, size - index); + array = newArray; + } + + array[index] = element; + size++; + modCount++; + } + + @Override + public boolean addAll(Collection collection) { + ensureIsMutable(); + + if (collection == null) { + throw new NullPointerException(); + } + + // We specialize when adding another BooleanArrayList to avoid boxing elements. + if (!(collection instanceof BooleanArrayList)) { + return super.addAll(collection); + } + + BooleanArrayList list = (BooleanArrayList) collection; + if (list.size == 0) { + return false; + } + + int overflow = Integer.MAX_VALUE - size; + if (overflow < list.size) { + // We can't actually represent a list this large. + throw new OutOfMemoryError(); + } + + int newSize = size + list.size; + if (newSize > array.length) { + array = Arrays.copyOf(array, newSize); + } + + System.arraycopy(list.array, 0, array, size, list.size); + size = newSize; + modCount++; + return true; + } + + @Override + public boolean remove(Object o) { + ensureIsMutable(); + for (int i = 0; i < size; i++) { + if (o.equals(array[i])) { + System.arraycopy(array, i + 1, array, i, size - i); + size--; + modCount++; + return true; + } + } + return false; + } + + @Override + public Boolean remove(int index) { + ensureIsMutable(); + ensureIndexInRange(index); + boolean value = array[index]; + System.arraycopy(array, index + 1, array, index, size - index); + size--; + modCount++; + return value; + } + + /** + * Ensures that the provided {@code index} is within the range of {@code [0, size]}. Throws an + * {@link IndexOutOfBoundsException} if it is not. + * + * @param index the index to verify is in range + */ + private void ensureIndexInRange(int index) { + if (index < 0 || index >= size) { + throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index)); + } + } + + private String makeOutOfBoundsExceptionMessage(int index) { + return "Index:" + index + ", Size:" + size; + } +} diff --git a/java/src/main/java/com/google/protobuf/Descriptors.java b/java/src/main/java/com/google/protobuf/Descriptors.java index b60bd36b..3658410c 100644 --- a/java/src/main/java/com/google/protobuf/Descriptors.java +++ b/java/src/main/java/com/google/protobuf/Descriptors.java @@ -644,6 +644,30 @@ public final class Descriptors { return false; } + /** Determines if the given field number is reserved. */ + public boolean isReservedNumber(final int number) { + for (final DescriptorProto.ReservedRange range : + proto.getReservedRangeList()) { + if (range.getStart() <= number && number < range.getEnd()) { + return true; + } + } + return false; + } + + /** Determines if the given field name is reserved. */ + public boolean isReservedName(final String name) { + if (name == null) { + throw new NullPointerException(); + } + for (final String reservedName : proto.getReservedNameList()) { + if (reservedName.equals(name)) { + return true; + } + } + return false; + } + /** * Indicates whether the message can be extended. That is, whether it has * any "extensions x to y" ranges declared on it. @@ -917,9 +941,18 @@ public final class Descriptors { return proto.getLabel() == FieldDescriptorProto.Label.LABEL_REPEATED; } - /** Does this field have the {@code [packed = true]} option? */ + /** Does this field have the {@code [packed = true]} option or is this field + * packable in proto3 and not explicitly setted to unpacked? + */ public boolean isPacked() { - return getOptions().getPacked(); + if (!isPackable()) { + return false; + } + if (getFile().getSyntax() == FileDescriptor.Syntax.PROTO2) { + return getOptions().getPacked(); + } else { + return !getOptions().hasPacked() || getOptions().getPacked(); + } } /** Can this field be packed? i.e. is it a repeated primitive field? */ @@ -2317,6 +2350,11 @@ public final class Descriptors { public int getFieldCount() { return fieldCount; } + /** Get a list of this message type's fields. */ + public List getFields() { + return Collections.unmodifiableList(Arrays.asList(fields)); + } + public FieldDescriptor getField(int index) { return fields[index]; } diff --git a/java/src/main/java/com/google/protobuf/DoubleArrayList.java b/java/src/main/java/com/google/protobuf/DoubleArrayList.java new file mode 100644 index 00000000..90ebe109 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/DoubleArrayList.java @@ -0,0 +1,243 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import com.google.protobuf.Internal.DoubleList; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.RandomAccess; + +/** + * An implementation of {@link DoubleList} on top of a primitive array. + * + * @author dweis@google.com (Daniel Weis) + */ +final class DoubleArrayList + extends AbstractProtobufList implements DoubleList, RandomAccess { + + private static final int DEFAULT_CAPACITY = 10; + + private static final DoubleArrayList EMPTY_LIST = new DoubleArrayList(); + static { + EMPTY_LIST.makeImmutable(); + } + + public static DoubleArrayList emptyList() { + return EMPTY_LIST; + } + + /** + * The backing store for the list. + */ + private double[] array; + + /** + * The size of the list distinct from the length of the array. That is, it is the number of + * elements set in the list. + */ + private int size; + + /** + * Constructs a new mutable {@code DoubleArrayList}. + */ + DoubleArrayList() { + array = new double[DEFAULT_CAPACITY]; + size = 0; + } + + /** + * Constructs a new mutable {@code DoubleArrayList} containing the same elements as {@code other}. + */ + DoubleArrayList(List other) { + if (other instanceof DoubleArrayList) { + DoubleArrayList list = (DoubleArrayList) other; + array = list.array.clone(); + size = list.size; + } else { + size = other.size(); + array = new double[size]; + for (int i = 0; i < size; i++) { + array[i] = other.get(i); + } + } + } + + @Override + public Double get(int index) { + return getDouble(index); + } + + @Override + public double getDouble(int index) { + ensureIndexInRange(index); + return array[index]; + } + + @Override + public int size() { + return size; + } + + @Override + public Double set(int index, Double element) { + return setDouble(index, element); + } + + @Override + public double setDouble(int index, double element) { + ensureIsMutable(); + ensureIndexInRange(index); + double previousValue = array[index]; + array[index] = element; + return previousValue; + } + + @Override + public void add(int index, Double element) { + addDouble(index, element); + } + + /** + * Like {@link #add(Double)} but more efficient in that it doesn't box the element. + */ + @Override + public void addDouble(double element) { + addDouble(size, element); + } + + /** + * Like {@link #add(int, Double)} but more efficient in that it doesn't box the element. + */ + private void addDouble(int index, double element) { + ensureIsMutable(); + if (index < 0 || index > size) { + throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index)); + } + + if (size < array.length) { + // Shift everything over to make room + System.arraycopy(array, index, array, index + 1, size - index); + } else { + // Resize to 1.5x the size + int length = ((size * 3) / 2) + 1; + double[] newArray = new double[length]; + + // Copy the first part directly + System.arraycopy(array, 0, newArray, 0, index); + + // Copy the rest shifted over by one to make room + System.arraycopy(array, index, newArray, index + 1, size - index); + array = newArray; + } + + array[index] = element; + size++; + modCount++; + } + + @Override + public boolean addAll(Collection collection) { + ensureIsMutable(); + + if (collection == null) { + throw new NullPointerException(); + } + + // We specialize when adding another DoubleArrayList to avoid boxing elements. + if (!(collection instanceof DoubleArrayList)) { + return super.addAll(collection); + } + + DoubleArrayList list = (DoubleArrayList) collection; + if (list.size == 0) { + return false; + } + + int overflow = Integer.MAX_VALUE - size; + if (overflow < list.size) { + // We can't actually represent a list this large. + throw new OutOfMemoryError(); + } + + int newSize = size + list.size; + if (newSize > array.length) { + array = Arrays.copyOf(array, newSize); + } + + System.arraycopy(list.array, 0, array, size, list.size); + size = newSize; + modCount++; + return true; + } + + @Override + public boolean remove(Object o) { + ensureIsMutable(); + for (int i = 0; i < size; i++) { + if (o.equals(array[i])) { + System.arraycopy(array, i + 1, array, i, size - i); + size--; + modCount++; + return true; + } + } + return false; + } + + @Override + public Double remove(int index) { + ensureIsMutable(); + ensureIndexInRange(index); + double value = array[index]; + System.arraycopy(array, index + 1, array, index, size - index); + size--; + modCount++; + return value; + } + + /** + * Ensures that the provided {@code index} is within the range of {@code [0, size]}. Throws an + * {@link IndexOutOfBoundsException} if it is not. + * + * @param index the index to verify is in range + */ + private void ensureIndexInRange(int index) { + if (index < 0 || index >= size) { + throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index)); + } + } + + private String makeOutOfBoundsExceptionMessage(int index) { + return "Index:" + index + ", Size:" + size; + } +} diff --git a/java/src/main/java/com/google/protobuf/FloatArrayList.java b/java/src/main/java/com/google/protobuf/FloatArrayList.java new file mode 100644 index 00000000..293eaff6 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/FloatArrayList.java @@ -0,0 +1,242 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import com.google.protobuf.Internal.FloatList; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.RandomAccess; + +/** + * An implementation of {@link FloatList} on top of a primitive array. + * + * @author dweis@google.com (Daniel Weis) + */ +final class FloatArrayList extends AbstractProtobufList implements FloatList, RandomAccess { + + private static final int DEFAULT_CAPACITY = 10; + + private static final FloatArrayList EMPTY_LIST = new FloatArrayList(); + static { + EMPTY_LIST.makeImmutable(); + } + + public static FloatArrayList emptyList() { + return EMPTY_LIST; + } + + /** + * The backing store for the list. + */ + private float[] array; + + /** + * The size of the list distinct from the length of the array. That is, it is the number of + * elements set in the list. + */ + private int size; + + /** + * Constructs a new mutable {@code FloatArrayList}. + */ + FloatArrayList() { + array = new float[DEFAULT_CAPACITY]; + size = 0; + } + + /** + * Constructs a new mutable {@code FloatArrayList} containing the same elements as {@code other}. + */ + FloatArrayList(List other) { + if (other instanceof FloatArrayList) { + FloatArrayList list = (FloatArrayList) other; + array = list.array.clone(); + size = list.size; + } else { + size = other.size(); + array = new float[size]; + for (int i = 0; i < size; i++) { + array[i] = other.get(i); + } + } + } + + @Override + public Float get(int index) { + return getFloat(index); + } + + @Override + public float getFloat(int index) { + ensureIndexInRange(index); + return array[index]; + } + + @Override + public int size() { + return size; + } + + @Override + public Float set(int index, Float element) { + return setFloat(index, element); + } + + @Override + public float setFloat(int index, float element) { + ensureIsMutable(); + ensureIndexInRange(index); + float previousValue = array[index]; + array[index] = element; + return previousValue; + } + + @Override + public void add(int index, Float element) { + addFloat(index, element); + } + + /** + * Like {@link #add(Float)} but more efficient in that it doesn't box the element. + */ + @Override + public void addFloat(float element) { + addFloat(size, element); + } + + /** + * Like {@link #add(int, Float)} but more efficient in that it doesn't box the element. + */ + private void addFloat(int index, float element) { + ensureIsMutable(); + if (index < 0 || index > size) { + throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index)); + } + + if (size < array.length) { + // Shift everything over to make room + System.arraycopy(array, index, array, index + 1, size - index); + } else { + // Resize to 1.5x the size + int length = ((size * 3) / 2) + 1; + float[] newArray = new float[length]; + + // Copy the first part directly + System.arraycopy(array, 0, newArray, 0, index); + + // Copy the rest shifted over by one to make room + System.arraycopy(array, index, newArray, index + 1, size - index); + array = newArray; + } + + array[index] = element; + size++; + modCount++; + } + + @Override + public boolean addAll(Collection collection) { + ensureIsMutable(); + + if (collection == null) { + throw new NullPointerException(); + } + + // We specialize when adding another FloatArrayList to avoid boxing elements. + if (!(collection instanceof FloatArrayList)) { + return super.addAll(collection); + } + + FloatArrayList list = (FloatArrayList) collection; + if (list.size == 0) { + return false; + } + + int overflow = Integer.MAX_VALUE - size; + if (overflow < list.size) { + // We can't actually represent a list this large. + throw new OutOfMemoryError(); + } + + int newSize = size + list.size; + if (newSize > array.length) { + array = Arrays.copyOf(array, newSize); + } + + System.arraycopy(list.array, 0, array, size, list.size); + size = newSize; + modCount++; + return true; + } + + @Override + public boolean remove(Object o) { + ensureIsMutable(); + for (int i = 0; i < size; i++) { + if (o.equals(array[i])) { + System.arraycopy(array, i + 1, array, i, size - i); + size--; + modCount++; + return true; + } + } + return false; + } + + @Override + public Float remove(int index) { + ensureIsMutable(); + ensureIndexInRange(index); + float value = array[index]; + System.arraycopy(array, index + 1, array, index, size - index); + size--; + modCount++; + return value; + } + + /** + * Ensures that the provided {@code index} is within the range of {@code [0, size]}. Throws an + * {@link IndexOutOfBoundsException} if it is not. + * + * @param index the index to verify is in range + */ + private void ensureIndexInRange(int index) { + if (index < 0 || index >= size) { + throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index)); + } + } + + private String makeOutOfBoundsExceptionMessage(int index) { + return "Index:" + index + ", Size:" + size; + } +} diff --git a/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java b/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java index 6839c9dd..bd6bc463 100644 --- a/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java +++ b/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java @@ -30,6 +30,12 @@ package com.google.protobuf; +import com.google.protobuf.Internal.BooleanList; +import com.google.protobuf.Internal.DoubleList; +import com.google.protobuf.Internal.FloatList; +import com.google.protobuf.Internal.IntList; +import com.google.protobuf.Internal.LongList; +import com.google.protobuf.Internal.ProtobufList; import com.google.protobuf.WireFormat.FieldType; import java.io.IOException; @@ -76,7 +82,11 @@ public abstract class GeneratedMessageLite< private static final long serialVersionUID = 1L; /** For use by generated code only. */ - protected UnknownFieldSetLite unknownFields; + protected UnknownFieldSetLite unknownFields = + UnknownFieldSetLite.getDefaultInstance(); + + /** For use by generated code only. */ + protected int memoizedSerializedSize = -1; @SuppressWarnings("unchecked") // Guaranteed by runtime. public final Parser getParserForType() { @@ -109,10 +119,67 @@ public abstract class GeneratedMessageLite< return unknownFields.mergeFieldFrom(tag, input); } - // The default behavior. If a message has required fields in its subtree, the - // generated code will override. - public boolean isInitialized() { - return true; + public final boolean isInitialized() { + return dynamicMethod(MethodToInvoke.IS_INITIALIZED, Boolean.TRUE) != null; + } + + public final BuilderType toBuilder() { + BuilderType builder = (BuilderType) dynamicMethod(MethodToInvoke.NEW_BUILDER); + builder.mergeFrom((MessageType) this); + return builder; + } + + /** + * Defines which method path to invoke in {@link GeneratedMessageLite + * #dynamicMethod(MethodToInvoke, Object...)}. + *

+ * For use by generated code only. + */ + public static enum MethodToInvoke { + IS_INITIALIZED, + PARSE_PARTIAL_FROM, + MERGE_FROM, + MAKE_IMMUTABLE, + NEW_INSTANCE, + NEW_BUILDER; + } + + /** + * A method that implements different types of operations described in {@link MethodToInvoke}. + * Theses different kinds of operations are required to implement message-level operations for + * builders in the runtime. This method bundles those operations to reduce the generated methods + * count. + *

    + *
  • {@code PARSE_PARTIAL_FROM} is parameterized with an {@link CodedInputStream} and + * {@link ExtensionRegistryLite}. It consumes the input stream, parsing the contents into the + * returned protocol buffer. If parsing throws an {@link InvalidProtocolBufferException}, the + * implementation wraps it in a RuntimeException + *
  • {@code NEW_INSTANCE} returns a new instance of the protocol buffer + *
  • {@code IS_INITIALIZED} is parameterized with a {@code Boolean} detailing whether to + * memoize. It returns {@code null} for false and the default instance for true. We optionally + * memoize to support the Builder case, where memoization is not desired. + *
  • {@code NEW_BUILDER} returns a {@code BuilderType} instance. + *
  • {@code MERGE_FROM} is parameterized with a {@code MessageType} and merges the fields from + * that instance into this instance. + *
  • {@code MAKE_IMMUTABLE} sets all internal fields to an immutable state. + *
+ * This method, plus the implementation of the Builder, enables the Builder class to be proguarded + * away entirely on Android. + *

+ * For use by generated code only. + */ + protected abstract Object dynamicMethod( + MethodToInvoke method, + Object... args); + + /** + * Merge some unknown fields into the {@link UnknownFieldSetLite} for this + * message. + * + *

For use by generated code only. + */ + protected final void mergeUnknownFields(UnknownFieldSetLite unknownFields) { + this.unknownFields = UnknownFieldSetLite.concat(this.unknownFields, unknownFields); } @SuppressWarnings("unchecked") @@ -122,24 +189,37 @@ public abstract class GeneratedMessageLite< extends AbstractMessageLite.Builder { private final MessageType defaultInstance; - - /** For use by generated code only. */ - protected UnknownFieldSetLite unknownFields = - UnknownFieldSetLite.getDefaultInstance(); + protected MessageType instance; + protected boolean isBuilt; protected Builder(MessageType defaultInstance) { this.defaultInstance = defaultInstance; + this.instance = (MessageType) defaultInstance.dynamicMethod(MethodToInvoke.NEW_INSTANCE); + isBuilt = false; } - // The default behavior. If a message has required fields in its subtree, - // the generated code will override. - public boolean isInitialized() { - return true; + /** + * Called before any method that would mutate the builder to ensure that it correctly copies + * any state before the write happens to preserve immutability guarantees. + */ + protected void copyOnWrite() { + if (isBuilt) { + MessageType newInstance = (MessageType) instance.dynamicMethod(MethodToInvoke.NEW_INSTANCE); + newInstance.dynamicMethod(MethodToInvoke.MERGE_FROM, instance); + instance = newInstance; + isBuilt = false; + } + } + + //@Override (Java 1.6 override semantics, but we must support 1.5) + public final boolean isInitialized() { + return GeneratedMessageLite.isInitialized(instance, false /* shouldMemoize */); } //@Override (Java 1.6 override semantics, but we must support 1.5) - public BuilderType clear() { - unknownFields = UnknownFieldSetLite.getDefaultInstance(); + public final BuilderType clear() { + // No need to copy on write since we're dropping the instance anyways. + instance = (MessageType) instance.dynamicMethod(MethodToInvoke.NEW_INSTANCE); return (BuilderType) this; } @@ -151,8 +231,12 @@ public abstract class GeneratedMessageLite< return builder; } - /** All subclasses implement this. */ - public abstract MessageType buildPartial(); + //@Override (Java 1.6 override semantics, but we must support 1.5) + public MessageType buildPartial() { + instance.dynamicMethod(MethodToInvoke.MAKE_IMMUTABLE); + isBuilt = true; + return instance; + } //@Override (Java 1.6 override semantics, but we must support 1.5) public final MessageType build() { @@ -162,9 +246,13 @@ public abstract class GeneratedMessageLite< } return result; } - + /** All subclasses implement this. */ - public abstract BuilderType mergeFrom(MessageType message); + public BuilderType mergeFrom(MessageType message) { + copyOnWrite(); + instance.dynamicMethod(MethodToInvoke.MERGE_FROM, message); + return (BuilderType) this; + } public MessageType getDefaultInstanceForType() { return defaultInstance; @@ -181,18 +269,6 @@ public abstract class GeneratedMessageLite< int tag) throws IOException { return unknownFields.mergeFieldFrom(tag, input); } - - /** - * Merge some unknown fields into the {@link UnknownFieldSetLite} for this - * message. - * - *

For use by generated code only. - */ - protected final BuilderType mergeUnknownFields( - final UnknownFieldSetLite unknownFields) { - this.unknownFields = UnknownFieldSetLite.concat(this.unknownFields, unknownFields); - return (BuilderType) this; - } public BuilderType mergeFrom( com.google.protobuf.CodedInputStream input, @@ -259,19 +335,13 @@ public abstract class GeneratedMessageLite< */ protected FieldSet extensions = FieldSet.newFieldSet(); - // -1 => not memoized, 0 => false, 1 => true. - private byte memoizedIsInitialized = -1; - - // The default behavior. If a message has required fields in its subtree, - // the generated code will override. - public boolean isInitialized() { - if (memoizedIsInitialized == -1) { - memoizedIsInitialized = (byte) (extensions.isInitialized() ? 1 : 0); + protected final void mergeExtensionFields(final MessageType other) { + if (extensions.isImmutable()) { + extensions = extensions.clone(); } - - return memoizedIsInitialized == 1; + extensions.mergeFrom(((ExtendableMessage) other).extensions); } - + private void verifyExtensionContainingType( final GeneratedExtension extension) { if (extension.getContainingTypeDefaultInstance() != @@ -420,46 +490,38 @@ public abstract class GeneratedMessageLite< implements ExtendableMessageOrBuilder { protected ExtendableBuilder(MessageType defaultInstance) { super(defaultInstance); - } - - private FieldSet extensions = FieldSet.emptySet(); - private boolean extensionsIsMutable; - - // The default behavior. If a message has required fields in its subtree, - // the generated code will override. - public boolean isInitialized() { - return extensions.isInitialized(); + + // TODO(dweis): This is kind of an unnecessary clone since we construct a + // new instance in the parent constructor which makes the extensions + // immutable. This extra allocation shouldn't matter in practice + // though. + instance.extensions = instance.extensions.clone(); } // For immutable message conversion. void internalSetExtensionSet(FieldSet extensions) { - this.extensions = extensions; + copyOnWrite(); + instance.extensions = extensions; } - @Override - public BuilderType clear() { - extensions.clear(); - extensionsIsMutable = false; - return super.clear(); + // @Override (Java 1.6 override semantics, but we must support 1.5) + protected void copyOnWrite() { + if (!isBuilt) { + return; + } + + super.copyOnWrite(); + instance.extensions = instance.extensions.clone(); } - private void ensureExtensionsIsMutable() { - if (!extensionsIsMutable) { - extensions = extensions.clone(); - extensionsIsMutable = true; + // @Override (Java 1.6 override semantics, but we must support 1.5) + public final MessageType buildPartial() { + if (isBuilt) { + return instance; } - } - /** - * Called by the build code path to create a copy of the extensions for - * building the message. - *

- * For use by generated code only. - */ - protected final FieldSet buildExtensions() { - extensions.makeImmutable(); - extensionsIsMutable = false; - return extensions; + instance.extensions.makeImmutable(); + return super.buildPartial(); } private void verifyExtensionContainingType( @@ -477,22 +539,14 @@ public abstract class GeneratedMessageLite< //@Override (Java 1.6 override semantics, but we must support 1.5) public final boolean hasExtension( final ExtensionLite extension) { - GeneratedExtension extensionLite = - checkIsLite(extension); - - verifyExtensionContainingType(extensionLite); - return extensions.hasField(extensionLite.descriptor); + return instance.hasExtension(extension); } /** Get the number of elements in a repeated extension. */ //@Override (Java 1.6 override semantics, but we must support 1.5) public final int getExtensionCount( final ExtensionLite> extension) { - GeneratedExtension> extensionLite = - checkIsLite(extension); - - verifyExtensionContainingType(extensionLite); - return extensions.getRepeatedFieldCount(extensionLite.descriptor); + return instance.getExtensionCount(extension); } /** Get the value of an extension. */ @@ -500,16 +554,7 @@ public abstract class GeneratedMessageLite< @SuppressWarnings("unchecked") public final Type getExtension( final ExtensionLite extension) { - GeneratedExtension extensionLite = - checkIsLite(extension); - - verifyExtensionContainingType(extensionLite); - final Object value = extensions.getField(extensionLite.descriptor); - if (value == null) { - return extensionLite.defaultValue; - } else { - return (Type) extensionLite.fromFieldSetType(value); - } + return instance.getExtension(extension); } /** Get one element of a repeated extension. */ @@ -518,12 +563,7 @@ public abstract class GeneratedMessageLite< public final Type getExtension( final ExtensionLite> extension, final int index) { - GeneratedExtension> extensionLite = - checkIsLite(extension); - - verifyExtensionContainingType(extensionLite); - return (Type) extensionLite.singularFromFieldSetType( - extensions.getRepeatedField(extensionLite.descriptor, index)); + return instance.getExtension(extension, index); } // This is implemented here only to work around an apparent bug in the @@ -542,9 +582,8 @@ public abstract class GeneratedMessageLite< checkIsLite(extension); verifyExtensionContainingType(extensionLite); - ensureExtensionsIsMutable(); - extensions.setField(extensionLite.descriptor, - extensionLite.toFieldSetType(value)); + copyOnWrite(); + instance.extensions.setField(extensionLite.descriptor, extensionLite.toFieldSetType(value)); return (BuilderType) this; } @@ -556,9 +595,9 @@ public abstract class GeneratedMessageLite< checkIsLite(extension); verifyExtensionContainingType(extensionLite); - ensureExtensionsIsMutable(); - extensions.setRepeatedField(extensionLite.descriptor, index, - extensionLite.singularToFieldSetType(value)); + copyOnWrite(); + instance.extensions.setRepeatedField( + extensionLite.descriptor, index, extensionLite.singularToFieldSetType(value)); return (BuilderType) this; } @@ -570,9 +609,9 @@ public abstract class GeneratedMessageLite< checkIsLite(extension); verifyExtensionContainingType(extensionLite); - ensureExtensionsIsMutable(); - extensions.addRepeatedField(extensionLite.descriptor, - extensionLite.singularToFieldSetType(value)); + copyOnWrite(); + instance.extensions.addRepeatedField( + extensionLite.descriptor, extensionLite.singularToFieldSetType(value)); return (BuilderType) this; } @@ -582,20 +621,10 @@ public abstract class GeneratedMessageLite< GeneratedExtension extensionLite = checkIsLite(extension); verifyExtensionContainingType(extensionLite); - ensureExtensionsIsMutable(); - extensions.clearField(extensionLite.descriptor); + copyOnWrite(); + instance.extensions.clearField(extensionLite.descriptor); return (BuilderType) this; } - - /** Called by subclasses to check if all extensions are initialized. */ - protected boolean extensionsAreInitialized() { - return extensions.isInitialized(); - } - - protected final void mergeExtensionFields(final MessageType other) { - ensureExtensionsIsMutable(); - extensions.mergeFrom(((ExtendableMessage) other).extensions); - } } //----------------------------------------------------------------- @@ -1113,4 +1142,133 @@ public abstract class GeneratedMessageLite< return (BuilderType) defaultInstance.toBuilder(); } } + + /** + * A static helper method for checking if a message is initialized, optionally memoizing. + *

+ * For use by generated code only. + */ + protected static final > boolean isInitialized( + T message, boolean shouldMemoize) { + return message.dynamicMethod(MethodToInvoke.IS_INITIALIZED, shouldMemoize) != null; + } + + protected static final > void makeImmutable(T message) { + message.dynamicMethod(MethodToInvoke.MAKE_IMMUTABLE); + } + + /** + * A static helper method for parsing a partial from input using the extension registry and the + * instance. + */ + static > T parsePartialFrom( + T instance, CodedInputStream input, ExtensionRegistryLite extensionRegistry) + throws InvalidProtocolBufferException { + try { + return (T) instance.dynamicMethod( + MethodToInvoke.PARSE_PARTIAL_FROM, input, extensionRegistry); + } catch (RuntimeException e) { + if (e.getCause() instanceof InvalidProtocolBufferException) { + throw (InvalidProtocolBufferException) e.getCause(); + } + throw e; + } + } + + /** + * A {@link Parser} implementation that delegates to the default instance. + *

+ * For use by generated code only. + */ + protected static class DefaultInstanceBasedParser> + extends AbstractParser { + + private T defaultInstance; + + public DefaultInstanceBasedParser(T defaultInstance) { + this.defaultInstance = defaultInstance; + } + + @Override + public T parsePartialFrom(CodedInputStream input, ExtensionRegistryLite extensionRegistry) + throws InvalidProtocolBufferException { + return GeneratedMessageLite.parsePartialFrom(defaultInstance, input, extensionRegistry); + } + } + + protected static IntList newIntList() { + return new IntArrayList(); + } + + protected static IntList newIntList(List toCopy) { + return new IntArrayList(toCopy); + } + + protected static IntList emptyIntList() { + return IntArrayList.emptyList(); + } + + protected static LongList newLongList() { + return new LongArrayList(); + } + + protected static LongList newLongList(List toCopy) { + return new LongArrayList(toCopy); + } + + protected static LongList emptyLongList() { + return LongArrayList.emptyList(); + } + + protected static FloatList newFloatList() { + return new FloatArrayList(); + } + + protected static FloatList newFloatList(List toCopy) { + return new FloatArrayList(toCopy); + } + + protected static FloatList emptyFloatList() { + return FloatArrayList.emptyList(); + } + + protected static DoubleList newDoubleList() { + return new DoubleArrayList(); + } + + protected static DoubleList newDoubleList(List toCopy) { + return new DoubleArrayList(toCopy); + } + + protected static DoubleList emptyDoubleList() { + return DoubleArrayList.emptyList(); + } + + protected static BooleanList newBooleanList() { + return new BooleanArrayList(); + } + + protected static BooleanList newBooleanList(List toCopy) { + return new BooleanArrayList(toCopy); + } + + protected static BooleanList emptyBooleanList() { + return BooleanArrayList.emptyList(); + } + + protected static ProtobufList newProtobufList() { + return new ProtobufArrayList(); + } + + protected static ProtobufList newProtobufList(List toCopy) { + return new ProtobufArrayList(toCopy); + } + + protected static ProtobufList emptyProtobufList() { + return ProtobufArrayList.emptyList(); + } + + protected static LazyStringArrayList emptyLazyStringArrayList() { + return LazyStringArrayList.emptyList(); + } } diff --git a/java/src/main/java/com/google/protobuf/IntArrayList.java b/java/src/main/java/com/google/protobuf/IntArrayList.java new file mode 100644 index 00000000..f7609cc9 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/IntArrayList.java @@ -0,0 +1,242 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import com.google.protobuf.Internal.IntList; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.RandomAccess; + +/** + * An implementation of {@link IntList} on top of a primitive array. + * + * @author dweis@google.com (Daniel Weis) + */ +final class IntArrayList extends AbstractProtobufList implements IntList, RandomAccess { + + private static final int DEFAULT_CAPACITY = 10; + + private static final IntArrayList EMPTY_LIST = new IntArrayList(); + static { + EMPTY_LIST.makeImmutable(); + } + + public static IntArrayList emptyList() { + return EMPTY_LIST; + } + + /** + * The backing store for the list. + */ + private int[] array; + + /** + * The size of the list distinct from the length of the array. That is, it is the number of + * elements set in the list. + */ + private int size; + + /** + * Constructs a new mutable {@code IntArrayList}. + */ + IntArrayList() { + array = new int[DEFAULT_CAPACITY]; + size = 0; + } + + /** + * Constructs a new mutable {@code IntArrayList} containing the same elements as {@code other}. + */ + IntArrayList(List other) { + if (other instanceof IntArrayList) { + IntArrayList list = (IntArrayList) other; + array = list.array.clone(); + size = list.size; + } else { + size = other.size(); + array = new int[size]; + for (int i = 0; i < size; i++) { + array[i] = other.get(i); + } + } + } + + @Override + public Integer get(int index) { + return getInt(index); + } + + @Override + public int getInt(int index) { + ensureIndexInRange(index); + return array[index]; + } + + @Override + public int size() { + return size; + } + + @Override + public Integer set(int index, Integer element) { + return setInt(index, element); + } + + @Override + public int setInt(int index, int element) { + ensureIsMutable(); + ensureIndexInRange(index); + int previousValue = array[index]; + array[index] = element; + return previousValue; + } + + @Override + public void add(int index, Integer element) { + addInt(index, element); + } + + /** + * Like {@link #add(Integer)} but more efficient in that it doesn't box the element. + */ + @Override + public void addInt(int element) { + addInt(size, element); + } + + /** + * Like {@link #add(int, Integer)} but more efficient in that it doesn't box the element. + */ + private void addInt(int index, int element) { + ensureIsMutable(); + if (index < 0 || index > size) { + throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index)); + } + + if (size < array.length) { + // Shift everything over to make room + System.arraycopy(array, index, array, index + 1, size - index); + } else { + // Resize to 1.5x the size + int length = ((size * 3) / 2) + 1; + int[] newArray = new int[length]; + + // Copy the first part directly + System.arraycopy(array, 0, newArray, 0, index); + + // Copy the rest shifted over by one to make room + System.arraycopy(array, index, newArray, index + 1, size - index); + array = newArray; + } + + array[index] = element; + size++; + modCount++; + } + + @Override + public boolean addAll(Collection collection) { + ensureIsMutable(); + + if (collection == null) { + throw new NullPointerException(); + } + + // We specialize when adding another IntArrayList to avoid boxing elements. + if (!(collection instanceof IntArrayList)) { + return super.addAll(collection); + } + + IntArrayList list = (IntArrayList) collection; + if (list.size == 0) { + return false; + } + + int overflow = Integer.MAX_VALUE - size; + if (overflow < list.size) { + // We can't actually represent a list this large. + throw new OutOfMemoryError(); + } + + int newSize = size + list.size; + if (newSize > array.length) { + array = Arrays.copyOf(array, newSize); + } + + System.arraycopy(list.array, 0, array, size, list.size); + size = newSize; + modCount++; + return true; + } + + @Override + public boolean remove(Object o) { + ensureIsMutable(); + for (int i = 0; i < size; i++) { + if (o.equals(array[i])) { + System.arraycopy(array, i + 1, array, i, size - i); + size--; + modCount++; + return true; + } + } + return false; + } + + @Override + public Integer remove(int index) { + ensureIsMutable(); + ensureIndexInRange(index); + int value = array[index]; + System.arraycopy(array, index + 1, array, index, size - index); + size--; + modCount++; + return value; + } + + /** + * Ensures that the provided {@code index} is within the range of {@code [0, size]}. Throws an + * {@link IndexOutOfBoundsException} if it is not. + * + * @param index the index to verify is in range + */ + private void ensureIndexInRange(int index) { + if (index < 0 || index >= size) { + throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index)); + } + } + + private String makeOutOfBoundsExceptionMessage(int index) { + return "Index:" + index + ", Size:" + size; + } +} diff --git a/java/src/main/java/com/google/protobuf/Internal.java b/java/src/main/java/com/google/protobuf/Internal.java index 74bf44c0..20054b79 100644 --- a/java/src/main/java/com/google/protobuf/Internal.java +++ b/java/src/main/java/com/google/protobuf/Internal.java @@ -30,6 +30,7 @@ package com.google.protobuf; +import java.io.IOException; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.util.AbstractList; @@ -532,4 +533,132 @@ public class Internal { } } } + + /** + * Extends {@link List} to add the capability to make the list immutable and inspect if it is + * modifiable. + */ + public static interface ProtobufList extends List { + + /** + * Makes this list immutable. All subsequent modifications will throw an + * {@link UnsupportedOperationException}. + */ + void makeImmutable(); + + /** + * Returns whether this list can be modified via the publicly accessible {@link List} methods. + */ + boolean isModifiable(); + } + + /** + * A {@link java.util.List} implementation that avoids boxing the elements into Integers if + * possible. Does not support null elements. + */ + public static interface IntList extends ProtobufList { + + /** + * Like {@link #get(int)} but more efficient in that it doesn't box the returned value. + */ + int getInt(int index); + + /** + * Like {@link #add(Integer)} but more efficient in that it doesn't box the element. + */ + void addInt(int element); + + /** + * Like {@link #set(int, Integer)} but more efficient in that it doesn't box the element. + */ + int setInt(int index, int element); + } + + /** + * A {@link java.util.List} implementation that avoids boxing the elements into Booleans if + * possible. Does not support null elements. + */ + public static interface BooleanList extends ProtobufList { + + /** + * Like {@link #get(int)} but more efficient in that it doesn't box the returned value. + */ + boolean getBoolean(int index); + + /** + * Like {@link #add(Boolean)} but more efficient in that it doesn't box the element. + */ + void addBoolean(boolean element); + + /** + * Like {@link #set(int, Boolean)} but more efficient in that it doesn't box the element. + */ + boolean setBoolean(int index, boolean element); + } + + /** + * A {@link java.util.List} implementation that avoids boxing the elements into Longs if + * possible. Does not support null elements. + */ + public static interface LongList extends ProtobufList { + + /** + * Like {@link #get(int)} but more efficient in that it doesn't box the returned value. + */ + long getLong(int index); + + /** + * Like {@link #add(Long)} but more efficient in that it doesn't box the element. + */ + void addLong(long element); + + /** + * Like {@link #set(int, Long)} but more efficient in that it doesn't box the element. + */ + long setLong(int index, long element); + } + + /** + * A {@link java.util.List} implementation that avoids boxing the elements into Doubles if + * possible. Does not support null elements. + */ + public static interface DoubleList extends ProtobufList { + + /** + * Like {@link #get(int)} but more efficient in that it doesn't box the returned value. + */ + double getDouble(int index); + + /** + * Like {@link #add(Double)} but more efficient in that it doesn't box the element. + */ + void addDouble(double element); + + /** + * Like {@link #set(int, Double)} but more efficient in that it doesn't box the element. + */ + double setDouble(int index, double element); + } + + /** + * A {@link java.util.List} implementation that avoids boxing the elements into Floats if + * possible. Does not support null elements. + */ + public static interface FloatList extends ProtobufList { + + /** + * Like {@link #get(int)} but more efficient in that it doesn't box the returned value. + */ + float getFloat(int index); + + /** + * Like {@link #add(Float)} but more efficient in that it doesn't box the element. + */ + void addFloat(float element); + + /** + * Like {@link #set(int, Float)} but more efficient in that it doesn't box the element. + */ + float setFloat(int index, float element); + } } diff --git a/java/src/main/java/com/google/protobuf/LazyStringArrayList.java b/java/src/main/java/com/google/protobuf/LazyStringArrayList.java index 2d40a51f..a2997e1c 100644 --- a/java/src/main/java/com/google/protobuf/LazyStringArrayList.java +++ b/java/src/main/java/com/google/protobuf/LazyStringArrayList.java @@ -62,11 +62,20 @@ import java.util.RandomAccess; * * @author jonp@google.com (Jon Perlow) */ -public class LazyStringArrayList extends AbstractList +public class LazyStringArrayList extends AbstractProtobufList implements LazyStringList, RandomAccess { + + private static final LazyStringArrayList EMPTY_LIST = new LazyStringArrayList(); + static { + EMPTY_LIST.makeImmutable(); + } + + static LazyStringArrayList emptyList() { + return EMPTY_LIST; + } - public static final LazyStringList EMPTY = - new LazyStringArrayList().getUnmodifiableView(); + // For compatibility with older runtimes. + public static final LazyStringList EMPTY = EMPTY_LIST; private final List list; @@ -116,12 +125,26 @@ public class LazyStringArrayList extends AbstractList @Override public String set(int index, String s) { + ensureIsMutable(); Object o = list.set(index, s); return asString(o); } @Override public void add(int index, String element) { + ensureIsMutable(); + list.add(index, element); + modCount++; + } + + private void add(int index, ByteString element) { + ensureIsMutable(); + list.add(index, element); + modCount++; + } + + private void add(int index, byte[] element) { + ensureIsMutable(); list.add(index, element); modCount++; } @@ -137,6 +160,7 @@ public class LazyStringArrayList extends AbstractList @Override public boolean addAll(int index, Collection c) { + ensureIsMutable(); // When copying from another LazyStringList, directly copy the underlying // elements rather than forcing each element to be decoded to a String. Collection collection = c instanceof LazyStringList @@ -148,6 +172,7 @@ public class LazyStringArrayList extends AbstractList // @Override public boolean addAllByteString(Collection values) { + ensureIsMutable(); boolean ret = list.addAll(values); modCount++; return ret; @@ -155,6 +180,7 @@ public class LazyStringArrayList extends AbstractList // @Override public boolean addAllByteArray(Collection c) { + ensureIsMutable(); boolean ret = list.addAll(c); modCount++; return ret; @@ -162,6 +188,7 @@ public class LazyStringArrayList extends AbstractList @Override public String remove(int index) { + ensureIsMutable(); Object o = list.remove(index); modCount++; return asString(o); @@ -169,18 +196,21 @@ public class LazyStringArrayList extends AbstractList @Override public void clear() { + ensureIsMutable(); list.clear(); modCount++; } // @Override public void add(ByteString element) { + ensureIsMutable(); list.add(element); modCount++; } // @Override public void add(byte[] element) { + ensureIsMutable(); list.add(element); modCount++; } @@ -207,14 +237,23 @@ public class LazyStringArrayList extends AbstractList // @Override public void set(int index, ByteString s) { - list.set(index, s); + setAndReturn(index, s); + } + + private Object setAndReturn(int index, ByteString s) { + ensureIsMutable(); + return list.set(index, s); } // @Override public void set(int index, byte[] s) { - list.set(index, s); + setAndReturn(index, s); + } + + private Object setAndReturn(int index, byte[] s) { + ensureIsMutable(); + return list.set(index, s); } - private static String asString(Object o) { if (o instanceof String) { @@ -253,6 +292,7 @@ public class LazyStringArrayList extends AbstractList // @Override public void mergeFrom(LazyStringList other) { + ensureIsMutable(); for (Object o : other.getUnderlyingElements()) { if (o instanceof byte[]) { byte[] b = (byte[]) o; @@ -267,20 +307,15 @@ public class LazyStringArrayList extends AbstractList private static class ByteArrayListView extends AbstractList implements RandomAccess { - private final List list; + private final LazyStringArrayList list; - ByteArrayListView(List list) { + ByteArrayListView(LazyStringArrayList list) { this.list = list; } @Override public byte[] get(int index) { - Object o = list.get(index); - byte[] b = asByteArray(o); - if (b != o) { - list.set(index, b); - } - return b; + return list.getByteArray(index); } @Override @@ -290,7 +325,7 @@ public class LazyStringArrayList extends AbstractList @Override public byte[] set(int index, byte[] s) { - Object o = list.set(index, s); + Object o = list.setAndReturn(index, s); modCount++; return asByteArray(o); } @@ -311,25 +346,20 @@ public class LazyStringArrayList extends AbstractList // @Override public List asByteArrayList() { - return new ByteArrayListView(list); + return new ByteArrayListView(this); } private static class ByteStringListView extends AbstractList implements RandomAccess { - private final List list; + private final LazyStringArrayList list; - ByteStringListView(List list) { + ByteStringListView(LazyStringArrayList list) { this.list = list; } @Override public ByteString get(int index) { - Object o = list.get(index); - ByteString b = asByteString(o); - if (b != o) { - list.set(index, b); - } - return b; + return list.getByteString(index); } @Override @@ -339,7 +369,7 @@ public class LazyStringArrayList extends AbstractList @Override public ByteString set(int index, ByteString s) { - Object o = list.set(index, s); + Object o = list.setAndReturn(index, s); modCount++; return asByteString(o); } @@ -360,12 +390,15 @@ public class LazyStringArrayList extends AbstractList // @Override public List asByteStringList() { - return new ByteStringListView(list); + return new ByteStringListView(this); } // @Override public LazyStringList getUnmodifiableView() { - return new UnmodifiableLazyStringList(this); + if (isModifiable()) { + return new UnmodifiableLazyStringList(this); + } + return this; } } diff --git a/java/src/main/java/com/google/protobuf/LongArrayList.java b/java/src/main/java/com/google/protobuf/LongArrayList.java new file mode 100644 index 00000000..298617ff --- /dev/null +++ b/java/src/main/java/com/google/protobuf/LongArrayList.java @@ -0,0 +1,242 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import com.google.protobuf.Internal.LongList; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.RandomAccess; + +/** + * An implementation of {@link LongList} on top of a primitive array. + * + * @author dweis@google.com (Daniel Weis) + */ +final class LongArrayList extends AbstractProtobufList implements LongList, RandomAccess { + + private static final int DEFAULT_CAPACITY = 10; + + private static final LongArrayList EMPTY_LIST = new LongArrayList(); + static { + EMPTY_LIST.makeImmutable(); + } + + public static LongArrayList emptyList() { + return EMPTY_LIST; + } + + /** + * The backing store for the list. + */ + private long[] array; + + /** + * The size of the list distinct from the length of the array. That is, it is the number of + * elements set in the list. + */ + private int size; + + /** + * Constructs a new mutable {@code LongArrayList}. + */ + LongArrayList() { + array = new long[DEFAULT_CAPACITY]; + size = 0; + } + + /** + * Constructs a new mutable {@code LongArrayList} containing the same elements as {@code other}. + */ + LongArrayList(List other) { + if (other instanceof LongArrayList) { + LongArrayList list = (LongArrayList) other; + array = list.array.clone(); + size = list.size; + } else { + size = other.size(); + array = new long[size]; + for (int i = 0; i < size; i++) { + array[i] = other.get(i); + } + } + } + + @Override + public Long get(int index) { + return getLong(index); + } + + @Override + public long getLong(int index) { + ensureIndexInRange(index); + return array[index]; + } + + @Override + public int size() { + return size; + } + + @Override + public Long set(int index, Long element) { + return setLong(index, element); + } + + @Override + public long setLong(int index, long element) { + ensureIsMutable(); + ensureIndexInRange(index); + long previousValue = array[index]; + array[index] = element; + return previousValue; + } + + @Override + public void add(int index, Long element) { + addLong(index, element); + } + + /** + * Like {@link #add(Long)} but more efficient in that it doesn't box the element. + */ + @Override + public void addLong(long element) { + addLong(size, element); + } + + /** + * Like {@link #add(int, Long)} but more efficient in that it doesn't box the element. + */ + private void addLong(int index, long element) { + ensureIsMutable(); + if (index < 0 || index > size) { + throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index)); + } + + if (size < array.length) { + // Shift everything over to make room + System.arraycopy(array, index, array, index + 1, size - index); + } else { + // Resize to 1.5x the size + int length = ((size * 3) / 2) + 1; + long[] newArray = new long[length]; + + // Copy the first part directly + System.arraycopy(array, 0, newArray, 0, index); + + // Copy the rest shifted over by one to make room + System.arraycopy(array, index, newArray, index + 1, size - index); + array = newArray; + } + + array[index] = element; + size++; + modCount++; + } + + @Override + public boolean addAll(Collection collection) { + ensureIsMutable(); + + if (collection == null) { + throw new NullPointerException(); + } + + // We specialize when adding another LongArrayList to avoid boxing elements. + if (!(collection instanceof LongArrayList)) { + return super.addAll(collection); + } + + LongArrayList list = (LongArrayList) collection; + if (list.size == 0) { + return false; + } + + int overflow = Integer.MAX_VALUE - size; + if (overflow < list.size) { + // We can't actually represent a list this large. + throw new OutOfMemoryError(); + } + + int newSize = size + list.size; + if (newSize > array.length) { + array = Arrays.copyOf(array, newSize); + } + + System.arraycopy(list.array, 0, array, size, list.size); + size = newSize; + modCount++; + return true; + } + + @Override + public boolean remove(Object o) { + ensureIsMutable(); + for (int i = 0; i < size; i++) { + if (o.equals(array[i])) { + System.arraycopy(array, i + 1, array, i, size - i); + size--; + modCount++; + return true; + } + } + return false; + } + + @Override + public Long remove(int index) { + ensureIsMutable(); + ensureIndexInRange(index); + long value = array[index]; + System.arraycopy(array, index + 1, array, index, size - index); + size--; + modCount++; + return value; + } + + /** + * Ensures that the provided {@code index} is within the range of {@code [0, size]}. Throws an + * {@link IndexOutOfBoundsException} if it is not. + * + * @param index the index to verify is in range + */ + private void ensureIndexInRange(int index) { + if (index < 0 || index >= size) { + throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index)); + } + } + + private String makeOutOfBoundsExceptionMessage(int index) { + return "Index:" + index + ", Size:" + size; + } +} diff --git a/java/src/main/java/com/google/protobuf/MapField.java b/java/src/main/java/com/google/protobuf/MapField.java index 82906d37..b290993c 100644 --- a/java/src/main/java/com/google/protobuf/MapField.java +++ b/java/src/main/java/com/google/protobuf/MapField.java @@ -30,9 +30,11 @@ package com.google.protobuf; +import com.google.protobuf.MapFieldLite.MutatabilityAwareMap; + import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -51,7 +53,7 @@ import java.util.Map; * and getList() concurrently in multiple threads. If write-access is needed, * all access must be synchronized. */ -public class MapField { +public class MapField implements MutabilityOracle { /** * Indicates where the data of this map field is currently stored. * @@ -72,8 +74,9 @@ public class MapField { */ private enum StorageMode {MAP, LIST, BOTH} + private volatile boolean isMutable; private volatile StorageMode mode; - private Map mapData; + private MutatabilityAwareMap mapData; private List listData; // Convert between a map entry Message and a key-value pair. @@ -110,20 +113,19 @@ public class MapField { private MapField( Converter converter, StorageMode mode, - Map mapData, - List listData) { + Map mapData) { this.converter = converter; + this.isMutable = true; this.mode = mode; - this.mapData = mapData; - this.listData = listData; + this.mapData = new MutatabilityAwareMap(this, mapData); + this.listData = null; } private MapField( MapEntry defaultEntry, StorageMode mode, - Map mapData, - List listData) { - this(new ImmutableMessageConverter(defaultEntry), mode, mapData, listData); + Map mapData) { + this(new ImmutableMessageConverter(defaultEntry), mode, mapData); } @@ -131,14 +133,14 @@ public class MapField { public static MapField emptyMapField( MapEntry defaultEntry) { return new MapField( - defaultEntry, StorageMode.MAP, Collections.emptyMap(), null); + defaultEntry, StorageMode.MAP, Collections.emptyMap()); } /** Creates a new mutable empty MapField. */ public static MapField newMapField(MapEntry defaultEntry) { return new MapField( - defaultEntry, StorageMode.MAP, new HashMap(), null); + defaultEntry, StorageMode.MAP, new LinkedHashMap()); } @@ -151,7 +153,7 @@ public class MapField { converter.convertMessageToKeyAndValue(message, map); } - private List convertMapToList(Map mapData) { + private List convertMapToList(MutatabilityAwareMap mapData) { List listData = new ArrayList(); for (Map.Entry entry : mapData.entrySet()) { listData.add( @@ -161,12 +163,12 @@ public class MapField { return listData; } - private Map convertListToMap(List listData) { - Map mapData = new HashMap(); + private MutatabilityAwareMap convertListToMap(List listData) { + Map mapData = new LinkedHashMap(); for (Message item : listData) { convertMessageToKeyAndValue(item, mapData); } - return mapData; + return new MutatabilityAwareMap(this, mapData); } /** Returns the content of this MapField as a read-only Map. */ @@ -199,7 +201,7 @@ public class MapField { } public void clear() { - mapData = new HashMap(); + mapData = new MutatabilityAwareMap(this, new LinkedHashMap()); mode = StorageMode.MAP; } @@ -221,7 +223,7 @@ public class MapField { /** Returns a deep copy of this MapField. */ public MapField copy() { return new MapField( - converter, StorageMode.MAP, MapFieldLite.copy(getMap()), null); + converter, StorageMode.MAP, MapFieldLite.copy(getMap())); } /** Gets the content of this MapField as a read-only List. */ @@ -256,4 +258,29 @@ public class MapField { Message getMapEntryMessageDefaultInstance() { return converter.getMessageDefaultInstance(); } + + /** + * Makes this list immutable. All subsequent modifications will throw an + * {@link UnsupportedOperationException}. + */ + public void makeImmutable() { + isMutable = false; + } + + /** + * Returns whether this field can be modified. + */ + public boolean isMutable() { + return isMutable; + } + + /* (non-Javadoc) + * @see com.google.protobuf.MutabilityOracle#ensureMutable() + */ + @Override + public void ensureMutable() { + if (!isMutable()) { + throw new UnsupportedOperationException(); + } + } } diff --git a/java/src/main/java/com/google/protobuf/MapFieldLite.java b/java/src/main/java/com/google/protobuf/MapFieldLite.java index 7f94c690..c17fa7b1 100644 --- a/java/src/main/java/com/google/protobuf/MapFieldLite.java +++ b/java/src/main/java/com/google/protobuf/MapFieldLite.java @@ -31,9 +31,12 @@ package com.google.protobuf; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; -import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.Map; +import java.util.Set; /** * Internal representation of map fields in generated lite-runtime messages. @@ -41,16 +44,21 @@ import java.util.Map; * This class is a protobuf implementation detail. Users shouldn't use this * class directly. */ -public class MapFieldLite { - private Map mapData; +public class MapFieldLite implements MutabilityOracle { + private MutatabilityAwareMap mapData; + private boolean isMutable; private MapFieldLite(Map mapData) { - this.mapData = mapData; + this.mapData = new MutatabilityAwareMap(this, mapData); + this.isMutable = true; } @SuppressWarnings({"rawtypes", "unchecked"}) private static final MapFieldLite EMPTY_MAP_FIELD = new MapFieldLite(Collections.emptyMap()); + static { + EMPTY_MAP_FIELD.makeImmutable(); + } /** Returns an singleton immutable empty MapFieldLite instance. */ @SuppressWarnings({"unchecked", "cast"}) @@ -60,7 +68,7 @@ public class MapFieldLite { /** Creates a new MapFieldLite instance. */ public static MapFieldLite newMapField() { - return new MapFieldLite(new HashMap()); + return new MapFieldLite(new LinkedHashMap()); } /** Gets the content of this MapField as a read-only Map. */ @@ -168,7 +176,7 @@ public class MapFieldLite { */ @SuppressWarnings("unchecked") static Map copy(Map map) { - Map result = new HashMap(); + Map result = new LinkedHashMap(); for (Map.Entry entry : map.entrySet()) { result.put(entry.getKey(), (V) copy(entry.getValue())); } @@ -179,4 +187,360 @@ public class MapFieldLite { public MapFieldLite copy() { return new MapFieldLite(copy(mapData)); } + + /** + * Makes this field immutable. All subsequent modifications will throw an + * {@link UnsupportedOperationException}. + */ + public void makeImmutable() { + isMutable = false; + } + + /** + * Returns whether this field can be modified. + */ + public boolean isMutable() { + return isMutable; + } + + @Override + public void ensureMutable() { + if (!isMutable()) { + throw new UnsupportedOperationException(); + } + } + + /** + * An internal map that checks for mutability before delegating. + */ + static class MutatabilityAwareMap implements Map { + private final MutabilityOracle mutabilityOracle; + private final Map delegate; + + MutatabilityAwareMap(MutabilityOracle mutabilityOracle, Map delegate) { + this.mutabilityOracle = mutabilityOracle; + this.delegate = delegate; + } + + @Override + public int size() { + return delegate.size(); + } + + @Override + public boolean isEmpty() { + return delegate.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return delegate.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return delegate.containsValue(value); + } + + @Override + public V get(Object key) { + return delegate.get(key); + } + + @Override + public V put(K key, V value) { + mutabilityOracle.ensureMutable(); + return delegate.put(key, value); + } + + @Override + public V remove(Object key) { + mutabilityOracle.ensureMutable(); + return delegate.remove(key); + } + + @Override + public void putAll(Map m) { + mutabilityOracle.ensureMutable(); + delegate.putAll(m); + } + + @Override + public void clear() { + mutabilityOracle.ensureMutable(); + delegate.clear(); + } + + @Override + public Set keySet() { + return new MutatabilityAwareSet(mutabilityOracle, delegate.keySet()); + } + + @Override + public Collection values() { + return new MutatabilityAwareCollection(mutabilityOracle, delegate.values()); + } + + @Override + public Set> entrySet() { + return new MutatabilityAwareSet>(mutabilityOracle, delegate.entrySet()); + } + + @Override + public boolean equals(Object o) { + return delegate.equals(o); + } + + @Override + public int hashCode() { + return delegate.hashCode(); + } + + @Override + public String toString() { + return delegate.toString(); + } + } + + /** + * An internal collection that checks for mutability before delegating. + */ + private static class MutatabilityAwareCollection implements Collection { + private final MutabilityOracle mutabilityOracle; + private final Collection delegate; + + MutatabilityAwareCollection(MutabilityOracle mutabilityOracle, Collection delegate) { + this.mutabilityOracle = mutabilityOracle; + this.delegate = delegate; + } + + @Override + public int size() { + return delegate.size(); + } + + @Override + public boolean isEmpty() { + return delegate.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return delegate.contains(o); + } + + @Override + public Iterator iterator() { + return new MutatabilityAwareIterator(mutabilityOracle, delegate.iterator()); + } + + @Override + public Object[] toArray() { + return delegate.toArray(); + } + + @Override + public T[] toArray(T[] a) { + return delegate.toArray(a); + } + + @Override + public boolean add(E e) { + // Unsupported operation in the delegate. + throw new UnsupportedOperationException(); + } + + @Override + public boolean remove(Object o) { + mutabilityOracle.ensureMutable(); + return delegate.remove(o); + } + + @Override + public boolean containsAll(Collection c) { + return delegate.containsAll(c); + } + + @Override + public boolean addAll(Collection c) { + // Unsupported operation in the delegate. + throw new UnsupportedOperationException(); + } + + @Override + public boolean removeAll(Collection c) { + mutabilityOracle.ensureMutable(); + return delegate.removeAll(c); + } + + @Override + public boolean retainAll(Collection c) { + mutabilityOracle.ensureMutable(); + return delegate.retainAll(c); + } + + @Override + public void clear() { + mutabilityOracle.ensureMutable(); + delegate.clear(); + } + + @Override + public boolean equals(Object o) { + return delegate.equals(o); + } + + @Override + public int hashCode() { + return delegate.hashCode(); + } + + @Override + public String toString() { + return delegate.toString(); + } + } + + /** + * An internal set that checks for mutability before delegating. + */ + private static class MutatabilityAwareSet implements Set { + private final MutabilityOracle mutabilityOracle; + private final Set delegate; + + MutatabilityAwareSet(MutabilityOracle mutabilityOracle, Set delegate) { + this.mutabilityOracle = mutabilityOracle; + this.delegate = delegate; + } + + @Override + public int size() { + return delegate.size(); + } + + @Override + public boolean isEmpty() { + return delegate.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return delegate.contains(o); + } + + @Override + public Iterator iterator() { + return new MutatabilityAwareIterator(mutabilityOracle, delegate.iterator()); + } + + @Override + public Object[] toArray() { + return delegate.toArray(); + } + + @Override + public T[] toArray(T[] a) { + return delegate.toArray(a); + } + + @Override + public boolean add(E e) { + mutabilityOracle.ensureMutable(); + return delegate.add(e); + } + + @Override + public boolean remove(Object o) { + mutabilityOracle.ensureMutable(); + return delegate.remove(o); + } + + @Override + public boolean containsAll(Collection c) { + return delegate.containsAll(c); + } + + @Override + public boolean addAll(Collection c) { + mutabilityOracle.ensureMutable(); + return delegate.addAll(c); + } + + @Override + public boolean retainAll(Collection c) { + mutabilityOracle.ensureMutable(); + return delegate.retainAll(c); + } + + @Override + public boolean removeAll(Collection c) { + mutabilityOracle.ensureMutable(); + return delegate.removeAll(c); + } + + @Override + public void clear() { + mutabilityOracle.ensureMutable(); + delegate.clear(); + } + + @Override + public boolean equals(Object o) { + return delegate.equals(o); + } + + @Override + public int hashCode() { + return delegate.hashCode(); + } + + @Override + public String toString() { + return delegate.toString(); + } + } + + /** + * An internal iterator that checks for mutability before delegating. + */ + private static class MutatabilityAwareIterator implements Iterator { + private final MutabilityOracle mutabilityOracle; + private final Iterator delegate; + + MutatabilityAwareIterator(MutabilityOracle mutabilityOracle, Iterator delegate) { + this.mutabilityOracle = mutabilityOracle; + this.delegate = delegate; + } + + @Override + public boolean hasNext() { + return delegate.hasNext(); + } + + @Override + public E next() { + return delegate.next(); + } + + @Override + public void remove() { + mutabilityOracle.ensureMutable(); + delegate.remove(); + } + + @Override + public boolean equals(Object obj) { + return delegate.equals(obj); + } + + @Override + public int hashCode() { + return delegate.hashCode(); + } + + @Override + public String toString() { + return delegate.toString(); + } + } } diff --git a/java/src/main/java/com/google/protobuf/MutabilityOracle.java b/java/src/main/java/com/google/protobuf/MutabilityOracle.java new file mode 100644 index 00000000..82b723c9 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/MutabilityOracle.java @@ -0,0 +1,48 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +/** + * Verifies that an object is mutable, throwing if not. + */ +interface MutabilityOracle { + static final MutabilityOracle IMMUTABLE = new MutabilityOracle() { + @Override + public void ensureMutable() { + throw new UnsupportedOperationException(); + } + }; + + /** + * Throws an {@link UnsupportedOperationException} if not mutable. + */ + void ensureMutable(); +} diff --git a/java/src/main/java/com/google/protobuf/ProtobufArrayList.java b/java/src/main/java/com/google/protobuf/ProtobufArrayList.java new file mode 100644 index 00000000..759368c9 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/ProtobufArrayList.java @@ -0,0 +1,95 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import com.google.protobuf.Internal.ProtobufList; + +import java.util.ArrayList; +import java.util.List; + +/** + * Implements {@link ProtobufList} for non-primitive and {@link String} types. + */ +class ProtobufArrayList extends AbstractProtobufList { + + private static final ProtobufArrayList EMPTY_LIST = new ProtobufArrayList(); + static { + EMPTY_LIST.makeImmutable(); + } + + @SuppressWarnings("unchecked") // Guaranteed safe by runtime. + public static ProtobufArrayList emptyList() { + return (ProtobufArrayList) EMPTY_LIST; + } + + private final List list; + + ProtobufArrayList() { + list = new ArrayList(); + } + + ProtobufArrayList(List toCopy) { + list = new ArrayList(toCopy); + } + + @Override + public void add(int index, E element) { + ensureIsMutable(); + list.add(index, element); + modCount++; + } + + @Override + public E get(int index) { + return list.get(index); + } + + @Override + public E remove(int index) { + ensureIsMutable(); + E toReturn = list.remove(index); + modCount++; + return toReturn; + } + + @Override + public E set(int index, E element) { + ensureIsMutable(); + E toReturn = list.set(index, element); + modCount++; + return toReturn; + } + + @Override + public int size() { + return list.size(); + } +} diff --git a/java/src/main/java/com/google/protobuf/TextFormat.java b/java/src/main/java/com/google/protobuf/TextFormat.java index dd2b4600..a79ce559 100644 --- a/java/src/main/java/com/google/protobuf/TextFormat.java +++ b/java/src/main/java/com/google/protobuf/TextFormat.java @@ -1725,7 +1725,7 @@ public final class TextFormat { * {@link #escapeBytes(ByteString)}. Two-digit hex escapes (starting with * "\x") are also recognized. */ - static ByteString unescapeBytes(final CharSequence charString) + public static ByteString unescapeBytes(final CharSequence charString) throws InvalidEscapeSequenceException { // First convert the Java character sequence to UTF-8 bytes. ByteString input = ByteString.copyFromUtf8(charString.toString()); @@ -1808,7 +1808,7 @@ public final class TextFormat { * Thrown by {@link TextFormat#unescapeBytes} and * {@link TextFormat#unescapeText} when an invalid escape sequence is seen. */ - static class InvalidEscapeSequenceException extends IOException { + public static class InvalidEscapeSequenceException extends IOException { private static final long serialVersionUID = -8164033650142593304L; InvalidEscapeSequenceException(final String description) { diff --git a/java/src/test/java/com/google/protobuf/BooleanArrayListTest.java b/java/src/test/java/com/google/protobuf/BooleanArrayListTest.java new file mode 100644 index 00000000..df89c263 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/BooleanArrayListTest.java @@ -0,0 +1,473 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import static java.util.Arrays.asList; + +import junit.framework.TestCase; + +import java.util.Collections; +import java.util.ConcurrentModificationException; +import java.util.Iterator; + +/** + * Tests for {@link BooleanArrayList}. + * + * @author dweis@google.com (Daniel Weis) + */ +public class BooleanArrayListTest extends TestCase { + + private static final BooleanArrayList UNARY_LIST = newImmutableBooleanArrayList(true); + private static final BooleanArrayList TERTIARY_LIST = + newImmutableBooleanArrayList(true, true, false); + + private BooleanArrayList list; + + @Override + protected void setUp() throws Exception { + list = new BooleanArrayList(); + } + + public void testEmptyListReturnsSameInstance() { + assertSame(BooleanArrayList.emptyList(), BooleanArrayList.emptyList()); + } + + public void testEmptyListIsImmutable() { + assertImmutable(BooleanArrayList.emptyList()); + } + + public void testMakeImmutable() { + list.addBoolean(true); + list.addBoolean(false); + list.addBoolean(true); + list.addBoolean(true); + list.makeImmutable(); + assertImmutable(list); + } + + public void testCopyConstructor() { + BooleanArrayList copy = new BooleanArrayList(TERTIARY_LIST); + assertEquals(TERTIARY_LIST, copy); + + copy = new BooleanArrayList(BooleanArrayList.emptyList()); + assertEquals(BooleanArrayList.emptyList(), copy); + + copy = new BooleanArrayList(asList(false, false, true)); + assertEquals(asList(false, false, true), copy); + + copy = new BooleanArrayList(Collections.emptyList()); + assertEquals(BooleanArrayList.emptyList(), copy); + } + + public void testModificationWithIteration() { + list.addAll(asList(true, false, false, true)); + Iterator iterator = list.iterator(); + assertEquals(4, list.size()); + assertEquals(true, (boolean) list.get(0)); + assertEquals(true, (boolean) iterator.next()); + list.set(0, true); + assertEquals(false, (boolean) iterator.next()); + + list.remove(0); + try { + iterator.next(); + fail(); + } catch (ConcurrentModificationException e) { + // expected + } + + iterator = list.iterator(); + list.add(0, false); + try { + iterator.next(); + fail(); + } catch (ConcurrentModificationException e) { + // expected + } + } + + public void testGet() { + assertEquals(true, (boolean) TERTIARY_LIST.get(0)); + assertEquals(true, (boolean) TERTIARY_LIST.get(1)); + assertEquals(false, (boolean) TERTIARY_LIST.get(2)); + + try { + TERTIARY_LIST.get(-1); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + TERTIARY_LIST.get(3); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testGetInt() { + assertEquals(true, TERTIARY_LIST.getBoolean(0)); + assertEquals(true, TERTIARY_LIST.getBoolean(1)); + assertEquals(false, TERTIARY_LIST.getBoolean(2)); + + try { + TERTIARY_LIST.get(-1); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + TERTIARY_LIST.get(3); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testSize() { + assertEquals(0, BooleanArrayList.emptyList().size()); + assertEquals(1, UNARY_LIST.size()); + assertEquals(3, TERTIARY_LIST.size()); + + list.addBoolean(true); + list.addBoolean(false); + list.addBoolean(false); + list.addBoolean(false); + assertEquals(4, list.size()); + + list.remove(0); + assertEquals(3, list.size()); + + list.add(true); + assertEquals(4, list.size()); + } + + public void testSet() { + list.addBoolean(false); + list.addBoolean(false); + + assertEquals(false, (boolean) list.set(0, true)); + assertEquals(true, list.getBoolean(0)); + + assertEquals(false, (boolean) list.set(1, false)); + assertEquals(false, list.getBoolean(1)); + + try { + list.set(-1, true); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + list.set(2, false); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testSetInt() { + list.addBoolean(true); + list.addBoolean(true); + + assertEquals(true, list.setBoolean(0, false)); + assertEquals(false, list.getBoolean(0)); + + assertEquals(true, list.setBoolean(1, false)); + assertEquals(false, list.getBoolean(1)); + + try { + list.setBoolean(-1, false); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + list.setBoolean(2, true); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testAdd() { + assertEquals(0, list.size()); + + assertTrue(list.add(true)); + assertEquals(asList(true), list); + + assertTrue(list.add(false)); + list.add(0, false); + assertEquals(asList(false, true, false), list); + + list.add(0, false); + list.add(0, true); + // Force a resize by getting up to 11 elements. + for (int i = 0; i < 6; i++) { + list.add(true); + } + assertEquals(asList(true, false, false, true, false, true, true, true, true, true, true), list); + + try { + list.add(-1, false); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + list.add(4, true); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testAddInt() { + assertEquals(0, list.size()); + + list.addBoolean(true); + assertEquals(asList(true), list); + + list.addBoolean(false); + assertEquals(asList(true, false), list); + } + + public void testAddAll() { + assertEquals(0, list.size()); + + assertTrue(list.addAll(Collections.singleton(false))); + assertEquals(1, list.size()); + assertEquals(false, (boolean) list.get(0)); + assertEquals(false, list.getBoolean(0)); + + assertTrue(list.addAll(asList(true, false, false, false, true))); + assertEquals(asList(false, true, false, false, false, true), list); + + assertTrue(list.addAll(TERTIARY_LIST)); + assertEquals(asList(false, true, false, false, false, true, true, true, false), list); + + assertFalse(list.addAll(Collections.emptyList())); + assertFalse(list.addAll(BooleanArrayList.emptyList())); + } + + public void testRemove() { + list.addAll(TERTIARY_LIST); + assertEquals(true, (boolean) list.remove(0)); + assertEquals(asList(true, false), list); + + assertTrue(list.remove(Boolean.TRUE)); + assertEquals(asList(false), list); + + assertFalse(list.remove(Boolean.TRUE)); + assertEquals(asList(false), list); + + assertEquals(false, (boolean) list.remove(0)); + assertEquals(asList(), list); + + try { + list.remove(-1); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + list.remove(0); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + private void assertImmutable(BooleanArrayList list) { + if (list.contains(1)) { + throw new RuntimeException("Cannot test the immutability of lists that contain 1."); + } + + try { + list.add(false); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.add(0, true); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(Collections.singletonList(false)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(new BooleanArrayList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(0, Collections.singleton(true)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(0, UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(0, Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addBoolean(true); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.clear(); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.remove(1); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.remove(new Object()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.removeAll(Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.removeAll(Collections.singleton(1)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.removeAll(UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.retainAll(Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.retainAll(Collections.singleton(1)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.retainAll(UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.set(0, true); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.setBoolean(0, false); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + } + + private static BooleanArrayList newImmutableBooleanArrayList(boolean... elements) { + BooleanArrayList list = new BooleanArrayList(); + for (boolean element : elements) { + list.addBoolean(element); + } + list.makeImmutable(); + return list; + } +} diff --git a/java/src/test/java/com/google/protobuf/DescriptorsTest.java b/java/src/test/java/com/google/protobuf/DescriptorsTest.java index 6cfa18d5..edd7fc46 100644 --- a/java/src/test/java/com/google/protobuf/DescriptorsTest.java +++ b/java/src/test/java/com/google/protobuf/DescriptorsTest.java @@ -56,6 +56,7 @@ import protobuf_unittest.UnittestProto.TestAllTypes; import protobuf_unittest.UnittestProto.TestExtremeDefaultValues; import protobuf_unittest.UnittestProto.TestMultipleExtensionRanges; import protobuf_unittest.UnittestProto.TestRequired; +import protobuf_unittest.UnittestProto.TestReservedFields; import protobuf_unittest.UnittestProto.TestService; import junit.framework.TestCase; @@ -687,6 +688,9 @@ public class DescriptorsTest extends TestCase { assertEquals(4, oneofDescriptor.getFieldCount()); assertSame(oneofDescriptor.getField(1), field); + + assertEquals(4, oneofDescriptor.getFields().size()); + assertEquals(oneofDescriptor.getFields().get(1), field); } public void testMessageDescriptorExtensions() throws Exception { @@ -702,6 +706,19 @@ public class DescriptorsTest extends TestCase { assertTrue(TestMultipleExtensionRanges.getDescriptor().isExtensionNumber(4143)); } + public void testReservedFields() { + Descriptor d = TestReservedFields.getDescriptor(); + assertTrue(d.isReservedNumber(2)); + assertFalse(d.isReservedNumber(8)); + assertTrue(d.isReservedNumber(9)); + assertTrue(d.isReservedNumber(10)); + assertTrue(d.isReservedNumber(11)); + assertFalse(d.isReservedNumber(12)); + assertFalse(d.isReservedName("foo")); + assertTrue(d.isReservedName("bar")); + assertTrue(d.isReservedName("baz")); + } + public void testToString() { assertEquals("protobuf_unittest.TestAllTypes.optional_uint64", UnittestProto.TestAllTypes.getDescriptor().findFieldByNumber( diff --git a/java/src/test/java/com/google/protobuf/DoubleArrayListTest.java b/java/src/test/java/com/google/protobuf/DoubleArrayListTest.java new file mode 100644 index 00000000..e7a73d70 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/DoubleArrayListTest.java @@ -0,0 +1,473 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import static java.util.Arrays.asList; + +import junit.framework.TestCase; + +import java.util.Collections; +import java.util.ConcurrentModificationException; +import java.util.Iterator; + +/** + * Tests for {@link DoubleArrayList}. + * + * @author dweis@google.com (Daniel Weis) + */ +public class DoubleArrayListTest extends TestCase { + + private static final DoubleArrayList UNARY_LIST = newImmutableDoubleArrayList(1); + private static final DoubleArrayList TERTIARY_LIST = + newImmutableDoubleArrayList(1, 2, 3); + + private DoubleArrayList list; + + @Override + protected void setUp() throws Exception { + list = new DoubleArrayList(); + } + + public void testEmptyListReturnsSameInstance() { + assertSame(DoubleArrayList.emptyList(), DoubleArrayList.emptyList()); + } + + public void testEmptyListIsImmutable() { + assertImmutable(DoubleArrayList.emptyList()); + } + + public void testMakeImmutable() { + list.addDouble(2); + list.addDouble(4); + list.addDouble(6); + list.addDouble(8); + list.makeImmutable(); + assertImmutable(list); + } + + public void testCopyConstructor() { + DoubleArrayList copy = new DoubleArrayList(TERTIARY_LIST); + assertEquals(TERTIARY_LIST, copy); + + copy = new DoubleArrayList(DoubleArrayList.emptyList()); + assertEquals(DoubleArrayList.emptyList(), copy); + + copy = new DoubleArrayList(asList(1D, 2D, 3D)); + assertEquals(asList(1D, 2D, 3D), copy); + + copy = new DoubleArrayList(Collections.emptyList()); + assertEquals(DoubleArrayList.emptyList(), copy); + } + + public void testModificationWithIteration() { + list.addAll(asList(1D, 2D, 3D, 4D)); + Iterator iterator = list.iterator(); + assertEquals(4, list.size()); + assertEquals(1D, (double) list.get(0)); + assertEquals(1D, (double) iterator.next()); + list.set(0, 1D); + assertEquals(2D, (double) iterator.next()); + + list.remove(0); + try { + iterator.next(); + fail(); + } catch (ConcurrentModificationException e) { + // expected + } + + iterator = list.iterator(); + list.add(0, 0D); + try { + iterator.next(); + fail(); + } catch (ConcurrentModificationException e) { + // expected + } + } + + public void testGet() { + assertEquals(1D, (double) TERTIARY_LIST.get(0)); + assertEquals(2D, (double) TERTIARY_LIST.get(1)); + assertEquals(3D, (double) TERTIARY_LIST.get(2)); + + try { + TERTIARY_LIST.get(-1); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + TERTIARY_LIST.get(3); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testGetInt() { + assertEquals(1D, TERTIARY_LIST.getDouble(0)); + assertEquals(2D, TERTIARY_LIST.getDouble(1)); + assertEquals(3D, TERTIARY_LIST.getDouble(2)); + + try { + TERTIARY_LIST.get(-1); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + TERTIARY_LIST.get(3); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testSize() { + assertEquals(0, DoubleArrayList.emptyList().size()); + assertEquals(1, UNARY_LIST.size()); + assertEquals(3, TERTIARY_LIST.size()); + + list.addDouble(2); + list.addDouble(4); + list.addDouble(6); + list.addDouble(8); + assertEquals(4, list.size()); + + list.remove(0); + assertEquals(3, list.size()); + + list.add(16D); + assertEquals(4, list.size()); + } + + public void testSet() { + list.addDouble(2); + list.addDouble(4); + + assertEquals(2D, (double) list.set(0, 0D)); + assertEquals(0D, list.getDouble(0)); + + assertEquals(4D, (double) list.set(1, 0D)); + assertEquals(0D, list.getDouble(1)); + + try { + list.set(-1, 0D); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + list.set(2, 0D); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testSetInt() { + list.addDouble(2); + list.addDouble(4); + + assertEquals(2D, list.setDouble(0, 0)); + assertEquals(0D, list.getDouble(0)); + + assertEquals(4D, list.setDouble(1, 0)); + assertEquals(0D, list.getDouble(1)); + + try { + list.setDouble(-1, 0); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + list.setDouble(2, 0); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testAdd() { + assertEquals(0, list.size()); + + assertTrue(list.add(2D)); + assertEquals(asList(2D), list); + + assertTrue(list.add(3D)); + list.add(0, 4D); + assertEquals(asList(4D, 2D, 3D), list); + + list.add(0, 1D); + list.add(0, 0D); + // Force a resize by getting up to 11 elements. + for (int i = 0; i < 6; i++) { + list.add(Double.valueOf(5 + i)); + } + assertEquals(asList(0D, 1D, 4D, 2D, 3D, 5D, 6D, 7D, 8D, 9D, 10D), list); + + try { + list.add(-1, 5D); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + list.add(4, 5D); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testAddInt() { + assertEquals(0, list.size()); + + list.addDouble(2); + assertEquals(asList(2D), list); + + list.addDouble(3); + assertEquals(asList(2D, 3D), list); + } + + public void testAddAll() { + assertEquals(0, list.size()); + + assertTrue(list.addAll(Collections.singleton(1D))); + assertEquals(1, list.size()); + assertEquals(1D, (double) list.get(0)); + assertEquals(1D, list.getDouble(0)); + + assertTrue(list.addAll(asList(2D, 3D, 4D, 5D, 6D))); + assertEquals(asList(1D, 2D, 3D, 4D, 5D, 6D), list); + + assertTrue(list.addAll(TERTIARY_LIST)); + assertEquals(asList(1D, 2D, 3D, 4D, 5D, 6D, 1D, 2D, 3D), list); + + assertFalse(list.addAll(Collections.emptyList())); + assertFalse(list.addAll(DoubleArrayList.emptyList())); + } + + public void testRemove() { + list.addAll(TERTIARY_LIST); + assertEquals(1D, (double) list.remove(0)); + assertEquals(asList(2D, 3D), list); + + assertTrue(list.remove(Double.valueOf(3))); + assertEquals(asList(2D), list); + + assertFalse(list.remove(Double.valueOf(3))); + assertEquals(asList(2D), list); + + assertEquals(2D, (double) list.remove(0)); + assertEquals(asList(), list); + + try { + list.remove(-1); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + list.remove(0); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + private void assertImmutable(DoubleArrayList list) { + if (list.contains(1)) { + throw new RuntimeException("Cannot test the immutability of lists that contain 1."); + } + + try { + list.add(1D); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.add(0, 1D); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(Collections.singletonList(1D)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(new DoubleArrayList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(0, Collections.singleton(1D)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(0, UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(0, Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addDouble(0); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.clear(); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.remove(1); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.remove(new Object()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.removeAll(Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.removeAll(Collections.singleton(1)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.removeAll(UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.retainAll(Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.retainAll(Collections.singleton(1)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.retainAll(UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.set(0, 0D); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.setDouble(0, 0); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + } + + private static DoubleArrayList newImmutableDoubleArrayList(double... elements) { + DoubleArrayList list = new DoubleArrayList(); + for (double element : elements) { + list.addDouble(element); + } + list.makeImmutable(); + return list; + } +} diff --git a/java/src/test/java/com/google/protobuf/FloatArrayListTest.java b/java/src/test/java/com/google/protobuf/FloatArrayListTest.java new file mode 100644 index 00000000..8f3e93dc --- /dev/null +++ b/java/src/test/java/com/google/protobuf/FloatArrayListTest.java @@ -0,0 +1,473 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import static java.util.Arrays.asList; + +import junit.framework.TestCase; + +import java.util.Collections; +import java.util.ConcurrentModificationException; +import java.util.Iterator; + +/** + * Tests for {@link FloatArrayList}. + * + * @author dweis@google.com (Daniel Weis) + */ +public class FloatArrayListTest extends TestCase { + + private static final FloatArrayList UNARY_LIST = newImmutableFloatArrayList(1); + private static final FloatArrayList TERTIARY_LIST = + newImmutableFloatArrayList(1, 2, 3); + + private FloatArrayList list; + + @Override + protected void setUp() throws Exception { + list = new FloatArrayList(); + } + + public void testEmptyListReturnsSameInstance() { + assertSame(FloatArrayList.emptyList(), FloatArrayList.emptyList()); + } + + public void testEmptyListIsImmutable() { + assertImmutable(FloatArrayList.emptyList()); + } + + public void testMakeImmutable() { + list.addFloat(2); + list.addFloat(4); + list.addFloat(6); + list.addFloat(8); + list.makeImmutable(); + assertImmutable(list); + } + + public void testCopyConstructor() { + FloatArrayList copy = new FloatArrayList(TERTIARY_LIST); + assertEquals(TERTIARY_LIST, copy); + + copy = new FloatArrayList(FloatArrayList.emptyList()); + assertEquals(FloatArrayList.emptyList(), copy); + + copy = new FloatArrayList(asList(1F, 2F, 3F)); + assertEquals(asList(1F, 2F, 3F), copy); + + copy = new FloatArrayList(Collections.emptyList()); + assertEquals(FloatArrayList.emptyList(), copy); + } + + public void testModificationWithIteration() { + list.addAll(asList(1F, 2F, 3F, 4F)); + Iterator iterator = list.iterator(); + assertEquals(4, list.size()); + assertEquals(1F, (float) list.get(0)); + assertEquals(1F, (float) iterator.next()); + list.set(0, 1F); + assertEquals(2F, (float) iterator.next()); + + list.remove(0); + try { + iterator.next(); + fail(); + } catch (ConcurrentModificationException e) { + // expected + } + + iterator = list.iterator(); + list.add(0, 0F); + try { + iterator.next(); + fail(); + } catch (ConcurrentModificationException e) { + // expected + } + } + + public void testGet() { + assertEquals(1F, (float) TERTIARY_LIST.get(0)); + assertEquals(2F, (float) TERTIARY_LIST.get(1)); + assertEquals(3F, (float) TERTIARY_LIST.get(2)); + + try { + TERTIARY_LIST.get(-1); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + TERTIARY_LIST.get(3); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testGetFloat() { + assertEquals(1F, TERTIARY_LIST.getFloat(0)); + assertEquals(2F, TERTIARY_LIST.getFloat(1)); + assertEquals(3F, TERTIARY_LIST.getFloat(2)); + + try { + TERTIARY_LIST.get(-1); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + TERTIARY_LIST.get(3); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testSize() { + assertEquals(0, FloatArrayList.emptyList().size()); + assertEquals(1, UNARY_LIST.size()); + assertEquals(3, TERTIARY_LIST.size()); + + list.addFloat(2); + list.addFloat(4); + list.addFloat(6); + list.addFloat(8); + assertEquals(4, list.size()); + + list.remove(0); + assertEquals(3, list.size()); + + list.add(16F); + assertEquals(4, list.size()); + } + + public void testSet() { + list.addFloat(2); + list.addFloat(4); + + assertEquals(2F, (float) list.set(0, 0F)); + assertEquals(0F, list.getFloat(0)); + + assertEquals(4F, (float) list.set(1, 0F)); + assertEquals(0F, list.getFloat(1)); + + try { + list.set(-1, 0F); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + list.set(2, 0F); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testSetFloat() { + list.addFloat(2); + list.addFloat(4); + + assertEquals(2F, list.setFloat(0, 0)); + assertEquals(0F, list.getFloat(0)); + + assertEquals(4F, list.setFloat(1, 0)); + assertEquals(0F, list.getFloat(1)); + + try { + list.setFloat(-1, 0); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + list.setFloat(2, 0); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testAdd() { + assertEquals(0, list.size()); + + assertTrue(list.add(2F)); + assertEquals(asList(2F), list); + + assertTrue(list.add(3F)); + list.add(0, 4F); + assertEquals(asList(4F, 2F, 3F), list); + + list.add(0, 1F); + list.add(0, 0F); + // Force a resize by getting up to 11 elements. + for (int i = 0; i < 6; i++) { + list.add(Float.valueOf(5 + i)); + } + assertEquals(asList(0F, 1F, 4F, 2F, 3F, 5F, 6F, 7F, 8F, 9F, 10F), list); + + try { + list.add(-1, 5F); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + list.add(4, 5F); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testAddFloat() { + assertEquals(0, list.size()); + + list.addFloat(2); + assertEquals(asList(2F), list); + + list.addFloat(3); + assertEquals(asList(2F, 3F), list); + } + + public void testAddAll() { + assertEquals(0, list.size()); + + assertTrue(list.addAll(Collections.singleton(1F))); + assertEquals(1, list.size()); + assertEquals(1F, (float) list.get(0)); + assertEquals(1F, list.getFloat(0)); + + assertTrue(list.addAll(asList(2F, 3F, 4F, 5F, 6F))); + assertEquals(asList(1F, 2F, 3F, 4F, 5F, 6F), list); + + assertTrue(list.addAll(TERTIARY_LIST)); + assertEquals(asList(1F, 2F, 3F, 4F, 5F, 6F, 1F, 2F, 3F), list); + + assertFalse(list.addAll(Collections.emptyList())); + assertFalse(list.addAll(FloatArrayList.emptyList())); + } + + public void testRemove() { + list.addAll(TERTIARY_LIST); + assertEquals(1F, (float) list.remove(0)); + assertEquals(asList(2F, 3F), list); + + assertTrue(list.remove(Float.valueOf(3))); + assertEquals(asList(2F), list); + + assertFalse(list.remove(Float.valueOf(3))); + assertEquals(asList(2F), list); + + assertEquals(2F, (float) list.remove(0)); + assertEquals(asList(), list); + + try { + list.remove(-1); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + list.remove(0); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + private void assertImmutable(FloatArrayList list) { + if (list.contains(1)) { + throw new RuntimeException("Cannot test the immutability of lists that contain 1."); + } + + try { + list.add(1F); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.add(0, 1F); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(Collections.singletonList(1F)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(new FloatArrayList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(0, Collections.singleton(1F)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(0, UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(0, Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addFloat(0); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.clear(); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.remove(1); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.remove(new Object()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.removeAll(Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.removeAll(Collections.singleton(1)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.removeAll(UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.retainAll(Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.retainAll(Collections.singleton(1)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.retainAll(UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.set(0, 0F); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.setFloat(0, 0); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + } + + private static FloatArrayList newImmutableFloatArrayList(int... elements) { + FloatArrayList list = new FloatArrayList(); + for (int element : elements) { + list.addFloat(element); + } + list.makeImmutable(); + return list; + } +} diff --git a/java/src/test/java/com/google/protobuf/IntArrayListTest.java b/java/src/test/java/com/google/protobuf/IntArrayListTest.java new file mode 100644 index 00000000..3733eb30 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/IntArrayListTest.java @@ -0,0 +1,473 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import static java.util.Arrays.asList; + +import junit.framework.TestCase; + +import java.util.Collections; +import java.util.ConcurrentModificationException; +import java.util.Iterator; + +/** + * Tests for {@link IntArrayList}. + * + * @author dweis@google.com (Daniel Weis) + */ +public class IntArrayListTest extends TestCase { + + private static final IntArrayList UNARY_LIST = newImmutableIntArrayList(1); + private static final IntArrayList TERTIARY_LIST = + newImmutableIntArrayList(1, 2, 3); + + private IntArrayList list; + + @Override + protected void setUp() throws Exception { + list = new IntArrayList(); + } + + public void testEmptyListReturnsSameInstance() { + assertSame(IntArrayList.emptyList(), IntArrayList.emptyList()); + } + + public void testEmptyListIsImmutable() { + assertImmutable(IntArrayList.emptyList()); + } + + public void testMakeImmutable() { + list.addInt(2); + list.addInt(4); + list.addInt(6); + list.addInt(8); + list.makeImmutable(); + assertImmutable(list); + } + + public void testCopyConstructor() { + IntArrayList copy = new IntArrayList(TERTIARY_LIST); + assertEquals(TERTIARY_LIST, copy); + + copy = new IntArrayList(IntArrayList.emptyList()); + assertEquals(IntArrayList.emptyList(), copy); + + copy = new IntArrayList(asList(1, 2, 3)); + assertEquals(asList(1, 2, 3), copy); + + copy = new IntArrayList(Collections.emptyList()); + assertEquals(IntArrayList.emptyList(), copy); + } + + public void testModificationWithIteration() { + list.addAll(asList(1, 2, 3, 4)); + Iterator iterator = list.iterator(); + assertEquals(4, list.size()); + assertEquals(1, (int) list.get(0)); + assertEquals(1, (int) iterator.next()); + list.set(0, 1); + assertEquals(2, (int) iterator.next()); + + list.remove(0); + try { + iterator.next(); + fail(); + } catch (ConcurrentModificationException e) { + // expected + } + + iterator = list.iterator(); + list.add(0, 0); + try { + iterator.next(); + fail(); + } catch (ConcurrentModificationException e) { + // expected + } + } + + public void testGet() { + assertEquals(1, (int) TERTIARY_LIST.get(0)); + assertEquals(2, (int) TERTIARY_LIST.get(1)); + assertEquals(3, (int) TERTIARY_LIST.get(2)); + + try { + TERTIARY_LIST.get(-1); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + TERTIARY_LIST.get(3); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testGetInt() { + assertEquals(1, TERTIARY_LIST.getInt(0)); + assertEquals(2, TERTIARY_LIST.getInt(1)); + assertEquals(3, TERTIARY_LIST.getInt(2)); + + try { + TERTIARY_LIST.get(-1); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + TERTIARY_LIST.get(3); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testSize() { + assertEquals(0, IntArrayList.emptyList().size()); + assertEquals(1, UNARY_LIST.size()); + assertEquals(3, TERTIARY_LIST.size()); + + list.addInt(2); + list.addInt(4); + list.addInt(6); + list.addInt(8); + assertEquals(4, list.size()); + + list.remove(0); + assertEquals(3, list.size()); + + list.add(16); + assertEquals(4, list.size()); + } + + public void testSet() { + list.addInt(2); + list.addInt(4); + + assertEquals(2, (int) list.set(0, 0)); + assertEquals(0, list.getInt(0)); + + assertEquals(4, (int) list.set(1, 0)); + assertEquals(0, list.getInt(1)); + + try { + list.set(-1, 0); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + list.set(2, 0); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testSetInt() { + list.addInt(2); + list.addInt(4); + + assertEquals(2, list.setInt(0, 0)); + assertEquals(0, list.getInt(0)); + + assertEquals(4, list.setInt(1, 0)); + assertEquals(0, list.getInt(1)); + + try { + list.setInt(-1, 0); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + list.setInt(2, 0); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testAdd() { + assertEquals(0, list.size()); + + assertTrue(list.add(2)); + assertEquals(asList(2), list); + + assertTrue(list.add(3)); + list.add(0, 4); + assertEquals(asList(4, 2, 3), list); + + list.add(0, 1); + list.add(0, 0); + // Force a resize by getting up to 11 elements. + for (int i = 0; i < 6; i++) { + list.add(5 + i); + } + assertEquals(asList(0, 1, 4, 2, 3, 5, 6, 7, 8, 9, 10), list); + + try { + list.add(-1, 5); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + list.add(4, 5); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testAddInt() { + assertEquals(0, list.size()); + + list.addInt(2); + assertEquals(asList(2), list); + + list.addInt(3); + assertEquals(asList(2, 3), list); + } + + public void testAddAll() { + assertEquals(0, list.size()); + + assertTrue(list.addAll(Collections.singleton(1))); + assertEquals(1, list.size()); + assertEquals(1, (int) list.get(0)); + assertEquals(1, list.getInt(0)); + + assertTrue(list.addAll(asList(2, 3, 4, 5, 6))); + assertEquals(asList(1, 2, 3, 4, 5, 6), list); + + assertTrue(list.addAll(TERTIARY_LIST)); + assertEquals(asList(1, 2, 3, 4, 5, 6, 1, 2, 3), list); + + assertFalse(list.addAll(Collections.emptyList())); + assertFalse(list.addAll(IntArrayList.emptyList())); + } + + public void testRemove() { + list.addAll(TERTIARY_LIST); + assertEquals(1, (int) list.remove(0)); + assertEquals(asList(2, 3), list); + + assertTrue(list.remove(Integer.valueOf(3))); + assertEquals(asList(2), list); + + assertFalse(list.remove(Integer.valueOf(3))); + assertEquals(asList(2), list); + + assertEquals(2, (int) list.remove(0)); + assertEquals(asList(), list); + + try { + list.remove(-1); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + list.remove(0); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + private void assertImmutable(IntArrayList list) { + if (list.contains(1)) { + throw new RuntimeException("Cannot test the immutability of lists that contain 1."); + } + + try { + list.add(1); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.add(0, 1); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(Collections.singletonList(1)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(new IntArrayList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(0, Collections.singleton(1)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(0, UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(0, Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addInt(0); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.clear(); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.remove(1); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.remove(new Object()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.removeAll(Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.removeAll(Collections.singleton(1)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.removeAll(UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.retainAll(Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.retainAll(Collections.singleton(1)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.retainAll(UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.set(0, 0); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.setInt(0, 0); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + } + + private static IntArrayList newImmutableIntArrayList(int... elements) { + IntArrayList list = new IntArrayList(); + for (int element : elements) { + list.addInt(element); + } + list.makeImmutable(); + return list; + } +} diff --git a/java/src/test/java/com/google/protobuf/LazyStringArrayListTest.java b/java/src/test/java/com/google/protobuf/LazyStringArrayListTest.java index f3012b96..0f42ac50 100644 --- a/java/src/test/java/com/google/protobuf/LazyStringArrayListTest.java +++ b/java/src/test/java/com/google/protobuf/LazyStringArrayListTest.java @@ -30,9 +30,13 @@ package com.google.protobuf; +import static java.util.Arrays.asList; + import junit.framework.TestCase; import java.util.ArrayList; +import java.util.ConcurrentModificationException; +import java.util.Iterator; import java.util.List; /** @@ -171,4 +175,188 @@ public class LazyStringArrayListTest extends TestCase { assertSame(BYTE_STRING_B, list2.getByteString(1)); assertSame(BYTE_STRING_C, list2.getByteString(2)); } + + public void testModificationWithIteration() { + LazyStringArrayList list = new LazyStringArrayList(); + list.addAll(asList(STRING_A, STRING_B, STRING_C)); + Iterator iterator = list.iterator(); + assertEquals(3, list.size()); + assertEquals(STRING_A, list.get(0)); + assertEquals(STRING_A, iterator.next()); + + // Does not structurally modify. + iterator = list.iterator(); + list.set(0, STRING_B); + iterator.next(); + + list.remove(0); + try { + iterator.next(); + fail(); + } catch (ConcurrentModificationException e) { + // expected + } + + iterator = list.iterator(); + list.add(0, STRING_C); + try { + iterator.next(); + fail(); + } catch (ConcurrentModificationException e) { + // expected + } + } + + public void testMakeImmutable() { + LazyStringArrayList list = new LazyStringArrayList(); + list.add(STRING_A); + list.add(STRING_B); + list.add(STRING_C); + list.makeImmutable(); + assertGenericListImmutable(list, STRING_A); + + // LazyStringArrayList has extra methods not covered in the generic + // assertion. + + try { + list.add(BYTE_STRING_A.toByteArray()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.add(BYTE_STRING_A); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAllByteArray(asList(BYTE_STRING_A.toByteArray())); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAllByteString(asList(BYTE_STRING_A)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.mergeFrom(new LazyStringArrayList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.set(0, BYTE_STRING_A.toByteArray()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.set(0, BYTE_STRING_A); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + } + + public void testImmutabilityPropagation() { + LazyStringArrayList list = new LazyStringArrayList(); + list.add(STRING_A); + list.makeImmutable(); + + assertGenericListImmutable(list.asByteStringList(), BYTE_STRING_A); + + // Arrays use reference equality so need to retrieve the underlying value + // to properly test deep immutability. + List byteArrayList = list.asByteArrayList(); + assertGenericListImmutable(byteArrayList, byteArrayList.get(0)); + } + + private static void assertGenericListImmutable(List list, T value) { + try { + list.add(value); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.add(0, value); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(asList(value)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(0, asList(value)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.clear(); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.remove(0); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.remove(value); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.removeAll(asList(value)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.retainAll(asList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.retainAll(asList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.set(0, value); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + } } diff --git a/java/src/test/java/com/google/protobuf/LiteTest.java b/java/src/test/java/com/google/protobuf/LiteTest.java index 4d8037fc..8c3b5e5c 100644 --- a/java/src/test/java/com/google/protobuf/LiteTest.java +++ b/java/src/test/java/com/google/protobuf/LiteTest.java @@ -30,9 +30,18 @@ package com.google.protobuf; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; + import com.google.protobuf.UnittestLite; -import com.google.protobuf.UnittestLite.TestAllTypesLite; +import com.google.protobuf.UnittestLite.ForeignEnumLite; +import com.google.protobuf.UnittestLite.ForeignMessageLite; import com.google.protobuf.UnittestLite.TestAllExtensionsLite; +import com.google.protobuf.UnittestLite.TestAllTypesLite; +import com.google.protobuf.UnittestLite.TestAllTypesLite.NestedMessage; +import com.google.protobuf.UnittestLite.TestAllTypesLite.OneofFieldCase; +import com.google.protobuf.UnittestLite.TestAllTypesLite.OptionalGroup; +import com.google.protobuf.UnittestLite.TestAllTypesLite.RepeatedGroup; import com.google.protobuf.UnittestLite.TestNestedExtensionLite; import junit.framework.TestCase; @@ -149,13 +158,1302 @@ public class LiteTest extends TestCase { public void testClone() { TestAllTypesLite.Builder expected = TestAllTypesLite.newBuilder() .setOptionalInt32(123); - assertEquals( - expected.getOptionalInt32(), expected.clone().getOptionalInt32()); + assertEquals( + expected.getOptionalInt32(), expected.clone().getOptionalInt32()); - TestAllExtensionsLite.Builder expected2 = TestAllExtensionsLite.newBuilder() - .setExtension(UnittestLite.optionalInt32ExtensionLite, 123); - assertEquals( - expected2.getExtension(UnittestLite.optionalInt32ExtensionLite), - expected2.clone().getExtension(UnittestLite.optionalInt32ExtensionLite)); + TestAllExtensionsLite.Builder expected2 = TestAllExtensionsLite.newBuilder() + .setExtension(UnittestLite.optionalInt32ExtensionLite, 123); + assertEquals( + expected2.getExtension(UnittestLite.optionalInt32ExtensionLite), + expected2.clone().getExtension(UnittestLite.optionalInt32ExtensionLite)); + } + + public void testAddAll() { + try { + TestAllTypesLite.newBuilder() + .addAllRepeatedBytes(null); + fail(); + } catch (NullPointerException e) { + // expected. + } + } + + public void testSanityCopyOnWrite() throws InvalidProtocolBufferException { + // Since builders are implemented as a thin wrapper around a message + // instance, we attempt to verify that we can't cause the builder to modify + // a produced message. + + TestAllTypesLite.Builder builder = TestAllTypesLite.newBuilder(); + TestAllTypesLite message = builder.build(); + TestAllTypesLite messageAfterBuild; + builder.setOptionalBool(true); + assertEquals(false, message.getOptionalBool()); + assertEquals(true, builder.getOptionalBool()); + messageAfterBuild = builder.build(); + assertEquals(true, messageAfterBuild.getOptionalBool()); + assertEquals(false, message.getOptionalBool()); + builder.clearOptionalBool(); + assertEquals(false, builder.getOptionalBool()); + assertEquals(true, messageAfterBuild.getOptionalBool()); + + message = builder.build(); + builder.setOptionalBytes(ByteString.copyFromUtf8("hi")); + assertEquals(ByteString.EMPTY, message.getOptionalBytes()); + assertEquals(ByteString.copyFromUtf8("hi"), builder.getOptionalBytes()); + messageAfterBuild = builder.build(); + assertEquals( + ByteString.copyFromUtf8("hi"), messageAfterBuild.getOptionalBytes()); + assertEquals(ByteString.EMPTY, message.getOptionalBytes()); + builder.clearOptionalBytes(); + assertEquals(ByteString.EMPTY, builder.getOptionalBytes()); + assertEquals( + ByteString.copyFromUtf8("hi"), messageAfterBuild.getOptionalBytes()); + + message = builder.build(); + builder.setOptionalCord("hi"); + assertEquals("", message.getOptionalCord()); + assertEquals("hi", builder.getOptionalCord()); + messageAfterBuild = builder.build(); + assertEquals("hi", messageAfterBuild.getOptionalCord()); + assertEquals("", message.getOptionalCord()); + builder.clearOptionalCord(); + assertEquals("", builder.getOptionalCord()); + assertEquals("hi", messageAfterBuild.getOptionalCord()); + + message = builder.build(); + builder.setOptionalCordBytes(ByteString.copyFromUtf8("no")); + assertEquals(ByteString.EMPTY, message.getOptionalCordBytes()); + assertEquals(ByteString.copyFromUtf8("no"), builder.getOptionalCordBytes()); + messageAfterBuild = builder.build(); + assertEquals( + ByteString.copyFromUtf8("no"), + messageAfterBuild.getOptionalCordBytes()); + assertEquals(ByteString.EMPTY, message.getOptionalCordBytes()); + builder.clearOptionalCord(); + assertEquals(ByteString.EMPTY, builder.getOptionalCordBytes()); + assertEquals( + ByteString.copyFromUtf8("no"), + messageAfterBuild.getOptionalCordBytes()); + + message = builder.build(); + builder.setOptionalDouble(1); + assertEquals(0D, message.getOptionalDouble()); + assertEquals(1D, builder.getOptionalDouble()); + messageAfterBuild = builder.build(); + assertEquals(1D, messageAfterBuild.getOptionalDouble()); + assertEquals(0D, message.getOptionalDouble()); + builder.clearOptionalDouble(); + assertEquals(0D, builder.getOptionalDouble()); + assertEquals(1D, messageAfterBuild.getOptionalDouble()); + + message = builder.build(); + builder.setOptionalFixed32(1); + assertEquals(0, message.getOptionalFixed32()); + assertEquals(1, builder.getOptionalFixed32()); + messageAfterBuild = builder.build(); + assertEquals(1, messageAfterBuild.getOptionalFixed32()); + assertEquals(0, message.getOptionalFixed32()); + builder.clearOptionalFixed32(); + assertEquals(0, builder.getOptionalFixed32()); + assertEquals(1, messageAfterBuild.getOptionalFixed32()); + + message = builder.build(); + builder.setOptionalFixed64(1); + assertEquals(0L, message.getOptionalFixed64()); + assertEquals(1L, builder.getOptionalFixed64()); + messageAfterBuild = builder.build(); + assertEquals(1L, messageAfterBuild.getOptionalFixed64()); + assertEquals(0L, message.getOptionalFixed64()); + builder.clearOptionalFixed64(); + assertEquals(0L, builder.getOptionalFixed64()); + assertEquals(1L, messageAfterBuild.getOptionalFixed64()); + + message = builder.build(); + builder.setOptionalFloat(1); + assertEquals(0F, message.getOptionalFloat()); + assertEquals(1F, builder.getOptionalFloat()); + messageAfterBuild = builder.build(); + assertEquals(1F, messageAfterBuild.getOptionalFloat()); + assertEquals(0F, message.getOptionalFloat()); + builder.clearOptionalFloat(); + assertEquals(0F, builder.getOptionalFloat()); + assertEquals(1F, messageAfterBuild.getOptionalFloat()); + + message = builder.build(); + builder.setOptionalForeignEnum(ForeignEnumLite.FOREIGN_LITE_BAR); + assertEquals( + ForeignEnumLite.FOREIGN_LITE_FOO, message.getOptionalForeignEnum()); + assertEquals( + ForeignEnumLite.FOREIGN_LITE_BAR, builder.getOptionalForeignEnum()); + messageAfterBuild = builder.build(); + assertEquals( + ForeignEnumLite.FOREIGN_LITE_BAR, + messageAfterBuild.getOptionalForeignEnum()); + assertEquals( + ForeignEnumLite.FOREIGN_LITE_FOO, message.getOptionalForeignEnum()); + builder.clearOptionalForeignEnum(); + assertEquals( + ForeignEnumLite.FOREIGN_LITE_FOO, builder.getOptionalForeignEnum()); + assertEquals( + ForeignEnumLite.FOREIGN_LITE_BAR, + messageAfterBuild.getOptionalForeignEnum()); + + message = builder.build(); + ForeignMessageLite foreignMessage = ForeignMessageLite.newBuilder() + .setC(1) + .build(); + builder.setOptionalForeignMessage(foreignMessage); + assertEquals( + ForeignMessageLite.getDefaultInstance(), + message.getOptionalForeignMessage()); + assertEquals(foreignMessage, builder.getOptionalForeignMessage()); + messageAfterBuild = builder.build(); + assertEquals(foreignMessage, messageAfterBuild.getOptionalForeignMessage()); + assertEquals( + ForeignMessageLite.getDefaultInstance(), + message.getOptionalForeignMessage()); + builder.clearOptionalForeignMessage(); + assertEquals( + ForeignMessageLite.getDefaultInstance(), + builder.getOptionalForeignMessage()); + assertEquals(foreignMessage, messageAfterBuild.getOptionalForeignMessage()); + + message = builder.build(); + ForeignMessageLite.Builder foreignMessageBuilder = + ForeignMessageLite.newBuilder() + .setC(3); + builder.setOptionalForeignMessage(foreignMessageBuilder); + assertEquals( + ForeignMessageLite.getDefaultInstance(), + message.getOptionalForeignMessage()); + // LITE_RUNTIME doesn't implement equals so we compare on a property and + // ensure the property isn't set on foreignMessage. + assertEquals(3, builder.getOptionalForeignMessage().getC()); + messageAfterBuild = builder.build(); + assertEquals(3, messageAfterBuild.getOptionalForeignMessage().getC()); + assertEquals( + ForeignMessageLite.getDefaultInstance(), + message.getOptionalForeignMessage()); + builder.clearOptionalForeignMessage(); + assertEquals( + ForeignMessageLite.getDefaultInstance(), + builder.getOptionalForeignMessage()); + assertEquals(3, messageAfterBuild.getOptionalForeignMessage().getC()); + + message = builder.build(); + OptionalGroup optionalGroup = OptionalGroup.newBuilder() + .setA(1) + .build(); + builder.setOptionalGroup(optionalGroup); + assertEquals( + OptionalGroup.getDefaultInstance(), message.getOptionalGroup()); + assertEquals(optionalGroup, builder.getOptionalGroup()); + messageAfterBuild = builder.build(); + assertEquals(optionalGroup, messageAfterBuild.getOptionalGroup()); + assertEquals( + OptionalGroup.getDefaultInstance(), message.getOptionalGroup()); + builder.clearOptionalGroup(); + assertEquals( + OptionalGroup.getDefaultInstance(), builder.getOptionalGroup()); + assertEquals(optionalGroup, messageAfterBuild.getOptionalGroup()); + + message = builder.build(); + OptionalGroup.Builder optionalGroupBuilder = OptionalGroup.newBuilder() + .setA(3); + builder.setOptionalGroup(optionalGroupBuilder); + assertEquals( + OptionalGroup.getDefaultInstance(), message.getOptionalGroup()); + // LITE_RUNTIME doesn't implement equals so we compare on a property and + // ensure the property isn't set on optionalGroup. + assertEquals(3, builder.getOptionalGroup().getA()); + messageAfterBuild = builder.build(); + assertEquals(3, messageAfterBuild.getOptionalGroup().getA()); + assertEquals( + OptionalGroup.getDefaultInstance(), message.getOptionalGroup()); + builder.clearOptionalGroup(); + assertEquals( + OptionalGroup.getDefaultInstance(), builder.getOptionalGroup()); + assertEquals(3, messageAfterBuild.getOptionalGroup().getA()); + + message = builder.build(); + builder.setOptionalInt32(1); + assertEquals(0, message.getOptionalInt32()); + assertEquals(1, builder.getOptionalInt32()); + messageAfterBuild = builder.build(); + assertEquals(1, messageAfterBuild.getOptionalInt32()); + assertEquals(0, message.getOptionalInt32()); + builder.clearOptionalInt32(); + assertEquals(0, builder.getOptionalInt32()); + assertEquals(1, messageAfterBuild.getOptionalInt32()); + + message = builder.build(); + builder.setOptionalInt64(1); + assertEquals(0L, message.getOptionalInt64()); + assertEquals(1L, builder.getOptionalInt64()); + messageAfterBuild = builder.build(); + assertEquals(1L, messageAfterBuild.getOptionalInt64()); + assertEquals(0L, message.getOptionalInt64()); + builder.clearOptionalInt64(); + assertEquals(0L, builder.getOptionalInt64()); + assertEquals(1L, messageAfterBuild.getOptionalInt64()); + + message = builder.build(); + NestedMessage nestedMessage = NestedMessage.newBuilder() + .setBb(1) + .build(); + builder.setOptionalLazyMessage(nestedMessage); + assertEquals( + NestedMessage.getDefaultInstance(), + message.getOptionalLazyMessage()); + assertEquals(nestedMessage, builder.getOptionalLazyMessage()); + messageAfterBuild = builder.build(); + assertEquals(nestedMessage, messageAfterBuild.getOptionalLazyMessage()); + assertEquals( + NestedMessage.getDefaultInstance(), + message.getOptionalLazyMessage()); + builder.clearOptionalLazyMessage(); + assertEquals( + NestedMessage.getDefaultInstance(), builder.getOptionalLazyMessage()); + assertEquals(nestedMessage, messageAfterBuild.getOptionalLazyMessage()); + + message = builder.build(); + NestedMessage.Builder nestedMessageBuilder = + NestedMessage.newBuilder() + .setBb(3); + builder.setOptionalLazyMessage(nestedMessageBuilder); + assertEquals( + NestedMessage.getDefaultInstance(), + message.getOptionalLazyMessage()); + // LITE_RUNTIME doesn't implement equals so we compare on a property. + assertEquals(3, builder.getOptionalLazyMessage().getBb()); + messageAfterBuild = builder.build(); + assertEquals(3, messageAfterBuild.getOptionalLazyMessage().getBb()); + assertEquals( + NestedMessage.getDefaultInstance(), + message.getOptionalLazyMessage()); + builder.clearOptionalLazyMessage(); + assertEquals( + NestedMessage.getDefaultInstance(), builder.getOptionalLazyMessage()); + assertEquals(3, messageAfterBuild.getOptionalLazyMessage().getBb()); + + message = builder.build(); + builder.setOptionalSfixed32(1); + assertEquals(0, message.getOptionalSfixed32()); + assertEquals(1, builder.getOptionalSfixed32()); + messageAfterBuild = builder.build(); + assertEquals(1, messageAfterBuild.getOptionalSfixed32()); + assertEquals(0, message.getOptionalSfixed32()); + builder.clearOptionalSfixed32(); + assertEquals(0, builder.getOptionalSfixed32()); + assertEquals(1, messageAfterBuild.getOptionalSfixed32()); + + message = builder.build(); + builder.setOptionalSfixed64(1); + assertEquals(0L, message.getOptionalSfixed64()); + assertEquals(1L, builder.getOptionalSfixed64()); + messageAfterBuild = builder.build(); + assertEquals(1L, messageAfterBuild.getOptionalSfixed64()); + assertEquals(0L, message.getOptionalSfixed64()); + builder.clearOptionalSfixed64(); + assertEquals(0L, builder.getOptionalSfixed64()); + assertEquals(1L, messageAfterBuild.getOptionalSfixed64()); + + message = builder.build(); + builder.setOptionalSint32(1); + assertEquals(0, message.getOptionalSint32()); + assertEquals(1, builder.getOptionalSint32()); + messageAfterBuild = builder.build(); + assertEquals(1, messageAfterBuild.getOptionalSint32()); + builder.clearOptionalSint32(); + assertEquals(0, builder.getOptionalSint32()); + assertEquals(1, messageAfterBuild.getOptionalSint32()); + + message = builder.build(); + builder.setOptionalSint64(1); + assertEquals(0L, message.getOptionalSint64()); + assertEquals(1L, builder.getOptionalSint64()); + messageAfterBuild = builder.build(); + assertEquals(1L, messageAfterBuild.getOptionalSint64()); + assertEquals(0L, message.getOptionalSint64()); + builder.clearOptionalSint64(); + assertEquals(0L, builder.getOptionalSint64()); + assertEquals(1L, messageAfterBuild.getOptionalSint64()); + + message = builder.build(); + builder.setOptionalString("hi"); + assertEquals("", message.getOptionalString()); + assertEquals("hi", builder.getOptionalString()); + messageAfterBuild = builder.build(); + assertEquals("hi", messageAfterBuild.getOptionalString()); + assertEquals("", message.getOptionalString()); + builder.clearOptionalString(); + assertEquals("", builder.getOptionalString()); + assertEquals("hi", messageAfterBuild.getOptionalString()); + + message = builder.build(); + builder.setOptionalStringBytes(ByteString.copyFromUtf8("no")); + assertEquals(ByteString.EMPTY, message.getOptionalStringBytes()); + assertEquals( + ByteString.copyFromUtf8("no"), builder.getOptionalStringBytes()); + messageAfterBuild = builder.build(); + assertEquals( + ByteString.copyFromUtf8("no"), + messageAfterBuild.getOptionalStringBytes()); + assertEquals(ByteString.EMPTY, message.getOptionalStringBytes()); + builder.clearOptionalString(); + assertEquals(ByteString.EMPTY, builder.getOptionalStringBytes()); + assertEquals( + ByteString.copyFromUtf8("no"), + messageAfterBuild.getOptionalStringBytes()); + + message = builder.build(); + builder.setOptionalStringPiece("hi"); + assertEquals("", message.getOptionalStringPiece()); + assertEquals("hi", builder.getOptionalStringPiece()); + messageAfterBuild = builder.build(); + assertEquals("hi", messageAfterBuild.getOptionalStringPiece()); + assertEquals("", message.getOptionalStringPiece()); + builder.clearOptionalStringPiece(); + assertEquals("", builder.getOptionalStringPiece()); + assertEquals("hi", messageAfterBuild.getOptionalStringPiece()); + + message = builder.build(); + builder.setOptionalStringPieceBytes(ByteString.copyFromUtf8("no")); + assertEquals(ByteString.EMPTY, message.getOptionalStringPieceBytes()); + assertEquals( + ByteString.copyFromUtf8("no"), builder.getOptionalStringPieceBytes()); + messageAfterBuild = builder.build(); + assertEquals( + ByteString.copyFromUtf8("no"), + messageAfterBuild.getOptionalStringPieceBytes()); + assertEquals(ByteString.EMPTY, message.getOptionalStringPieceBytes()); + builder.clearOptionalStringPiece(); + assertEquals(ByteString.EMPTY, builder.getOptionalStringPieceBytes()); + assertEquals( + ByteString.copyFromUtf8("no"), + messageAfterBuild.getOptionalStringPieceBytes()); + + message = builder.build(); + builder.setOptionalUint32(1); + assertEquals(0, message.getOptionalUint32()); + assertEquals(1, builder.getOptionalUint32()); + messageAfterBuild = builder.build(); + assertEquals(1, messageAfterBuild.getOptionalUint32()); + assertEquals(0, message.getOptionalUint32()); + builder.clearOptionalUint32(); + assertEquals(0, builder.getOptionalUint32()); + assertEquals(1, messageAfterBuild.getOptionalUint32()); + + message = builder.build(); + builder.setOptionalUint64(1); + assertEquals(0L, message.getOptionalUint64()); + assertEquals(1L, builder.getOptionalUint64()); + messageAfterBuild = builder.build(); + assertEquals(1L, messageAfterBuild.getOptionalUint64()); + assertEquals(0L, message.getOptionalUint64()); + builder.clearOptionalUint64(); + assertEquals(0L, builder.getOptionalUint64()); + assertEquals(1L, messageAfterBuild.getOptionalUint64()); + + message = builder.build(); + builder.addAllRepeatedBool(singletonList(true)); + assertEquals(emptyList(), message.getRepeatedBoolList()); + assertEquals(singletonList(true), builder.getRepeatedBoolList()); + assertEquals(emptyList(), message.getRepeatedBoolList()); + messageAfterBuild = builder.build(); + builder.clearRepeatedBool(); + assertEquals(emptyList(), builder.getRepeatedBoolList()); + assertEquals(singletonList(true), messageAfterBuild.getRepeatedBoolList()); + + message = builder.build(); + builder.addAllRepeatedBytes(singletonList(ByteString.copyFromUtf8("hi"))); + assertEquals(emptyList(), message.getRepeatedBytesList()); + assertEquals( + singletonList(ByteString.copyFromUtf8("hi")), + builder.getRepeatedBytesList()); + assertEquals(emptyList(), message.getRepeatedBytesList()); + messageAfterBuild = builder.build(); + builder.clearRepeatedBytes(); + assertEquals(emptyList(), builder.getRepeatedBytesList()); + assertEquals( + singletonList(ByteString.copyFromUtf8("hi")), + messageAfterBuild.getRepeatedBytesList()); + + message = builder.build(); + builder.addAllRepeatedCord(singletonList("hi")); + assertEquals(emptyList(), message.getRepeatedCordList()); + assertEquals(singletonList("hi"), builder.getRepeatedCordList()); + assertEquals(emptyList(), message.getRepeatedCordList()); + messageAfterBuild = builder.build(); + builder.clearRepeatedCord(); + assertEquals(emptyList(), builder.getRepeatedCordList()); + assertEquals(singletonList("hi"), messageAfterBuild.getRepeatedCordList()); + + message = builder.build(); + builder.addAllRepeatedDouble(singletonList(1D)); + assertEquals(emptyList(), message.getRepeatedDoubleList()); + assertEquals(singletonList(1D), builder.getRepeatedDoubleList()); + assertEquals(emptyList(), message.getRepeatedDoubleList()); + messageAfterBuild = builder.build(); + builder.clearRepeatedDouble(); + assertEquals(emptyList(), builder.getRepeatedDoubleList()); + assertEquals(singletonList(1D), messageAfterBuild.getRepeatedDoubleList()); + + message = builder.build(); + builder.addAllRepeatedFixed32(singletonList(1)); + assertEquals(emptyList(), message.getRepeatedFixed32List()); + assertEquals(singletonList(1), builder.getRepeatedFixed32List()); + assertEquals(emptyList(), message.getRepeatedFixed32List()); + messageAfterBuild = builder.build(); + builder.clearRepeatedFixed32(); + assertEquals(emptyList(), builder.getRepeatedFixed32List()); + assertEquals(singletonList(1), messageAfterBuild.getRepeatedFixed32List()); + + message = builder.build(); + builder.addAllRepeatedFixed64(singletonList(1L)); + assertEquals(emptyList(), message.getRepeatedFixed64List()); + assertEquals(singletonList(1L), builder.getRepeatedFixed64List()); + assertEquals(emptyList(), message.getRepeatedFixed64List()); + messageAfterBuild = builder.build(); + builder.clearRepeatedFixed64(); + assertEquals(emptyList(), builder.getRepeatedFixed64List()); + assertEquals(singletonList(1L), messageAfterBuild.getRepeatedFixed64List()); + + message = builder.build(); + builder.addAllRepeatedFloat(singletonList(1F)); + assertEquals(emptyList(), message.getRepeatedFloatList()); + assertEquals(singletonList(1F), builder.getRepeatedFloatList()); + assertEquals(emptyList(), message.getRepeatedFloatList()); + messageAfterBuild = builder.build(); + builder.clearRepeatedFloat(); + assertEquals(emptyList(), builder.getRepeatedFloatList()); + assertEquals(singletonList(1F), messageAfterBuild.getRepeatedFloatList()); + + message = builder.build(); + builder.addAllRepeatedForeignEnum( + singletonList(ForeignEnumLite.FOREIGN_LITE_BAR)); + assertEquals(emptyList(), message.getRepeatedForeignEnumList()); + assertEquals( + singletonList(ForeignEnumLite.FOREIGN_LITE_BAR), + builder.getRepeatedForeignEnumList()); + assertEquals(emptyList(), message.getRepeatedForeignEnumList()); + messageAfterBuild = builder.build(); + builder.clearRepeatedForeignEnum(); + assertEquals(emptyList(), builder.getRepeatedForeignEnumList()); + assertEquals( + singletonList(ForeignEnumLite.FOREIGN_LITE_BAR), + messageAfterBuild.getRepeatedForeignEnumList()); + + message = builder.build(); + builder.addAllRepeatedForeignMessage(singletonList(foreignMessage)); + assertEquals(emptyList(), message.getRepeatedForeignMessageList()); + assertEquals( + singletonList(foreignMessage), builder.getRepeatedForeignMessageList()); + assertEquals(emptyList(), message.getRepeatedForeignMessageList()); + messageAfterBuild = builder.build(); + builder.clearRepeatedForeignMessage(); + assertEquals(emptyList(), builder.getRepeatedForeignMessageList()); + assertEquals( + singletonList(foreignMessage), + messageAfterBuild.getRepeatedForeignMessageList()); + + message = builder.build(); + builder.addAllRepeatedGroup( + singletonList(RepeatedGroup.getDefaultInstance())); + assertEquals(emptyList(), message.getRepeatedGroupList()); + assertEquals( + singletonList(RepeatedGroup.getDefaultInstance()), + builder.getRepeatedGroupList()); + assertEquals(emptyList(), message.getRepeatedGroupList()); + messageAfterBuild = builder.build(); + builder.clearRepeatedGroup(); + assertEquals(emptyList(), builder.getRepeatedGroupList()); + assertEquals( + singletonList(RepeatedGroup.getDefaultInstance()), + messageAfterBuild.getRepeatedGroupList()); + + message = builder.build(); + builder.addAllRepeatedInt32(singletonList(1)); + assertEquals(emptyList(), message.getRepeatedInt32List()); + assertEquals(singletonList(1), builder.getRepeatedInt32List()); + assertEquals(emptyList(), message.getRepeatedInt32List()); + messageAfterBuild = builder.build(); + builder.clearRepeatedInt32(); + assertEquals(emptyList(), builder.getRepeatedInt32List()); + assertEquals(singletonList(1), messageAfterBuild.getRepeatedInt32List()); + + message = builder.build(); + builder.addAllRepeatedInt64(singletonList(1L)); + assertEquals(emptyList(), message.getRepeatedInt64List()); + assertEquals(singletonList(1L), builder.getRepeatedInt64List()); + assertEquals(emptyList(), message.getRepeatedInt64List()); + messageAfterBuild = builder.build(); + builder.clearRepeatedInt64(); + assertEquals(emptyList(), builder.getRepeatedInt64List()); + assertEquals(singletonList(1L), messageAfterBuild.getRepeatedInt64List()); + + message = builder.build(); + builder.addAllRepeatedLazyMessage(singletonList(nestedMessage)); + assertEquals(emptyList(), message.getRepeatedLazyMessageList()); + assertEquals( + singletonList(nestedMessage), builder.getRepeatedLazyMessageList()); + assertEquals(emptyList(), message.getRepeatedLazyMessageList()); + messageAfterBuild = builder.build(); + builder.clearRepeatedLazyMessage(); + assertEquals(emptyList(), builder.getRepeatedLazyMessageList()); + assertEquals( + singletonList(nestedMessage), + messageAfterBuild.getRepeatedLazyMessageList()); + + message = builder.build(); + builder.addAllRepeatedSfixed32(singletonList(1)); + assertEquals(emptyList(), message.getRepeatedSfixed32List()); + assertEquals(singletonList(1), builder.getRepeatedSfixed32List()); + assertEquals(emptyList(), message.getRepeatedSfixed32List()); + messageAfterBuild = builder.build(); + builder.clearRepeatedSfixed32(); + assertEquals(emptyList(), builder.getRepeatedSfixed32List()); + assertEquals(singletonList(1), messageAfterBuild.getRepeatedSfixed32List()); + + message = builder.build(); + builder.addAllRepeatedSfixed64(singletonList(1L)); + assertEquals(emptyList(), message.getRepeatedSfixed64List()); + assertEquals(singletonList(1L), builder.getRepeatedSfixed64List()); + assertEquals(emptyList(), message.getRepeatedSfixed64List()); + messageAfterBuild = builder.build(); + builder.clearRepeatedSfixed64(); + assertEquals(emptyList(), builder.getRepeatedSfixed64List()); + assertEquals( + singletonList(1L), messageAfterBuild.getRepeatedSfixed64List()); + + message = builder.build(); + builder.addAllRepeatedSint32(singletonList(1)); + assertEquals(emptyList(), message.getRepeatedSint32List()); + assertEquals(singletonList(1), builder.getRepeatedSint32List()); + assertEquals(emptyList(), message.getRepeatedSint32List()); + messageAfterBuild = builder.build(); + builder.clearRepeatedSint32(); + assertEquals(emptyList(), builder.getRepeatedSint32List()); + assertEquals(singletonList(1), messageAfterBuild.getRepeatedSint32List()); + + message = builder.build(); + builder.addAllRepeatedSint64(singletonList(1L)); + assertEquals(emptyList(), message.getRepeatedSint64List()); + assertEquals(singletonList(1L), builder.getRepeatedSint64List()); + assertEquals(emptyList(), message.getRepeatedSint64List()); + messageAfterBuild = builder.build(); + builder.clearRepeatedSint64(); + assertEquals(emptyList(), builder.getRepeatedSint64List()); + assertEquals(singletonList(1L), messageAfterBuild.getRepeatedSint64List()); + + message = builder.build(); + builder.addAllRepeatedString(singletonList("hi")); + assertEquals(emptyList(), message.getRepeatedStringList()); + assertEquals(singletonList("hi"), builder.getRepeatedStringList()); + assertEquals(emptyList(), message.getRepeatedStringList()); + messageAfterBuild = builder.build(); + builder.clearRepeatedString(); + assertEquals(emptyList(), builder.getRepeatedStringList()); + assertEquals( + singletonList("hi"), messageAfterBuild.getRepeatedStringList()); + + message = builder.build(); + builder.addAllRepeatedStringPiece(singletonList("hi")); + assertEquals(emptyList(), message.getRepeatedStringPieceList()); + assertEquals(singletonList("hi"), builder.getRepeatedStringPieceList()); + assertEquals(emptyList(), message.getRepeatedStringPieceList()); + messageAfterBuild = builder.build(); + builder.clearRepeatedStringPiece(); + assertEquals(emptyList(), builder.getRepeatedStringPieceList()); + assertEquals( + singletonList("hi"), messageAfterBuild.getRepeatedStringPieceList()); + + message = builder.build(); + builder.addAllRepeatedUint32(singletonList(1)); + assertEquals(emptyList(), message.getRepeatedUint32List()); + assertEquals(singletonList(1), builder.getRepeatedUint32List()); + assertEquals(emptyList(), message.getRepeatedUint32List()); + messageAfterBuild = builder.build(); + builder.clearRepeatedUint32(); + assertEquals(emptyList(), builder.getRepeatedUint32List()); + assertEquals(singletonList(1), messageAfterBuild.getRepeatedUint32List()); + + message = builder.build(); + builder.addAllRepeatedUint64(singletonList(1L)); + assertEquals(emptyList(), message.getRepeatedUint64List()); + assertEquals(singletonList(1L), builder.getRepeatedUint64List()); + assertEquals(emptyList(), message.getRepeatedUint64List()); + messageAfterBuild = builder.build(); + builder.clearRepeatedUint64(); + assertEquals(emptyList(), builder.getRepeatedUint64List()); + assertEquals(singletonList(1L), messageAfterBuild.getRepeatedUint64List()); + + message = builder.build(); + builder.addRepeatedBool(true); + assertEquals(emptyList(), message.getRepeatedBoolList()); + assertEquals(singletonList(true), builder.getRepeatedBoolList()); + assertEquals(emptyList(), message.getRepeatedBoolList()); + messageAfterBuild = builder.build(); + builder.clearRepeatedBool(); + assertEquals(emptyList(), builder.getRepeatedBoolList()); + assertEquals(singletonList(true), messageAfterBuild.getRepeatedBoolList()); + + message = builder.build(); + builder.addRepeatedBytes(ByteString.copyFromUtf8("hi")); + assertEquals(emptyList(), message.getRepeatedBytesList()); + assertEquals( + singletonList(ByteString.copyFromUtf8("hi")), + builder.getRepeatedBytesList()); + assertEquals(emptyList(), message.getRepeatedBytesList()); + messageAfterBuild = builder.build(); + builder.clearRepeatedBytes(); + assertEquals(emptyList(), builder.getRepeatedBytesList()); + assertEquals( + singletonList(ByteString.copyFromUtf8("hi")), + messageAfterBuild.getRepeatedBytesList()); + + message = builder.build(); + builder.addRepeatedCord("hi"); + assertEquals(emptyList(), message.getRepeatedCordList()); + assertEquals(singletonList("hi"), builder.getRepeatedCordList()); + assertEquals(emptyList(), message.getRepeatedCordList()); + messageAfterBuild = builder.build(); + builder.clearRepeatedCord(); + assertEquals(emptyList(), builder.getRepeatedCordList()); + assertEquals(singletonList("hi"), messageAfterBuild.getRepeatedCordList()); + + message = builder.build(); + builder.addRepeatedDouble(1D); + assertEquals(emptyList(), message.getRepeatedDoubleList()); + assertEquals(singletonList(1D), builder.getRepeatedDoubleList()); + assertEquals(emptyList(), message.getRepeatedDoubleList()); + messageAfterBuild = builder.build(); + builder.clearRepeatedDouble(); + assertEquals(emptyList(), builder.getRepeatedDoubleList()); + assertEquals(singletonList(1D), messageAfterBuild.getRepeatedDoubleList()); + + message = builder.build(); + builder.addRepeatedFixed32(1); + assertEquals(emptyList(), message.getRepeatedFixed32List()); + assertEquals(singletonList(1), builder.getRepeatedFixed32List()); + assertEquals(emptyList(), message.getRepeatedFixed32List()); + messageAfterBuild = builder.build(); + builder.clearRepeatedFixed32(); + assertEquals(emptyList(), builder.getRepeatedFixed32List()); + assertEquals(singletonList(1), messageAfterBuild.getRepeatedFixed32List()); + + message = builder.build(); + builder.addRepeatedFixed64(1L); + assertEquals(emptyList(), message.getRepeatedFixed64List()); + assertEquals(singletonList(1L), builder.getRepeatedFixed64List()); + assertEquals(emptyList(), message.getRepeatedFixed64List()); + messageAfterBuild = builder.build(); + builder.clearRepeatedFixed64(); + assertEquals(emptyList(), builder.getRepeatedFixed64List()); + assertEquals(singletonList(1L), messageAfterBuild.getRepeatedFixed64List()); + + message = builder.build(); + builder.addRepeatedFloat(1F); + assertEquals(emptyList(), message.getRepeatedFloatList()); + assertEquals(singletonList(1F), builder.getRepeatedFloatList()); + assertEquals(emptyList(), message.getRepeatedFloatList()); + messageAfterBuild = builder.build(); + builder.clearRepeatedFloat(); + assertEquals(emptyList(), builder.getRepeatedFloatList()); + assertEquals(singletonList(1F), messageAfterBuild.getRepeatedFloatList()); + + message = builder.build(); + builder.addRepeatedForeignEnum(ForeignEnumLite.FOREIGN_LITE_BAR); + assertEquals(emptyList(), message.getRepeatedForeignEnumList()); + assertEquals( + singletonList(ForeignEnumLite.FOREIGN_LITE_BAR), + builder.getRepeatedForeignEnumList()); + assertEquals(emptyList(), message.getRepeatedForeignEnumList()); + messageAfterBuild = builder.build(); + builder.clearRepeatedForeignEnum(); + assertEquals(emptyList(), builder.getRepeatedForeignEnumList()); + assertEquals( + singletonList(ForeignEnumLite.FOREIGN_LITE_BAR), + messageAfterBuild.getRepeatedForeignEnumList()); + + message = builder.build(); + builder.addRepeatedForeignMessage(foreignMessage); + assertEquals(emptyList(), message.getRepeatedForeignMessageList()); + assertEquals( + singletonList(foreignMessage), builder.getRepeatedForeignMessageList()); + assertEquals(emptyList(), message.getRepeatedForeignMessageList()); + messageAfterBuild = builder.build(); + builder.removeRepeatedForeignMessage(0); + assertEquals(emptyList(), builder.getRepeatedForeignMessageList()); + assertEquals( + singletonList(foreignMessage), + messageAfterBuild.getRepeatedForeignMessageList()); + + message = builder.build(); + builder.addRepeatedGroup(RepeatedGroup.getDefaultInstance()); + assertEquals(emptyList(), message.getRepeatedGroupList()); + assertEquals( + singletonList(RepeatedGroup.getDefaultInstance()), + builder.getRepeatedGroupList()); + assertEquals(emptyList(), message.getRepeatedGroupList()); + messageAfterBuild = builder.build(); + builder.removeRepeatedGroup(0); + assertEquals(emptyList(), builder.getRepeatedGroupList()); + assertEquals( + singletonList(RepeatedGroup.getDefaultInstance()), + messageAfterBuild.getRepeatedGroupList()); + + message = builder.build(); + builder.addRepeatedInt32(1); + assertEquals(emptyList(), message.getRepeatedInt32List()); + assertEquals(singletonList(1), builder.getRepeatedInt32List()); + assertEquals(emptyList(), message.getRepeatedInt32List()); + messageAfterBuild = builder.build(); + builder.clearRepeatedInt32(); + assertEquals(emptyList(), builder.getRepeatedInt32List()); + assertEquals(singletonList(1), messageAfterBuild.getRepeatedInt32List()); + + message = builder.build(); + builder.addRepeatedInt64(1L); + assertEquals(emptyList(), message.getRepeatedInt64List()); + assertEquals(singletonList(1L), builder.getRepeatedInt64List()); + assertEquals(emptyList(), message.getRepeatedInt64List()); + messageAfterBuild = builder.build(); + builder.clearRepeatedInt64(); + assertEquals(emptyList(), builder.getRepeatedInt64List()); + assertEquals(singletonList(1L), messageAfterBuild.getRepeatedInt64List()); + + message = builder.build(); + builder.addRepeatedLazyMessage(nestedMessage); + assertEquals(emptyList(), message.getRepeatedLazyMessageList()); + assertEquals( + singletonList(nestedMessage), builder.getRepeatedLazyMessageList()); + assertEquals(emptyList(), message.getRepeatedLazyMessageList()); + messageAfterBuild = builder.build(); + builder.removeRepeatedLazyMessage(0); + assertEquals(emptyList(), builder.getRepeatedLazyMessageList()); + assertEquals( + singletonList(nestedMessage), + messageAfterBuild.getRepeatedLazyMessageList()); + + message = builder.build(); + builder.addRepeatedSfixed32(1); + assertEquals(emptyList(), message.getRepeatedSfixed32List()); + assertEquals(singletonList(1), builder.getRepeatedSfixed32List()); + assertEquals(emptyList(), message.getRepeatedSfixed32List()); + messageAfterBuild = builder.build(); + builder.clearRepeatedSfixed32(); + assertEquals(emptyList(), builder.getRepeatedSfixed32List()); + assertEquals(singletonList(1), messageAfterBuild.getRepeatedSfixed32List()); + + message = builder.build(); + builder.addRepeatedSfixed64(1L); + assertEquals(emptyList(), message.getRepeatedSfixed64List()); + assertEquals(singletonList(1L), builder.getRepeatedSfixed64List()); + assertEquals(emptyList(), message.getRepeatedSfixed64List()); + messageAfterBuild = builder.build(); + builder.clearRepeatedSfixed64(); + assertEquals(emptyList(), builder.getRepeatedSfixed64List()); + assertEquals( + singletonList(1L), messageAfterBuild.getRepeatedSfixed64List()); + + message = builder.build(); + builder.addRepeatedSint32(1); + assertEquals(emptyList(), message.getRepeatedSint32List()); + assertEquals(singletonList(1), builder.getRepeatedSint32List()); + assertEquals(emptyList(), message.getRepeatedSint32List()); + messageAfterBuild = builder.build(); + builder.clearRepeatedSint32(); + assertEquals(emptyList(), builder.getRepeatedSint32List()); + assertEquals(singletonList(1), messageAfterBuild.getRepeatedSint32List()); + + message = builder.build(); + builder.addRepeatedSint64(1L); + assertEquals(emptyList(), message.getRepeatedSint64List()); + assertEquals(singletonList(1L), builder.getRepeatedSint64List()); + assertEquals(emptyList(), message.getRepeatedSint64List()); + messageAfterBuild = builder.build(); + builder.clearRepeatedSint64(); + assertEquals(emptyList(), builder.getRepeatedSint64List()); + assertEquals(singletonList(1L), messageAfterBuild.getRepeatedSint64List()); + + message = builder.build(); + builder.addRepeatedString("hi"); + assertEquals(emptyList(), message.getRepeatedStringList()); + assertEquals(singletonList("hi"), builder.getRepeatedStringList()); + assertEquals(emptyList(), message.getRepeatedStringList()); + messageAfterBuild = builder.build(); + builder.clearRepeatedString(); + assertEquals(emptyList(), builder.getRepeatedStringList()); + assertEquals( + singletonList("hi"), messageAfterBuild.getRepeatedStringList()); + + message = builder.build(); + builder.addRepeatedStringPiece("hi"); + assertEquals(emptyList(), message.getRepeatedStringPieceList()); + assertEquals(singletonList("hi"), builder.getRepeatedStringPieceList()); + assertEquals(emptyList(), message.getRepeatedStringPieceList()); + messageAfterBuild = builder.build(); + builder.clearRepeatedStringPiece(); + assertEquals(emptyList(), builder.getRepeatedStringPieceList()); + assertEquals( + singletonList("hi"), messageAfterBuild.getRepeatedStringPieceList()); + + message = builder.build(); + builder.addRepeatedUint32(1); + assertEquals(emptyList(), message.getRepeatedUint32List()); + assertEquals(singletonList(1), builder.getRepeatedUint32List()); + assertEquals(emptyList(), message.getRepeatedUint32List()); + messageAfterBuild = builder.build(); + builder.clearRepeatedUint32(); + assertEquals(emptyList(), builder.getRepeatedUint32List()); + assertEquals(singletonList(1), messageAfterBuild.getRepeatedUint32List()); + + message = builder.build(); + builder.addRepeatedUint64(1L); + assertEquals(emptyList(), message.getRepeatedUint64List()); + assertEquals(singletonList(1L), builder.getRepeatedUint64List()); + assertEquals(emptyList(), message.getRepeatedUint64List()); + messageAfterBuild = builder.build(); + builder.clearRepeatedUint64(); + assertEquals(emptyList(), builder.getRepeatedUint64List()); + assertEquals(singletonList(1L), messageAfterBuild.getRepeatedUint64List()); + + message = builder.build(); + builder.addRepeatedBool(true); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedBoolCount()); + builder.setRepeatedBool(0, false); + assertEquals(true, messageAfterBuild.getRepeatedBool(0)); + assertEquals(false, builder.getRepeatedBool(0)); + builder.clearRepeatedBool(); + + message = builder.build(); + builder.addRepeatedBytes(ByteString.copyFromUtf8("hi")); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedBytesCount()); + builder.setRepeatedBytes(0, ByteString.EMPTY); + assertEquals( + ByteString.copyFromUtf8("hi"), messageAfterBuild.getRepeatedBytes(0)); + assertEquals(ByteString.EMPTY, builder.getRepeatedBytes(0)); + builder.clearRepeatedBytes(); + + message = builder.build(); + builder.addRepeatedCord("hi"); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedCordCount()); + builder.setRepeatedCord(0, ""); + assertEquals("hi", messageAfterBuild.getRepeatedCord(0)); + assertEquals("", builder.getRepeatedCord(0)); + builder.clearRepeatedCord(); + message = builder.build(); + + builder.addRepeatedCordBytes(ByteString.copyFromUtf8("hi")); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedCordCount()); + builder.setRepeatedCord(0, ""); + assertEquals( + ByteString.copyFromUtf8("hi"), messageAfterBuild.getRepeatedCordBytes(0)); + assertEquals(ByteString.EMPTY, builder.getRepeatedCordBytes(0)); + builder.clearRepeatedCord(); + + message = builder.build(); + builder.addRepeatedDouble(1D); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedDoubleCount()); + builder.setRepeatedDouble(0, 0D); + assertEquals(1D, messageAfterBuild.getRepeatedDouble(0)); + assertEquals(0D, builder.getRepeatedDouble(0)); + builder.clearRepeatedDouble(); + + message = builder.build(); + builder.addRepeatedFixed32(1); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedFixed32Count()); + builder.setRepeatedFixed32(0, 0); + assertEquals(1, messageAfterBuild.getRepeatedFixed32(0)); + assertEquals(0, builder.getRepeatedFixed32(0)); + builder.clearRepeatedFixed32(); + + message = builder.build(); + builder.addRepeatedFixed64(1L); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedFixed64Count()); + builder.setRepeatedFixed64(0, 0L); + assertEquals(1L, messageAfterBuild.getRepeatedFixed64(0)); + assertEquals(0L, builder.getRepeatedFixed64(0)); + builder.clearRepeatedFixed64(); + + message = builder.build(); + builder.addRepeatedFloat(1F); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedFloatCount()); + builder.setRepeatedFloat(0, 0F); + assertEquals(1F, messageAfterBuild.getRepeatedFloat(0)); + assertEquals(0F, builder.getRepeatedFloat(0)); + builder.clearRepeatedFloat(); + + message = builder.build(); + builder.addRepeatedForeignEnum(ForeignEnumLite.FOREIGN_LITE_BAR); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedForeignEnumCount()); + builder.setRepeatedForeignEnum(0, ForeignEnumLite.FOREIGN_LITE_FOO); + assertEquals( + ForeignEnumLite.FOREIGN_LITE_BAR, + messageAfterBuild.getRepeatedForeignEnum(0)); + assertEquals( + ForeignEnumLite.FOREIGN_LITE_FOO, builder.getRepeatedForeignEnum(0)); + builder.clearRepeatedForeignEnum(); + + message = builder.build(); + builder.addRepeatedForeignMessage(foreignMessage); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedForeignMessageCount()); + builder.setRepeatedForeignMessage( + 0, ForeignMessageLite.getDefaultInstance()); + assertEquals( + foreignMessage, messageAfterBuild.getRepeatedForeignMessage(0)); + assertEquals( + ForeignMessageLite.getDefaultInstance(), + builder.getRepeatedForeignMessage(0)); + builder.clearRepeatedForeignMessage(); + + message = builder.build(); + builder.addRepeatedForeignMessage(foreignMessageBuilder); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedForeignMessageCount()); + builder.setRepeatedForeignMessage( + 0, ForeignMessageLite.getDefaultInstance()); + // LITE_RUNTIME doesn't implement equals so we compare on a property. + assertEquals(3, messageAfterBuild.getRepeatedForeignMessage(0).getC()); + assertEquals( + ForeignMessageLite.getDefaultInstance(), + builder.getRepeatedForeignMessage(0)); + builder.clearRepeatedForeignMessage(); + + message = builder.build(); + builder.addRepeatedForeignMessage(0, foreignMessage); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedForeignMessageCount()); + builder.setRepeatedForeignMessage(0, foreignMessageBuilder); + assertEquals( + foreignMessage, messageAfterBuild.getRepeatedForeignMessage(0)); + // LITE_RUNTIME doesn't implement equals so we compare on a property. + assertEquals(3, builder.getRepeatedForeignMessage(0).getC()); + builder.clearRepeatedForeignMessage(); + + message = builder.build(); + RepeatedGroup repeatedGroup = RepeatedGroup.newBuilder() + .setA(1) + .build(); + builder.addRepeatedGroup(repeatedGroup); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedGroupCount()); + builder.setRepeatedGroup(0, RepeatedGroup.getDefaultInstance()); + assertEquals(repeatedGroup, messageAfterBuild.getRepeatedGroup(0)); + assertEquals( + RepeatedGroup.getDefaultInstance(), builder.getRepeatedGroup(0)); + builder.clearRepeatedGroup(); + + message = builder.build(); + builder.addRepeatedGroup(0, repeatedGroup); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedGroupCount()); + builder.setRepeatedGroup(0, RepeatedGroup.getDefaultInstance()); + assertEquals(repeatedGroup, messageAfterBuild.getRepeatedGroup(0)); + assertEquals( + RepeatedGroup.getDefaultInstance(), builder.getRepeatedGroup(0)); + builder.clearRepeatedGroup(); + + message = builder.build(); + RepeatedGroup.Builder repeatedGroupBuilder = RepeatedGroup.newBuilder() + .setA(3); + builder.addRepeatedGroup(repeatedGroupBuilder); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedGroupCount()); + builder.setRepeatedGroup(0, RepeatedGroup.getDefaultInstance()); + // LITE_RUNTIME doesn't implement equals so we compare on a property and + // ensure the property isn't set on repeatedGroup. + assertEquals(3, messageAfterBuild.getRepeatedGroup(0).getA()); + assertEquals( + RepeatedGroup.getDefaultInstance(), builder.getRepeatedGroup(0)); + builder.clearRepeatedGroup(); + + message = builder.build(); + builder.addRepeatedGroup(0, repeatedGroupBuilder); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedGroupCount()); + builder.setRepeatedGroup(0, RepeatedGroup.getDefaultInstance()); + // LITE_RUNTIME doesn't implement equals so we compare on a property and + // ensure the property isn't set on repeatedGroup. + assertEquals(3, messageAfterBuild.getRepeatedGroup(0).getA()); + assertEquals( + RepeatedGroup.getDefaultInstance(), builder.getRepeatedGroup(0)); + builder.clearRepeatedGroup(); + + message = builder.build(); + builder.addRepeatedInt32(1); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedInt32Count()); + builder.setRepeatedInt32(0, 0); + assertEquals(1, messageAfterBuild.getRepeatedInt32(0)); + assertEquals(0, builder.getRepeatedInt32(0)); + builder.clearRepeatedInt32(); + + message = builder.build(); + builder.addRepeatedInt64(1L); + messageAfterBuild = builder.build(); + assertEquals(0L, message.getRepeatedInt64Count()); + builder.setRepeatedInt64(0, 0L); + assertEquals(1L, messageAfterBuild.getRepeatedInt64(0)); + assertEquals(0L, builder.getRepeatedInt64(0)); + builder.clearRepeatedInt64(); + + message = builder.build(); + builder.addRepeatedLazyMessage(nestedMessage); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedLazyMessageCount()); + builder.setRepeatedLazyMessage(0, NestedMessage.getDefaultInstance()); + assertEquals(nestedMessage, messageAfterBuild.getRepeatedLazyMessage(0)); + assertEquals( + NestedMessage.getDefaultInstance(), builder.getRepeatedLazyMessage(0)); + builder.clearRepeatedLazyMessage(); + + message = builder.build(); + builder.addRepeatedLazyMessage(0, nestedMessage); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedLazyMessageCount()); + builder.setRepeatedLazyMessage(0, NestedMessage.getDefaultInstance()); + assertEquals(nestedMessage, messageAfterBuild.getRepeatedLazyMessage(0)); + assertEquals( + NestedMessage.getDefaultInstance(), builder.getRepeatedLazyMessage(0)); + builder.clearRepeatedLazyMessage(); + + message = builder.build(); + builder.addRepeatedLazyMessage(nestedMessageBuilder); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedLazyMessageCount()); + builder.setRepeatedLazyMessage(0, NestedMessage.getDefaultInstance()); + // LITE_RUNTIME doesn't implement equals so we compare on a property and + // ensure the property isn't set on repeatedGroup. + assertEquals(3, messageAfterBuild.getRepeatedLazyMessage(0).getBb()); + assertEquals( + NestedMessage.getDefaultInstance(), builder.getRepeatedLazyMessage(0)); + builder.clearRepeatedLazyMessage(); + + message = builder.build(); + builder.addRepeatedLazyMessage(0, nestedMessageBuilder); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedLazyMessageCount()); + builder.setRepeatedLazyMessage(0, NestedMessage.getDefaultInstance()); + // LITE_RUNTIME doesn't implement equals so we compare on a property and + // ensure the property isn't set on repeatedGroup. + assertEquals(3, messageAfterBuild.getRepeatedLazyMessage(0).getBb()); + assertEquals( + NestedMessage.getDefaultInstance(), builder.getRepeatedLazyMessage(0)); + builder.clearRepeatedLazyMessage(); + + message = builder.build(); + builder.addRepeatedSfixed32(1); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedSfixed32Count()); + builder.setRepeatedSfixed32(0, 0); + assertEquals(1, messageAfterBuild.getRepeatedSfixed32(0)); + assertEquals(0, builder.getRepeatedSfixed32(0)); + builder.clearRepeatedSfixed32(); + + message = builder.build(); + builder.addRepeatedSfixed64(1L); + messageAfterBuild = builder.build(); + assertEquals(0L, message.getRepeatedSfixed64Count()); + builder.setRepeatedSfixed64(0, 0L); + assertEquals(1L, messageAfterBuild.getRepeatedSfixed64(0)); + assertEquals(0L, builder.getRepeatedSfixed64(0)); + builder.clearRepeatedSfixed64(); + + message = builder.build(); + builder.addRepeatedSint32(1); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedSint32Count()); + builder.setRepeatedSint32(0, 0); + assertEquals(1, messageAfterBuild.getRepeatedSint32(0)); + assertEquals(0, builder.getRepeatedSint32(0)); + builder.clearRepeatedSint32(); + + message = builder.build(); + builder.addRepeatedSint64(1L); + messageAfterBuild = builder.build(); + assertEquals(0L, message.getRepeatedSint64Count()); + builder.setRepeatedSint64(0, 0L); + assertEquals(1L, messageAfterBuild.getRepeatedSint64(0)); + assertEquals(0L, builder.getRepeatedSint64(0)); + builder.clearRepeatedSint64(); + + message = builder.build(); + builder.addRepeatedString("hi"); + messageAfterBuild = builder.build(); + assertEquals(0L, message.getRepeatedStringCount()); + builder.setRepeatedString(0, ""); + assertEquals("hi", messageAfterBuild.getRepeatedString(0)); + assertEquals("", builder.getRepeatedString(0)); + builder.clearRepeatedString(); + + message = builder.build(); + builder.addRepeatedStringBytes(ByteString.copyFromUtf8("hi")); + messageAfterBuild = builder.build(); + assertEquals(0L, message.getRepeatedStringCount()); + builder.setRepeatedString(0, ""); + assertEquals( + ByteString.copyFromUtf8("hi"), + messageAfterBuild.getRepeatedStringBytes(0)); + assertEquals(ByteString.EMPTY, builder.getRepeatedStringBytes(0)); + builder.clearRepeatedString(); + + message = builder.build(); + builder.addRepeatedStringPiece("hi"); + messageAfterBuild = builder.build(); + assertEquals(0L, message.getRepeatedStringPieceCount()); + builder.setRepeatedStringPiece(0, ""); + assertEquals("hi", messageAfterBuild.getRepeatedStringPiece(0)); + assertEquals("", builder.getRepeatedStringPiece(0)); + builder.clearRepeatedStringPiece(); + + message = builder.build(); + builder.addRepeatedStringPieceBytes(ByteString.copyFromUtf8("hi")); + messageAfterBuild = builder.build(); + assertEquals(0L, message.getRepeatedStringPieceCount()); + builder.setRepeatedStringPiece(0, ""); + assertEquals( + ByteString.copyFromUtf8("hi"), + messageAfterBuild.getRepeatedStringPieceBytes(0)); + assertEquals(ByteString.EMPTY, builder.getRepeatedStringPieceBytes(0)); + builder.clearRepeatedStringPiece(); + + message = builder.build(); + builder.addRepeatedUint32(1); + messageAfterBuild = builder.build(); + assertEquals(0, message.getRepeatedUint32Count()); + builder.setRepeatedUint32(0, 0); + assertEquals(1, messageAfterBuild.getRepeatedUint32(0)); + assertEquals(0, builder.getRepeatedUint32(0)); + builder.clearRepeatedUint32(); + + message = builder.build(); + builder.addRepeatedUint64(1L); + messageAfterBuild = builder.build(); + assertEquals(0L, message.getRepeatedUint64Count()); + builder.setRepeatedUint64(0, 0L); + assertEquals(1L, messageAfterBuild.getRepeatedUint64(0)); + assertEquals(0L, builder.getRepeatedUint64(0)); + builder.clearRepeatedUint64(); + + message = builder.build(); + assertEquals(0, message.getSerializedSize()); + builder.mergeFrom(TestAllTypesLite.newBuilder() + .setOptionalBool(true) + .build()); + assertEquals(0, message.getSerializedSize()); + assertEquals(true, builder.build().getOptionalBool()); + builder.clearOptionalBool(); + + message = builder.build(); + assertEquals(0, message.getSerializedSize()); + builder.mergeFrom(TestAllTypesLite.newBuilder() + .setOptionalBool(true) + .build()); + assertEquals(0, message.getSerializedSize()); + assertEquals(true, builder.build().getOptionalBool()); + builder.clear(); + assertEquals(0, builder.build().getSerializedSize()); + + message = builder.build(); + assertEquals(0, message.getSerializedSize()); + builder.mergeOptionalForeignMessage(foreignMessage); + assertEquals(0, message.getSerializedSize()); + assertEquals( + foreignMessage.getC(), + builder.build().getOptionalForeignMessage().getC()); + builder.clearOptionalForeignMessage(); + + message = builder.build(); + assertEquals(0, message.getSerializedSize()); + builder.mergeOptionalLazyMessage(nestedMessage); + assertEquals(0, message.getSerializedSize()); + assertEquals( + nestedMessage.getBb(), + builder.build().getOptionalLazyMessage().getBb()); + builder.clearOptionalLazyMessage(); + + message = builder.build(); + builder.setOneofString("hi"); + assertEquals( + OneofFieldCase.ONEOFFIELD_NOT_SET, message.getOneofFieldCase()); + assertEquals(OneofFieldCase.ONEOF_STRING, builder.getOneofFieldCase()); + assertEquals("hi", builder.getOneofString()); + messageAfterBuild = builder.build(); + assertEquals( + OneofFieldCase.ONEOF_STRING, messageAfterBuild.getOneofFieldCase()); + assertEquals("hi", messageAfterBuild.getOneofString()); + builder.setOneofUint32(1); + assertEquals( + OneofFieldCase.ONEOF_STRING, messageAfterBuild.getOneofFieldCase()); + assertEquals("hi", messageAfterBuild.getOneofString()); + assertEquals(OneofFieldCase.ONEOF_UINT32, builder.getOneofFieldCase()); + assertEquals(1, builder.getOneofUint32()); + + TestAllExtensionsLite.Builder extendableMessageBuilder = + TestAllExtensionsLite.newBuilder(); + TestAllExtensionsLite extendableMessage = extendableMessageBuilder.build(); + extendableMessageBuilder.setExtension( + UnittestLite.optionalInt32ExtensionLite, 1); + assertFalse(extendableMessage.hasExtension( + UnittestLite.optionalInt32ExtensionLite)); + extendableMessage = extendableMessageBuilder.build(); + assertEquals( + 1, (int) extendableMessageBuilder.getExtension( + UnittestLite.optionalInt32ExtensionLite)); + assertEquals( + 1, (int) extendableMessage.getExtension( + UnittestLite.optionalInt32ExtensionLite)); + extendableMessageBuilder.setExtension( + UnittestLite.optionalInt32ExtensionLite, 3); + assertEquals( + 3, (int) extendableMessageBuilder.getExtension( + UnittestLite.optionalInt32ExtensionLite)); + assertEquals( + 1, (int) extendableMessage.getExtension( + UnittestLite.optionalInt32ExtensionLite)); + extendableMessage = extendableMessageBuilder.build(); + assertEquals( + 3, (int) extendableMessageBuilder.getExtension( + UnittestLite.optionalInt32ExtensionLite)); + assertEquals( + 3, (int) extendableMessage.getExtension( + UnittestLite.optionalInt32ExtensionLite)); + + // No extension registry, so it should be in unknown fields. + extendableMessage = + TestAllExtensionsLite.parseFrom(extendableMessage.toByteArray()); + assertFalse(extendableMessage.hasExtension( + UnittestLite.optionalInt32ExtensionLite)); + + extendableMessageBuilder = extendableMessage.toBuilder(); + extendableMessageBuilder.mergeFrom(TestAllExtensionsLite.newBuilder() + .setExtension(UnittestLite.optionalFixed32ExtensionLite, 11) + .build()); + + extendableMessage = extendableMessageBuilder.build(); + ExtensionRegistryLite registry = ExtensionRegistryLite.newInstance(); + UnittestLite.registerAllExtensions(registry); + extendableMessage = TestAllExtensionsLite.parseFrom( + extendableMessage.toByteArray(), registry); + + // The unknown field was preserved. + assertEquals( + 3, (int) extendableMessage.getExtension( + UnittestLite.optionalInt32ExtensionLite)); + assertEquals( + 11, (int) extendableMessage.getExtension( + UnittestLite.optionalFixed32ExtensionLite)); } } diff --git a/java/src/test/java/com/google/protobuf/LiteralByteStringTest.java b/java/src/test/java/com/google/protobuf/LiteralByteStringTest.java index 7b201a9d..958b6a7e 100644 --- a/java/src/test/java/com/google/protobuf/LiteralByteStringTest.java +++ b/java/src/test/java/com/google/protobuf/LiteralByteStringTest.java @@ -307,7 +307,8 @@ public class LiteralByteStringTest extends TestCase { public void testToString_returnsCanonicalEmptyString() throws UnsupportedEncodingException{ assertSame(classUnderTest + " must be the same string references", - ByteString.EMPTY.toString(Internal.UTF_8), new LiteralByteString(new byte[]{}).toString(Internal.UTF_8)); + ByteString.EMPTY.toString(Internal.UTF_8), + new LiteralByteString(new byte[]{}).toString(Internal.UTF_8)); } public void testToString_raisesException() throws UnsupportedEncodingException{ diff --git a/java/src/test/java/com/google/protobuf/LongArrayListTest.java b/java/src/test/java/com/google/protobuf/LongArrayListTest.java new file mode 100644 index 00000000..3a52ec7f --- /dev/null +++ b/java/src/test/java/com/google/protobuf/LongArrayListTest.java @@ -0,0 +1,473 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import static java.util.Arrays.asList; + +import junit.framework.TestCase; + +import java.util.Collections; +import java.util.ConcurrentModificationException; +import java.util.Iterator; + +/** + * Tests for {@link LongArrayList}. + * + * @author dweis@google.com (Daniel Weis) + */ +public class LongArrayListTest extends TestCase { + + private static final LongArrayList UNARY_LIST = newImmutableLongArrayList(1); + private static final LongArrayList TERTIARY_LIST = + newImmutableLongArrayList(1, 2, 3); + + private LongArrayList list; + + @Override + protected void setUp() throws Exception { + list = new LongArrayList(); + } + + public void testEmptyListReturnsSameInstance() { + assertSame(LongArrayList.emptyList(), LongArrayList.emptyList()); + } + + public void testEmptyListIsImmutable() { + assertImmutable(LongArrayList.emptyList()); + } + + public void testMakeImmutable() { + list.addLong(2); + list.addLong(4); + list.addLong(6); + list.addLong(8); + list.makeImmutable(); + assertImmutable(list); + } + + public void testCopyConstructor() { + LongArrayList copy = new LongArrayList(TERTIARY_LIST); + assertEquals(TERTIARY_LIST, copy); + + copy = new LongArrayList(LongArrayList.emptyList()); + assertEquals(LongArrayList.emptyList(), copy); + + copy = new LongArrayList(asList(1L, 2L, 3L)); + assertEquals(asList(1L, 2L, 3L), copy); + + copy = new LongArrayList(Collections.emptyList()); + assertEquals(LongArrayList.emptyList(), copy); + } + + public void testModificationWithIteration() { + list.addAll(asList(1L, 2L, 3L, 4L)); + Iterator iterator = list.iterator(); + assertEquals(4, list.size()); + assertEquals(1, (long) list.get(0)); + assertEquals(1, (long) iterator.next()); + list.set(0, 1L); + assertEquals(2, (long) iterator.next()); + + list.remove(0); + try { + iterator.next(); + fail(); + } catch (ConcurrentModificationException e) { + // expected + } + + iterator = list.iterator(); + list.add(0, 0L); + try { + iterator.next(); + fail(); + } catch (ConcurrentModificationException e) { + // expected + } + } + + public void testGet() { + assertEquals(1, (long) TERTIARY_LIST.get(0)); + assertEquals(2, (long) TERTIARY_LIST.get(1)); + assertEquals(3, (long) TERTIARY_LIST.get(2)); + + try { + TERTIARY_LIST.get(-1); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + TERTIARY_LIST.get(3); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testGetLong() { + assertEquals(1, TERTIARY_LIST.getLong(0)); + assertEquals(2, TERTIARY_LIST.getLong(1)); + assertEquals(3, TERTIARY_LIST.getLong(2)); + + try { + TERTIARY_LIST.get(-1); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + TERTIARY_LIST.get(3); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testSize() { + assertEquals(0, LongArrayList.emptyList().size()); + assertEquals(1, UNARY_LIST.size()); + assertEquals(3, TERTIARY_LIST.size()); + + list.addLong(2); + list.addLong(4); + list.addLong(6); + list.addLong(8); + assertEquals(4, list.size()); + + list.remove(0); + assertEquals(3, list.size()); + + list.add(16L); + assertEquals(4, list.size()); + } + + public void testSet() { + list.addLong(2); + list.addLong(4); + + assertEquals(2, (long) list.set(0, 0L)); + assertEquals(0, list.getLong(0)); + + assertEquals(4, (long) list.set(1, 0L)); + assertEquals(0, list.getLong(1)); + + try { + list.set(-1, 0L); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + list.set(2, 0L); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testSetLong() { + list.addLong(2); + list.addLong(4); + + assertEquals(2, list.setLong(0, 0)); + assertEquals(0, list.getLong(0)); + + assertEquals(4, list.setLong(1, 0)); + assertEquals(0, list.getLong(1)); + + try { + list.setLong(-1, 0); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + list.setLong(2, 0); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testAdd() { + assertEquals(0, list.size()); + + assertTrue(list.add(2L)); + assertEquals(asList(2L), list); + + assertTrue(list.add(3L)); + list.add(0, 4L); + assertEquals(asList(4L, 2L, 3L), list); + + list.add(0, 1L); + list.add(0, 0L); + // Force a resize by getting up to 11 elements. + for (int i = 0; i < 6; i++) { + list.add(Long.valueOf(5 + i)); + } + assertEquals(asList(0L, 1L, 4L, 2L, 3L, 5L, 6L, 7L, 8L, 9L, 10L), list); + + try { + list.add(-1, 5L); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + list.add(4, 5L); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + public void testAddLong() { + assertEquals(0, list.size()); + + list.addLong(2); + assertEquals(asList(2L), list); + + list.addLong(3); + assertEquals(asList(2L, 3L), list); + } + + public void testAddAll() { + assertEquals(0, list.size()); + + assertTrue(list.addAll(Collections.singleton(1L))); + assertEquals(1, list.size()); + assertEquals(1, (long) list.get(0)); + assertEquals(1, list.getLong(0)); + + assertTrue(list.addAll(asList(2L, 3L, 4L, 5L, 6L))); + assertEquals(asList(1L, 2L, 3L, 4L, 5L, 6L), list); + + assertTrue(list.addAll(TERTIARY_LIST)); + assertEquals(asList(1L, 2L, 3L, 4L, 5L, 6L, 1L, 2L, 3L), list); + + assertFalse(list.addAll(Collections.emptyList())); + assertFalse(list.addAll(LongArrayList.emptyList())); + } + + public void testRemove() { + list.addAll(TERTIARY_LIST); + assertEquals(1, (long) list.remove(0)); + assertEquals(asList(2L, 3L), list); + + assertTrue(list.remove(3L)); + assertEquals(asList(2L), list); + + assertFalse(list.remove(3L)); + assertEquals(asList(2L), list); + + assertEquals(2, (long) list.remove(0)); + assertEquals(asList(), list); + + try { + list.remove(-1); + fail(); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + list.remove(0); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + private void assertImmutable(LongArrayList list) { + if (list.contains(1)) { + throw new RuntimeException("Cannot test the immutability of lists that contain 1."); + } + + try { + list.add(1L); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.add(0, 1L); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(Collections.singletonList(1L)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(new LongArrayList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(0, Collections.singleton(1L)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(0, UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(0, Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addLong(0); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.clear(); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.remove(1); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.remove(new Object()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.removeAll(Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.removeAll(Collections.singleton(1)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.removeAll(UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.retainAll(Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.retainAll(Collections.singleton(1)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.retainAll(UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.set(0, 0L); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.setLong(0, 0); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + } + + private static LongArrayList newImmutableLongArrayList(long... elements) { + LongArrayList list = new LongArrayList(); + for (long element : elements) { + list.addLong(element); + } + list.makeImmutable(); + return list; + } +} diff --git a/java/src/test/java/com/google/protobuf/MapForProto2LiteTest.java b/java/src/test/java/com/google/protobuf/MapForProto2LiteTest.java index 22dbeb76..6cff689f 100644 --- a/java/src/test/java/com/google/protobuf/MapForProto2LiteTest.java +++ b/java/src/test/java/com/google/protobuf/MapForProto2LiteTest.java @@ -36,6 +36,11 @@ import map_lite_test.MapForProto2TestProto.TestUnknownEnumValue; import junit.framework.TestCase; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + /** * Unit tests for map fields. */ @@ -170,6 +175,140 @@ public class MapForProto2LiteTest extends TestCase { assertEquals(0, message.getStringToInt32Field().size()); } + public void testSanityCopyOnWrite() throws InvalidProtocolBufferException { + // Since builders are implemented as a thin wrapper around a message + // instance, we attempt to verify that we can't cause the builder to modify + // a produced message. + + TestMap.Builder builder = TestMap.newBuilder(); + TestMap message = builder.build(); + Map intMap = builder.getMutableInt32ToInt32Field(); + intMap.put(1, 2); + assertTrue(message.getInt32ToInt32Field().isEmpty()); + message = builder.build(); + try { + intMap.put(2, 3); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + assertEquals(newMap(1, 2), message.getInt32ToInt32Field()); + assertEquals(newMap(1, 2), builder.getInt32ToInt32Field()); + builder.getMutableInt32ToInt32Field().put(2, 3); + assertEquals(newMap(1, 2), message.getInt32ToInt32Field()); + assertEquals(newMap(1, 2, 2, 3), builder.getInt32ToInt32Field()); + } + + public void testMutableMapLifecycle() { + TestMap.Builder builder = TestMap.newBuilder(); + Map intMap = builder.getMutableInt32ToInt32Field(); + intMap.put(1, 2); + assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field()); + try { + intMap.put(2, 3); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + assertEquals(newMap(1, 2), builder.getInt32ToInt32Field()); + builder.getMutableInt32ToInt32Field().put(2, 3); + assertEquals(newMap(1, 2, 2, 3), builder.getInt32ToInt32Field()); + + Map enumMap = builder.getMutableInt32ToEnumField(); + enumMap.put(1, TestMap.EnumValue.BAR); + assertEquals(newMap(1, TestMap.EnumValue.BAR), builder.build().getInt32ToEnumField()); + try { + enumMap.put(2, TestMap.EnumValue.FOO); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + assertEquals(newMap(1, TestMap.EnumValue.BAR), builder.getInt32ToEnumField()); + builder.getMutableInt32ToEnumField().put(2, TestMap.EnumValue.FOO); + assertEquals( + newMap(1, TestMap.EnumValue.BAR, 2, TestMap.EnumValue.FOO), + builder.getInt32ToEnumField()); + + Map stringMap = builder.getMutableInt32ToStringField(); + stringMap.put(1, "1"); + assertEquals(newMap(1, "1"), builder.build().getInt32ToStringField()); + try { + stringMap.put(2, "2"); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + assertEquals(newMap(1, "1"), builder.getInt32ToStringField()); + builder.getMutableInt32ToStringField().put(2, "2"); + assertEquals( + newMap(1, "1", 2, "2"), + builder.getInt32ToStringField()); + + Map messageMap = builder.getMutableInt32ToMessageField(); + messageMap.put(1, TestMap.MessageValue.getDefaultInstance()); + assertEquals(newMap(1, TestMap.MessageValue.getDefaultInstance()), + builder.build().getInt32ToMessageField()); + try { + messageMap.put(2, TestMap.MessageValue.getDefaultInstance()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + assertEquals(newMap(1, TestMap.MessageValue.getDefaultInstance()), + builder.getInt32ToMessageField()); + builder.getMutableInt32ToMessageField().put(2, TestMap.MessageValue.getDefaultInstance()); + assertEquals( + newMap(1, TestMap.MessageValue.getDefaultInstance(), + 2, TestMap.MessageValue.getDefaultInstance()), + builder.getInt32ToMessageField()); + } + + public void testMutableMapLifecycle_collections() { + TestMap.Builder builder = TestMap.newBuilder(); + Map intMap = builder.getMutableInt32ToInt32Field(); + intMap.put(1, 2); + assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field()); + try { + intMap.remove(2); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + try { + intMap.entrySet().remove(new Object()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + try { + intMap.entrySet().iterator().remove(); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + try { + intMap.keySet().remove(new Object()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + try { + intMap.values().remove(new Object()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + try { + intMap.values().iterator().remove(); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + assertEquals(newMap(1, 2), intMap); + assertEquals(newMap(1, 2), builder.getInt32ToInt32Field()); + assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field()); + } + public void testGettersAndSetters() throws Exception { TestMap.Builder builder = TestMap.newBuilder(); TestMap message = builder.build(); @@ -274,4 +413,26 @@ public class MapForProto2LiteTest extends TestCase { assertEquals(54321, messageWithUnknownEnums.getInt32ToInt32Field().get(2).intValue()); } + + public void testIterationOrder() throws Exception { + TestMap.Builder builder = TestMap.newBuilder(); + setMapValues(builder); + TestMap message = builder.build(); + + assertEquals(Arrays.asList("1", "2", "3"), + new ArrayList(message.getStringToInt32Field().keySet())); + } + + private static Map newMap(K key1, V value1) { + Map map = new HashMap(); + map.put(key1, value1); + return map; + } + + private static Map newMap(K key1, V value1, K key2, V value2) { + Map map = new HashMap(); + map.put(key1, value1); + map.put(key2, value2); + return map; + } } diff --git a/java/src/test/java/com/google/protobuf/MapForProto2Test.java b/java/src/test/java/com/google/protobuf/MapForProto2Test.java index 78cba1b4..7e984040 100644 --- a/java/src/test/java/com/google/protobuf/MapForProto2Test.java +++ b/java/src/test/java/com/google/protobuf/MapForProto2Test.java @@ -40,6 +40,7 @@ import map_test.MapForProto2TestProto.TestUnknownEnumValue; import junit.framework.TestCase; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -177,6 +178,116 @@ public class MapForProto2Test extends TestCase { assertEquals(0, message.getInt32ToMessageField().size()); assertEquals(0, message.getStringToInt32Field().size()); } + + public void testMutableMapLifecycle() { + TestMap.Builder builder = TestMap.newBuilder(); + Map intMap = builder.getMutableInt32ToInt32Field(); + intMap.put(1, 2); + assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field()); + try { + intMap.put(2, 3); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + assertEquals(newMap(1, 2), builder.getInt32ToInt32Field()); + builder.getMutableInt32ToInt32Field().put(2, 3); + assertEquals(newMap(1, 2, 2, 3), builder.getInt32ToInt32Field()); + + Map enumMap = builder.getMutableInt32ToEnumField(); + enumMap.put(1, TestMap.EnumValue.BAR); + assertEquals(newMap(1, TestMap.EnumValue.BAR), builder.build().getInt32ToEnumField()); + try { + enumMap.put(2, TestMap.EnumValue.FOO); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + assertEquals(newMap(1, TestMap.EnumValue.BAR), builder.getInt32ToEnumField()); + builder.getMutableInt32ToEnumField().put(2, TestMap.EnumValue.FOO); + assertEquals( + newMap(1, TestMap.EnumValue.BAR, 2, TestMap.EnumValue.FOO), + builder.getInt32ToEnumField()); + + Map stringMap = builder.getMutableInt32ToStringField(); + stringMap.put(1, "1"); + assertEquals(newMap(1, "1"), builder.build().getInt32ToStringField()); + try { + stringMap.put(2, "2"); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + assertEquals(newMap(1, "1"), builder.getInt32ToStringField()); + builder.getMutableInt32ToStringField().put(2, "2"); + assertEquals( + newMap(1, "1", 2, "2"), + builder.getInt32ToStringField()); + + Map messageMap = builder.getMutableInt32ToMessageField(); + messageMap.put(1, TestMap.MessageValue.getDefaultInstance()); + assertEquals(newMap(1, TestMap.MessageValue.getDefaultInstance()), + builder.build().getInt32ToMessageField()); + try { + messageMap.put(2, TestMap.MessageValue.getDefaultInstance()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + assertEquals(newMap(1, TestMap.MessageValue.getDefaultInstance()), + builder.getInt32ToMessageField()); + builder.getMutableInt32ToMessageField().put(2, TestMap.MessageValue.getDefaultInstance()); + assertEquals( + newMap(1, TestMap.MessageValue.getDefaultInstance(), + 2, TestMap.MessageValue.getDefaultInstance()), + builder.getInt32ToMessageField()); + } + + public void testMutableMapLifecycle_collections() { + TestMap.Builder builder = TestMap.newBuilder(); + Map intMap = builder.getMutableInt32ToInt32Field(); + intMap.put(1, 2); + assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field()); + try { + intMap.remove(2); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + try { + intMap.entrySet().remove(new Object()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + try { + intMap.entrySet().iterator().remove(); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + try { + intMap.keySet().remove(new Object()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + try { + intMap.values().remove(new Object()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + try { + intMap.values().iterator().remove(); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + assertEquals(newMap(1, 2), intMap); + assertEquals(newMap(1, 2), builder.getInt32ToInt32Field()); + assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field()); + } public void testGettersAndSetters() throws Exception { TestMap.Builder builder = TestMap.newBuilder(); @@ -513,4 +624,41 @@ public class MapForProto2Test extends TestCase { assertEquals(2, message.getRecursiveMapField().get(1).getValue()); assertEquals(4, message.getRecursiveMapField().get(3).getValue()); } + + public void testIterationOrder() throws Exception { + TestMap.Builder builder = TestMap.newBuilder(); + setMapValues(builder); + TestMap message = builder.build(); + + assertEquals(Arrays.asList("1", "2", "3"), + new ArrayList(message.getStringToInt32Field().keySet())); + } + + // Regression test for b/20494788 + public void testMapInitializationOrder() throws Exception { + assertEquals("RedactAllTypes", map_test.RedactAllTypes + .getDefaultInstance().getDescriptorForType().getName()); + + map_test.Message1.Builder builder = + map_test.Message1.newBuilder(); + builder.getMutableMapField().put("key", true); + map_test.Message1 message = builder.build(); + Message mapEntry = (Message) message.getRepeatedField( + message.getDescriptorForType().findFieldByName("map_field"), 0); + assertEquals(2, mapEntry.getAllFields().size()); + } + + private static Map newMap(K key1, V value1) { + Map map = new HashMap(); + map.put(key1, value1); + return map; + } + + private static Map newMap(K key1, V value1, K key2, V value2) { + Map map = new HashMap(); + map.put(key1, value1); + map.put(key2, value2); + return map; + } } + diff --git a/java/src/test/java/com/google/protobuf/MapTest.java b/java/src/test/java/com/google/protobuf/MapTest.java index b8e67b7c..0509be15 100644 --- a/java/src/test/java/com/google/protobuf/MapTest.java +++ b/java/src/test/java/com/google/protobuf/MapTest.java @@ -41,6 +41,7 @@ import map_test.MapTestProto.TestOnChangeEventPropagation; import junit.framework.TestCase; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -178,7 +179,117 @@ public class MapTest extends TestCase { assertEquals(0, message.getInt32ToMessageField().size()); assertEquals(0, message.getStringToInt32Field().size()); } + + public void testMutableMapLifecycle() { + TestMap.Builder builder = TestMap.newBuilder(); + Map intMap = builder.getMutableInt32ToInt32Field(); + intMap.put(1, 2); + assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field()); + try { + intMap.put(2, 3); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + assertEquals(newMap(1, 2), builder.getInt32ToInt32Field()); + builder.getMutableInt32ToInt32Field().put(2, 3); + assertEquals(newMap(1, 2, 2, 3), builder.getInt32ToInt32Field()); + + Map enumMap = builder.getMutableInt32ToEnumField(); + enumMap.put(1, TestMap.EnumValue.BAR); + assertEquals(newMap(1, TestMap.EnumValue.BAR), builder.build().getInt32ToEnumField()); + try { + enumMap.put(2, TestMap.EnumValue.FOO); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + assertEquals(newMap(1, TestMap.EnumValue.BAR), builder.getInt32ToEnumField()); + builder.getMutableInt32ToEnumField().put(2, TestMap.EnumValue.FOO); + assertEquals( + newMap(1, TestMap.EnumValue.BAR, 2, TestMap.EnumValue.FOO), + builder.getInt32ToEnumField()); + + Map stringMap = builder.getMutableInt32ToStringField(); + stringMap.put(1, "1"); + assertEquals(newMap(1, "1"), builder.build().getInt32ToStringField()); + try { + stringMap.put(2, "2"); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + assertEquals(newMap(1, "1"), builder.getInt32ToStringField()); + builder.getMutableInt32ToStringField().put(2, "2"); + assertEquals( + newMap(1, "1", 2, "2"), + builder.getInt32ToStringField()); + + Map messageMap = builder.getMutableInt32ToMessageField(); + messageMap.put(1, TestMap.MessageValue.getDefaultInstance()); + assertEquals(newMap(1, TestMap.MessageValue.getDefaultInstance()), + builder.build().getInt32ToMessageField()); + try { + messageMap.put(2, TestMap.MessageValue.getDefaultInstance()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + assertEquals(newMap(1, TestMap.MessageValue.getDefaultInstance()), + builder.getInt32ToMessageField()); + builder.getMutableInt32ToMessageField().put(2, TestMap.MessageValue.getDefaultInstance()); + assertEquals( + newMap(1, TestMap.MessageValue.getDefaultInstance(), + 2, TestMap.MessageValue.getDefaultInstance()), + builder.getInt32ToMessageField()); + } + public void testMutableMapLifecycle_collections() { + TestMap.Builder builder = TestMap.newBuilder(); + Map intMap = builder.getMutableInt32ToInt32Field(); + intMap.put(1, 2); + assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field()); + try { + intMap.remove(2); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + try { + intMap.entrySet().remove(new Object()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + try { + intMap.entrySet().iterator().remove(); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + try { + intMap.keySet().remove(new Object()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + try { + intMap.values().remove(new Object()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + try { + intMap.values().iterator().remove(); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + assertEquals(newMap(1, 2), intMap); + assertEquals(newMap(1, 2), builder.getInt32ToInt32Field()); + assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field()); + } + public void testGettersAndSetters() throws Exception { TestMap.Builder builder = TestMap.newBuilder(); TestMap message = builder.build(); @@ -611,4 +722,26 @@ public class MapTest extends TestCase { assertEquals(data.get(entry.getKey()) + 1, entry.getValue().intValue()); } } + + public void testIterationOrder() throws Exception { + TestMap.Builder builder = TestMap.newBuilder(); + setMapValues(builder); + TestMap message = builder.build(); + + assertEquals(Arrays.asList("1", "2", "3"), + new ArrayList(message.getStringToInt32Field().keySet())); + } + + private static Map newMap(K key1, V value1) { + Map map = new HashMap(); + map.put(key1, value1); + return map; + } + + private static Map newMap(K key1, V value1, K key2, V value2) { + Map map = new HashMap(); + map.put(key1, value1); + map.put(key2, value2); + return map; + } } diff --git a/java/src/test/java/com/google/protobuf/ProtobufArrayListTest.java b/java/src/test/java/com/google/protobuf/ProtobufArrayListTest.java new file mode 100644 index 00000000..f9f5e9c7 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/ProtobufArrayListTest.java @@ -0,0 +1,303 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import static java.util.Arrays.asList; + +import junit.framework.TestCase; + +import java.util.Collections; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.List; + +/** + * Tests for {@link ProtobufArrayList}. + */ +public class ProtobufArrayListTest extends TestCase { + + private static final ProtobufArrayList UNARY_LIST = newImmutableProtoArrayList(1); + private static final ProtobufArrayList TERTIARY_LIST = + newImmutableProtoArrayList(1, 2, 3); + + private ProtobufArrayList list; + + @Override + protected void setUp() throws Exception { + list = new ProtobufArrayList(); + } + + public void testEmptyListReturnsSameInstance() { + assertSame(ProtobufArrayList.emptyList(), ProtobufArrayList.emptyList()); + } + + public void testEmptyListIsImmutable() { + assertImmutable(ProtobufArrayList.emptyList()); + } + + public void testCopyConstructor() { + ProtobufArrayList copy = new ProtobufArrayList(TERTIARY_LIST); + assertEquals(TERTIARY_LIST, copy); + + copy = new ProtobufArrayList(IntArrayList.emptyList()); + assertEquals(ProtobufArrayList.emptyList(), copy); + + copy = new ProtobufArrayList(asList(1, 2, 3)); + assertEquals(asList(1, 2, 3), copy); + + copy = new ProtobufArrayList(Collections.emptyList()); + assertEquals(ProtobufArrayList.emptyList(), copy); + } + + public void testModificationWithIteration() { + list.addAll(asList(1, 2, 3, 4)); + Iterator iterator = list.iterator(); + assertEquals(4, list.size()); + assertEquals(1, (int) list.get(0)); + assertEquals(1, (int) iterator.next()); + + list.remove(0); + try { + iterator.next(); + fail(); + } catch (ConcurrentModificationException e) { + // expected + } + + iterator = list.iterator(); + list.set(0, 1); + try { + iterator.next(); + fail(); + } catch (ConcurrentModificationException e) { + // expected + } + + iterator = list.iterator(); + list.add(0, 0); + try { + iterator.next(); + fail(); + } catch (ConcurrentModificationException e) { + // expected + } + } + + public void testMakeImmutable() { + list.add(2); + list.add(4); + list.add(6); + list.add(8); + list.makeImmutable(); + assertImmutable(list); + } + + public void testRemove() { + list.add(2); + list.add(4); + list.add(6); + + list.remove(1); + assertEquals(asList(2, 6), list); + + list.remove(1); + assertEquals(asList(2), list); + + list.remove(0); + assertEquals(asList(), list); + } + + public void testGet() { + list.add(2); + list.add(6); + + assertEquals(2, (int) list.get(0)); + assertEquals(6, (int) list.get(1)); + } + + public void testSet() { + list.add(2); + list.add(6); + + list.set(0, 1); + assertEquals(1, (int) list.get(0)); + list.set(1, 2); + assertEquals(2, (int) list.get(1)); + } + + private void assertImmutable(List list) { + if (list.contains(1)) { + throw new RuntimeException("Cannot test the immutability of lists that contain 1."); + } + + try { + list.add(1); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.add(0, 1); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(Collections.singletonList(1)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(new ProtobufArrayList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(0, Collections.singleton(1)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(0, UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.addAll(0, Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.clear(); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.remove(1); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.remove(new Object()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.removeAll(Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.removeAll(Collections.singleton(1)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.removeAll(UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.retainAll(Collections.emptyList()); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.retainAll(Collections.singleton(1)); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.retainAll(UNARY_LIST); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + list.set(0, 0); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + } + + private static ProtobufArrayList newImmutableProtoArrayList(int... elements) { + ProtobufArrayList list = new ProtobufArrayList(); + for (int element : elements) { + list.add(element); + } + list.makeImmutable(); + return list; + } +} diff --git a/java/src/test/java/com/google/protobuf/field_presence_test.proto b/java/src/test/java/com/google/protobuf/field_presence_test.proto index 8d8ea8ef..8f3ca8c6 100644 --- a/java/src/test/java/com/google/protobuf/field_presence_test.proto +++ b/java/src/test/java/com/google/protobuf/field_presence_test.proto @@ -45,15 +45,15 @@ message TestAllTypes { BAZ = 2; } message NestedMessage { - optional int32 value = 1; + int32 value = 1; } - optional int32 optional_int32 = 1; - optional string optional_string = 2; - optional bytes optional_bytes = 3; - optional NestedEnum optional_nested_enum = 4; - optional NestedMessage optional_nested_message = 5; - optional protobuf_unittest.TestRequired optional_proto2_message = 6; + int32 optional_int32 = 1; + string optional_string = 2; + bytes optional_bytes = 3; + NestedEnum optional_nested_enum = 4; + NestedMessage optional_nested_message = 5; + protobuf_unittest.TestRequired optional_proto2_message = 6; oneof oneof_field { int32 oneof_int32 = 11; @@ -75,12 +75,12 @@ message TestAllTypes { } message TestOptionalFieldsOnly { - optional int32 optional_int32 = 1; - optional string optional_string = 2; - optional bytes optional_bytes = 3; - optional TestAllTypes.NestedEnum optional_nested_enum = 4; - optional TestAllTypes.NestedMessage optional_nested_message = 5; - optional protobuf_unittest.TestRequired optional_proto2_message = 6; + int32 optional_int32 = 1; + string optional_string = 2; + bytes optional_bytes = 3; + TestAllTypes.NestedEnum optional_nested_enum = 4; + TestAllTypes.NestedMessage optional_nested_message = 5; + protobuf_unittest.TestRequired optional_proto2_message = 6; } message TestRepeatedFieldsOnly { diff --git a/java/src/test/java/com/google/protobuf/map_initialization_order_test.proto b/java/src/test/java/com/google/protobuf/map_initialization_order_test.proto new file mode 100644 index 00000000..b02ac599 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/map_initialization_order_test.proto @@ -0,0 +1,61 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Regression test for a map initilaization order bug. The bug only manifests +// when: +// 1. A message contains map fields and is also extendable. +// 2. There is a file-level extension defined in the same file referencing +// the above message as the extension type. +// 3. The program executes in the following order: +// a. getDescriptor() is called on another message in the same file. +// b. use protobuf reflection to access the map field. +// The symptom is a NullPointerException being thrown. +syntax = "proto2"; + +package map_test; + +option java_package = "map_test"; +option java_outer_classname = "MapInitializationOrderTest"; +option java_multiple_files = true; + +// Mirrors the structure of +// javatests/com/google/cloud/common/logging/logging_test.proto. + +message Message1 { + map map_field = 1; + extensions 1000 to max; +} + +extend Message1 { + optional Message1 recursive_extension = 1001; +} + +message RedactAllTypes { +} diff --git a/java/src/test/java/com/google/protobuf/map_test.proto b/java/src/test/java/com/google/protobuf/map_test.proto index bf692c22..2f7709be 100644 --- a/java/src/test/java/com/google/protobuf/map_test.proto +++ b/java/src/test/java/com/google/protobuf/map_test.proto @@ -39,7 +39,7 @@ option java_generate_equals_and_hash = true; message TestMap { message MessageValue { - optional int32 value = 1; + int32 value = 1; } enum EnumValue { FOO = 0; @@ -60,5 +60,5 @@ message TestMap { // propagate the onChange event and mark its parent dirty when a change // is made to a map field. message TestOnChangeEventPropagation { - optional TestMap optional_message = 1; + TestMap optional_message = 1; } diff --git a/python/google/protobuf/descriptor.py b/python/google/protobuf/descriptor.py index f7a58ca0..970b1a88 100755 --- a/python/google/protobuf/descriptor.py +++ b/python/google/protobuf/descriptor.py @@ -245,9 +245,6 @@ class Descriptor(_NestedDescriptorBase): is_extendable: Does this type define any extension ranges? - options: (descriptor_pb2.MessageOptions) Protocol message options or None - to use default message options. - oneofs: (list of OneofDescriptor) The list of descriptors for oneof fields in this message. oneofs_by_name: (dict str -> OneofDescriptor) Same objects as in |oneofs|, @@ -265,7 +262,7 @@ class Descriptor(_NestedDescriptorBase): file=None, serialized_start=None, serialized_end=None, syntax=None): _message.Message._CheckCalledFromGeneratedFile() - return _message.Message._GetMessageDescriptor(full_name) + return _message.default_pool.FindMessageTypeByName(full_name) # NOTE(tmarek): The file argument redefining a builtin is nothing we can # fix right now since we don't know how many clients already rely on the @@ -495,9 +492,9 @@ class FieldDescriptor(DescriptorBase): has_default_value=True, containing_oneof=None): _message.Message._CheckCalledFromGeneratedFile() if is_extension: - return _message.Message._GetExtensionDescriptor(full_name) + return _message.default_pool.FindExtensionByName(full_name) else: - return _message.Message._GetFieldDescriptor(full_name) + return _message.default_pool.FindFieldByName(full_name) def __init__(self, name, full_name, index, number, type, cpp_type, label, default_value, message_type, enum_type, containing_type, @@ -528,14 +525,9 @@ class FieldDescriptor(DescriptorBase): self.containing_oneof = containing_oneof if api_implementation.Type() == 'cpp': if is_extension: - # pylint: disable=protected-access - self._cdescriptor = ( - _message.Message._GetExtensionDescriptor(full_name)) - # pylint: enable=protected-access + self._cdescriptor = _message.default_pool.FindExtensionByName(full_name) else: - # pylint: disable=protected-access - self._cdescriptor = _message.Message._GetFieldDescriptor(full_name) - # pylint: enable=protected-access + self._cdescriptor = _message.default_pool.FindFieldByName(full_name) else: self._cdescriptor = None @@ -592,7 +584,7 @@ class EnumDescriptor(_NestedDescriptorBase): containing_type=None, options=None, file=None, serialized_start=None, serialized_end=None): _message.Message._CheckCalledFromGeneratedFile() - return _message.Message._GetEnumDescriptor(full_name) + return _message.default_pool.FindEnumTypeByName(full_name) def __init__(self, name, full_name, filename, values, containing_type=None, options=None, file=None, @@ -677,7 +669,7 @@ class OneofDescriptor(object): def __new__(cls, name, full_name, index, containing_type, fields): _message.Message._CheckCalledFromGeneratedFile() - return _message.Message._GetOneofDescriptor(full_name) + return _message.default_pool.FindOneofByName(full_name) def __init__(self, name, full_name, index, containing_type, fields): """Arguments are as described in the attribute description above.""" @@ -788,12 +780,8 @@ class FileDescriptor(DescriptorBase): dependencies=None, syntax=None): # FileDescriptor() is called from various places, not only from generated # files, to register dynamic proto files and messages. - # TODO(amauryfa): Expose BuildFile() as a public function and make this - # constructor an implementation detail. if serialized_pb: - # pylint: disable=protected-access2 - return _message.Message._BuildFile(serialized_pb) - # pylint: enable=protected-access + return _message.default_pool.AddSerializedFile(serialized_pb) else: return super(FileDescriptor, cls).__new__(cls) @@ -814,9 +802,7 @@ class FileDescriptor(DescriptorBase): if (api_implementation.Type() == 'cpp' and self.serialized_pb is not None): - # pylint: disable=protected-access - _message.Message._BuildFile(self.serialized_pb) - # pylint: enable=protected-access + _message.default_pool.AddSerializedFile(self.serialized_pb) def CopyToProto(self, proto): """Copies this to a descriptor_pb2.FileDescriptorProto. @@ -864,10 +850,10 @@ def MakeDescriptor(desc_proto, package='', build_file_if_cpp=True, file_descriptor_proto = descriptor_pb2.FileDescriptorProto() file_descriptor_proto.message_type.add().MergeFrom(desc_proto) - # Generate a random name for this proto file to prevent conflicts with - # any imported ones. We need to specify a file name so BuildFile accepts - # our FileDescriptorProto, but it is not important what that file name - # is actually set to. + # Generate a random name for this proto file to prevent conflicts with any + # imported ones. We need to specify a file name so the descriptor pool + # accepts our FileDescriptorProto, but it is not important what that file + # name is actually set to. proto_name = str(uuid.uuid4()) if package: @@ -877,10 +863,8 @@ def MakeDescriptor(desc_proto, package='', build_file_if_cpp=True, else: file_descriptor_proto.name = proto_name + '.proto' - # pylint: disable=protected-access - result = _message.Message._BuildFile( - file_descriptor_proto.SerializeToString()) - # pylint: enable=protected-access + _message.default_pool.Add(file_descriptor_proto) + result = _message.default_pool.FindFileByName(file_descriptor_proto.name) if _USE_C_DESCRIPTORS: return result.message_types_by_name[desc_proto.name] diff --git a/python/google/protobuf/descriptor_pool.py b/python/google/protobuf/descriptor_pool.py index 7e7701f8..1244ba7c 100644 --- a/python/google/protobuf/descriptor_pool.py +++ b/python/google/protobuf/descriptor_pool.py @@ -113,6 +113,20 @@ class DescriptorPool(object): self._internal_db.Add(file_desc_proto) + def AddSerializedFile(self, serialized_file_desc_proto): + """Adds the FileDescriptorProto and its types to this pool. + + Args: + serialized_file_desc_proto: A bytes string, serialization of the + FileDescriptorProto to add. + """ + + # pylint: disable=g-import-not-at-top + from google.protobuf import descriptor_pb2 + file_desc_proto = descriptor_pb2.FileDescriptorProto.FromString( + serialized_file_desc_proto) + self.Add(file_desc_proto) + def AddDescriptor(self, desc): """Adds a Descriptor to the pool, non-recursively. @@ -320,17 +334,17 @@ class DescriptorPool(object): file_descriptor, None, scope)) for index, extension_proto in enumerate(file_proto.extension): - extension_desc = self.MakeFieldDescriptor( + extension_desc = self._MakeFieldDescriptor( extension_proto, file_proto.package, index, is_extension=True) extension_desc.containing_type = self._GetTypeFromScope( file_descriptor.package, extension_proto.extendee, scope) - self.SetFieldType(extension_proto, extension_desc, + self._SetFieldType(extension_proto, extension_desc, file_descriptor.package, scope) file_descriptor.extensions_by_name[extension_desc.name] = ( extension_desc) for desc_proto in file_proto.message_type: - self.SetAllFieldTypes(file_proto.package, desc_proto, scope) + self._SetAllFieldTypes(file_proto.package, desc_proto, scope) if file_proto.package: desc_proto_prefix = _PrefixWithDot(file_proto.package) @@ -381,10 +395,11 @@ class DescriptorPool(object): enums = [ self._ConvertEnumDescriptor(enum, desc_name, file_desc, None, scope) for enum in desc_proto.enum_type] - fields = [self.MakeFieldDescriptor(field, desc_name, index) + fields = [self._MakeFieldDescriptor(field, desc_name, index) for index, field in enumerate(desc_proto.field)] extensions = [ - self.MakeFieldDescriptor(extension, desc_name, index, is_extension=True) + self._MakeFieldDescriptor(extension, desc_name, index, + is_extension=True) for index, extension in enumerate(desc_proto.extension)] oneofs = [ descriptor.OneofDescriptor(desc.name, '.'.join((desc_name, desc.name)), @@ -464,8 +479,8 @@ class DescriptorPool(object): self._enum_descriptors[enum_name] = desc return desc - def MakeFieldDescriptor(self, field_proto, message_name, index, - is_extension=False): + def _MakeFieldDescriptor(self, field_proto, message_name, index, + is_extension=False): """Creates a field descriptor from a FieldDescriptorProto. For message and enum type fields, this method will do a look up @@ -506,7 +521,7 @@ class DescriptorPool(object): extension_scope=None, options=field_proto.options) - def SetAllFieldTypes(self, package, desc_proto, scope): + def _SetAllFieldTypes(self, package, desc_proto, scope): """Sets all the descriptor's fields's types. This method also sets the containing types on any extensions. @@ -527,18 +542,18 @@ class DescriptorPool(object): nested_package = '.'.join([package, desc_proto.name]) for field_proto, field_desc in zip(desc_proto.field, main_desc.fields): - self.SetFieldType(field_proto, field_desc, nested_package, scope) + self._SetFieldType(field_proto, field_desc, nested_package, scope) for extension_proto, extension_desc in ( zip(desc_proto.extension, main_desc.extensions)): extension_desc.containing_type = self._GetTypeFromScope( nested_package, extension_proto.extendee, scope) - self.SetFieldType(extension_proto, extension_desc, nested_package, scope) + self._SetFieldType(extension_proto, extension_desc, nested_package, scope) for nested_type in desc_proto.nested_type: - self.SetAllFieldTypes(nested_package, nested_type, scope) + self._SetAllFieldTypes(nested_package, nested_type, scope) - def SetFieldType(self, field_proto, field_desc, package, scope): + def _SetFieldType(self, field_proto, field_desc, package, scope): """Sets the field's type, cpp_type, message_type and enum_type. Args: diff --git a/python/google/protobuf/internal/containers.py b/python/google/protobuf/internal/containers.py index d976f9e1..72c2fa01 100755 --- a/python/google/protobuf/internal/containers.py +++ b/python/google/protobuf/internal/containers.py @@ -41,6 +41,146 @@ are: __author__ = 'petar@google.com (Petar Petrov)' +import sys + +if sys.version_info[0] < 3: + # We would use collections.MutableMapping all the time, but in Python 2 it + # doesn't define __slots__. This causes two significant problems: + # + # 1. we can't disallow arbitrary attribute assignment, even if our derived + # classes *do* define __slots__. + # + # 2. we can't safely derive a C type from it without __slots__ defined (the + # interpreter expects to find a dict at tp_dictoffset, which we can't + # robustly provide. And we don't want an instance dict anyway. + # + # So this is the Python 2.7 definition of Mapping/MutableMapping functions + # verbatim, except that: + # 1. We declare __slots__. + # 2. We don't declare this as a virtual base class. The classes defined + # in collections are the interesting base classes, not us. + # + # Note: deriving from object is critical. It is the only thing that makes + # this a true type, allowing us to derive from it in C++ cleanly and making + # __slots__ properly disallow arbitrary element assignment. + from collections import Mapping as _Mapping + + class Mapping(object): + __slots__ = () + + def get(self, key, default=None): + try: + return self[key] + except KeyError: + return default + + def __contains__(self, key): + try: + self[key] + except KeyError: + return False + else: + return True + + def iterkeys(self): + return iter(self) + + def itervalues(self): + for key in self: + yield self[key] + + def iteritems(self): + for key in self: + yield (key, self[key]) + + def keys(self): + return list(self) + + def items(self): + return [(key, self[key]) for key in self] + + def values(self): + return [self[key] for key in self] + + # Mappings are not hashable by default, but subclasses can change this + __hash__ = None + + def __eq__(self, other): + if not isinstance(other, _Mapping): + return NotImplemented + return dict(self.items()) == dict(other.items()) + + def __ne__(self, other): + return not (self == other) + + class MutableMapping(Mapping): + __slots__ = () + + __marker = object() + + def pop(self, key, default=__marker): + try: + value = self[key] + except KeyError: + if default is self.__marker: + raise + return default + else: + del self[key] + return value + + def popitem(self): + try: + key = next(iter(self)) + except StopIteration: + raise KeyError + value = self[key] + del self[key] + return key, value + + def clear(self): + try: + while True: + self.popitem() + except KeyError: + pass + + def update(*args, **kwds): + if len(args) > 2: + raise TypeError("update() takes at most 2 positional " + "arguments ({} given)".format(len(args))) + elif not args: + raise TypeError("update() takes at least 1 argument (0 given)") + self = args[0] + other = args[1] if len(args) >= 2 else () + + if isinstance(other, Mapping): + for key in other: + self[key] = other[key] + elif hasattr(other, "keys"): + for key in other.keys(): + self[key] = other[key] + else: + for key, value in other: + self[key] = value + for key, value in kwds.items(): + self[key] = value + + def setdefault(self, key, default=None): + try: + return self[key] + except KeyError: + self[key] = default + return default + + _Mapping.register(Mapping) + +else: + # In Python 3 we can just use MutableMapping directly, because it defines + # __slots__. + from collections import MutableMapping + + class BaseContainer(object): """Base container class.""" @@ -286,3 +426,160 @@ class RepeatedCompositeFieldContainer(BaseContainer): raise TypeError('Can only compare repeated composite fields against ' 'other repeated composite fields.') return self._values == other._values + + +class ScalarMap(MutableMapping): + + """Simple, type-checked, dict-like container for holding repeated scalars.""" + + # Disallows assignment to other attributes. + __slots__ = ['_key_checker', '_value_checker', '_values', '_message_listener'] + + def __init__(self, message_listener, key_checker, value_checker): + """ + Args: + message_listener: A MessageListener implementation. + The ScalarMap will call this object's Modified() method when it + is modified. + key_checker: A type_checkers.ValueChecker instance to run on keys + inserted into this container. + value_checker: A type_checkers.ValueChecker instance to run on values + inserted into this container. + """ + self._message_listener = message_listener + self._key_checker = key_checker + self._value_checker = value_checker + self._values = {} + + def __getitem__(self, key): + try: + return self._values[key] + except KeyError: + key = self._key_checker.CheckValue(key) + val = self._value_checker.DefaultValue() + self._values[key] = val + return val + + def __contains__(self, item): + return item in self._values + + # We need to override this explicitly, because our defaultdict-like behavior + # will make the default implementation (from our base class) always insert + # the key. + def get(self, key, default=None): + if key in self: + return self[key] + else: + return default + + def __setitem__(self, key, value): + checked_key = self._key_checker.CheckValue(key) + checked_value = self._value_checker.CheckValue(value) + self._values[checked_key] = checked_value + self._message_listener.Modified() + + def __delitem__(self, key): + del self._values[key] + self._message_listener.Modified() + + def __len__(self): + return len(self._values) + + def __iter__(self): + return iter(self._values) + + def MergeFrom(self, other): + self._values.update(other._values) + self._message_listener.Modified() + + # This is defined in the abstract base, but we can do it much more cheaply. + def clear(self): + self._values.clear() + self._message_listener.Modified() + + +class MessageMap(MutableMapping): + + """Simple, type-checked, dict-like container for with submessage values.""" + + # Disallows assignment to other attributes. + __slots__ = ['_key_checker', '_values', '_message_listener', + '_message_descriptor'] + + def __init__(self, message_listener, message_descriptor, key_checker): + """ + Args: + message_listener: A MessageListener implementation. + The ScalarMap will call this object's Modified() method when it + is modified. + key_checker: A type_checkers.ValueChecker instance to run on keys + inserted into this container. + value_checker: A type_checkers.ValueChecker instance to run on values + inserted into this container. + """ + self._message_listener = message_listener + self._message_descriptor = message_descriptor + self._key_checker = key_checker + self._values = {} + + def __getitem__(self, key): + try: + return self._values[key] + except KeyError: + key = self._key_checker.CheckValue(key) + new_element = self._message_descriptor._concrete_class() + new_element._SetListener(self._message_listener) + self._values[key] = new_element + self._message_listener.Modified() + + return new_element + + def get_or_create(self, key): + """get_or_create() is an alias for getitem (ie. map[key]). + + Args: + key: The key to get or create in the map. + + This is useful in cases where you want to be explicit that the call is + mutating the map. This can avoid lint errors for statements like this + that otherwise would appear to be pointless statements: + + msg.my_map[key] + """ + return self[key] + + # We need to override this explicitly, because our defaultdict-like behavior + # will make the default implementation (from our base class) always insert + # the key. + def get(self, key, default=None): + if key in self: + return self[key] + else: + return default + + def __contains__(self, item): + return item in self._values + + def __setitem__(self, key, value): + raise ValueError('May not set values directly, call my_map[key].foo = 5') + + def __delitem__(self, key): + del self._values[key] + self._message_listener.Modified() + + def __len__(self): + return len(self._values) + + def __iter__(self): + return iter(self._values) + + def MergeFrom(self, other): + for key in other: + self[key].MergeFrom(other[key]) + # self._message_listener.Modified() not required here, because + # mutations to submessages already propagate. + + # This is defined in the abstract base, but we can do it much more cheaply. + def clear(self): + self._values.clear() + self._message_listener.Modified() diff --git a/python/google/protobuf/internal/decoder.py b/python/google/protobuf/internal/decoder.py index 0f500606..3837eaea 100755 --- a/python/google/protobuf/internal/decoder.py +++ b/python/google/protobuf/internal/decoder.py @@ -732,6 +732,50 @@ def MessageSetItemDecoder(extensions_by_number): return DecodeItem +# -------------------------------------------------------------------- + +def MapDecoder(field_descriptor, new_default, is_message_map): + """Returns a decoder for a map field.""" + + key = field_descriptor + tag_bytes = encoder.TagBytes(field_descriptor.number, + wire_format.WIRETYPE_LENGTH_DELIMITED) + tag_len = len(tag_bytes) + local_DecodeVarint = _DecodeVarint + # Can't read _concrete_class yet; might not be initialized. + message_type = field_descriptor.message_type + + def DecodeMap(buffer, pos, end, message, field_dict): + submsg = message_type._concrete_class() + value = field_dict.get(key) + if value is None: + value = field_dict.setdefault(key, new_default(message)) + while 1: + # Read length. + (size, pos) = local_DecodeVarint(buffer, pos) + new_pos = pos + size + if new_pos > end: + raise _DecodeError('Truncated message.') + # Read sub-message. + submsg.Clear() + if submsg._InternalParse(buffer, pos, new_pos) != new_pos: + # The only reason _InternalParse would return early is if it + # encountered an end-group tag. + raise _DecodeError('Unexpected end-group tag.') + + if is_message_map: + value[submsg.key].MergeFrom(submsg.value) + else: + value[submsg.key] = submsg.value + + # Predict that the next tag is another copy of the same repeated field. + pos = new_pos + tag_len + if buffer[new_pos:pos] != tag_bytes or new_pos == end: + # Prediction failed. Return. + return new_pos + + return DecodeMap + # -------------------------------------------------------------------- # Optimization is not as heavy here because calls to SkipField() are rare, # except for handling end-group tags. diff --git a/python/google/protobuf/internal/descriptor_database_test.py b/python/google/protobuf/internal/descriptor_database_test.py index 56fe14e9..8416e157 100644 --- a/python/google/protobuf/internal/descriptor_database_test.py +++ b/python/google/protobuf/internal/descriptor_database_test.py @@ -35,7 +35,6 @@ __author__ = 'matthewtoia@google.com (Matt Toia)' import unittest - from google.protobuf import descriptor_pb2 from google.protobuf.internal import factory_test2_pb2 from google.protobuf import descriptor_database diff --git a/python/google/protobuf/internal/descriptor_pool_test.py b/python/google/protobuf/internal/descriptor_pool_test.py index 7d145f42..d159cc62 100644 --- a/python/google/protobuf/internal/descriptor_pool_test.py +++ b/python/google/protobuf/internal/descriptor_pool_test.py @@ -37,6 +37,7 @@ __author__ = 'matthewtoia@google.com (Matt Toia)' import os import unittest +import unittest from google.protobuf import unittest_pb2 from google.protobuf import descriptor_pb2 from google.protobuf.internal import api_implementation @@ -226,6 +227,13 @@ class DescriptorPoolTest(unittest.TestCase): db.Add(self.factory_test2_fd) self.testFindMessageTypeByName() + def testAddSerializedFile(self): + db = descriptor_database.DescriptorDatabase() + self.pool = descriptor_pool.DescriptorPool(db) + self.pool.AddSerializedFile(self.factory_test1_fd.SerializeToString()) + self.pool.AddSerializedFile(self.factory_test2_fd.SerializeToString()) + self.testFindMessageTypeByName() + def testComplexNesting(self): test1_desc = descriptor_pb2.FileDescriptorProto.FromString( descriptor_pool_test1_pb2.DESCRIPTOR.serialized_pb) @@ -510,6 +518,43 @@ class AddDescriptorTest(unittest.TestCase): 'protobuf_unittest.TestAllTypes') +@unittest.skipIf( + api_implementation.Type() != 'cpp', + 'default_pool is only supported by the C++ implementation') +class DefaultPoolTest(unittest.TestCase): + + def testFindMethods(self): + # pylint: disable=g-import-not-at-top + from google.protobuf.pyext import _message + pool = _message.default_pool + self.assertIs( + pool.FindFileByName('google/protobuf/unittest.proto'), + unittest_pb2.DESCRIPTOR) + self.assertIs( + pool.FindMessageTypeByName('protobuf_unittest.TestAllTypes'), + unittest_pb2.TestAllTypes.DESCRIPTOR) + self.assertIs( + pool.FindFieldByName('protobuf_unittest.TestAllTypes.optional_int32'), + unittest_pb2.TestAllTypes.DESCRIPTOR.fields_by_name['optional_int32']) + self.assertIs( + pool.FindExtensionByName('protobuf_unittest.optional_int32_extension'), + unittest_pb2.DESCRIPTOR.extensions_by_name['optional_int32_extension']) + self.assertIs( + pool.FindEnumTypeByName('protobuf_unittest.ForeignEnum'), + unittest_pb2.ForeignEnum.DESCRIPTOR) + self.assertIs( + pool.FindOneofByName('protobuf_unittest.TestAllTypes.oneof_field'), + unittest_pb2.TestAllTypes.DESCRIPTOR.oneofs_by_name['oneof_field']) + + def testAddFileDescriptor(self): + # pylint: disable=g-import-not-at-top + from google.protobuf.pyext import _message + pool = _message.default_pool + file_desc = descriptor_pb2.FileDescriptorProto(name='some/file.proto') + pool.Add(file_desc) + pool.AddSerializedFile(file_desc.SerializeToString()) + + TEST1_FILE = ProtoFile( 'google/protobuf/internal/descriptor_pool_test1.proto', 'google.protobuf.python.internal', diff --git a/python/google/protobuf/internal/descriptor_test.py b/python/google/protobuf/internal/descriptor_test.py index 335caee6..26866f3a 100755 --- a/python/google/protobuf/internal/descriptor_test.py +++ b/python/google/protobuf/internal/descriptor_test.py @@ -35,8 +35,8 @@ __author__ = 'robinson@google.com (Will Robinson)' import sys -import unittest +import unittest from google.protobuf import unittest_custom_options_pb2 from google.protobuf import unittest_import_pb2 from google.protobuf import unittest_pb2 diff --git a/python/google/protobuf/internal/encoder.py b/python/google/protobuf/internal/encoder.py index 38a5138a..752f4eab 100755 --- a/python/google/protobuf/internal/encoder.py +++ b/python/google/protobuf/internal/encoder.py @@ -314,7 +314,7 @@ def MessageSizer(field_number, is_repeated, is_packed): # -------------------------------------------------------------------- -# MessageSet is special. +# MessageSet is special: it needs custom logic to compute its size properly. def MessageSetItemSizer(field_number): @@ -339,6 +339,32 @@ def MessageSetItemSizer(field_number): return FieldSize +# -------------------------------------------------------------------- +# Map is special: it needs custom logic to compute its size properly. + + +def MapSizer(field_descriptor): + """Returns a sizer for a map field.""" + + # Can't look at field_descriptor.message_type._concrete_class because it may + # not have been initialized yet. + message_type = field_descriptor.message_type + message_sizer = MessageSizer(field_descriptor.number, False, False) + + def FieldSize(map_value): + total = 0 + for key in map_value: + value = map_value[key] + # It's wasteful to create the messages and throw them away one second + # later since we'll do the same for the actual encode. But there's not an + # obvious way to avoid this within the current design without tons of code + # duplication. + entry_msg = message_type._concrete_class(key=key, value=value) + total += message_sizer(entry_msg) + return total + + return FieldSize + # ==================================================================== # Encoders! @@ -786,3 +812,30 @@ def MessageSetItemEncoder(field_number): return write(end_bytes) return EncodeField + + +# -------------------------------------------------------------------- +# As before, Map is special. + + +def MapEncoder(field_descriptor): + """Encoder for extensions of MessageSet. + + Maps always have a wire format like this: + message MapEntry { + key_type key = 1; + value_type value = 2; + } + repeated MapEntry map = N; + """ + # Can't look at field_descriptor.message_type._concrete_class because it may + # not have been initialized yet. + message_type = field_descriptor.message_type + encode_message = MessageEncoder(field_descriptor.number, False, False) + + def EncodeField(write, value): + for key in value: + entry_msg = message_type._concrete_class(key=key, value=value[key]) + encode_message(write, entry_msg) + + return EncodeField diff --git a/python/google/protobuf/internal/generator_test.py b/python/google/protobuf/internal/generator_test.py index ccc5860b..5c07cbe6 100755 --- a/python/google/protobuf/internal/generator_test.py +++ b/python/google/protobuf/internal/generator_test.py @@ -42,7 +42,6 @@ further ensures that we can use Python protocol message objects as we expect. __author__ = 'robinson@google.com (Will Robinson)' import unittest - from google.protobuf.internal import test_bad_identifiers_pb2 from google.protobuf import unittest_custom_options_pb2 from google.protobuf import unittest_import_pb2 diff --git a/python/google/protobuf/internal/message_factory_test.py b/python/google/protobuf/internal/message_factory_test.py index 626c3fc9..b8694f96 100644 --- a/python/google/protobuf/internal/message_factory_test.py +++ b/python/google/protobuf/internal/message_factory_test.py @@ -35,7 +35,6 @@ __author__ = 'matthewtoia@google.com (Matt Toia)' import unittest - from google.protobuf import descriptor_pb2 from google.protobuf.internal import factory_test1_pb2 from google.protobuf.internal import factory_test2_pb2 diff --git a/python/google/protobuf/internal/message_test.py b/python/google/protobuf/internal/message_test.py index 4ecaa1c7..320ff0d2 100755 --- a/python/google/protobuf/internal/message_test.py +++ b/python/google/protobuf/internal/message_test.py @@ -50,7 +50,9 @@ import pickle import sys import unittest +import unittest from google.protobuf.internal import _parameterized +from google.protobuf import map_unittest_pb2 from google.protobuf import unittest_pb2 from google.protobuf import unittest_proto3_arena_pb2 from google.protobuf.internal import api_implementation @@ -125,10 +127,17 @@ class MessageTest(unittest.TestCase): self.assertEqual(unpickled_message, golden_message) def testPositiveInfinity(self, message_module): - golden_data = (b'\x5D\x00\x00\x80\x7F' - b'\x61\x00\x00\x00\x00\x00\x00\xF0\x7F' - b'\xCD\x02\x00\x00\x80\x7F' - b'\xD1\x02\x00\x00\x00\x00\x00\x00\xF0\x7F') + if message_module is unittest_pb2: + golden_data = (b'\x5D\x00\x00\x80\x7F' + b'\x61\x00\x00\x00\x00\x00\x00\xF0\x7F' + b'\xCD\x02\x00\x00\x80\x7F' + b'\xD1\x02\x00\x00\x00\x00\x00\x00\xF0\x7F') + else: + golden_data = (b'\x5D\x00\x00\x80\x7F' + b'\x61\x00\x00\x00\x00\x00\x00\xF0\x7F' + b'\xCA\x02\x04\x00\x00\x80\x7F' + b'\xD2\x02\x08\x00\x00\x00\x00\x00\x00\xF0\x7F') + golden_message = message_module.TestAllTypes() golden_message.ParseFromString(golden_data) self.assertTrue(IsPosInf(golden_message.optional_float)) @@ -138,10 +147,17 @@ class MessageTest(unittest.TestCase): self.assertEqual(golden_data, golden_message.SerializeToString()) def testNegativeInfinity(self, message_module): - golden_data = (b'\x5D\x00\x00\x80\xFF' - b'\x61\x00\x00\x00\x00\x00\x00\xF0\xFF' - b'\xCD\x02\x00\x00\x80\xFF' - b'\xD1\x02\x00\x00\x00\x00\x00\x00\xF0\xFF') + if message_module is unittest_pb2: + golden_data = (b'\x5D\x00\x00\x80\xFF' + b'\x61\x00\x00\x00\x00\x00\x00\xF0\xFF' + b'\xCD\x02\x00\x00\x80\xFF' + b'\xD1\x02\x00\x00\x00\x00\x00\x00\xF0\xFF') + else: + golden_data = (b'\x5D\x00\x00\x80\xFF' + b'\x61\x00\x00\x00\x00\x00\x00\xF0\xFF' + b'\xCA\x02\x04\x00\x00\x80\xFF' + b'\xD2\x02\x08\x00\x00\x00\x00\x00\x00\xF0\xFF') + golden_message = message_module.TestAllTypes() golden_message.ParseFromString(golden_data) self.assertTrue(IsNegInf(golden_message.optional_float)) @@ -1034,64 +1050,132 @@ class Proto2Test(unittest.TestCase): self.assertEqual(len(parsing_merge.Extensions[ unittest_pb2.TestParsingMerge.repeated_ext]), 3) + def testPythonicInit(self): + message = unittest_pb2.TestAllTypes( + optional_int32=100, + optional_fixed32=200, + optional_float=300.5, + optional_bytes=b'x', + optionalgroup={'a': 400}, + optional_nested_message={'bb': 500}, + optional_nested_enum='BAZ', + repeatedgroup=[{'a': 600}, + {'a': 700}], + repeated_nested_enum=['FOO', unittest_pb2.TestAllTypes.BAR], + default_int32=800, + oneof_string='y') + self.assertTrue(isinstance(message, unittest_pb2.TestAllTypes)) + self.assertEqual(100, message.optional_int32) + self.assertEqual(200, message.optional_fixed32) + self.assertEqual(300.5, message.optional_float) + self.assertEqual(b'x', message.optional_bytes) + self.assertEqual(400, message.optionalgroup.a) + self.assertTrue(isinstance(message.optional_nested_message, + unittest_pb2.TestAllTypes.NestedMessage)) + self.assertEqual(500, message.optional_nested_message.bb) + self.assertEqual(unittest_pb2.TestAllTypes.BAZ, + message.optional_nested_enum) + self.assertEqual(2, len(message.repeatedgroup)) + self.assertEqual(600, message.repeatedgroup[0].a) + self.assertEqual(700, message.repeatedgroup[1].a) + self.assertEqual(2, len(message.repeated_nested_enum)) + self.assertEqual(unittest_pb2.TestAllTypes.FOO, + message.repeated_nested_enum[0]) + self.assertEqual(unittest_pb2.TestAllTypes.BAR, + message.repeated_nested_enum[1]) + self.assertEqual(800, message.default_int32) + self.assertEqual('y', message.oneof_string) + self.assertFalse(message.HasField('optional_int64')) + self.assertEqual(0, len(message.repeated_float)) + self.assertEqual(42, message.default_int64) + + message = unittest_pb2.TestAllTypes(optional_nested_enum=u'BAZ') + self.assertEqual(unittest_pb2.TestAllTypes.BAZ, + message.optional_nested_enum) + + with self.assertRaises(ValueError): + unittest_pb2.TestAllTypes( + optional_nested_message={'INVALID_NESTED_FIELD': 17}) + + with self.assertRaises(TypeError): + unittest_pb2.TestAllTypes( + optional_nested_message={'bb': 'INVALID_VALUE_TYPE'}) + + with self.assertRaises(ValueError): + unittest_pb2.TestAllTypes(optional_nested_enum='INVALID_LABEL') + + with self.assertRaises(ValueError): + unittest_pb2.TestAllTypes(repeated_nested_enum='FOO') + # Class to test proto3-only features/behavior (updated field presence & enums) class Proto3Test(unittest.TestCase): + # Utility method for comparing equality with a map. + def assertMapIterEquals(self, map_iter, dict_value): + # Avoid mutating caller's copy. + dict_value = dict(dict_value) + + for k, v in map_iter: + self.assertEqual(v, dict_value[k]) + del dict_value[k] + + self.assertEqual({}, dict_value) + def testFieldPresence(self): message = unittest_proto3_arena_pb2.TestAllTypes() # We can't test presence of non-repeated, non-submessage fields. with self.assertRaises(ValueError): - message.HasField("optional_int32") + message.HasField('optional_int32') with self.assertRaises(ValueError): - message.HasField("optional_float") + message.HasField('optional_float') with self.assertRaises(ValueError): - message.HasField("optional_string") + message.HasField('optional_string') with self.assertRaises(ValueError): - message.HasField("optional_bool") + message.HasField('optional_bool') # But we can still test presence of submessage fields. - self.assertFalse(message.HasField("optional_nested_message")) + self.assertFalse(message.HasField('optional_nested_message')) # As with proto2, we can't test presence of fields that don't exist, or # repeated fields. with self.assertRaises(ValueError): - message.HasField("field_doesnt_exist") + message.HasField('field_doesnt_exist') with self.assertRaises(ValueError): - message.HasField("repeated_int32") + message.HasField('repeated_int32') with self.assertRaises(ValueError): - message.HasField("repeated_nested_message") + message.HasField('repeated_nested_message') # Fields should default to their type-specific default. self.assertEqual(0, message.optional_int32) self.assertEqual(0, message.optional_float) - self.assertEqual("", message.optional_string) + self.assertEqual('', message.optional_string) self.assertEqual(False, message.optional_bool) self.assertEqual(0, message.optional_nested_message.bb) # Setting a submessage should still return proper presence information. message.optional_nested_message.bb = 0 - self.assertTrue(message.HasField("optional_nested_message")) + self.assertTrue(message.HasField('optional_nested_message')) # Set the fields to non-default values. message.optional_int32 = 5 message.optional_float = 1.1 - message.optional_string = "abc" + message.optional_string = 'abc' message.optional_bool = True message.optional_nested_message.bb = 15 # Clearing the fields unsets them and resets their value to default. - message.ClearField("optional_int32") - message.ClearField("optional_float") - message.ClearField("optional_string") - message.ClearField("optional_bool") - message.ClearField("optional_nested_message") + message.ClearField('optional_int32') + message.ClearField('optional_float') + message.ClearField('optional_string') + message.ClearField('optional_bool') + message.ClearField('optional_nested_message') self.assertEqual(0, message.optional_int32) self.assertEqual(0, message.optional_float) - self.assertEqual("", message.optional_string) + self.assertEqual('', message.optional_string) self.assertEqual(False, message.optional_bool) self.assertEqual(0, message.optional_nested_message.bb) @@ -1113,6 +1197,393 @@ class Proto3Test(unittest.TestCase): self.assertEqual(1234567, m2.optional_nested_enum) self.assertEqual(7654321, m2.repeated_nested_enum[0]) + # Map isn't really a proto3-only feature. But there is no proto2 equivalent + # of google/protobuf/map_unittest.proto right now, so it's not easy to + # test both with the same test like we do for the other proto2/proto3 tests. + # (google/protobuf/map_protobuf_unittest.proto is very different in the set + # of messages and fields it contains). + def testScalarMapDefaults(self): + msg = map_unittest_pb2.TestMap() + + # Scalars start out unset. + self.assertFalse(-123 in msg.map_int32_int32) + self.assertFalse(-2**33 in msg.map_int64_int64) + self.assertFalse(123 in msg.map_uint32_uint32) + self.assertFalse(2**33 in msg.map_uint64_uint64) + self.assertFalse('abc' in msg.map_string_string) + self.assertFalse(888 in msg.map_int32_enum) + + # Accessing an unset key returns the default. + self.assertEqual(0, msg.map_int32_int32[-123]) + self.assertEqual(0, msg.map_int64_int64[-2**33]) + self.assertEqual(0, msg.map_uint32_uint32[123]) + self.assertEqual(0, msg.map_uint64_uint64[2**33]) + self.assertEqual('', msg.map_string_string['abc']) + self.assertEqual(0, msg.map_int32_enum[888]) + + # It also sets the value in the map + self.assertTrue(-123 in msg.map_int32_int32) + self.assertTrue(-2**33 in msg.map_int64_int64) + self.assertTrue(123 in msg.map_uint32_uint32) + self.assertTrue(2**33 in msg.map_uint64_uint64) + self.assertTrue('abc' in msg.map_string_string) + self.assertTrue(888 in msg.map_int32_enum) + + self.assertTrue(isinstance(msg.map_string_string['abc'], unicode)) + + # Accessing an unset key still throws TypeError of the type of the key + # is incorrect. + with self.assertRaises(TypeError): + msg.map_string_string[123] + + self.assertFalse(123 in msg.map_string_string) + + def testMapGet(self): + # Need to test that get() properly returns the default, even though the dict + # has defaultdict-like semantics. + msg = map_unittest_pb2.TestMap() + + self.assertIsNone(msg.map_int32_int32.get(5)) + self.assertEquals(10, msg.map_int32_int32.get(5, 10)) + self.assertIsNone(msg.map_int32_int32.get(5)) + + msg.map_int32_int32[5] = 15 + self.assertEquals(15, msg.map_int32_int32.get(5)) + + self.assertIsNone(msg.map_int32_foreign_message.get(5)) + self.assertEquals(10, msg.map_int32_foreign_message.get(5, 10)) + + submsg = msg.map_int32_foreign_message[5] + self.assertIs(submsg, msg.map_int32_foreign_message.get(5)) + + def testScalarMap(self): + msg = map_unittest_pb2.TestMap() + + self.assertEqual(0, len(msg.map_int32_int32)) + self.assertFalse(5 in msg.map_int32_int32) + + msg.map_int32_int32[-123] = -456 + msg.map_int64_int64[-2**33] = -2**34 + msg.map_uint32_uint32[123] = 456 + msg.map_uint64_uint64[2**33] = 2**34 + msg.map_string_string['abc'] = '123' + msg.map_int32_enum[888] = 2 + + self.assertEqual([], msg.FindInitializationErrors()) + + self.assertEqual(1, len(msg.map_string_string)) + + # Bad key. + with self.assertRaises(TypeError): + msg.map_string_string[123] = '123' + + # Verify that trying to assign a bad key doesn't actually add a member to + # the map. + self.assertEqual(1, len(msg.map_string_string)) + + # Bad value. + with self.assertRaises(TypeError): + msg.map_string_string['123'] = 123 + + serialized = msg.SerializeToString() + msg2 = map_unittest_pb2.TestMap() + msg2.ParseFromString(serialized) + + # Bad key. + with self.assertRaises(TypeError): + msg2.map_string_string[123] = '123' + + # Bad value. + with self.assertRaises(TypeError): + msg2.map_string_string['123'] = 123 + + self.assertEqual(-456, msg2.map_int32_int32[-123]) + self.assertEqual(-2**34, msg2.map_int64_int64[-2**33]) + self.assertEqual(456, msg2.map_uint32_uint32[123]) + self.assertEqual(2**34, msg2.map_uint64_uint64[2**33]) + self.assertEqual('123', msg2.map_string_string['abc']) + self.assertEqual(2, msg2.map_int32_enum[888]) + + def testStringUnicodeConversionInMap(self): + msg = map_unittest_pb2.TestMap() + + unicode_obj = u'\u1234' + bytes_obj = unicode_obj.encode('utf8') + + msg.map_string_string[bytes_obj] = bytes_obj + + (key, value) = msg.map_string_string.items()[0] + + self.assertEqual(key, unicode_obj) + self.assertEqual(value, unicode_obj) + + self.assertTrue(isinstance(key, unicode)) + self.assertTrue(isinstance(value, unicode)) + + def testMessageMap(self): + msg = map_unittest_pb2.TestMap() + + self.assertEqual(0, len(msg.map_int32_foreign_message)) + self.assertFalse(5 in msg.map_int32_foreign_message) + + msg.map_int32_foreign_message[123] + # get_or_create() is an alias for getitem. + msg.map_int32_foreign_message.get_or_create(-456) + + self.assertEqual(2, len(msg.map_int32_foreign_message)) + self.assertIn(123, msg.map_int32_foreign_message) + self.assertIn(-456, msg.map_int32_foreign_message) + self.assertEqual(2, len(msg.map_int32_foreign_message)) + + # Bad key. + with self.assertRaises(TypeError): + msg.map_int32_foreign_message['123'] + + # Can't assign directly to submessage. + with self.assertRaises(ValueError): + msg.map_int32_foreign_message[999] = msg.map_int32_foreign_message[123] + + # Verify that trying to assign a bad key doesn't actually add a member to + # the map. + self.assertEqual(2, len(msg.map_int32_foreign_message)) + + serialized = msg.SerializeToString() + msg2 = map_unittest_pb2.TestMap() + msg2.ParseFromString(serialized) + + self.assertEqual(2, len(msg2.map_int32_foreign_message)) + self.assertIn(123, msg2.map_int32_foreign_message) + self.assertIn(-456, msg2.map_int32_foreign_message) + self.assertEqual(2, len(msg2.map_int32_foreign_message)) + + def testMergeFrom(self): + msg = map_unittest_pb2.TestMap() + msg.map_int32_int32[12] = 34 + msg.map_int32_int32[56] = 78 + msg.map_int64_int64[22] = 33 + msg.map_int32_foreign_message[111].c = 5 + msg.map_int32_foreign_message[222].c = 10 + + msg2 = map_unittest_pb2.TestMap() + msg2.map_int32_int32[12] = 55 + msg2.map_int64_int64[88] = 99 + msg2.map_int32_foreign_message[222].c = 15 + + msg2.MergeFrom(msg) + + self.assertEqual(34, msg2.map_int32_int32[12]) + self.assertEqual(78, msg2.map_int32_int32[56]) + self.assertEqual(33, msg2.map_int64_int64[22]) + self.assertEqual(99, msg2.map_int64_int64[88]) + self.assertEqual(5, msg2.map_int32_foreign_message[111].c) + self.assertEqual(10, msg2.map_int32_foreign_message[222].c) + + # Verify that there is only one entry per key, even though the MergeFrom + # may have internally created multiple entries for a single key in the + # list representation. + as_dict = {} + for key in msg2.map_int32_foreign_message: + self.assertFalse(key in as_dict) + as_dict[key] = msg2.map_int32_foreign_message[key].c + + self.assertEqual({111: 5, 222: 10}, as_dict) + + # Special case: test that delete of item really removes the item, even if + # there might have physically been duplicate keys due to the previous merge. + # This is only a special case for the C++ implementation which stores the + # map as an array. + del msg2.map_int32_int32[12] + self.assertFalse(12 in msg2.map_int32_int32) + + del msg2.map_int32_foreign_message[222] + self.assertFalse(222 in msg2.map_int32_foreign_message) + + def testIntegerMapWithLongs(self): + msg = map_unittest_pb2.TestMap() + msg.map_int32_int32[long(-123)] = long(-456) + msg.map_int64_int64[long(-2**33)] = long(-2**34) + msg.map_uint32_uint32[long(123)] = long(456) + msg.map_uint64_uint64[long(2**33)] = long(2**34) + + serialized = msg.SerializeToString() + msg2 = map_unittest_pb2.TestMap() + msg2.ParseFromString(serialized) + + self.assertEqual(-456, msg2.map_int32_int32[-123]) + self.assertEqual(-2**34, msg2.map_int64_int64[-2**33]) + self.assertEqual(456, msg2.map_uint32_uint32[123]) + self.assertEqual(2**34, msg2.map_uint64_uint64[2**33]) + + def testMapAssignmentCausesPresence(self): + msg = map_unittest_pb2.TestMapSubmessage() + msg.test_map.map_int32_int32[123] = 456 + + serialized = msg.SerializeToString() + msg2 = map_unittest_pb2.TestMapSubmessage() + msg2.ParseFromString(serialized) + + self.assertEqual(msg, msg2) + + # Now test that various mutations of the map properly invalidate the + # cached size of the submessage. + msg.test_map.map_int32_int32[888] = 999 + serialized = msg.SerializeToString() + msg2.ParseFromString(serialized) + self.assertEqual(msg, msg2) + + msg.test_map.map_int32_int32.clear() + serialized = msg.SerializeToString() + msg2.ParseFromString(serialized) + self.assertEqual(msg, msg2) + + def testMapAssignmentCausesPresenceForSubmessages(self): + msg = map_unittest_pb2.TestMapSubmessage() + msg.test_map.map_int32_foreign_message[123].c = 5 + + serialized = msg.SerializeToString() + msg2 = map_unittest_pb2.TestMapSubmessage() + msg2.ParseFromString(serialized) + + self.assertEqual(msg, msg2) + + # Now test that various mutations of the map properly invalidate the + # cached size of the submessage. + msg.test_map.map_int32_foreign_message[888].c = 7 + serialized = msg.SerializeToString() + msg2.ParseFromString(serialized) + self.assertEqual(msg, msg2) + + msg.test_map.map_int32_foreign_message[888].MergeFrom( + msg.test_map.map_int32_foreign_message[123]) + serialized = msg.SerializeToString() + msg2.ParseFromString(serialized) + self.assertEqual(msg, msg2) + + msg.test_map.map_int32_foreign_message.clear() + serialized = msg.SerializeToString() + msg2.ParseFromString(serialized) + self.assertEqual(msg, msg2) + + def testModifyMapWhileIterating(self): + msg = map_unittest_pb2.TestMap() + + string_string_iter = iter(msg.map_string_string) + int32_foreign_iter = iter(msg.map_int32_foreign_message) + + msg.map_string_string['abc'] = '123' + msg.map_int32_foreign_message[5].c = 5 + + with self.assertRaises(RuntimeError): + for key in string_string_iter: + pass + + with self.assertRaises(RuntimeError): + for key in int32_foreign_iter: + pass + + def testSubmessageMap(self): + msg = map_unittest_pb2.TestMap() + + submsg = msg.map_int32_foreign_message[111] + self.assertIs(submsg, msg.map_int32_foreign_message[111]) + self.assertTrue(isinstance(submsg, unittest_pb2.ForeignMessage)) + + submsg.c = 5 + + serialized = msg.SerializeToString() + msg2 = map_unittest_pb2.TestMap() + msg2.ParseFromString(serialized) + + self.assertEqual(5, msg2.map_int32_foreign_message[111].c) + + # Doesn't allow direct submessage assignment. + with self.assertRaises(ValueError): + msg.map_int32_foreign_message[88] = unittest_pb2.ForeignMessage() + + def testMapIteration(self): + msg = map_unittest_pb2.TestMap() + + for k, v in msg.map_int32_int32.iteritems(): + # Should not be reached. + self.assertTrue(False) + + msg.map_int32_int32[2] = 4 + msg.map_int32_int32[3] = 6 + msg.map_int32_int32[4] = 8 + self.assertEqual(3, len(msg.map_int32_int32)) + + matching_dict = {2: 4, 3: 6, 4: 8} + self.assertMapIterEquals(msg.map_int32_int32.iteritems(), matching_dict) + + def testMapIterationClearMessage(self): + # Iterator needs to work even if message and map are deleted. + msg = map_unittest_pb2.TestMap() + + msg.map_int32_int32[2] = 4 + msg.map_int32_int32[3] = 6 + msg.map_int32_int32[4] = 8 + + it = msg.map_int32_int32.iteritems() + del msg + + matching_dict = {2: 4, 3: 6, 4: 8} + self.assertMapIterEquals(it, matching_dict) + + def testMapConstruction(self): + msg = map_unittest_pb2.TestMap(map_int32_int32={1: 2, 3: 4}) + self.assertEqual(2, msg.map_int32_int32[1]) + self.assertEqual(4, msg.map_int32_int32[3]) + + msg = map_unittest_pb2.TestMap( + map_int32_foreign_message={3: unittest_pb2.ForeignMessage(c=5)}) + self.assertEqual(5, msg.map_int32_foreign_message[3].c) + + def testMapValidAfterFieldCleared(self): + # Map needs to work even if field is cleared. + # For the C++ implementation this tests the correctness of + # ScalarMapContainer::Release() + msg = map_unittest_pb2.TestMap() + map = msg.map_int32_int32 + + map[2] = 4 + map[3] = 6 + map[4] = 8 + + msg.ClearField('map_int32_int32') + matching_dict = {2: 4, 3: 6, 4: 8} + self.assertMapIterEquals(map.iteritems(), matching_dict) + + def testMapIterValidAfterFieldCleared(self): + # Map iterator needs to work even if field is cleared. + # For the C++ implementation this tests the correctness of + # ScalarMapContainer::Release() + msg = map_unittest_pb2.TestMap() + + msg.map_int32_int32[2] = 4 + msg.map_int32_int32[3] = 6 + msg.map_int32_int32[4] = 8 + + it = msg.map_int32_int32.iteritems() + + msg.ClearField('map_int32_int32') + matching_dict = {2: 4, 3: 6, 4: 8} + self.assertMapIterEquals(it, matching_dict) + + def testMapDelete(self): + msg = map_unittest_pb2.TestMap() + + self.assertEqual(0, len(msg.map_int32_int32)) + + msg.map_int32_int32[4] = 6 + self.assertEqual(1, len(msg.map_int32_int32)) + + with self.assertRaises(KeyError): + del msg.map_int32_int32[88] + + del msg.map_int32_int32[4] + self.assertEqual(0, len(msg.map_int32_int32)) + + class ValidTypeNamesTest(unittest.TestCase): diff --git a/python/google/protobuf/internal/proto_builder_test.py b/python/google/protobuf/internal/proto_builder_test.py index b1e57f35..edaf3fa3 100644 --- a/python/google/protobuf/internal/proto_builder_test.py +++ b/python/google/protobuf/internal/proto_builder_test.py @@ -32,6 +32,7 @@ """Tests for google.protobuf.proto_builder.""" +import collections import unittest from google.protobuf import descriptor_pb2 @@ -43,10 +44,11 @@ from google.protobuf import text_format class ProtoBuilderTest(unittest.TestCase): def setUp(self): - self._fields = { - 'foo': descriptor_pb2.FieldDescriptorProto.TYPE_INT64, - 'bar': descriptor_pb2.FieldDescriptorProto.TYPE_STRING, - } + self.ordered_fields = collections.OrderedDict([ + ('foo', descriptor_pb2.FieldDescriptorProto.TYPE_INT64), + ('bar', descriptor_pb2.FieldDescriptorProto.TYPE_STRING), + ]) + self._fields = dict(self.ordered_fields) def testMakeSimpleProtoClass(self): """Test that we can create a proto class.""" @@ -59,6 +61,17 @@ class ProtoBuilderTest(unittest.TestCase): self.assertMultiLineEqual( 'bar: "asdf"\nfoo: 12345\n', text_format.MessageToString(proto)) + def testOrderedFields(self): + """Test that the field order is maintained when given an OrderedDict.""" + proto_cls = proto_builder.MakeSimpleProtoClass( + self.ordered_fields, + full_name='net.proto2.python.public.proto_builder_test.OrderedTest') + proto = proto_cls() + proto.foo = 12345 + proto.bar = 'asdf' + self.assertMultiLineEqual( + 'foo: 12345\nbar: "asdf"\n', text_format.MessageToString(proto)) + def testMakeSameProtoClassTwice(self): """Test that the DescriptorPool is used.""" pool = descriptor_pool.DescriptorPool() diff --git a/python/google/protobuf/internal/python_message.py b/python/google/protobuf/internal/python_message.py index 54f584ae..ca9f7675 100755 --- a/python/google/protobuf/internal/python_message.py +++ b/python/google/protobuf/internal/python_message.py @@ -61,9 +61,11 @@ if sys.version_info[0] < 3: except ImportError: from StringIO import StringIO as BytesIO import copy_reg as copyreg + _basestring = basestring else: from io import BytesIO import copyreg + _basestring = str import struct import weakref @@ -77,6 +79,7 @@ from google.protobuf.internal import type_checkers from google.protobuf.internal import wire_format from google.protobuf import descriptor as descriptor_mod from google.protobuf import message as message_mod +from google.protobuf import symbol_database from google.protobuf import text_format _FieldDescriptor = descriptor_mod.FieldDescriptor @@ -101,6 +104,7 @@ def InitMessage(descriptor, cls): for field in descriptor.fields: _AttachFieldHelpers(cls, field) + descriptor._concrete_class = cls # pylint: disable=protected-access _AddEnumValues(descriptor, cls) _AddInitMethod(descriptor, cls) _AddPropertiesForFields(descriptor, cls) @@ -198,12 +202,37 @@ def _IsMessageSetExtension(field): field.label == _FieldDescriptor.LABEL_OPTIONAL) +def _IsMapField(field): + return (field.type == _FieldDescriptor.TYPE_MESSAGE and + field.message_type.has_options and + field.message_type.GetOptions().map_entry) + + +def _IsMessageMapField(field): + value_type = field.message_type.fields_by_name["value"] + return value_type.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE + + def _AttachFieldHelpers(cls, field_descriptor): is_repeated = (field_descriptor.label == _FieldDescriptor.LABEL_REPEATED) - is_packed = (field_descriptor.has_options and - field_descriptor.GetOptions().packed) - - if _IsMessageSetExtension(field_descriptor): + is_packable = (is_repeated and + wire_format.IsTypePackable(field_descriptor.type)) + if not is_packable: + is_packed = False + elif field_descriptor.containing_type.syntax == "proto2": + is_packed = (field_descriptor.has_options and + field_descriptor.GetOptions().packed) + else: + has_packed_false = (field_descriptor.has_options and + field_descriptor.GetOptions().HasField("packed") and + field_descriptor.GetOptions().packed == False) + is_packed = not has_packed_false + is_map_entry = _IsMapField(field_descriptor) + + if is_map_entry: + field_encoder = encoder.MapEncoder(field_descriptor) + sizer = encoder.MapSizer(field_descriptor) + elif _IsMessageSetExtension(field_descriptor): field_encoder = encoder.MessageSetItemEncoder(field_descriptor.number) sizer = encoder.MessageSetItemSizer(field_descriptor.number) else: @@ -228,9 +257,16 @@ def _AttachFieldHelpers(cls, field_descriptor): if field_descriptor.containing_oneof is not None: oneof_descriptor = field_descriptor - field_decoder = type_checkers.TYPE_TO_DECODER[decode_type]( - field_descriptor.number, is_repeated, is_packed, - field_descriptor, field_descriptor._default_constructor) + if is_map_entry: + is_message_map = _IsMessageMapField(field_descriptor) + + field_decoder = decoder.MapDecoder( + field_descriptor, _GetInitializeDefaultForMap(field_descriptor), + is_message_map) + else: + field_decoder = type_checkers.TYPE_TO_DECODER[decode_type]( + field_descriptor.number, is_repeated, is_packed, + field_descriptor, field_descriptor._default_constructor) cls._decoders_by_tag[tag_bytes] = (field_decoder, oneof_descriptor) @@ -265,6 +301,26 @@ def _AddEnumValues(descriptor, cls): setattr(cls, enum_value.name, enum_value.number) +def _GetInitializeDefaultForMap(field): + if field.label != _FieldDescriptor.LABEL_REPEATED: + raise ValueError('map_entry set on non-repeated field %s' % ( + field.name)) + fields_by_name = field.message_type.fields_by_name + key_checker = type_checkers.GetTypeChecker(fields_by_name['key']) + + value_field = fields_by_name['value'] + if _IsMessageMapField(field): + def MakeMessageMapDefault(message): + return containers.MessageMap( + message._listener_for_children, value_field.message_type, key_checker) + return MakeMessageMapDefault + else: + value_checker = type_checkers.GetTypeChecker(value_field) + def MakePrimitiveMapDefault(message): + return containers.ScalarMap( + message._listener_for_children, key_checker, value_checker) + return MakePrimitiveMapDefault + def _DefaultValueConstructorForField(field): """Returns a function which returns a default value for a field. @@ -279,6 +335,9 @@ def _DefaultValueConstructorForField(field): value may refer back to |message| via a weak reference. """ + if _IsMapField(field): + return _GetInitializeDefaultForMap(field) + if field.label == _FieldDescriptor.LABEL_REPEATED: if field.has_default_value and field.default_value != []: raise ValueError('Repeated field default value not empty list: %s' % ( @@ -329,7 +388,22 @@ def _ReraiseTypeErrorWithFieldName(message_name, field_name): def _AddInitMethod(message_descriptor, cls): """Adds an __init__ method to cls.""" - fields = message_descriptor.fields + + def _GetIntegerEnumValue(enum_type, value): + """Convert a string or integer enum value to an integer. + + If the value is a string, it is converted to the enum value in + enum_type with the same name. If the value is not a string, it's + returned as-is. (No conversion or bounds-checking is done.) + """ + if isinstance(value, _basestring): + try: + return enum_type.values_by_name[value].number + except KeyError: + raise ValueError('Enum type %s: unknown label "%s"' % ( + enum_type.full_name, value)) + return value + def init(self, **kwargs): self._cached_byte_size = 0 self._cached_byte_size_dirty = len(kwargs) > 0 @@ -352,19 +426,37 @@ def _AddInitMethod(message_descriptor, cls): if field.label == _FieldDescriptor.LABEL_REPEATED: copy = field._default_constructor(self) if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: # Composite - for val in field_value: - copy.add().MergeFrom(val) + if _IsMapField(field): + if _IsMessageMapField(field): + for key in field_value: + copy[key].MergeFrom(field_value[key]) + else: + copy.update(field_value) + else: + for val in field_value: + if isinstance(val, dict): + copy.add(**val) + else: + copy.add().MergeFrom(val) else: # Scalar + if field.cpp_type == _FieldDescriptor.CPPTYPE_ENUM: + field_value = [_GetIntegerEnumValue(field.enum_type, val) + for val in field_value] copy.extend(field_value) self._fields[field] = copy elif field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: copy = field._default_constructor(self) + new_val = field_value + if isinstance(field_value, dict): + new_val = field.message_type._concrete_class(**field_value) try: - copy.MergeFrom(field_value) + copy.MergeFrom(new_val) except TypeError: _ReraiseTypeErrorWithFieldName(message_descriptor.name, field_name) self._fields[field] = copy else: + if field.cpp_type == _FieldDescriptor.CPPTYPE_ENUM: + field_value = _GetIntegerEnumValue(field.enum_type, field_value) try: setattr(self, field_name, field_value) except TypeError: @@ -758,6 +850,26 @@ def _AddHasExtensionMethod(cls): return extension_handle in self._fields cls.HasExtension = HasExtension +def _UnpackAny(msg): + type_url = msg.type_url + db = symbol_database.Default() + + if not type_url: + return None + + # TODO(haberman): For now we just strip the hostname. Better logic will be + # required. + type_name = type_url.split("/")[-1] + descriptor = db.pool.FindMessageTypeByName(type_name) + + if descriptor is None: + return None + + message_class = db.GetPrototype(descriptor) + message = message_class() + + message.ParseFromString(msg.value) + return message def _AddEqualsMethod(message_descriptor, cls): """Helper for _AddMessageMethods().""" @@ -769,6 +881,12 @@ def _AddEqualsMethod(message_descriptor, cls): if self is other: return True + if self.DESCRIPTOR.full_name == "google.protobuf.Any": + any_a = _UnpackAny(self) + any_b = _UnpackAny(other) + if any_a and any_b: + return any_a == any_b + if not self.ListFields() == other.ListFields(): return False @@ -961,6 +1079,9 @@ def _AddIsInitializedMethod(message_descriptor, cls): for field, value in list(self._fields.items()): # dict can change size! if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: if field.label == _FieldDescriptor.LABEL_REPEATED: + if (field.message_type.has_options and + field.message_type.GetOptions().map_entry): + continue for element in value: if not element.IsInitialized(): if errors is not None: @@ -996,16 +1117,26 @@ def _AddIsInitializedMethod(message_descriptor, cls): else: name = field.name - if field.label == _FieldDescriptor.LABEL_REPEATED: + if _IsMapField(field): + if _IsMessageMapField(field): + for key in value: + element = value[key] + prefix = "%s[%d]." % (name, key) + sub_errors = element.FindInitializationErrors() + errors += [prefix + error for error in sub_errors] + else: + # ScalarMaps can't have any initialization errors. + pass + elif field.label == _FieldDescriptor.LABEL_REPEATED: for i in xrange(len(value)): element = value[i] prefix = "%s[%d]." % (name, i) sub_errors = element.FindInitializationErrors() - errors += [ prefix + error for error in sub_errors ] + errors += [prefix + error for error in sub_errors] else: prefix = name + "." sub_errors = value.FindInitializationErrors() - errors += [ prefix + error for error in sub_errors ] + errors += [prefix + error for error in sub_errors] return errors diff --git a/python/google/protobuf/internal/reflection_test.py b/python/google/protobuf/internal/reflection_test.py index ae79c78b..4eca4989 100755 --- a/python/google/protobuf/internal/reflection_test.py +++ b/python/google/protobuf/internal/reflection_test.py @@ -39,8 +39,8 @@ import copy import gc import operator import struct -import unittest +import unittest from google.protobuf import unittest_import_pb2 from google.protobuf import unittest_mset_pb2 from google.protobuf import unittest_pb2 @@ -1798,8 +1798,8 @@ class ReflectionTest(unittest.TestCase): def testBadArguments(self): # Some of these assertions used to segfault. from google.protobuf.pyext import _message - self.assertRaises(TypeError, _message.Message._GetFieldDescriptor, 3) - self.assertRaises(TypeError, _message.Message._GetExtensionDescriptor, 42) + self.assertRaises(TypeError, _message.default_pool.FindFieldByName, 3) + self.assertRaises(TypeError, _message.default_pool.FindExtensionByName, 42) self.assertRaises(TypeError, unittest_pb2.TestAllTypes().__getattribute__, 42) diff --git a/python/google/protobuf/internal/service_reflection_test.py b/python/google/protobuf/internal/service_reflection_test.py index de462124..9967255a 100755 --- a/python/google/protobuf/internal/service_reflection_test.py +++ b/python/google/protobuf/internal/service_reflection_test.py @@ -35,7 +35,6 @@ __author__ = 'petar@google.com (Petar Petrov)' import unittest - from google.protobuf import unittest_pb2 from google.protobuf import service_reflection from google.protobuf import service @@ -81,7 +80,7 @@ class FooUnitTest(unittest.TestCase): self.assertEqual('Method Bar not implemented.', rpc_controller.failure_message) self.assertEqual(None, self.callback_response) - + class MyServiceImpl(unittest_pb2.TestService): def Foo(self, rpc_controller, request, done): self.foo_called = True diff --git a/python/google/protobuf/internal/symbol_database_test.py b/python/google/protobuf/internal/symbol_database_test.py index c888aff7..80b83bc2 100644 --- a/python/google/protobuf/internal/symbol_database_test.py +++ b/python/google/protobuf/internal/symbol_database_test.py @@ -33,7 +33,6 @@ """Tests for google.protobuf.symbol_database.""" import unittest - from google.protobuf import unittest_pb2 from google.protobuf import symbol_database diff --git a/python/google/protobuf/internal/test_util.py b/python/google/protobuf/internal/test_util.py index d84e3836..0cbdbad9 100755 --- a/python/google/protobuf/internal/test_util.py +++ b/python/google/protobuf/internal/test_util.py @@ -75,7 +75,8 @@ def SetAllNonLazyFields(message): message.optional_string = u'115' message.optional_bytes = b'116' - message.optionalgroup.a = 117 + if IsProto2(message): + message.optionalgroup.a = 117 message.optional_nested_message.bb = 118 message.optional_foreign_message.c = 119 message.optional_import_message.d = 120 @@ -109,7 +110,8 @@ def SetAllNonLazyFields(message): message.repeated_string.append(u'215') message.repeated_bytes.append(b'216') - message.repeatedgroup.add().a = 217 + if IsProto2(message): + message.repeatedgroup.add().a = 217 message.repeated_nested_message.add().bb = 218 message.repeated_foreign_message.add().c = 219 message.repeated_import_message.add().d = 220 @@ -140,7 +142,8 @@ def SetAllNonLazyFields(message): message.repeated_string.append(u'315') message.repeated_bytes.append(b'316') - message.repeatedgroup.add().a = 317 + if IsProto2(message): + message.repeatedgroup.add().a = 317 message.repeated_nested_message.add().bb = 318 message.repeated_foreign_message.add().c = 319 message.repeated_import_message.add().d = 320 @@ -396,7 +399,8 @@ def ExpectAllFieldsSet(test_case, message): test_case.assertTrue(message.HasField('optional_string')) test_case.assertTrue(message.HasField('optional_bytes')) - test_case.assertTrue(message.HasField('optionalgroup')) + if IsProto2(message): + test_case.assertTrue(message.HasField('optionalgroup')) test_case.assertTrue(message.HasField('optional_nested_message')) test_case.assertTrue(message.HasField('optional_foreign_message')) test_case.assertTrue(message.HasField('optional_import_message')) @@ -430,7 +434,8 @@ def ExpectAllFieldsSet(test_case, message): test_case.assertEqual('115', message.optional_string) test_case.assertEqual(b'116', message.optional_bytes) - test_case.assertEqual(117, message.optionalgroup.a) + if IsProto2(message): + test_case.assertEqual(117, message.optionalgroup.a) test_case.assertEqual(118, message.optional_nested_message.bb) test_case.assertEqual(119, message.optional_foreign_message.c) test_case.assertEqual(120, message.optional_import_message.d) @@ -463,7 +468,8 @@ def ExpectAllFieldsSet(test_case, message): test_case.assertEqual(2, len(message.repeated_string)) test_case.assertEqual(2, len(message.repeated_bytes)) - test_case.assertEqual(2, len(message.repeatedgroup)) + if IsProto2(message): + test_case.assertEqual(2, len(message.repeatedgroup)) test_case.assertEqual(2, len(message.repeated_nested_message)) test_case.assertEqual(2, len(message.repeated_foreign_message)) test_case.assertEqual(2, len(message.repeated_import_message)) @@ -491,7 +497,8 @@ def ExpectAllFieldsSet(test_case, message): test_case.assertEqual('215', message.repeated_string[0]) test_case.assertEqual(b'216', message.repeated_bytes[0]) - test_case.assertEqual(217, message.repeatedgroup[0].a) + if IsProto2(message): + test_case.assertEqual(217, message.repeatedgroup[0].a) test_case.assertEqual(218, message.repeated_nested_message[0].bb) test_case.assertEqual(219, message.repeated_foreign_message[0].c) test_case.assertEqual(220, message.repeated_import_message[0].d) @@ -521,7 +528,8 @@ def ExpectAllFieldsSet(test_case, message): test_case.assertEqual('315', message.repeated_string[1]) test_case.assertEqual(b'316', message.repeated_bytes[1]) - test_case.assertEqual(317, message.repeatedgroup[1].a) + if IsProto2(message): + test_case.assertEqual(317, message.repeatedgroup[1].a) test_case.assertEqual(318, message.repeated_nested_message[1].bb) test_case.assertEqual(319, message.repeated_foreign_message[1].c) test_case.assertEqual(320, message.repeated_import_message[1].d) diff --git a/python/google/protobuf/internal/text_encoding_test.py b/python/google/protobuf/internal/text_encoding_test.py index 27896b94..5df13b78 100755 --- a/python/google/protobuf/internal/text_encoding_test.py +++ b/python/google/protobuf/internal/text_encoding_test.py @@ -33,7 +33,6 @@ """Tests for google.protobuf.text_encoding.""" import unittest - from google.protobuf import text_encoding TEST_VALUES = [ diff --git a/python/google/protobuf/internal/text_format_test.py b/python/google/protobuf/internal/text_format_test.py index bf7e06ee..06bd1ee5 100755 --- a/python/google/protobuf/internal/text_format_test.py +++ b/python/google/protobuf/internal/text_format_test.py @@ -37,8 +37,10 @@ __author__ = 'kenton@google.com (Kenton Varda)' import re import unittest +import unittest from google.protobuf.internal import _parameterized +from google.protobuf import map_unittest_pb2 from google.protobuf import unittest_mset_pb2 from google.protobuf import unittest_pb2 from google.protobuf import unittest_proto3_arena_pb2 @@ -309,31 +311,6 @@ class TextFormatTest(TextFormatBase): r'"unknown_field".'), text_format.Parse, text, message) - def testParseGroupNotClosed(self, message_module): - message = message_module.TestAllTypes() - text = 'RepeatedGroup: <' - self.assertRaisesRegexp( - text_format.ParseError, '1:16 : Expected ">".', - text_format.Parse, text, message) - - text = 'RepeatedGroup: {' - self.assertRaisesRegexp( - text_format.ParseError, '1:16 : Expected "}".', - text_format.Parse, text, message) - - def testParseEmptyGroup(self, message_module): - message = message_module.TestAllTypes() - text = 'OptionalGroup: {}' - text_format.Parse(text, message) - self.assertTrue(message.HasField('optionalgroup')) - - message.Clear() - - message = message_module.TestAllTypes() - text = 'OptionalGroup: <>' - text_format.Parse(text, message) - self.assertTrue(message.HasField('optionalgroup')) - def testParseBadEnumValue(self, message_module): message = message_module.TestAllTypes() text = 'optional_nested_enum: BARR' @@ -408,6 +385,14 @@ class TextFormatTest(TextFormatBase): # Ideally the schemas would be made more similar so these tests could pass. class OnlyWorksWithProto2RightNowTests(TextFormatBase): + def testPrintAllFieldsPointy(self, message_module): + message = unittest_pb2.TestAllTypes() + test_util.SetAllFields(message) + self.CompareToGoldenFile( + self.RemoveRedundantZeros( + text_format.MessageToString(message, pointy_brackets=True)), + 'text_format_unittest_data_pointy_oneof.txt') + def testParseGolden(self): golden_text = '\n'.join(self.ReadGolden('text_format_unittest_data.txt')) parsed_message = unittest_pb2.TestAllTypes() @@ -471,8 +456,49 @@ class OnlyWorksWithProto2RightNowTests(TextFormatBase): test_util.SetAllFields(message) self.assertEquals(message, parsed_message) + def testPrintMap(self): + message = map_unittest_pb2.TestMap() -# Tests of proto2-only features (MessageSet and extensions). + message.map_int32_int32[-123] = -456 + message.map_int64_int64[-2**33] = -2**34 + message.map_uint32_uint32[123] = 456 + message.map_uint64_uint64[2**33] = 2**34 + message.map_string_string["abc"] = "123" + message.map_int32_foreign_message[111].c = 5 + + # Maps are serialized to text format using their underlying repeated + # representation. + self.CompareToGoldenText( + text_format.MessageToString(message), + 'map_int32_int32 {\n' + ' key: -123\n' + ' value: -456\n' + '}\n' + 'map_int64_int64 {\n' + ' key: -8589934592\n' + ' value: -17179869184\n' + '}\n' + 'map_uint32_uint32 {\n' + ' key: 123\n' + ' value: 456\n' + '}\n' + 'map_uint64_uint64 {\n' + ' key: 8589934592\n' + ' value: 17179869184\n' + '}\n' + 'map_string_string {\n' + ' key: "abc"\n' + ' value: "123"\n' + '}\n' + 'map_int32_foreign_message {\n' + ' key: 111\n' + ' value {\n' + ' c: 5\n' + ' }\n' + '}\n') + + +# Tests of proto2-only features (MessageSet, extensions, etc.). class Proto2Tests(TextFormatBase): def testPrintMessageSet(self): @@ -620,6 +646,69 @@ class Proto2Tests(TextFormatBase): 'have multiple "optional_int32" fields.'), text_format.Parse, text, message) + def testParseGroupNotClosed(self): + message = unittest_pb2.TestAllTypes() + text = 'RepeatedGroup: <' + self.assertRaisesRegexp( + text_format.ParseError, '1:16 : Expected ">".', + text_format.Parse, text, message) + text = 'RepeatedGroup: {' + self.assertRaisesRegexp( + text_format.ParseError, '1:16 : Expected "}".', + text_format.Parse, text, message) + + def testParseEmptyGroup(self): + message = unittest_pb2.TestAllTypes() + text = 'OptionalGroup: {}' + text_format.Parse(text, message) + self.assertTrue(message.HasField('optionalgroup')) + + message.Clear() + + message = unittest_pb2.TestAllTypes() + text = 'OptionalGroup: <>' + text_format.Parse(text, message) + self.assertTrue(message.HasField('optionalgroup')) + + # Maps aren't really proto2-only, but our test schema only has maps for + # proto2. + def testParseMap(self): + text = ('map_int32_int32 {\n' + ' key: -123\n' + ' value: -456\n' + '}\n' + 'map_int64_int64 {\n' + ' key: -8589934592\n' + ' value: -17179869184\n' + '}\n' + 'map_uint32_uint32 {\n' + ' key: 123\n' + ' value: 456\n' + '}\n' + 'map_uint64_uint64 {\n' + ' key: 8589934592\n' + ' value: 17179869184\n' + '}\n' + 'map_string_string {\n' + ' key: "abc"\n' + ' value: "123"\n' + '}\n' + 'map_int32_foreign_message {\n' + ' key: 111\n' + ' value {\n' + ' c: 5\n' + ' }\n' + '}\n') + message = map_unittest_pb2.TestMap() + text_format.Parse(text, message) + + self.assertEqual(-456, message.map_int32_int32[-123]) + self.assertEqual(-2**34, message.map_int64_int64[-2**33]) + self.assertEqual(456, message.map_uint32_uint32[123]) + self.assertEqual(2**34, message.map_uint64_uint64[2**33]) + self.assertEqual("123", message.map_string_string["abc"]) + self.assertEqual(5, message.map_int32_foreign_message[111].c) + class TokenizerTest(unittest.TestCase): diff --git a/python/google/protobuf/internal/type_checkers.py b/python/google/protobuf/internal/type_checkers.py index 76c056c4..f20e526a 100755 --- a/python/google/protobuf/internal/type_checkers.py +++ b/python/google/protobuf/internal/type_checkers.py @@ -129,6 +129,9 @@ class IntValueChecker(object): proposed_value = self._TYPE(proposed_value) return proposed_value + def DefaultValue(self): + return 0 + class EnumValueChecker(object): @@ -146,6 +149,9 @@ class EnumValueChecker(object): raise ValueError('Unknown enum value: %d' % proposed_value) return proposed_value + def DefaultValue(self): + return self._enum_type.values[0].number + class UnicodeValueChecker(object): @@ -171,6 +177,9 @@ class UnicodeValueChecker(object): (proposed_value)) return proposed_value + def DefaultValue(self): + return u"" + class Int32ValueChecker(IntValueChecker): # We're sure to use ints instead of longs here since comparison may be more diff --git a/python/google/protobuf/internal/unknown_fields_test.py b/python/google/protobuf/internal/unknown_fields_test.py index 9337ae8a..1b81ae79 100755 --- a/python/google/protobuf/internal/unknown_fields_test.py +++ b/python/google/protobuf/internal/unknown_fields_test.py @@ -36,7 +36,6 @@ __author__ = 'bohdank@google.com (Bohdan Koval)' import unittest - from google.protobuf import unittest_mset_pb2 from google.protobuf import unittest_pb2 from google.protobuf import unittest_proto3_arena_pb2 diff --git a/python/google/protobuf/internal/wire_format_test.py b/python/google/protobuf/internal/wire_format_test.py index 5cd7fcb9..78dc1167 100755 --- a/python/google/protobuf/internal/wire_format_test.py +++ b/python/google/protobuf/internal/wire_format_test.py @@ -35,7 +35,6 @@ __author__ = 'robinson@google.com (Will Robinson)' import unittest - from google.protobuf import message from google.protobuf.internal import wire_format diff --git a/python/google/protobuf/proto_builder.py b/python/google/protobuf/proto_builder.py index 1fa28f1a..7489cf63 100644 --- a/python/google/protobuf/proto_builder.py +++ b/python/google/protobuf/proto_builder.py @@ -30,6 +30,7 @@ """Dynamic Protobuf class creator.""" +import collections import hashlib import os @@ -59,7 +60,9 @@ def MakeSimpleProtoClass(fields, full_name, pool=None): Note: this doesn't validate field names! Args: - fields: dict of {name: field_type} mappings for each field in the proto. + fields: dict of {name: field_type} mappings for each field in the proto. If + this is an OrderedDict the order will be maintained, otherwise the + fields will be sorted by name. full_name: str, the fully-qualified name of the proto type. pool: optional DescriptorPool instance. Returns: @@ -73,12 +76,19 @@ def MakeSimpleProtoClass(fields, full_name, pool=None): # The factory's DescriptorPool doesn't know about this class yet. pass + # Get a list of (name, field_type) tuples from the fields dict. If fields was + # an OrderedDict we keep the order, but otherwise we sort the field to ensure + # consistent ordering. + field_items = fields.items() + if not isinstance(fields, collections.OrderedDict): + field_items = sorted(field_items) + # Use a consistent file name that is unlikely to conflict with any imported # proto files. fields_hash = hashlib.sha1() - for f_name, f_type in sorted(fields.items()): - fields_hash.update(f_name.encode('utf8')) - fields_hash.update(str(f_type).encode('utf8')) + for f_name, f_type in field_items: + fields_hash.update(f_name.encode('utf-8')) + fields_hash.update(str(f_type).encode('utf-8')) proto_file_name = fields_hash.hexdigest() + '.proto' package, name = full_name.rsplit('.', 1) @@ -87,7 +97,7 @@ def MakeSimpleProtoClass(fields, full_name, pool=None): file_proto.package = package desc_proto = file_proto.message_type.add() desc_proto.name = name - for f_number, (f_name, f_type) in enumerate(sorted(fields.items()), 1): + for f_number, (f_name, f_type) in enumerate(field_items, 1): field_proto = desc_proto.field.add() field_proto.name = f_name field_proto.number = f_number diff --git a/python/google/protobuf/pyext/descriptor.cc b/python/google/protobuf/pyext/descriptor.cc index e77d0bb9..2160757b 100644 --- a/python/google/protobuf/pyext/descriptor.cc +++ b/python/google/protobuf/pyext/descriptor.cc @@ -43,8 +43,6 @@ #include #include -#define C(str) const_cast(str) - #if PY_MAJOR_VERSION >= 3 #define PyString_FromStringAndSize PyUnicode_FromStringAndSize #define PyString_Check PyUnicode_Check @@ -257,8 +255,14 @@ namespace descriptor { // Creates or retrieve a Python descriptor of the specified type. // Objects are interned: the same descriptor will return the same object if it // was kept alive. +// 'was_created' is an optional pointer to a bool, and is set to true if a new +// object was allocated. // Always return a new reference. -PyObject* NewInternedDescriptor(PyTypeObject* type, const void* descriptor) { +PyObject* NewInternedDescriptor(PyTypeObject* type, const void* descriptor, + bool* was_created) { + if (was_created) { + *was_created = false; + } if (descriptor == NULL) { PyErr_BadInternalCall(); return NULL; @@ -283,6 +287,9 @@ PyObject* NewInternedDescriptor(PyTypeObject* type, const void* descriptor) { GetDescriptorPool()->interned_descriptors->insert( std::make_pair(descriptor, reinterpret_cast(py_descriptor))); + if (was_created) { + *was_created = true; + } return reinterpret_cast(py_descriptor); } @@ -298,9 +305,7 @@ static PyGetSetDef Getters[] = { PyTypeObject PyBaseDescriptor_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) - // Keep the fully qualified _message symbol in a line for opensource. - "google.protobuf.internal._message." - "DescriptorBase", // tp_name + FULL_MODULE_NAME ".DescriptorBase", // tp_name sizeof(PyBaseDescriptor), // tp_basicsize 0, // tp_itemsize (destructor)Dealloc, // tp_dealloc @@ -357,7 +362,7 @@ static PyObject* GetFullName(PyBaseDescriptor* self, void *closure) { } static PyObject* GetFile(PyBaseDescriptor *self, void *closure) { - return PyFileDescriptor_New(_GetDescriptor(self)->file()); + return PyFileDescriptor_FromDescriptor(_GetDescriptor(self)->file()); } static PyObject* GetConcreteClass(PyBaseDescriptor* self, void *closure) { @@ -367,17 +372,6 @@ static PyObject* GetConcreteClass(PyBaseDescriptor* self, void *closure) { return concrete_class; } -static int SetConcreteClass(PyBaseDescriptor *self, PyObject *value, - void *closure) { - // This attribute is also set from reflection.py. Check that it's actually a - // no-op. - if (value != cdescriptor_pool::GetMessageClass( - GetDescriptorPool(), _GetDescriptor(self))) { - PyErr_SetString(PyExc_AttributeError, "Cannot change _concrete_class"); - } - return 0; -} - static PyObject* GetFieldsByName(PyBaseDescriptor* self, void *closure) { return NewMessageFieldsByName(_GetDescriptor(self)); } @@ -452,7 +446,7 @@ static PyObject* GetContainingType(PyBaseDescriptor *self, void *closure) { const Descriptor* containing_type = _GetDescriptor(self)->containing_type(); if (containing_type) { - return PyMessageDescriptor_New(containing_type); + return PyMessageDescriptor_FromDescriptor(containing_type); } else { Py_RETURN_NONE; } @@ -515,29 +509,34 @@ static PyObject* GetSyntax(PyBaseDescriptor *self, void *closure) { } static PyGetSetDef Getters[] = { - { C("name"), (getter)GetName, NULL, "Last name", NULL}, - { C("full_name"), (getter)GetFullName, NULL, "Full name", NULL}, - { C("_concrete_class"), (getter)GetConcreteClass, (setter)SetConcreteClass, "concrete class", NULL}, - { C("file"), (getter)GetFile, NULL, "File descriptor", NULL}, - - { C("fields"), (getter)GetFieldsSeq, NULL, "Fields sequence", NULL}, - { C("fields_by_name"), (getter)GetFieldsByName, NULL, "Fields by name", NULL}, - { C("fields_by_number"), (getter)GetFieldsByNumber, NULL, "Fields by number", NULL}, - { C("nested_types"), (getter)GetNestedTypesSeq, NULL, "Nested types sequence", NULL}, - { C("nested_types_by_name"), (getter)GetNestedTypesByName, NULL, "Nested types by name", NULL}, - { C("extensions"), (getter)GetExtensions, NULL, "Extensions Sequence", NULL}, - { C("extensions_by_name"), (getter)GetExtensionsByName, NULL, "Extensions by name", NULL}, - { C("extension_ranges"), (getter)GetExtensionRanges, NULL, "Extension ranges", NULL}, - { C("enum_types"), (getter)GetEnumsSeq, NULL, "Enum sequence", NULL}, - { C("enum_types_by_name"), (getter)GetEnumTypesByName, NULL, "Enum types by name", NULL}, - { C("enum_values_by_name"), (getter)GetEnumValuesByName, NULL, "Enum values by name", NULL}, - { C("oneofs_by_name"), (getter)GetOneofsByName, NULL, "Oneofs by name", NULL}, - { C("oneofs"), (getter)GetOneofsSeq, NULL, "Oneofs by name", NULL}, - { C("containing_type"), (getter)GetContainingType, (setter)SetContainingType, "Containing type", NULL}, - { C("is_extendable"), (getter)IsExtendable, (setter)NULL, NULL, NULL}, - { C("has_options"), (getter)GetHasOptions, (setter)SetHasOptions, "Has Options", NULL}, - { C("_options"), (getter)NULL, (setter)SetOptions, "Options", NULL}, - { C("syntax"), (getter)GetSyntax, (setter)NULL, "Syntax", NULL}, + { "name", (getter)GetName, NULL, "Last name"}, + { "full_name", (getter)GetFullName, NULL, "Full name"}, + { "_concrete_class", (getter)GetConcreteClass, NULL, "concrete class"}, + { "file", (getter)GetFile, NULL, "File descriptor"}, + + { "fields", (getter)GetFieldsSeq, NULL, "Fields sequence"}, + { "fields_by_name", (getter)GetFieldsByName, NULL, "Fields by name"}, + { "fields_by_number", (getter)GetFieldsByNumber, NULL, "Fields by number"}, + { "nested_types", (getter)GetNestedTypesSeq, NULL, "Nested types sequence"}, + { "nested_types_by_name", (getter)GetNestedTypesByName, NULL, + "Nested types by name"}, + { "extensions", (getter)GetExtensions, NULL, "Extensions Sequence"}, + { "extensions_by_name", (getter)GetExtensionsByName, NULL, + "Extensions by name"}, + { "extension_ranges", (getter)GetExtensionRanges, NULL, "Extension ranges"}, + { "enum_types", (getter)GetEnumsSeq, NULL, "Enum sequence"}, + { "enum_types_by_name", (getter)GetEnumTypesByName, NULL, + "Enum types by name"}, + { "enum_values_by_name", (getter)GetEnumValuesByName, NULL, + "Enum values by name"}, + { "oneofs_by_name", (getter)GetOneofsByName, NULL, "Oneofs by name"}, + { "oneofs", (getter)GetOneofsSeq, NULL, "Oneofs by name"}, + { "containing_type", (getter)GetContainingType, (setter)SetContainingType, + "Containing type"}, + { "is_extendable", (getter)IsExtendable, (setter)NULL}, + { "has_options", (getter)GetHasOptions, (setter)SetHasOptions, "Has Options"}, + { "_options", (getter)NULL, (setter)SetOptions, "Options"}, + { "syntax", (getter)GetSyntax, (setter)NULL, "Syntax"}, {NULL} }; @@ -552,9 +551,7 @@ static PyMethodDef Methods[] = { PyTypeObject PyMessageDescriptor_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) - // Keep the fully qualified _message symbol in a line for opensource. - C("google.protobuf.internal._message." - "MessageDescriptor"), // tp_name + FULL_MODULE_NAME ".MessageDescriptor", // tp_name sizeof(PyBaseDescriptor), // tp_basicsize 0, // tp_itemsize 0, // tp_dealloc @@ -573,7 +570,7 @@ PyTypeObject PyMessageDescriptor_Type = { 0, // tp_setattro 0, // tp_as_buffer Py_TPFLAGS_DEFAULT, // tp_flags - C("A Message Descriptor"), // tp_doc + "A Message Descriptor", // tp_doc 0, // tp_traverse 0, // tp_clear 0, // tp_richcompare @@ -586,10 +583,10 @@ PyTypeObject PyMessageDescriptor_Type = { &descriptor::PyBaseDescriptor_Type, // tp_base }; -PyObject* PyMessageDescriptor_New( +PyObject* PyMessageDescriptor_FromDescriptor( const Descriptor* message_descriptor) { return descriptor::NewInternedDescriptor( - &PyMessageDescriptor_Type, message_descriptor); + &PyMessageDescriptor_Type, message_descriptor, NULL); } const Descriptor* PyMessageDescriptor_AsDescriptor(PyObject* obj) { @@ -715,7 +712,7 @@ static PyObject* GetCDescriptor(PyObject *self, void *closure) { static PyObject *GetEnumType(PyBaseDescriptor *self, void *closure) { const EnumDescriptor* enum_type = _GetDescriptor(self)->enum_type(); if (enum_type) { - return PyEnumDescriptor_New(enum_type); + return PyEnumDescriptor_FromDescriptor(enum_type); } else { Py_RETURN_NONE; } @@ -728,7 +725,7 @@ static int SetEnumType(PyBaseDescriptor *self, PyObject *value, void *closure) { static PyObject *GetMessageType(PyBaseDescriptor *self, void *closure) { const Descriptor* message_type = _GetDescriptor(self)->message_type(); if (message_type) { - return PyMessageDescriptor_New(message_type); + return PyMessageDescriptor_FromDescriptor(message_type); } else { Py_RETURN_NONE; } @@ -743,7 +740,7 @@ static PyObject* GetContainingType(PyBaseDescriptor *self, void *closure) { const Descriptor* containing_type = _GetDescriptor(self)->containing_type(); if (containing_type) { - return PyMessageDescriptor_New(containing_type); + return PyMessageDescriptor_FromDescriptor(containing_type); } else { Py_RETURN_NONE; } @@ -758,7 +755,7 @@ static PyObject* GetExtensionScope(PyBaseDescriptor *self, void *closure) { const Descriptor* extension_scope = _GetDescriptor(self)->extension_scope(); if (extension_scope) { - return PyMessageDescriptor_New(extension_scope); + return PyMessageDescriptor_FromDescriptor(extension_scope); } else { Py_RETURN_NONE; } @@ -768,7 +765,7 @@ static PyObject* GetContainingOneof(PyBaseDescriptor *self, void *closure) { const OneofDescriptor* containing_oneof = _GetDescriptor(self)->containing_oneof(); if (containing_oneof) { - return PyOneofDescriptor_New(containing_oneof); + return PyOneofDescriptor_FromDescriptor(containing_oneof); } else { Py_RETURN_NONE; } @@ -803,26 +800,30 @@ static int SetOptions(PyBaseDescriptor *self, PyObject *value, static PyGetSetDef Getters[] = { - { C("full_name"), (getter)GetFullName, NULL, "Full name", NULL}, - { C("name"), (getter)GetName, NULL, "Unqualified name", NULL}, - { C("type"), (getter)GetType, NULL, "C++ Type", NULL}, - { C("cpp_type"), (getter)GetCppType, NULL, "C++ Type", NULL}, - { C("label"), (getter)GetLabel, NULL, "Label", NULL}, - { C("number"), (getter)GetNumber, NULL, "Number", NULL}, - { C("index"), (getter)GetIndex, NULL, "Index", NULL}, - { C("default_value"), (getter)GetDefaultValue, NULL, "Default Value", NULL}, - { C("has_default_value"), (getter)HasDefaultValue, NULL, NULL, NULL}, - { C("is_extension"), (getter)IsExtension, NULL, "ID", NULL}, - { C("id"), (getter)GetID, NULL, "ID", NULL}, - { C("_cdescriptor"), (getter)GetCDescriptor, NULL, "HAACK REMOVE ME", NULL}, - - { C("message_type"), (getter)GetMessageType, (setter)SetMessageType, "Message type", NULL}, - { C("enum_type"), (getter)GetEnumType, (setter)SetEnumType, "Enum type", NULL}, - { C("containing_type"), (getter)GetContainingType, (setter)SetContainingType, "Containing type", NULL}, - { C("extension_scope"), (getter)GetExtensionScope, (setter)NULL, "Extension scope", NULL}, - { C("containing_oneof"), (getter)GetContainingOneof, (setter)SetContainingOneof, "Containing oneof", NULL}, - { C("has_options"), (getter)GetHasOptions, (setter)SetHasOptions, "Has Options", NULL}, - { C("_options"), (getter)NULL, (setter)SetOptions, "Options", NULL}, + { "full_name", (getter)GetFullName, NULL, "Full name"}, + { "name", (getter)GetName, NULL, "Unqualified name"}, + { "type", (getter)GetType, NULL, "C++ Type"}, + { "cpp_type", (getter)GetCppType, NULL, "C++ Type"}, + { "label", (getter)GetLabel, NULL, "Label"}, + { "number", (getter)GetNumber, NULL, "Number"}, + { "index", (getter)GetIndex, NULL, "Index"}, + { "default_value", (getter)GetDefaultValue, NULL, "Default Value"}, + { "has_default_value", (getter)HasDefaultValue}, + { "is_extension", (getter)IsExtension, NULL, "ID"}, + { "id", (getter)GetID, NULL, "ID"}, + { "_cdescriptor", (getter)GetCDescriptor, NULL, "HAACK REMOVE ME"}, + + { "message_type", (getter)GetMessageType, (setter)SetMessageType, + "Message type"}, + { "enum_type", (getter)GetEnumType, (setter)SetEnumType, "Enum type"}, + { "containing_type", (getter)GetContainingType, (setter)SetContainingType, + "Containing type"}, + { "extension_scope", (getter)GetExtensionScope, (setter)NULL, + "Extension scope"}, + { "containing_oneof", (getter)GetContainingOneof, (setter)SetContainingOneof, + "Containing oneof"}, + { "has_options", (getter)GetHasOptions, (setter)SetHasOptions, "Has Options"}, + { "_options", (getter)NULL, (setter)SetOptions, "Options"}, {NULL} }; @@ -835,8 +836,7 @@ static PyMethodDef Methods[] = { PyTypeObject PyFieldDescriptor_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) - C("google.protobuf.internal." - "_message.FieldDescriptor"), // tp_name + FULL_MODULE_NAME ".FieldDescriptor", // tp_name sizeof(PyBaseDescriptor), // tp_basicsize 0, // tp_itemsize 0, // tp_dealloc @@ -855,7 +855,7 @@ PyTypeObject PyFieldDescriptor_Type = { 0, // tp_setattro 0, // tp_as_buffer Py_TPFLAGS_DEFAULT, // tp_flags - C("A Field Descriptor"), // tp_doc + "A Field Descriptor", // tp_doc 0, // tp_traverse 0, // tp_clear 0, // tp_richcompare @@ -868,10 +868,10 @@ PyTypeObject PyFieldDescriptor_Type = { &descriptor::PyBaseDescriptor_Type, // tp_base }; -PyObject* PyFieldDescriptor_New( +PyObject* PyFieldDescriptor_FromDescriptor( const FieldDescriptor* field_descriptor) { return descriptor::NewInternedDescriptor( - &PyFieldDescriptor_Type, field_descriptor); + &PyFieldDescriptor_Type, field_descriptor, NULL); } const FieldDescriptor* PyFieldDescriptor_AsDescriptor(PyObject* obj) { @@ -900,7 +900,7 @@ static PyObject* GetName(PyBaseDescriptor *self, void *closure) { } static PyObject* GetFile(PyBaseDescriptor *self, void *closure) { - return PyFileDescriptor_New(_GetDescriptor(self)->file()); + return PyFileDescriptor_FromDescriptor(_GetDescriptor(self)->file()); } static PyObject* GetEnumvaluesByName(PyBaseDescriptor* self, void *closure) { @@ -919,7 +919,7 @@ static PyObject* GetContainingType(PyBaseDescriptor *self, void *closure) { const Descriptor* containing_type = _GetDescriptor(self)->containing_type(); if (containing_type) { - return PyMessageDescriptor_New(containing_type); + return PyMessageDescriptor_FromDescriptor(containing_type); } else { Py_RETURN_NONE; } @@ -964,16 +964,19 @@ static PyMethodDef Methods[] = { }; static PyGetSetDef Getters[] = { - { C("full_name"), (getter)GetFullName, NULL, "Full name", NULL}, - { C("name"), (getter)GetName, NULL, "last name", NULL}, - { C("file"), (getter)GetFile, NULL, "File descriptor", NULL}, - { C("values"), (getter)GetEnumvaluesSeq, NULL, "values", NULL}, - { C("values_by_name"), (getter)GetEnumvaluesByName, NULL, "Enumvalues by name", NULL}, - { C("values_by_number"), (getter)GetEnumvaluesByNumber, NULL, "Enumvalues by number", NULL}, - - { C("containing_type"), (getter)GetContainingType, (setter)SetContainingType, "Containing type", NULL}, - { C("has_options"), (getter)GetHasOptions, (setter)SetHasOptions, "Has Options", NULL}, - { C("_options"), (getter)NULL, (setter)SetOptions, "Options", NULL}, + { "full_name", (getter)GetFullName, NULL, "Full name"}, + { "name", (getter)GetName, NULL, "last name"}, + { "file", (getter)GetFile, NULL, "File descriptor"}, + { "values", (getter)GetEnumvaluesSeq, NULL, "values"}, + { "values_by_name", (getter)GetEnumvaluesByName, NULL, + "Enum values by name"}, + { "values_by_number", (getter)GetEnumvaluesByNumber, NULL, + "Enum values by number"}, + + { "containing_type", (getter)GetContainingType, (setter)SetContainingType, + "Containing type"}, + { "has_options", (getter)GetHasOptions, (setter)SetHasOptions, "Has Options"}, + { "_options", (getter)NULL, (setter)SetOptions, "Options"}, {NULL} }; @@ -981,9 +984,7 @@ static PyGetSetDef Getters[] = { PyTypeObject PyEnumDescriptor_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) - // Keep the fully qualified _message symbol in a line for opensource. - C("google.protobuf.internal._message." - "EnumDescriptor"), // tp_name + FULL_MODULE_NAME ".EnumDescriptor", // tp_name sizeof(PyBaseDescriptor), // tp_basicsize 0, // tp_itemsize 0, // tp_dealloc @@ -1002,7 +1003,7 @@ PyTypeObject PyEnumDescriptor_Type = { 0, // tp_setattro 0, // tp_as_buffer Py_TPFLAGS_DEFAULT, // tp_flags - C("A Enum Descriptor"), // tp_doc + "A Enum Descriptor", // tp_doc 0, // tp_traverse 0, // tp_clear 0, // tp_richcompare @@ -1015,10 +1016,10 @@ PyTypeObject PyEnumDescriptor_Type = { &descriptor::PyBaseDescriptor_Type, // tp_base }; -PyObject* PyEnumDescriptor_New( +PyObject* PyEnumDescriptor_FromDescriptor( const EnumDescriptor* enum_descriptor) { return descriptor::NewInternedDescriptor( - &PyEnumDescriptor_Type, enum_descriptor); + &PyEnumDescriptor_Type, enum_descriptor, NULL); } namespace enumvalue_descriptor { @@ -1042,7 +1043,7 @@ static PyObject* GetIndex(PyBaseDescriptor *self, void *closure) { } static PyObject* GetType(PyBaseDescriptor *self, void *closure) { - return PyEnumDescriptor_New(_GetDescriptor(self)->type()); + return PyEnumDescriptor_FromDescriptor(_GetDescriptor(self)->type()); } static PyObject* GetHasOptions(PyBaseDescriptor *self, void *closure) { @@ -1069,13 +1070,13 @@ static int SetOptions(PyBaseDescriptor *self, PyObject *value, static PyGetSetDef Getters[] = { - { C("name"), (getter)GetName, NULL, "name", NULL}, - { C("number"), (getter)GetNumber, NULL, "number", NULL}, - { C("index"), (getter)GetIndex, NULL, "index", NULL}, - { C("type"), (getter)GetType, NULL, "index", NULL}, + { "name", (getter)GetName, NULL, "name"}, + { "number", (getter)GetNumber, NULL, "number"}, + { "index", (getter)GetIndex, NULL, "index"}, + { "type", (getter)GetType, NULL, "index"}, - { C("has_options"), (getter)GetHasOptions, (setter)SetHasOptions, "Has Options", NULL}, - { C("_options"), (getter)NULL, (setter)SetOptions, "Options", NULL}, + { "has_options", (getter)GetHasOptions, (setter)SetHasOptions, "Has Options"}, + { "_options", (getter)NULL, (setter)SetOptions, "Options"}, {NULL} }; @@ -1088,8 +1089,7 @@ static PyMethodDef Methods[] = { PyTypeObject PyEnumValueDescriptor_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) - C("google.protobuf.internal." - "_message.EnumValueDescriptor"), // tp_name + FULL_MODULE_NAME ".EnumValueDescriptor", // tp_name sizeof(PyBaseDescriptor), // tp_basicsize 0, // tp_itemsize 0, // tp_dealloc @@ -1108,7 +1108,7 @@ PyTypeObject PyEnumValueDescriptor_Type = { 0, // tp_setattro 0, // tp_as_buffer Py_TPFLAGS_DEFAULT, // tp_flags - C("A EnumValue Descriptor"), // tp_doc + "A EnumValue Descriptor", // tp_doc 0, // tp_traverse 0, // tp_clear 0, // tp_richcompare @@ -1121,10 +1121,10 @@ PyTypeObject PyEnumValueDescriptor_Type = { &descriptor::PyBaseDescriptor_Type, // tp_base }; -PyObject* PyEnumValueDescriptor_New( +PyObject* PyEnumValueDescriptor_FromDescriptor( const EnumValueDescriptor* enumvalue_descriptor) { return descriptor::NewInternedDescriptor( - &PyEnumValueDescriptor_Type, enumvalue_descriptor); + &PyEnumValueDescriptor_Type, enumvalue_descriptor, NULL); } namespace file_descriptor { @@ -1218,18 +1218,20 @@ static PyObject* CopyToProto(PyFileDescriptor *self, PyObject *target) { } static PyGetSetDef Getters[] = { - { C("name"), (getter)GetName, NULL, "name", NULL}, - { C("package"), (getter)GetPackage, NULL, "package", NULL}, - { C("serialized_pb"), (getter)GetSerializedPb, NULL, NULL, NULL}, - { C("message_types_by_name"), (getter)GetMessageTypesByName, NULL, "Messages by name", NULL}, - { C("enum_types_by_name"), (getter)GetEnumTypesByName, NULL, "Enums by name", NULL}, - { C("extensions_by_name"), (getter)GetExtensionsByName, NULL, "Extensions by name", NULL}, - { C("dependencies"), (getter)GetDependencies, NULL, "Dependencies", NULL}, - { C("public_dependencies"), (getter)GetPublicDependencies, NULL, "Dependencies", NULL}, - - { C("has_options"), (getter)GetHasOptions, (setter)SetHasOptions, "Has Options", NULL}, - { C("_options"), (getter)NULL, (setter)SetOptions, "Options", NULL}, - { C("syntax"), (getter)GetSyntax, (setter)NULL, "Syntax", NULL}, + { "name", (getter)GetName, NULL, "name"}, + { "package", (getter)GetPackage, NULL, "package"}, + { "serialized_pb", (getter)GetSerializedPb}, + { "message_types_by_name", (getter)GetMessageTypesByName, NULL, + "Messages by name"}, + { "enum_types_by_name", (getter)GetEnumTypesByName, NULL, "Enums by name"}, + { "extensions_by_name", (getter)GetExtensionsByName, NULL, + "Extensions by name"}, + { "dependencies", (getter)GetDependencies, NULL, "Dependencies"}, + { "public_dependencies", (getter)GetPublicDependencies, NULL, "Dependencies"}, + + { "has_options", (getter)GetHasOptions, (setter)SetHasOptions, "Has Options"}, + { "_options", (getter)NULL, (setter)SetOptions, "Options"}, + { "syntax", (getter)GetSyntax, (setter)NULL, "Syntax"}, {NULL} }; @@ -1243,11 +1245,10 @@ static PyMethodDef Methods[] = { PyTypeObject PyFileDescriptor_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) - C("google.protobuf.internal." - "_message.FileDescriptor"), // tp_name + FULL_MODULE_NAME ".FileDescriptor", // tp_name sizeof(PyFileDescriptor), // tp_basicsize 0, // tp_itemsize - (destructor)file_descriptor::Dealloc, // tp_dealloc + (destructor)file_descriptor::Dealloc, // tp_dealloc 0, // tp_print 0, // tp_getattr 0, // tp_setattr @@ -1263,7 +1264,7 @@ PyTypeObject PyFileDescriptor_Type = { 0, // tp_setattro 0, // tp_as_buffer Py_TPFLAGS_DEFAULT, // tp_flags - C("A File Descriptor"), // tp_doc + "A File Descriptor", // tp_doc 0, // tp_traverse 0, // tp_clear 0, // tp_richcompare @@ -1284,23 +1285,28 @@ PyTypeObject PyFileDescriptor_Type = { PyObject_Del, // tp_free }; -PyObject* PyFileDescriptor_New(const FileDescriptor* file_descriptor) { - return descriptor::NewInternedDescriptor( - &PyFileDescriptor_Type, file_descriptor); +PyObject* PyFileDescriptor_FromDescriptor( + const FileDescriptor* file_descriptor) { + return PyFileDescriptor_FromDescriptorWithSerializedPb(file_descriptor, + NULL); } -PyObject* PyFileDescriptor_NewWithPb( +PyObject* PyFileDescriptor_FromDescriptorWithSerializedPb( const FileDescriptor* file_descriptor, PyObject *serialized_pb) { - PyObject* py_descriptor = PyFileDescriptor_New(file_descriptor); + bool was_created; + PyObject* py_descriptor = descriptor::NewInternedDescriptor( + &PyFileDescriptor_Type, file_descriptor, &was_created); if (py_descriptor == NULL) { return NULL; } - if (serialized_pb != NULL) { + if (was_created) { PyFileDescriptor* cfile_descriptor = reinterpret_cast(py_descriptor); Py_XINCREF(serialized_pb); cfile_descriptor->serialized_pb = serialized_pb; } + // TODO(amauryfa): In the case of a cached object, check that serialized_pb + // is the same as before. return py_descriptor; } @@ -1333,19 +1339,19 @@ static PyObject* GetContainingType(PyBaseDescriptor *self, void *closure) { const Descriptor* containing_type = _GetDescriptor(self)->containing_type(); if (containing_type) { - return PyMessageDescriptor_New(containing_type); + return PyMessageDescriptor_FromDescriptor(containing_type); } else { Py_RETURN_NONE; } } static PyGetSetDef Getters[] = { - { C("name"), (getter)GetName, NULL, "Name", NULL}, - { C("full_name"), (getter)GetFullName, NULL, "Full name", NULL}, - { C("index"), (getter)GetIndex, NULL, "Index", NULL}, + { "name", (getter)GetName, NULL, "Name"}, + { "full_name", (getter)GetFullName, NULL, "Full name"}, + { "index", (getter)GetIndex, NULL, "Index"}, - { C("containing_type"), (getter)GetContainingType, NULL, "Containing type", NULL}, - { C("fields"), (getter)GetFields, NULL, "Fields", NULL}, + { "containing_type", (getter)GetContainingType, NULL, "Containing type"}, + { "fields", (getter)GetFields, NULL, "Fields"}, {NULL} }; @@ -1353,8 +1359,7 @@ static PyGetSetDef Getters[] = { PyTypeObject PyOneofDescriptor_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) - C("google.protobuf.internal." - "_message.OneofDescriptor"), // tp_name + FULL_MODULE_NAME ".OneofDescriptor", // tp_name sizeof(PyBaseDescriptor), // tp_basicsize 0, // tp_itemsize 0, // tp_dealloc @@ -1373,7 +1378,7 @@ PyTypeObject PyOneofDescriptor_Type = { 0, // tp_setattro 0, // tp_as_buffer Py_TPFLAGS_DEFAULT, // tp_flags - C("A Oneof Descriptor"), // tp_doc + "A Oneof Descriptor", // tp_doc 0, // tp_traverse 0, // tp_clear 0, // tp_richcompare @@ -1386,10 +1391,10 @@ PyTypeObject PyOneofDescriptor_Type = { &descriptor::PyBaseDescriptor_Type, // tp_base }; -PyObject* PyOneofDescriptor_New( +PyObject* PyOneofDescriptor_FromDescriptor( const OneofDescriptor* oneof_descriptor) { return descriptor::NewInternedDescriptor( - &PyOneofDescriptor_Type, oneof_descriptor); + &PyOneofDescriptor_Type, oneof_descriptor, NULL); } // Add a enum values to a type dictionary. diff --git a/python/google/protobuf/pyext/descriptor.h b/python/google/protobuf/pyext/descriptor.h index ba6e7298..b2550406 100644 --- a/python/google/protobuf/pyext/descriptor.h +++ b/python/google/protobuf/pyext/descriptor.h @@ -48,21 +48,24 @@ extern PyTypeObject PyEnumValueDescriptor_Type; extern PyTypeObject PyFileDescriptor_Type; extern PyTypeObject PyOneofDescriptor_Type; -// Return a new reference to a Descriptor object. +// Wraps a Descriptor in a Python object. // The C++ pointer is usually borrowed from the global DescriptorPool. // In any case, it must stay alive as long as the Python object. -PyObject* PyMessageDescriptor_New(const Descriptor* descriptor); -PyObject* PyFieldDescriptor_New(const FieldDescriptor* descriptor); -PyObject* PyEnumDescriptor_New(const EnumDescriptor* descriptor); -PyObject* PyEnumValueDescriptor_New(const EnumValueDescriptor* descriptor); -PyObject* PyOneofDescriptor_New(const OneofDescriptor* descriptor); -PyObject* PyFileDescriptor_New(const FileDescriptor* file_descriptor); +// Returns a new reference. +PyObject* PyMessageDescriptor_FromDescriptor(const Descriptor* descriptor); +PyObject* PyFieldDescriptor_FromDescriptor(const FieldDescriptor* descriptor); +PyObject* PyEnumDescriptor_FromDescriptor(const EnumDescriptor* descriptor); +PyObject* PyEnumValueDescriptor_FromDescriptor( + const EnumValueDescriptor* descriptor); +PyObject* PyOneofDescriptor_FromDescriptor(const OneofDescriptor* descriptor); +PyObject* PyFileDescriptor_FromDescriptor( + const FileDescriptor* file_descriptor); // Alternate constructor of PyFileDescriptor, used when we already have a // serialized FileDescriptorProto that can be cached. // Returns a new reference. -PyObject* PyFileDescriptor_NewWithPb(const FileDescriptor* file_descriptor, - PyObject* serialized_pb); +PyObject* PyFileDescriptor_FromDescriptorWithSerializedPb( + const FileDescriptor* file_descriptor, PyObject* serialized_pb); // Return the C++ descriptor pointer. // This function checks the parameter type; on error, return NULL with a Python diff --git a/python/google/protobuf/pyext/descriptor_containers.cc b/python/google/protobuf/pyext/descriptor_containers.cc index 06edebf8..92e11e31 100644 --- a/python/google/protobuf/pyext/descriptor_containers.cc +++ b/python/google/protobuf/pyext/descriptor_containers.cc @@ -898,7 +898,7 @@ static ItemDescriptor GetByIndex(PyContainer* self, int index) { } static PyObject* NewObjectFromItem(ItemDescriptor item) { - return PyFieldDescriptor_New(item); + return PyFieldDescriptor_FromDescriptor(item); } static const string& GetItemName(ItemDescriptor item) { @@ -956,7 +956,7 @@ static ItemDescriptor GetByIndex(PyContainer* self, int index) { } static PyObject* NewObjectFromItem(ItemDescriptor item) { - return PyMessageDescriptor_New(item); + return PyMessageDescriptor_FromDescriptor(item); } static const string& GetItemName(ItemDescriptor item) { @@ -1006,7 +1006,7 @@ static ItemDescriptor GetByIndex(PyContainer* self, int index) { } static PyObject* NewObjectFromItem(ItemDescriptor item) { - return PyEnumDescriptor_New(item); + return PyEnumDescriptor_FromDescriptor(item); } static const string& GetItemName(ItemDescriptor item) { @@ -1082,7 +1082,7 @@ static ItemDescriptor GetByIndex(PyContainer* self, int index) { } static PyObject* NewObjectFromItem(ItemDescriptor item) { - return PyEnumValueDescriptor_New(item); + return PyEnumValueDescriptor_FromDescriptor(item); } static const string& GetItemName(ItemDescriptor item) { @@ -1124,7 +1124,7 @@ static ItemDescriptor GetByIndex(PyContainer* self, int index) { } static PyObject* NewObjectFromItem(ItemDescriptor item) { - return PyFieldDescriptor_New(item); + return PyFieldDescriptor_FromDescriptor(item); } static const string& GetItemName(ItemDescriptor item) { @@ -1174,7 +1174,7 @@ static ItemDescriptor GetByIndex(PyContainer* self, int index) { } static PyObject* NewObjectFromItem(ItemDescriptor item) { - return PyOneofDescriptor_New(item); + return PyOneofDescriptor_FromDescriptor(item); } static const string& GetItemName(ItemDescriptor item) { @@ -1238,7 +1238,7 @@ static ItemDescriptor GetByNumber(PyContainer* self, int number) { } static PyObject* NewObjectFromItem(ItemDescriptor item) { - return PyEnumValueDescriptor_New(item); + return PyEnumValueDescriptor_FromDescriptor(item); } static const string& GetItemName(ItemDescriptor item) { @@ -1302,7 +1302,7 @@ static ItemDescriptor GetByIndex(PyContainer* self, int index) { } static PyObject* NewObjectFromItem(ItemDescriptor item) { - return PyFieldDescriptor_New(item); + return PyFieldDescriptor_FromDescriptor(item); } static int GetItemIndex(ItemDescriptor item) { @@ -1354,7 +1354,7 @@ static ItemDescriptor GetByIndex(PyContainer* self, int index) { } static PyObject* NewObjectFromItem(ItemDescriptor item) { - return PyMessageDescriptor_New(item); + return PyMessageDescriptor_FromDescriptor(item); } static const string& GetItemName(ItemDescriptor item) { @@ -1400,7 +1400,7 @@ static ItemDescriptor GetByIndex(PyContainer* self, int index) { } static PyObject* NewObjectFromItem(ItemDescriptor item) { - return PyEnumDescriptor_New(item); + return PyEnumDescriptor_FromDescriptor(item); } static const string& GetItemName(ItemDescriptor item) { @@ -1446,7 +1446,7 @@ static ItemDescriptor GetByIndex(PyContainer* self, int index) { } static PyObject* NewObjectFromItem(ItemDescriptor item) { - return PyFieldDescriptor_New(item); + return PyFieldDescriptor_FromDescriptor(item); } static const string& GetItemName(ItemDescriptor item) { @@ -1488,7 +1488,7 @@ static ItemDescriptor GetByIndex(PyContainer* self, int index) { } static PyObject* NewObjectFromItem(ItemDescriptor item) { - return PyFileDescriptor_New(item); + return PyFileDescriptor_FromDescriptor(item); } static DescriptorContainerDef ContainerDef = { @@ -1522,7 +1522,7 @@ static ItemDescriptor GetByIndex(PyContainer* self, int index) { } static PyObject* NewObjectFromItem(ItemDescriptor item) { - return PyFileDescriptor_New(item); + return PyFileDescriptor_FromDescriptor(item); } static DescriptorContainerDef ContainerDef = { diff --git a/python/google/protobuf/pyext/descriptor_containers.h b/python/google/protobuf/pyext/descriptor_containers.h index d81537de..8fbdaff9 100644 --- a/python/google/protobuf/pyext/descriptor_containers.h +++ b/python/google/protobuf/pyext/descriptor_containers.h @@ -28,6 +28,9 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#ifndef GOOGLE_PROTOBUF_PYTHON_CPP_DESCRIPTOR_CONTAINERS_H__ +#define GOOGLE_PROTOBUF_PYTHON_CPP_DESCRIPTOR_CONTAINERS_H__ + // Mappings and Sequences of descriptors. // They implement containers like fields_by_name, EnumDescriptor.values... // See descriptor_containers.cc for more description. @@ -92,4 +95,6 @@ PyObject* NewFilePublicDependencies(const FileDescriptor* descriptor); } // namespace python } // namespace protobuf + } // namespace google +#endif // GOOGLE_PROTOBUF_PYTHON_CPP_DESCRIPTOR_CONTAINERS_H__ diff --git a/python/google/protobuf/pyext/descriptor_pool.cc b/python/google/protobuf/pyext/descriptor_pool.cc index bc3077bc..ecd90847 100644 --- a/python/google/protobuf/pyext/descriptor_pool.cc +++ b/python/google/protobuf/pyext/descriptor_pool.cc @@ -35,10 +35,9 @@ #include #include #include +#include #include -#define C(str) const_cast(str) - #if PY_MAJOR_VERSION >= 3 #define PyString_FromStringAndSize PyUnicode_FromStringAndSize #if PY_VERSION_HEX < 0x03030000 @@ -108,11 +107,11 @@ PyObject* FindMessageByName(PyDescriptorPool* self, PyObject* arg) { self->pool->FindMessageTypeByName(string(name, name_size)); if (message_descriptor == NULL) { - PyErr_Format(PyExc_TypeError, "Couldn't find message %.200s", name); + PyErr_Format(PyExc_KeyError, "Couldn't find message %.200s", name); return NULL; } - return PyMessageDescriptor_New(message_descriptor); + return PyMessageDescriptor_FromDescriptor(message_descriptor); } // Add a message class to our database. @@ -158,6 +157,24 @@ PyObject *GetMessageClass(PyDescriptorPool* self, } } +PyObject* FindFileByName(PyDescriptorPool* self, PyObject* arg) { + Py_ssize_t name_size; + char* name; + if (PyString_AsStringAndSize(arg, &name, &name_size) < 0) { + return NULL; + } + + const FileDescriptor* file_descriptor = + self->pool->FindFileByName(string(name, name_size)); + if (file_descriptor == NULL) { + PyErr_Format(PyExc_KeyError, "Couldn't find file %.200s", + name); + return NULL; + } + + return PyFileDescriptor_FromDescriptor(file_descriptor); +} + PyObject* FindFieldByName(PyDescriptorPool* self, PyObject* arg) { Py_ssize_t name_size; char* name; @@ -168,12 +185,12 @@ PyObject* FindFieldByName(PyDescriptorPool* self, PyObject* arg) { const FieldDescriptor* field_descriptor = self->pool->FindFieldByName(string(name, name_size)); if (field_descriptor == NULL) { - PyErr_Format(PyExc_TypeError, "Couldn't find field %.200s", + PyErr_Format(PyExc_KeyError, "Couldn't find field %.200s", name); return NULL; } - return PyFieldDescriptor_New(field_descriptor); + return PyFieldDescriptor_FromDescriptor(field_descriptor); } PyObject* FindExtensionByName(PyDescriptorPool* self, PyObject* arg) { @@ -186,11 +203,11 @@ PyObject* FindExtensionByName(PyDescriptorPool* self, PyObject* arg) { const FieldDescriptor* field_descriptor = self->pool->FindExtensionByName(string(name, name_size)); if (field_descriptor == NULL) { - PyErr_Format(PyExc_TypeError, "Couldn't find field %.200s", name); + PyErr_Format(PyExc_KeyError, "Couldn't find extension field %.200s", name); return NULL; } - return PyFieldDescriptor_New(field_descriptor); + return PyFieldDescriptor_FromDescriptor(field_descriptor); } PyObject* FindEnumTypeByName(PyDescriptorPool* self, PyObject* arg) { @@ -203,11 +220,11 @@ PyObject* FindEnumTypeByName(PyDescriptorPool* self, PyObject* arg) { const EnumDescriptor* enum_descriptor = self->pool->FindEnumTypeByName(string(name, name_size)); if (enum_descriptor == NULL) { - PyErr_Format(PyExc_TypeError, "Couldn't find enum %.200s", name); + PyErr_Format(PyExc_KeyError, "Couldn't find enum %.200s", name); return NULL; } - return PyEnumDescriptor_New(enum_descriptor); + return PyEnumDescriptor_FromDescriptor(enum_descriptor); } PyObject* FindOneofByName(PyDescriptorPool* self, PyObject* arg) { @@ -220,70 +237,13 @@ PyObject* FindOneofByName(PyDescriptorPool* self, PyObject* arg) { const OneofDescriptor* oneof_descriptor = self->pool->FindOneofByName(string(name, name_size)); if (oneof_descriptor == NULL) { - PyErr_Format(PyExc_TypeError, "Couldn't find oneof %.200s", name); + PyErr_Format(PyExc_KeyError, "Couldn't find oneof %.200s", name); return NULL; } - return PyOneofDescriptor_New(oneof_descriptor); + return PyOneofDescriptor_FromDescriptor(oneof_descriptor); } -static PyMethodDef Methods[] = { - { C("FindFieldByName"), - (PyCFunction)FindFieldByName, - METH_O, - C("Searches for a field descriptor by full name.") }, - { C("FindExtensionByName"), - (PyCFunction)FindExtensionByName, - METH_O, - C("Searches for extension descriptor by full name.") }, - {NULL} -}; - -} // namespace cdescriptor_pool - -PyTypeObject PyDescriptorPool_Type = { - PyVarObject_HEAD_INIT(&PyType_Type, 0) - C("google.protobuf.internal." - "_message.DescriptorPool"), // tp_name - sizeof(PyDescriptorPool), // tp_basicsize - 0, // tp_itemsize - (destructor)cdescriptor_pool::Dealloc, // tp_dealloc - 0, // tp_print - 0, // tp_getattr - 0, // tp_setattr - 0, // tp_compare - 0, // tp_repr - 0, // tp_as_number - 0, // tp_as_sequence - 0, // tp_as_mapping - 0, // tp_hash - 0, // tp_call - 0, // tp_str - 0, // tp_getattro - 0, // tp_setattro - 0, // tp_as_buffer - Py_TPFLAGS_DEFAULT, // tp_flags - C("A Descriptor Pool"), // tp_doc - 0, // tp_traverse - 0, // tp_clear - 0, // tp_richcompare - 0, // tp_weaklistoffset - 0, // tp_iter - 0, // tp_iternext - cdescriptor_pool::Methods, // tp_methods - 0, // tp_members - 0, // tp_getset - 0, // tp_base - 0, // tp_dict - 0, // tp_descr_get - 0, // tp_descr_set - 0, // tp_dictoffset - 0, // tp_init - 0, // tp_alloc - 0, // tp_new - PyObject_Del, // tp_free -}; - // The code below loads new Descriptors from a serialized FileDescriptorProto. @@ -301,6 +261,7 @@ class BuildFileErrorCollector : public DescriptorPool::ErrorCollector { if (!had_errors) { error_message += ("Invalid proto descriptor for file \"" + filename + "\":\n"); + had_errors = true; } // As this only happens on failure and will result in the program not // running at all, no effort is made to optimize this string manipulation. @@ -311,7 +272,7 @@ class BuildFileErrorCollector : public DescriptorPool::ErrorCollector { bool had_errors; }; -PyObject* Python_BuildFile(PyObject* ignored, PyObject* serialized_pb) { +PyObject* AddSerializedFile(PyDescriptorPool* self, PyObject* serialized_pb) { char* message_type; Py_ssize_t message_len; @@ -330,13 +291,14 @@ PyObject* Python_BuildFile(PyObject* ignored, PyObject* serialized_pb) { const FileDescriptor* generated_file = DescriptorPool::generated_pool()->FindFileByName(file_proto.name()); if (generated_file != NULL) { - return PyFileDescriptor_NewWithPb(generated_file, serialized_pb); + return PyFileDescriptor_FromDescriptorWithSerializedPb( + generated_file, serialized_pb); } BuildFileErrorCollector error_collector; const FileDescriptor* descriptor = - GetDescriptorPool()->pool->BuildFileCollectingErrors(file_proto, - &error_collector); + self->pool->BuildFileCollectingErrors(file_proto, + &error_collector); if (descriptor == NULL) { PyErr_Format(PyExc_TypeError, "Couldn't build proto file into descriptor pool!\n%s", @@ -344,9 +306,84 @@ PyObject* Python_BuildFile(PyObject* ignored, PyObject* serialized_pb) { return NULL; } - return PyFileDescriptor_NewWithPb(descriptor, serialized_pb); + return PyFileDescriptor_FromDescriptorWithSerializedPb( + descriptor, serialized_pb); +} + +PyObject* Add(PyDescriptorPool* self, PyObject* file_descriptor_proto) { + ScopedPyObjectPtr serialized_pb( + PyObject_CallMethod(file_descriptor_proto, "SerializeToString", NULL)); + if (serialized_pb == NULL) { + return NULL; + } + return AddSerializedFile(self, serialized_pb); } +static PyMethodDef Methods[] = { + { "Add", (PyCFunction)Add, METH_O, + "Adds the FileDescriptorProto and its types to this pool." }, + { "AddSerializedFile", (PyCFunction)AddSerializedFile, METH_O, + "Adds a serialized FileDescriptorProto to this pool." }, + + { "FindFileByName", (PyCFunction)FindFileByName, METH_O, + "Searches for a file descriptor by its .proto name." }, + { "FindMessageTypeByName", (PyCFunction)FindMessageByName, METH_O, + "Searches for a message descriptor by full name." }, + { "FindFieldByName", (PyCFunction)FindFieldByName, METH_O, + "Searches for a field descriptor by full name." }, + { "FindExtensionByName", (PyCFunction)FindExtensionByName, METH_O, + "Searches for extension descriptor by full name." }, + { "FindEnumTypeByName", (PyCFunction)FindEnumTypeByName, METH_O, + "Searches for enum type descriptor by full name." }, + { "FindOneofByName", (PyCFunction)FindOneofByName, METH_O, + "Searches for oneof descriptor by full name." }, + {NULL} +}; + +} // namespace cdescriptor_pool + +PyTypeObject PyDescriptorPool_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + FULL_MODULE_NAME ".DescriptorPool", // tp_name + sizeof(PyDescriptorPool), // tp_basicsize + 0, // tp_itemsize + (destructor)cdescriptor_pool::Dealloc, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + 0, // tp_repr + 0, // tp_as_number + 0, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + 0, // tp_getattro + 0, // tp_setattro + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + "A Descriptor Pool", // tp_doc + 0, // tp_traverse + 0, // tp_clear + 0, // tp_richcompare + 0, // tp_weaklistoffset + 0, // tp_iter + 0, // tp_iternext + cdescriptor_pool::Methods, // tp_methods + 0, // tp_members + 0, // tp_getset + 0, // tp_base + 0, // tp_dict + 0, // tp_descr_get + 0, // tp_descr_set + 0, // tp_dictoffset + 0, // tp_init + 0, // tp_alloc + 0, // tp_new + PyObject_Del, // tp_free +}; + static PyDescriptorPool* global_cdescriptor_pool = NULL; bool InitDescriptorPool() { diff --git a/python/google/protobuf/pyext/descriptor_pool.h b/python/google/protobuf/pyext/descriptor_pool.h index 4e494b89..efb1abeb 100644 --- a/python/google/protobuf/pyext/descriptor_pool.h +++ b/python/google/protobuf/pyext/descriptor_pool.h @@ -95,6 +95,8 @@ const Descriptor* FindMessageTypeByName(PyDescriptorPool* self, const Descriptor* RegisterMessageClass( PyDescriptorPool* self, PyObject* message_class, PyObject* descriptor); +// The function below are also exposed as methods of the DescriptorPool type. + // Retrieves the Python class registered with the given message descriptor. // // Returns a *borrowed* reference if found, otherwise returns NULL with an @@ -134,12 +136,8 @@ PyObject* FindOneofByName(PyDescriptorPool* self, PyObject* arg); } // namespace cdescriptor_pool -// Implement the Python "_BuildFile" method, it takes a serialized -// FileDescriptorProto, and adds it to the C++ DescriptorPool. -// It returns a new FileDescriptor object, or NULL when an exception is raised. -PyObject* Python_BuildFile(PyObject* ignored, PyObject* args); - // Retrieve the global descriptor pool owned by the _message module. +// Returns a *borrowed* reference. PyDescriptorPool* GetDescriptorPool(); // Initialize objects used by this module. diff --git a/python/google/protobuf/pyext/extension_dict.cc b/python/google/protobuf/pyext/extension_dict.cc index 8e38fc42..b8d18f8d 100644 --- a/python/google/protobuf/pyext/extension_dict.cc +++ b/python/google/protobuf/pyext/extension_dict.cc @@ -51,16 +51,6 @@ namespace python { namespace extension_dict { -// TODO(tibell): Always use self->message for clarity, just like in -// RepeatedCompositeContainer. -static Message* GetMessage(ExtensionDict* self) { - if (self->parent != NULL) { - return self->parent->message; - } else { - return self->message; - } -} - PyObject* len(ExtensionDict* self) { #if PY_MAJOR_VERSION >= 3 return PyLong_FromLong(PyDict_Size(self->values)); @@ -89,7 +79,7 @@ int ReleaseExtension(ExtensionDict* self, } } else if (descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { if (cmessage::ReleaseSubMessage( - GetMessage(self), descriptor, + self->parent, descriptor, reinterpret_cast(extension)) < 0) { return -1; } @@ -109,7 +99,7 @@ PyObject* subscript(ExtensionDict* self, PyObject* key) { if (descriptor->label() != FieldDescriptor::LABEL_REPEATED && descriptor->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) { - return cmessage::InternalGetScalar(self->parent, descriptor); + return cmessage::InternalGetScalar(self->parent->message, descriptor); } PyObject* value = PyDict_GetItem(self->values, key); @@ -266,8 +256,7 @@ static PyMethodDef Methods[] = { PyTypeObject ExtensionDict_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) - "google.protobuf.internal." - "cpp._message.ExtensionDict", // tp_name + FULL_MODULE_NAME ".ExtensionDict", // tp_name sizeof(ExtensionDict), // tp_basicsize 0, // tp_itemsize (destructor)extension_dict::dealloc, // tp_dealloc diff --git a/python/google/protobuf/pyext/message.cc b/python/google/protobuf/pyext/message.cc index a2b357b2..a4843e8d 100644 --- a/python/google/protobuf/pyext/message.cc +++ b/python/google/protobuf/pyext/message.cc @@ -59,6 +59,8 @@ #include #include #include +#include +#include #include #include @@ -93,9 +95,9 @@ static const FieldDescriptor* GetFieldDescriptor( static const Descriptor* GetMessageDescriptor(PyTypeObject* cls); static string GetMessageName(CMessage* self); int InternalReleaseFieldByDescriptor( + CMessage* self, const FieldDescriptor* field_descriptor, - PyObject* composite_field, - Message* parent_message); + PyObject* composite_field); } // namespace cmessage // --------------------------------------------------------------------- @@ -127,10 +129,29 @@ static int VisitCompositeField(const FieldDescriptor* descriptor, Visitor visitor) { if (descriptor->label() == FieldDescriptor::LABEL_REPEATED) { if (descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { - RepeatedCompositeContainer* container = - reinterpret_cast(child); - if (visitor.VisitRepeatedCompositeContainer(container) == -1) - return -1; + if (descriptor->is_map()) { + const Descriptor* entry_type = descriptor->message_type(); + const FieldDescriptor* value_type = + entry_type->FindFieldByName("value"); + if (value_type->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + MessageMapContainer* container = + reinterpret_cast(child); + if (visitor.VisitMessageMapContainer(container) == -1) { + return -1; + } + } else { + ScalarMapContainer* container = + reinterpret_cast(child); + if (visitor.VisitScalarMapContainer(container) == -1) { + return -1; + } + } + } else { + RepeatedCompositeContainer* container = + reinterpret_cast(child); + if (visitor.VisitRepeatedCompositeContainer(container) == -1) + return -1; + } } else { RepeatedScalarContainer* container = reinterpret_cast(child); @@ -444,7 +465,7 @@ static int MaybeReleaseOverlappingOneofField( } if (InternalReleaseFieldByDescriptor( - existing_field, child_message, message) < 0) { + cmessage, existing_field, child_message) < 0) { return -1; } return PyDict_DelItemString(cmessage->composite_fields, field_name); @@ -483,6 +504,16 @@ struct FixupMessageReference : public ChildVisitor { return 0; } + int VisitScalarMapContainer(ScalarMapContainer* container) { + container->message = message_; + return 0; + } + + int VisitMessageMapContainer(MessageMapContainer* container) { + container->message = message_; + return 0; + } + private: Message* message_; }; @@ -500,6 +531,9 @@ int AssureWritable(CMessage* self) { self->message->GetDescriptor()); self->message = prototype->New(); self->owner.reset(self->message); + // Cascade the new owner to eventual children: even if this message is + // empty, some submessages or repeated containers might exist already. + SetOwner(self, self->owner); } else { // Otherwise, we need a mutable child message. if (AssureWritable(self->parent) == -1) @@ -520,8 +554,9 @@ int AssureWritable(CMessage* self) { // When a CMessage is made writable its Message pointer is updated // to point to a new mutable Message. When that happens we need to // update any references to the old, read-only CMessage. There are - // three places such references occur: RepeatedScalarContainer, - // RepeatedCompositeContainer, and ExtensionDict. + // five places such references occur: RepeatedScalarContainer, + // RepeatedCompositeContainer, ScalarMapContainer, MessageMapContainer, + // and ExtensionDict. if (self->extensions != NULL) self->extensions->message = self->message; if (ForEachCompositeField(self, FixupMessageReference(self->message)) == -1) @@ -583,15 +618,43 @@ const FieldDescriptor* GetExtensionDescriptor(PyObject* extension) { return PyFieldDescriptor_AsDescriptor(extension); } +// If value is a string, convert it into an enum value based on the labels in +// descriptor, otherwise simply return value. Always returns a new reference. +static PyObject* GetIntegerEnumValue(const FieldDescriptor& descriptor, + PyObject* value) { + if (PyString_Check(value) || PyUnicode_Check(value)) { + const EnumDescriptor* enum_descriptor = descriptor.enum_type(); + if (enum_descriptor == NULL) { + PyErr_SetString(PyExc_TypeError, "not an enum field"); + return NULL; + } + char* enum_label; + Py_ssize_t size; + if (PyString_AsStringAndSize(value, &enum_label, &size) < 0) { + return NULL; + } + const EnumValueDescriptor* enum_value_descriptor = + enum_descriptor->FindValueByName(string(enum_label, size)); + if (enum_value_descriptor == NULL) { + PyErr_SetString(PyExc_ValueError, "unknown enum label"); + return NULL; + } + return PyInt_FromLong(enum_value_descriptor->number()); + } + Py_INCREF(value); + return value; +} + // If cmessage_list is not NULL, this function releases values into the // container CMessages instead of just removing. Repeated composite container // needs to do this to make sure CMessages stay alive if they're still // referenced after deletion. Repeated scalar container doesn't need to worry. int InternalDeleteRepeatedField( - Message* message, + CMessage* self, const FieldDescriptor* field_descriptor, PyObject* slice, PyObject* cmessage_list) { + Message* message = self->message; Py_ssize_t length, from, to, step, slice_length; const Reflection* reflection = message->GetReflection(); int min, max; @@ -665,7 +728,7 @@ int InternalDeleteRepeatedField( CMessage* last_cmessage = reinterpret_cast( PyList_GET_ITEM(cmessage_list, PyList_GET_SIZE(cmessage_list) - 1)); repeated_composite_container::ReleaseLastTo( - field_descriptor, message, last_cmessage); + self, field_descriptor, last_cmessage); if (PySequence_DelItem(cmessage_list, -1) < 0) { return -1; } @@ -696,16 +759,90 @@ int InitAttributes(CMessage* self, PyObject* kwargs) { PyString_AsString(name)); return -1; } - if (descriptor->label() == FieldDescriptor::LABEL_REPEATED) { + if (descriptor->is_map()) { + ScopedPyObjectPtr map(GetAttr(self, name)); + const FieldDescriptor* value_descriptor = + descriptor->message_type()->FindFieldByName("value"); + if (value_descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + Py_ssize_t map_pos = 0; + PyObject* map_key; + PyObject* map_value; + while (PyDict_Next(value, &map_pos, &map_key, &map_value)) { + ScopedPyObjectPtr function_return; + function_return.reset(PyObject_GetItem(map.get(), map_key)); + if (function_return.get() == NULL) { + return -1; + } + ScopedPyObjectPtr ok(PyObject_CallMethod( + function_return.get(), "MergeFrom", "O", map_value)); + if (ok.get() == NULL) { + return -1; + } + } + } else { + ScopedPyObjectPtr function_return; + function_return.reset( + PyObject_CallMethod(map.get(), "update", "O", value)); + if (function_return.get() == NULL) { + return -1; + } + } + } else if (descriptor->label() == FieldDescriptor::LABEL_REPEATED) { ScopedPyObjectPtr container(GetAttr(self, name)); if (container == NULL) { return -1; } if (descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { - if (repeated_composite_container::Extend( - reinterpret_cast(container.get()), - value) - == NULL) { + RepeatedCompositeContainer* rc_container = + reinterpret_cast(container.get()); + ScopedPyObjectPtr iter(PyObject_GetIter(value)); + if (iter == NULL) { + PyErr_SetString(PyExc_TypeError, "Value must be iterable"); + return -1; + } + ScopedPyObjectPtr next; + while ((next.reset(PyIter_Next(iter))) != NULL) { + PyObject* kwargs = (PyDict_Check(next) ? next.get() : NULL); + ScopedPyObjectPtr new_msg( + repeated_composite_container::Add(rc_container, NULL, kwargs)); + if (new_msg == NULL) { + return -1; + } + if (kwargs == NULL) { + // next was not a dict, it's a message we need to merge + ScopedPyObjectPtr merged( + MergeFrom(reinterpret_cast(new_msg.get()), next)); + if (merged == NULL) { + return -1; + } + } + } + if (PyErr_Occurred()) { + // Check to see how PyIter_Next() exited. + return -1; + } + } else if (descriptor->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) { + RepeatedScalarContainer* rs_container = + reinterpret_cast(container.get()); + ScopedPyObjectPtr iter(PyObject_GetIter(value)); + if (iter == NULL) { + PyErr_SetString(PyExc_TypeError, "Value must be iterable"); + return -1; + } + ScopedPyObjectPtr next; + while ((next.reset(PyIter_Next(iter))) != NULL) { + ScopedPyObjectPtr enum_value(GetIntegerEnumValue(*descriptor, next)); + if (enum_value == NULL) { + return -1; + } + ScopedPyObjectPtr new_msg( + repeated_scalar_container::Append(rs_container, enum_value)); + if (new_msg == NULL) { + return -1; + } + } + if (PyErr_Occurred()) { + // Check to see how PyIter_Next() exited. return -1; } } else { @@ -721,12 +858,26 @@ int InitAttributes(CMessage* self, PyObject* kwargs) { if (message == NULL) { return -1; } - if (MergeFrom(reinterpret_cast(message.get()), - value) == NULL) { - return -1; + CMessage* cmessage = reinterpret_cast(message.get()); + if (PyDict_Check(value)) { + if (InitAttributes(cmessage, value) < 0) { + return -1; + } + } else { + ScopedPyObjectPtr merged(MergeFrom(cmessage, value)); + if (merged == NULL) { + return -1; + } } } else { - if (SetAttr(self, name, value) < 0) { + ScopedPyObjectPtr new_val; + if (descriptor->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) { + new_val.reset(GetIntegerEnumValue(*descriptor, value)); + if (new_val == NULL) { + return -1; + } + } + if (SetAttr(self, name, (new_val == NULL) ? value : new_val) < 0) { return -1; } } @@ -789,7 +940,6 @@ static PyObject* New(PyTypeObject* type, } self->message = default_message->New(); self->owner.reset(self->message); - return reinterpret_cast(self); } @@ -830,6 +980,16 @@ struct ClearWeakReferences : public ChildVisitor { return 0; } + int VisitScalarMapContainer(ScalarMapContainer* container) { + container->parent = NULL; + return 0; + } + + int VisitMessageMapContainer(MessageMapContainer* container) { + container->parent = NULL; + return 0; + } + int VisitCMessage(CMessage* cmessage, const FieldDescriptor* field_descriptor) { cmessage->parent = NULL; @@ -1064,6 +1224,16 @@ struct SetOwnerVisitor : public ChildVisitor { return 0; } + int VisitScalarMapContainer(ScalarMapContainer* container) { + scalar_map_container::SetOwner(container, new_owner_); + return 0; + } + + int VisitMessageMapContainer(MessageMapContainer* container) { + message_map_container::SetOwner(container, new_owner_); + return 0; + } + int VisitCMessage(CMessage* cmessage, const FieldDescriptor* field_descriptor) { return SetOwner(cmessage, new_owner_); @@ -1084,11 +1254,11 @@ int SetOwner(CMessage* self, const shared_ptr& new_owner) { // Releases the message specified by 'field' and returns the // pointer. If the field does not exist a new message is created using // 'descriptor'. The caller takes ownership of the returned pointer. -Message* ReleaseMessage(Message* message, +Message* ReleaseMessage(CMessage* self, const Descriptor* descriptor, const FieldDescriptor* field_descriptor) { - Message* released_message = message->GetReflection()->ReleaseMessage( - message, field_descriptor, message_factory); + Message* released_message = self->message->GetReflection()->ReleaseMessage( + self->message, field_descriptor, message_factory); // ReleaseMessage will return NULL which differs from // child_cmessage->message, if the field does not exist. In this case, // the latter points to the default instance via a const_cast<>, so we @@ -1102,12 +1272,12 @@ Message* ReleaseMessage(Message* message, return released_message; } -int ReleaseSubMessage(Message* message, +int ReleaseSubMessage(CMessage* self, const FieldDescriptor* field_descriptor, CMessage* child_cmessage) { // Release the Message shared_ptr released_message(ReleaseMessage( - message, child_cmessage->message->GetDescriptor(), field_descriptor)); + self, child_cmessage->message->GetDescriptor(), field_descriptor)); child_cmessage->message = released_message.get(); child_cmessage->owner.swap(released_message); child_cmessage->parent = NULL; @@ -1119,8 +1289,8 @@ int ReleaseSubMessage(Message* message, struct ReleaseChild : public ChildVisitor { // message must outlive this object. - explicit ReleaseChild(Message* parent_message) : - parent_message_(parent_message) {} + explicit ReleaseChild(CMessage* parent) : + parent_(parent) {} int VisitRepeatedCompositeContainer(RepeatedCompositeContainer* container) { return repeated_composite_container::Release( @@ -1132,23 +1302,33 @@ struct ReleaseChild : public ChildVisitor { reinterpret_cast(container)); } + int VisitScalarMapContainer(ScalarMapContainer* container) { + return scalar_map_container::Release( + reinterpret_cast(container)); + } + + int VisitMessageMapContainer(MessageMapContainer* container) { + return message_map_container::Release( + reinterpret_cast(container)); + } + int VisitCMessage(CMessage* cmessage, const FieldDescriptor* field_descriptor) { - return ReleaseSubMessage(parent_message_, field_descriptor, + return ReleaseSubMessage(parent_, field_descriptor, reinterpret_cast(cmessage)); } - Message* parent_message_; + CMessage* parent_; }; int InternalReleaseFieldByDescriptor( + CMessage* self, const FieldDescriptor* field_descriptor, - PyObject* composite_field, - Message* parent_message) { + PyObject* composite_field) { return VisitCompositeField( field_descriptor, composite_field, - ReleaseChild(parent_message)); + ReleaseChild(self)); } PyObject* ClearFieldByDescriptor( @@ -1200,8 +1380,8 @@ PyObject* ClearField(CMessage* self, PyObject* arg) { // Only release the field if there's a possibility that there are // references to it. if (composite_field != NULL) { - if (InternalReleaseFieldByDescriptor(field_descriptor, - composite_field, message) < 0) { + if (InternalReleaseFieldByDescriptor(self, field_descriptor, + composite_field) < 0) { return NULL; } PyDict_DelItem(self->composite_fields, arg); @@ -1219,7 +1399,7 @@ PyObject* ClearField(CMessage* self, PyObject* arg) { PyObject* Clear(CMessage* self) { AssureWritable(self); - if (ForEachCompositeField(self, ReleaseChild(self->message)) == -1) + if (ForEachCompositeField(self, ReleaseChild(self)) == -1) return NULL; // The old ExtensionDict still aliases this CMessage, but all its @@ -1582,7 +1762,8 @@ static PyObject* ListFields(CMessage* self) { } if (fields[i]->is_extension()) { - ScopedPyObjectPtr extension_field(PyFieldDescriptor_New(fields[i])); + ScopedPyObjectPtr extension_field( + PyFieldDescriptor_FromDescriptor(fields[i])); if (extension_field == NULL) { return NULL; } @@ -1616,7 +1797,8 @@ static PyObject* ListFields(CMessage* self) { PyErr_SetString(PyExc_ValueError, "bad string"); return NULL; } - ScopedPyObjectPtr field_descriptor(PyFieldDescriptor_New(fields[i])); + ScopedPyObjectPtr field_descriptor( + PyFieldDescriptor_FromDescriptor(fields[i])); if (field_descriptor == NULL) { return NULL; } @@ -1683,10 +1865,8 @@ static PyObject* RichCompare(CMessage* self, PyObject* other, int opid) { } } -PyObject* InternalGetScalar( - CMessage* self, - const FieldDescriptor* field_descriptor) { - Message* message = self->message; +PyObject* InternalGetScalar(const Message* message, + const FieldDescriptor* field_descriptor) { const Reflection* reflection = message->GetReflection(); if (!CheckFieldBelongsToMessage(field_descriptor, message)) { @@ -1739,12 +1919,12 @@ PyObject* InternalGetScalar( if (!message->GetReflection()->SupportsUnknownEnumValues() && !message->GetReflection()->HasField(*message, field_descriptor)) { // Look for the value in the unknown fields. - UnknownFieldSet* unknown_field_set = - message->GetReflection()->MutableUnknownFields(message); - for (int i = 0; i < unknown_field_set->field_count(); ++i) { - if (unknown_field_set->field(i).number() == + const UnknownFieldSet& unknown_field_set = + message->GetReflection()->GetUnknownFields(*message); + for (int i = 0; i < unknown_field_set.field_count(); ++i) { + if (unknown_field_set.field(i).number() == field_descriptor->number()) { - result = PyInt_FromLong(unknown_field_set->field(i).varint()); + result = PyInt_FromLong(unknown_field_set.field(i).varint()); break; } } @@ -1793,21 +1973,16 @@ PyObject* InternalGetSubMessage( return reinterpret_cast(cmsg); } -int InternalSetScalar( - CMessage* self, +int InternalSetNonOneofScalar( + Message* message, const FieldDescriptor* field_descriptor, PyObject* arg) { - Message* message = self->message; const Reflection* reflection = message->GetReflection(); if (!CheckFieldBelongsToMessage(field_descriptor, message)) { return -1; } - if (MaybeReleaseOverlappingOneofField(self, field_descriptor) < 0) { - return -1; - } - switch (field_descriptor->cpp_type()) { case FieldDescriptor::CPPTYPE_INT32: { GOOGLE_CHECK_GET_INT32(arg, value, -1); @@ -1878,6 +2053,21 @@ int InternalSetScalar( return 0; } +int InternalSetScalar( + CMessage* self, + const FieldDescriptor* field_descriptor, + PyObject* arg) { + if (!CheckFieldBelongsToMessage(field_descriptor, self->message)) { + return -1; + } + + if (MaybeReleaseOverlappingOneofField(self, field_descriptor) < 0) { + return -1; + } + + return InternalSetNonOneofScalar(self->message, field_descriptor, arg); +} + PyObject* FromString(PyTypeObject* cls, PyObject* serialized) { PyObject* py_cmsg = PyObject_CallObject( reinterpret_cast(cls), NULL); @@ -1955,7 +2145,8 @@ static PyObject* AddDescriptors(PyObject* cls, PyObject* descriptor) { // which was built previously. for (int i = 0; i < message_descriptor->enum_type_count(); ++i) { const EnumDescriptor* enum_descriptor = message_descriptor->enum_type(i); - ScopedPyObjectPtr enum_type(PyEnumDescriptor_New(enum_descriptor)); + ScopedPyObjectPtr enum_type( + PyEnumDescriptor_FromDescriptor(enum_descriptor)); if (enum_type == NULL) { return NULL; } @@ -1993,7 +2184,7 @@ static PyObject* AddDescriptors(PyObject* cls, PyObject* descriptor) { // which was defined previously. for (int i = 0; i < message_descriptor->extension_count(); ++i) { const google::protobuf::FieldDescriptor* field = message_descriptor->extension(i); - ScopedPyObjectPtr extension_field(PyFieldDescriptor_New(field)); + ScopedPyObjectPtr extension_field(PyFieldDescriptor_FromDescriptor(field)); if (extension_field == NULL) { return NULL; } @@ -2097,26 +2288,6 @@ PyObject* SetState(CMessage* self, PyObject* state) { } // CMessage static methods: -PyObject* _GetMessageDescriptor(PyObject* unused, PyObject* arg) { - return cdescriptor_pool::FindMessageByName(GetDescriptorPool(), arg); -} - -PyObject* _GetFieldDescriptor(PyObject* unused, PyObject* arg) { - return cdescriptor_pool::FindFieldByName(GetDescriptorPool(), arg); -} - -PyObject* _GetExtensionDescriptor(PyObject* unused, PyObject* arg) { - return cdescriptor_pool::FindExtensionByName(GetDescriptorPool(), arg); -} - -PyObject* _GetEnumDescriptor(PyObject* unused, PyObject* arg) { - return cdescriptor_pool::FindEnumTypeByName(GetDescriptorPool(), arg); -} - -PyObject* _GetOneofDescriptor(PyObject* unused, PyObject* arg) { - return cdescriptor_pool::FindOneofByName(GetDescriptorPool(), arg); -} - PyObject* _CheckCalledFromGeneratedFile(PyObject* unused, PyObject* unused_arg) { if (!_CalledFromGeneratedFile(1)) { @@ -2188,21 +2359,6 @@ static PyMethodDef Methods[] = { "or None if no field is set." }, // Static Methods. - { "_BuildFile", (PyCFunction)Python_BuildFile, METH_O | METH_STATIC, - "Registers a new protocol buffer file in the global C++ descriptor pool." }, - { "_GetMessageDescriptor", (PyCFunction)_GetMessageDescriptor, - METH_O | METH_STATIC, "Finds a message descriptor in the message pool." }, - { "_GetFieldDescriptor", (PyCFunction)_GetFieldDescriptor, - METH_O | METH_STATIC, "Finds a field descriptor in the message pool." }, - { "_GetExtensionDescriptor", (PyCFunction)_GetExtensionDescriptor, - METH_O | METH_STATIC, - "Finds a extension descriptor in the message pool." }, - { "_GetEnumDescriptor", (PyCFunction)_GetEnumDescriptor, - METH_O | METH_STATIC, - "Finds an enum descriptor in the message pool." }, - { "_GetOneofDescriptor", (PyCFunction)_GetOneofDescriptor, - METH_O | METH_STATIC, - "Finds an oneof descriptor in the message pool." }, { "_CheckCalledFromGeneratedFile", (PyCFunction)_CheckCalledFromGeneratedFile, METH_NOARGS | METH_STATIC, "Raises TypeError if the caller is not in a _pb2.py file."}, @@ -2234,6 +2390,31 @@ PyObject* GetAttr(CMessage* self, PyObject* name) { reinterpret_cast(self), name); } + if (field_descriptor->is_map()) { + PyObject* py_container = NULL; + const Descriptor* entry_type = field_descriptor->message_type(); + const FieldDescriptor* value_type = entry_type->FindFieldByName("value"); + if (value_type->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + PyObject* value_class = cdescriptor_pool::GetMessageClass( + GetDescriptorPool(), value_type->message_type()); + if (value_class == NULL) { + return NULL; + } + py_container = message_map_container::NewContainer(self, field_descriptor, + value_class); + } else { + py_container = scalar_map_container::NewContainer(self, field_descriptor); + } + if (py_container == NULL) { + return NULL; + } + if (!SetCompositeField(self, name, py_container)) { + Py_DECREF(py_container); + return NULL; + } + return py_container; + } + if (field_descriptor->label() == FieldDescriptor::LABEL_REPEATED) { PyObject* py_container = NULL; if (field_descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { @@ -2267,7 +2448,7 @@ PyObject* GetAttr(CMessage* self, PyObject* name) { return sub_message; } - return InternalGetScalar(self, field_descriptor); + return InternalGetScalar(self->message, field_descriptor); } int SetAttr(CMessage* self, PyObject* name, PyObject* value) { @@ -2304,9 +2485,7 @@ int SetAttr(CMessage* self, PyObject* name, PyObject* value) { PyTypeObject CMessage_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) - // Keep the fully qualified _message symbol in a line for opensource. - "google.protobuf.pyext._message." - "CMessage", // tp_name + FULL_MODULE_NAME ".CMessage", // tp_name sizeof(CMessage), // tp_basicsize 0, // tp_itemsize (destructor)cmessage::Dealloc, // tp_dealloc @@ -2401,7 +2580,7 @@ void InitGlobals() { k_extensions_by_name = PyString_FromString("_extensions_by_name"); k_extensions_by_number = PyString_FromString("_extensions_by_number"); - message_factory = new DynamicMessageFactory(GetDescriptorPool()->pool); + message_factory = new DynamicMessageFactory(); message_factory->SetDelegateToGeneratedFactory(true); } @@ -2469,6 +2648,61 @@ bool InitProto2MessageModule(PyObject *m) { reinterpret_cast( &RepeatedCompositeContainer_Type)); + // ScalarMapContainer_Type derives from our MutableMapping type. + PyObject* containers = + PyImport_ImportModule("google.protobuf.internal.containers"); + if (containers == NULL) { + return false; + } + + PyObject* mutable_mapping = + PyObject_GetAttrString(containers, "MutableMapping"); + Py_DECREF(containers); + + if (mutable_mapping == NULL) { + return false; + } + + if (!PyObject_TypeCheck(mutable_mapping, &PyType_Type)) { + Py_DECREF(mutable_mapping); + return false; + } + + ScalarMapContainer_Type.tp_base = + reinterpret_cast(mutable_mapping); + + if (PyType_Ready(&ScalarMapContainer_Type) < 0) { + return false; + } + + PyModule_AddObject(m, "ScalarMapContainer", + reinterpret_cast(&ScalarMapContainer_Type)); + + if (PyType_Ready(&ScalarMapIterator_Type) < 0) { + return false; + } + + PyModule_AddObject(m, "ScalarMapIterator", + reinterpret_cast(&ScalarMapIterator_Type)); + + Py_INCREF(mutable_mapping); + MessageMapContainer_Type.tp_base = + reinterpret_cast(mutable_mapping); + + if (PyType_Ready(&MessageMapContainer_Type) < 0) { + return false; + } + + PyModule_AddObject(m, "MessageMapContainer", + reinterpret_cast(&MessageMapContainer_Type)); + + if (PyType_Ready(&MessageMapIterator_Type) < 0) { + return false; + } + + PyModule_AddObject(m, "MessageMapIterator", + reinterpret_cast(&MessageMapIterator_Type)); + ExtensionDict_Type.tp_hash = PyObject_HashNotImplemented; if (PyType_Ready(&ExtensionDict_Type) < 0) { return false; @@ -2478,6 +2712,12 @@ bool InitProto2MessageModule(PyObject *m) { m, "ExtensionDict", reinterpret_cast(&ExtensionDict_Type)); + // Expose the DescriptorPool used to hold all descriptors added from generated + // pb2.py files. + Py_INCREF(GetDescriptorPool()); // PyModule_AddObject steals a reference. + PyModule_AddObject( + m, "default_pool", reinterpret_cast(GetDescriptorPool())); + // This implementation provides full Descriptor types, we advertise it so that // descriptor.py can use them in replacement of the Python classes. PyModule_AddIntConstant(m, "_USE_C_DESCRIPTORS", 1); diff --git a/python/google/protobuf/pyext/message.h b/python/google/protobuf/pyext/message.h index 2f2da795..7360b207 100644 --- a/python/google/protobuf/pyext/message.h +++ b/python/google/protobuf/pyext/message.h @@ -120,7 +120,7 @@ CMessage* NewEmptyMessage(PyObject* type, const Descriptor* descriptor); // A new message will be created if this is a read-only default instance. // // Corresponds to reflection api method ReleaseMessage. -int ReleaseSubMessage(Message* message, +int ReleaseSubMessage(CMessage* self, const FieldDescriptor* field_descriptor, CMessage* child_cmessage); @@ -144,7 +144,7 @@ PyObject* InternalGetSubMessage( // by slice will be removed from cmessage_list by this function. // // Corresponds to reflection api method RemoveLast. -int InternalDeleteRepeatedField(Message* message, +int InternalDeleteRepeatedField(CMessage* self, const FieldDescriptor* field_descriptor, PyObject* slice, PyObject* cmessage_list); @@ -153,10 +153,15 @@ int InternalSetScalar(CMessage* self, const FieldDescriptor* field_descriptor, PyObject* value); +// Sets the specified scalar value to the message. Requires it is not a Oneof. +int InternalSetNonOneofScalar(Message* message, + const FieldDescriptor* field_descriptor, + PyObject* arg); + // Retrieves the specified scalar value from the message. // // Returns a new python reference. -PyObject* InternalGetScalar(CMessage* self, +PyObject* InternalGetScalar(const Message* message, const FieldDescriptor* field_descriptor); // Clears the message, removing all contained data. Extension dictionary and @@ -279,7 +284,7 @@ extern PyObject* kint64min_py; extern PyObject* kint64max_py; extern PyObject* kuint64max_py; -#define C(str) const_cast(str) +#define FULL_MODULE_NAME "google.protobuf.pyext._message" void FormatTypeError(PyObject* arg, char* expected_types); template diff --git a/python/google/protobuf/pyext/message_map_container.cc b/python/google/protobuf/pyext/message_map_container.cc new file mode 100644 index 00000000..ab8d8fb9 --- /dev/null +++ b/python/google/protobuf/pyext/message_map_container.cc @@ -0,0 +1,540 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: haberman@google.com (Josh Haberman) + +#include + +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace python { + +struct MessageMapIterator { + PyObject_HEAD; + + // This dict contains the full contents of what we want to iterate over. + // There's no way to avoid building this, because the list representation + // (which is canonical) can contain duplicate keys. So at the very least we + // need a set that lets us skip duplicate keys. And at the point that we're + // doing that, we might as well just build the actual dict we're iterating + // over and use dict's built-in iterator. + PyObject* dict; + + // An iterator on dict. + PyObject* iter; + + // A pointer back to the container, so we can notice changes to the version. + MessageMapContainer* container; + + // The version of the map when we took the iterator to it. + // + // We store this so that if the map is modified during iteration we can throw + // an error. + uint64 version; +}; + +static MessageMapIterator* GetIter(PyObject* obj) { + return reinterpret_cast(obj); +} + +namespace message_map_container { + +static MessageMapContainer* GetMap(PyObject* obj) { + return reinterpret_cast(obj); +} + +// The private constructor of MessageMapContainer objects. +PyObject* NewContainer(CMessage* parent, + const google::protobuf::FieldDescriptor* parent_field_descriptor, + PyObject* concrete_class) { + if (!CheckFieldBelongsToMessage(parent_field_descriptor, parent->message)) { + return NULL; + } + + PyObject* obj = PyType_GenericAlloc(&MessageMapContainer_Type, 0); + if (obj == NULL) { + return PyErr_Format(PyExc_RuntimeError, + "Could not allocate new container."); + } + + MessageMapContainer* self = GetMap(obj); + + self->message = parent->message; + self->parent = parent; + self->parent_field_descriptor = parent_field_descriptor; + self->owner = parent->owner; + self->version = 0; + + self->key_field_descriptor = + parent_field_descriptor->message_type()->FindFieldByName("key"); + self->value_field_descriptor = + parent_field_descriptor->message_type()->FindFieldByName("value"); + + self->message_dict = PyDict_New(); + if (self->message_dict == NULL) { + return PyErr_Format(PyExc_RuntimeError, + "Could not allocate message dict."); + } + + Py_INCREF(concrete_class); + self->subclass_init = concrete_class; + + if (self->key_field_descriptor == NULL || + self->value_field_descriptor == NULL) { + Py_DECREF(obj); + return PyErr_Format(PyExc_KeyError, + "Map entry descriptor did not have key/value fields"); + } + + return obj; +} + +// Initializes the underlying Message object of "to" so it becomes a new parent +// repeated scalar, and copies all the values from "from" to it. A child scalar +// container can be released by passing it as both from and to (e.g. making it +// the recipient of the new parent message and copying the values from itself). +static int InitializeAndCopyToParentContainer( + MessageMapContainer* from, + MessageMapContainer* to) { + // For now we require from == to, re-evaluate if we want to support deep copy + // as in repeated_composite_container.cc. + GOOGLE_DCHECK(from == to); + Message* old_message = from->message; + Message* new_message = old_message->New(); + to->parent = NULL; + to->parent_field_descriptor = from->parent_field_descriptor; + to->message = new_message; + to->owner.reset(new_message); + + vector fields; + fields.push_back(from->parent_field_descriptor); + old_message->GetReflection()->SwapFields(old_message, new_message, fields); + return 0; +} + +static PyObject* GetCMessage(MessageMapContainer* self, Message* entry) { + // Get or create the CMessage object corresponding to this message. + Message* message = entry->GetReflection()->MutableMessage( + entry, self->value_field_descriptor); + ScopedPyObjectPtr key(PyLong_FromVoidPtr(message)); + PyObject* ret = PyDict_GetItem(self->message_dict, key); + + if (ret == NULL) { + CMessage* cmsg = cmessage::NewEmptyMessage(self->subclass_init, + message->GetDescriptor()); + ret = reinterpret_cast(cmsg); + + if (cmsg == NULL) { + return NULL; + } + cmsg->owner = self->owner; + cmsg->message = message; + cmsg->parent = self->parent; + + if (PyDict_SetItem(self->message_dict, key, ret) < 0) { + Py_DECREF(ret); + return NULL; + } + } else { + Py_INCREF(ret); + } + + return ret; +} + +int Release(MessageMapContainer* self) { + InitializeAndCopyToParentContainer(self, self); + return 0; +} + +void SetOwner(MessageMapContainer* self, + const shared_ptr& new_owner) { + self->owner = new_owner; +} + +Py_ssize_t Length(PyObject* _self) { + MessageMapContainer* self = GetMap(_self); + google::protobuf::Message* message = self->message; + return message->GetReflection()->FieldSize(*message, + self->parent_field_descriptor); +} + +int MapKeyMatches(MessageMapContainer* self, const Message* entry, + PyObject* key) { + // TODO(haberman): do we need more strict type checking? + ScopedPyObjectPtr entry_key( + cmessage::InternalGetScalar(entry, self->key_field_descriptor)); + int ret = PyObject_RichCompareBool(key, entry_key, Py_EQ); + return ret; +} + +int SetItem(PyObject *_self, PyObject *key, PyObject *v) { + if (v) { + PyErr_Format(PyExc_ValueError, + "Direct assignment of submessage not allowed"); + return -1; + } + + // Now we know that this is a delete, not a set. + + MessageMapContainer* self = GetMap(_self); + cmessage::AssureWritable(self->parent); + + Message* message = self->message; + const Reflection* reflection = message->GetReflection(); + size_t size = + reflection->FieldSize(*message, self->parent_field_descriptor); + + // Right now the Reflection API doesn't support map lookup, so we implement it + // via linear search. We need to search from the end because the underlying + // representation can have duplicates if a user calls MergeFrom(); the last + // one needs to win. + // + // TODO(haberman): add lookup API to Reflection API. + bool found = false; + for (int i = size - 1; i >= 0; i--) { + Message* entry = reflection->MutableRepeatedMessage( + message, self->parent_field_descriptor, i); + int matches = MapKeyMatches(self, entry, key); + if (matches < 0) return -1; + if (matches) { + found = true; + if (i != size - 1) { + reflection->SwapElements(message, self->parent_field_descriptor, i, + size - 1); + } + reflection->RemoveLast(message, self->parent_field_descriptor); + + // Can't exit now, the repeated field representation of maps allows + // duplicate keys, and we have to be sure to remove all of them. + } + } + + if (!found) { + PyErr_Format(PyExc_KeyError, "Key not present in map"); + return -1; + } + + self->version++; + + return 0; +} + +PyObject* GetIterator(PyObject *_self) { + MessageMapContainer* self = GetMap(_self); + + ScopedPyObjectPtr obj(PyType_GenericAlloc(&MessageMapIterator_Type, 0)); + if (obj == NULL) { + return PyErr_Format(PyExc_KeyError, "Could not allocate iterator"); + } + + MessageMapIterator* iter = GetIter(obj); + + Py_INCREF(self); + iter->container = self; + iter->version = self->version; + iter->dict = PyDict_New(); + if (iter->dict == NULL) { + return PyErr_Format(PyExc_RuntimeError, + "Could not allocate dict for iterator."); + } + + // Build the entire map into a dict right now. Start from the beginning so + // that later entries win in the case of duplicates. + Message* message = self->message; + const Reflection* reflection = message->GetReflection(); + + // Right now the Reflection API doesn't support map lookup, so we implement it + // via linear search. We need to search from the end because the underlying + // representation can have duplicates if a user calls MergeFrom(); the last + // one needs to win. + // + // TODO(haberman): add lookup API to Reflection API. + size_t size = + reflection->FieldSize(*message, self->parent_field_descriptor); + for (int i = size - 1; i >= 0; i--) { + Message* entry = reflection->MutableRepeatedMessage( + message, self->parent_field_descriptor, i); + ScopedPyObjectPtr key( + cmessage::InternalGetScalar(entry, self->key_field_descriptor)); + if (PyDict_SetItem(iter->dict, key.get(), GetCMessage(self, entry)) < 0) { + return PyErr_Format(PyExc_RuntimeError, + "SetItem failed in iterator construction."); + } + } + + iter->iter = PyObject_GetIter(iter->dict); + + return obj.release(); +} + +PyObject* GetItem(PyObject* _self, PyObject* key) { + MessageMapContainer* self = GetMap(_self); + cmessage::AssureWritable(self->parent); + Message* message = self->message; + const Reflection* reflection = message->GetReflection(); + + // Right now the Reflection API doesn't support map lookup, so we implement it + // via linear search. We need to search from the end because the underlying + // representation can have duplicates if a user calls MergeFrom(); the last + // one needs to win. + // + // TODO(haberman): add lookup API to Reflection API. + size_t size = + reflection->FieldSize(*message, self->parent_field_descriptor); + for (int i = size - 1; i >= 0; i--) { + Message* entry = reflection->MutableRepeatedMessage( + message, self->parent_field_descriptor, i); + int matches = MapKeyMatches(self, entry, key); + if (matches < 0) return NULL; + if (matches) { + return GetCMessage(self, entry); + } + } + + // Key is not already present; insert a new entry. + Message* entry = + reflection->AddMessage(message, self->parent_field_descriptor); + + self->version++; + + if (cmessage::InternalSetNonOneofScalar(entry, self->key_field_descriptor, + key) < 0) { + reflection->RemoveLast(message, self->parent_field_descriptor); + return NULL; + } + + return GetCMessage(self, entry); +} + +PyObject* Contains(PyObject* _self, PyObject* key) { + MessageMapContainer* self = GetMap(_self); + Message* message = self->message; + const Reflection* reflection = message->GetReflection(); + + // Right now the Reflection API doesn't support map lookup, so we implement it + // via linear search. + // + // TODO(haberman): add lookup API to Reflection API. + size_t size = + reflection->FieldSize(*message, self->parent_field_descriptor); + for (int i = 0; i < size; i++) { + Message* entry = reflection->MutableRepeatedMessage( + message, self->parent_field_descriptor, i); + int matches = MapKeyMatches(self, entry, key); + if (matches < 0) return NULL; + if (matches) { + Py_RETURN_TRUE; + } + } + + Py_RETURN_FALSE; +} + +PyObject* Clear(PyObject* _self) { + MessageMapContainer* self = GetMap(_self); + cmessage::AssureWritable(self->parent); + Message* message = self->message; + const Reflection* reflection = message->GetReflection(); + + self->version++; + reflection->ClearField(message, self->parent_field_descriptor); + + Py_RETURN_NONE; +} + +PyObject* Get(PyObject* self, PyObject* args) { + PyObject* key; + PyObject* default_value = NULL; + if (PyArg_ParseTuple(args, "O|O", &key, &default_value) < 0) { + return NULL; + } + + ScopedPyObjectPtr is_present(Contains(self, key)); + if (is_present.get() == NULL) { + return NULL; + } + + if (PyObject_IsTrue(is_present.get())) { + return GetItem(self, key); + } else { + if (default_value != NULL) { + Py_INCREF(default_value); + return default_value; + } else { + Py_RETURN_NONE; + } + } +} + +static PyMappingMethods MpMethods = { + Length, // mp_length + GetItem, // mp_subscript + SetItem, // mp_ass_subscript +}; + +static void Dealloc(PyObject* _self) { + MessageMapContainer* self = GetMap(_self); + self->owner.reset(); + Py_DECREF(self->message_dict); + Py_TYPE(_self)->tp_free(_self); +} + +static PyMethodDef Methods[] = { + { "__contains__", (PyCFunction)Contains, METH_O, + "Tests whether the map contains this element."}, + { "clear", (PyCFunction)Clear, METH_NOARGS, + "Removes all elements from the map."}, + { "get", Get, METH_VARARGS, + "Gets the value for the given key if present, or otherwise a default" }, + { "get_or_create", GetItem, METH_O, + "Alias for getitem, useful to make explicit that the map is mutated." }, + /* + { "__deepcopy__", (PyCFunction)DeepCopy, METH_VARARGS, + "Makes a deep copy of the class." }, + { "__reduce__", (PyCFunction)Reduce, METH_NOARGS, + "Outputs picklable representation of the repeated field." }, + */ + {NULL, NULL}, +}; + +} // namespace message_map_container + +namespace message_map_iterator { + +static void Dealloc(PyObject* _self) { + MessageMapIterator* self = GetIter(_self); + Py_DECREF(self->dict); + Py_DECREF(self->iter); + Py_DECREF(self->container); + Py_TYPE(_self)->tp_free(_self); +} + +PyObject* IterNext(PyObject* _self) { + MessageMapIterator* self = GetIter(_self); + + // This won't catch mutations to the map performed by MergeFrom(); no easy way + // to address that. + if (self->version != self->container->version) { + return PyErr_Format(PyExc_RuntimeError, + "Map modified during iteration."); + } + + return PyIter_Next(self->iter); +} + +} // namespace message_map_iterator + +PyTypeObject MessageMapContainer_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + FULL_MODULE_NAME ".MessageMapContainer", // tp_name + sizeof(MessageMapContainer), // tp_basicsize + 0, // tp_itemsize + message_map_container::Dealloc, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + 0, // tp_repr + 0, // tp_as_number + 0, // tp_as_sequence + &message_map_container::MpMethods, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + 0, // tp_getattro + 0, // tp_setattro + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + "A map container for message", // tp_doc + 0, // tp_traverse + 0, // tp_clear + 0, // tp_richcompare + 0, // tp_weaklistoffset + message_map_container::GetIterator, // tp_iter + 0, // tp_iternext + message_map_container::Methods, // tp_methods + 0, // tp_members + 0, // tp_getset + 0, // tp_base + 0, // tp_dict + 0, // tp_descr_get + 0, // tp_descr_set + 0, // tp_dictoffset + 0, // tp_init +}; + +PyTypeObject MessageMapIterator_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + FULL_MODULE_NAME ".MessageMapIterator", // tp_name + sizeof(MessageMapIterator), // tp_basicsize + 0, // tp_itemsize + message_map_iterator::Dealloc, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + 0, // tp_repr + 0, // tp_as_number + 0, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + 0, // tp_getattro + 0, // tp_setattro + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + "A scalar map iterator", // tp_doc + 0, // tp_traverse + 0, // tp_clear + 0, // tp_richcompare + 0, // tp_weaklistoffset + PyObject_SelfIter, // tp_iter + message_map_iterator::IterNext, // tp_iternext + 0, // tp_methods + 0, // tp_members + 0, // tp_getset + 0, // tp_base + 0, // tp_dict + 0, // tp_descr_get + 0, // tp_descr_set + 0, // tp_dictoffset + 0, // tp_init +}; + +} // namespace python +} // namespace protobuf +} // namespace google diff --git a/python/google/protobuf/pyext/message_map_container.h b/python/google/protobuf/pyext/message_map_container.h new file mode 100644 index 00000000..4ca0aecc --- /dev/null +++ b/python/google/protobuf/pyext/message_map_container.h @@ -0,0 +1,117 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef GOOGLE_PROTOBUF_PYTHON_CPP_MESSAGE_MAP_CONTAINER_H__ +#define GOOGLE_PROTOBUF_PYTHON_CPP_MESSAGE_MAP_CONTAINER_H__ + +#include + +#include +#ifndef _SHARED_PTR_H +#include +#endif + +#include + +namespace google { +namespace protobuf { + +class Message; + +using internal::shared_ptr; + +namespace python { + +struct CMessage; + +struct MessageMapContainer { + PyObject_HEAD; + + // This is the top-level C++ Message object that owns the whole + // proto tree. Every Python MessageMapContainer holds a + // reference to it in order to keep it alive as long as there's a + // Python object that references any part of the tree. + shared_ptr owner; + + // Pointer to the C++ Message that contains this container. The + // MessageMapContainer does not own this pointer. + Message* message; + + // Weak reference to a parent CMessage object (i.e. may be NULL.) + // + // Used to make sure all ancestors are also mutable when first + // modifying the container. + CMessage* parent; + + // Pointer to the parent's descriptor that describes this + // field. Used together with the parent's message when making a + // default message instance mutable. + // The pointer is owned by the global DescriptorPool. + const FieldDescriptor* parent_field_descriptor; + const FieldDescriptor* key_field_descriptor; + const FieldDescriptor* value_field_descriptor; + + // A callable that is used to create new child messages. + PyObject* subclass_init; + + // A dict mapping Message* -> CMessage. + PyObject* message_dict; + + // We bump this whenever we perform a mutation, to invalidate existing + // iterators. + uint64 version; +}; + +extern PyTypeObject MessageMapContainer_Type; +extern PyTypeObject MessageMapIterator_Type; + +namespace message_map_container { + +// Builds a MessageMapContainer object, from a parent message and a +// field descriptor. +extern PyObject* NewContainer(CMessage* parent, + const FieldDescriptor* parent_field_descriptor, + PyObject* concrete_class); + +// Releases the messages in the container to a new message. +// +// Returns 0 on success, -1 on failure. +int Release(MessageMapContainer* self); + +// Set the owner field of self and any children of self. +void SetOwner(MessageMapContainer* self, + const shared_ptr& new_owner); + +} // namespace message_map_container +} // namespace python +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_PYTHON_CPP_MESSAGE_MAP_CONTAINER_H__ diff --git a/python/google/protobuf/pyext/repeated_composite_container.cc b/python/google/protobuf/pyext/repeated_composite_container.cc index 0fe98e73..86b75d0f 100644 --- a/python/google/protobuf/pyext/repeated_composite_container.cc +++ b/python/google/protobuf/pyext/repeated_composite_container.cc @@ -367,8 +367,8 @@ int AssignSubscript(RepeatedCompositeContainer* self, } // Delete from the underlying Message, if any. - if (self->message != NULL) { - if (cmessage::InternalDeleteRepeatedField(self->message, + if (self->parent != NULL) { + if (cmessage::InternalDeleteRepeatedField(self->parent, self->parent_field_descriptor, slice, self->child_messages) < 0) { @@ -572,47 +572,35 @@ static PyObject* Pop(RepeatedCompositeContainer* self, return item; } -// The caller takes ownership of the returned Message. -Message* ReleaseLast(const FieldDescriptor* field, - const Descriptor* type, - Message* message) { +// Release field of parent message and transfer the ownership to target. +void ReleaseLastTo(CMessage* parent, + const FieldDescriptor* field, + CMessage* target) { + GOOGLE_CHECK_NOTNULL(parent); GOOGLE_CHECK_NOTNULL(field); - GOOGLE_CHECK_NOTNULL(type); - GOOGLE_CHECK_NOTNULL(message); + GOOGLE_CHECK_NOTNULL(target); - Message* released_message = message->GetReflection()->ReleaseLast( - message, field); + shared_ptr released_message( + parent->message->GetReflection()->ReleaseLast(parent->message, field)); // TODO(tibell): Deal with proto1. // ReleaseMessage will return NULL which differs from // child_cmessage->message, if the field does not exist. In this case, // the latter points to the default instance via a const_cast<>, so we // have to reset it to a new mutable object since we are taking ownership. - if (released_message == NULL) { + if (released_message.get() == NULL) { const Message* prototype = - cmessage::GetMessageFactory()->GetPrototype(type); + cmessage::GetMessageFactory()->GetPrototype( + target->message->GetDescriptor()); GOOGLE_CHECK_NOTNULL(prototype); - return prototype->New(); - } else { - return released_message; + released_message.reset(prototype->New()); } -} -// Release field of message and transfer the ownership to cmessage. -void ReleaseLastTo(const FieldDescriptor* field, - Message* message, - CMessage* cmessage) { - GOOGLE_CHECK_NOTNULL(field); - GOOGLE_CHECK_NOTNULL(message); - GOOGLE_CHECK_NOTNULL(cmessage); - - shared_ptr released_message( - ReleaseLast(field, cmessage->message->GetDescriptor(), message)); - cmessage->parent = NULL; - cmessage->parent_field_descriptor = NULL; - cmessage->message = released_message.get(); - cmessage->read_only = false; - cmessage::SetOwner(cmessage, released_message); + target->parent = NULL; + target->parent_field_descriptor = NULL; + target->message = released_message.get(); + target->read_only = false; + cmessage::SetOwner(target, released_message); } // Called to release a container using @@ -635,7 +623,7 @@ int Release(RepeatedCompositeContainer* self) { for (Py_ssize_t i = size - 1; i >= 0; --i) { CMessage* child_cmessage = reinterpret_cast( PyList_GET_ITEM(self->child_messages, i)); - ReleaseLastTo(field, message, child_cmessage); + ReleaseLastTo(self->parent, field, child_cmessage); } // Detach from containing message. @@ -732,9 +720,7 @@ static PyMethodDef Methods[] = { PyTypeObject RepeatedCompositeContainer_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) - // Keep the fully qualified _message symbol in a line for opensource. - "google.protobuf.pyext._message." - "RepeatedCompositeContainer", // tp_name + FULL_MODULE_NAME ".RepeatedCompositeContainer", // tp_name sizeof(RepeatedCompositeContainer), // tp_basicsize 0, // tp_itemsize (destructor)repeated_composite_container::Dealloc, // tp_dealloc diff --git a/python/google/protobuf/pyext/repeated_composite_container.h b/python/google/protobuf/pyext/repeated_composite_container.h index ce7cee0f..e0f21360 100644 --- a/python/google/protobuf/pyext/repeated_composite_container.h +++ b/python/google/protobuf/pyext/repeated_composite_container.h @@ -161,13 +161,13 @@ int SetOwner(RepeatedCompositeContainer* self, const shared_ptr& new_owner); // Removes the last element of the repeated message field 'field' on -// the Message 'message', and transfers the ownership of the released -// Message to 'cmessage'. +// the Message 'parent', and transfers the ownership of the released +// Message to 'target'. // // Corresponds to reflection api method ReleaseMessage. -void ReleaseLastTo(const FieldDescriptor* field, - Message* message, - CMessage* cmessage); +void ReleaseLastTo(CMessage* parent, + const FieldDescriptor* field, + CMessage* target); } // namespace repeated_composite_container } // namespace python diff --git a/python/google/protobuf/pyext/repeated_scalar_container.cc b/python/google/protobuf/pyext/repeated_scalar_container.cc index 110a4c85..fd196836 100644 --- a/python/google/protobuf/pyext/repeated_scalar_container.cc +++ b/python/google/protobuf/pyext/repeated_scalar_container.cc @@ -102,7 +102,7 @@ static int AssignItem(RepeatedScalarContainer* self, if (arg == NULL) { ScopedPyObjectPtr py_index(PyLong_FromLong(index)); - return cmessage::InternalDeleteRepeatedField(message, field_descriptor, + return cmessage::InternalDeleteRepeatedField(self->parent, field_descriptor, py_index, NULL); } @@ -470,7 +470,7 @@ static int AssSubscript(RepeatedScalarContainer* self, if (value == NULL) { return cmessage::InternalDeleteRepeatedField( - message, field_descriptor, slice, NULL); + self->parent, field_descriptor, slice, NULL); } if (!create_list) { @@ -769,9 +769,7 @@ static PyMethodDef Methods[] = { PyTypeObject RepeatedScalarContainer_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) - // Keep the fully qualified _message symbol in a line for opensource. - "google.protobuf.pyext._message." - "RepeatedScalarContainer", // tp_name + FULL_MODULE_NAME ".RepeatedScalarContainer", // tp_name sizeof(RepeatedScalarContainer), // tp_basicsize 0, // tp_itemsize (destructor)repeated_scalar_container::Dealloc, // tp_dealloc diff --git a/python/google/protobuf/pyext/scalar_map_container.cc b/python/google/protobuf/pyext/scalar_map_container.cc new file mode 100644 index 00000000..6f731d27 --- /dev/null +++ b/python/google/protobuf/pyext/scalar_map_container.cc @@ -0,0 +1,514 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: haberman@google.com (Josh Haberman) + +#include + +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace python { + +struct ScalarMapIterator { + PyObject_HEAD; + + // This dict contains the full contents of what we want to iterate over. + // There's no way to avoid building this, because the list representation + // (which is canonical) can contain duplicate keys. So at the very least we + // need a set that lets us skip duplicate keys. And at the point that we're + // doing that, we might as well just build the actual dict we're iterating + // over and use dict's built-in iterator. + PyObject* dict; + + // An iterator on dict. + PyObject* iter; + + // A pointer back to the container, so we can notice changes to the version. + ScalarMapContainer* container; + + // The version of the map when we took the iterator to it. + // + // We store this so that if the map is modified during iteration we can throw + // an error. + uint64 version; +}; + +static ScalarMapIterator* GetIter(PyObject* obj) { + return reinterpret_cast(obj); +} + +namespace scalar_map_container { + +static ScalarMapContainer* GetMap(PyObject* obj) { + return reinterpret_cast(obj); +} + +// The private constructor of ScalarMapContainer objects. +PyObject *NewContainer( + CMessage* parent, const google::protobuf::FieldDescriptor* parent_field_descriptor) { + if (!CheckFieldBelongsToMessage(parent_field_descriptor, parent->message)) { + return NULL; + } + + ScopedPyObjectPtr obj(PyType_GenericAlloc(&ScalarMapContainer_Type, 0)); + if (obj.get() == NULL) { + return PyErr_Format(PyExc_RuntimeError, + "Could not allocate new container."); + } + + ScalarMapContainer* self = GetMap(obj); + + self->message = parent->message; + self->parent = parent; + self->parent_field_descriptor = parent_field_descriptor; + self->owner = parent->owner; + self->version = 0; + + self->key_field_descriptor = + parent_field_descriptor->message_type()->FindFieldByName("key"); + self->value_field_descriptor = + parent_field_descriptor->message_type()->FindFieldByName("value"); + + if (self->key_field_descriptor == NULL || + self->value_field_descriptor == NULL) { + return PyErr_Format(PyExc_KeyError, + "Map entry descriptor did not have key/value fields"); + } + + return obj.release(); +} + +// Initializes the underlying Message object of "to" so it becomes a new parent +// repeated scalar, and copies all the values from "from" to it. A child scalar +// container can be released by passing it as both from and to (e.g. making it +// the recipient of the new parent message and copying the values from itself). +static int InitializeAndCopyToParentContainer( + ScalarMapContainer* from, + ScalarMapContainer* to) { + // For now we require from == to, re-evaluate if we want to support deep copy + // as in repeated_scalar_container.cc. + GOOGLE_DCHECK(from == to); + Message* old_message = from->message; + Message* new_message = old_message->New(); + to->parent = NULL; + to->parent_field_descriptor = from->parent_field_descriptor; + to->message = new_message; + to->owner.reset(new_message); + + vector fields; + fields.push_back(from->parent_field_descriptor); + old_message->GetReflection()->SwapFields(old_message, new_message, fields); + return 0; +} + +int Release(ScalarMapContainer* self) { + return InitializeAndCopyToParentContainer(self, self); +} + +void SetOwner(ScalarMapContainer* self, + const shared_ptr& new_owner) { + self->owner = new_owner; +} + +Py_ssize_t Length(PyObject* _self) { + ScalarMapContainer* self = GetMap(_self); + google::protobuf::Message* message = self->message; + return message->GetReflection()->FieldSize(*message, + self->parent_field_descriptor); +} + +int MapKeyMatches(ScalarMapContainer* self, const Message* entry, + PyObject* key) { + // TODO(haberman): do we need more strict type checking? + ScopedPyObjectPtr entry_key( + cmessage::InternalGetScalar(entry, self->key_field_descriptor)); + int ret = PyObject_RichCompareBool(key, entry_key, Py_EQ); + return ret; +} + +PyObject* GetItem(PyObject* _self, PyObject* key) { + ScalarMapContainer* self = GetMap(_self); + + Message* message = self->message; + const Reflection* reflection = message->GetReflection(); + + // Right now the Reflection API doesn't support map lookup, so we implement it + // via linear search. + // + // TODO(haberman): add lookup API to Reflection API. + size_t size = reflection->FieldSize(*message, self->parent_field_descriptor); + for (int i = size - 1; i >= 0; i--) { + const Message& entry = reflection->GetRepeatedMessage( + *message, self->parent_field_descriptor, i); + int matches = MapKeyMatches(self, &entry, key); + if (matches < 0) return NULL; + if (matches) { + return cmessage::InternalGetScalar(&entry, self->value_field_descriptor); + } + } + + // Need to add a new entry. + Message* entry = + reflection->AddMessage(message, self->parent_field_descriptor); + PyObject* ret = NULL; + + if (cmessage::InternalSetNonOneofScalar(entry, self->key_field_descriptor, + key) >= 0) { + ret = cmessage::InternalGetScalar(entry, self->value_field_descriptor); + } + + self->version++; + + // If there was a type error above, it set the Python exception. + return ret; +} + +int SetItem(PyObject *_self, PyObject *key, PyObject *v) { + ScalarMapContainer* self = GetMap(_self); + cmessage::AssureWritable(self->parent); + + Message* message = self->message; + const Reflection* reflection = message->GetReflection(); + size_t size = + reflection->FieldSize(*message, self->parent_field_descriptor); + self->version++; + + if (v) { + // Set item. + // + // Right now the Reflection API doesn't support map lookup, so we implement + // it via linear search. + // + // TODO(haberman): add lookup API to Reflection API. + for (int i = size - 1; i >= 0; i--) { + Message* entry = reflection->MutableRepeatedMessage( + message, self->parent_field_descriptor, i); + int matches = MapKeyMatches(self, entry, key); + if (matches < 0) return -1; + if (matches) { + return cmessage::InternalSetNonOneofScalar( + entry, self->value_field_descriptor, v); + } + } + + // Key is not already present; insert a new entry. + Message* entry = + reflection->AddMessage(message, self->parent_field_descriptor); + + if (cmessage::InternalSetNonOneofScalar(entry, self->key_field_descriptor, + key) < 0 || + cmessage::InternalSetNonOneofScalar(entry, self->value_field_descriptor, + v) < 0) { + reflection->RemoveLast(message, self->parent_field_descriptor); + return -1; + } + + return 0; + } else { + bool found = false; + for (int i = size - 1; i >= 0; i--) { + Message* entry = reflection->MutableRepeatedMessage( + message, self->parent_field_descriptor, i); + int matches = MapKeyMatches(self, entry, key); + if (matches < 0) return -1; + if (matches) { + found = true; + if (i != size - 1) { + reflection->SwapElements(message, self->parent_field_descriptor, i, + size - 1); + } + reflection->RemoveLast(message, self->parent_field_descriptor); + + // Can't exit now, the repeated field representation of maps allows + // duplicate keys, and we have to be sure to remove all of them. + } + } + + if (found) { + return 0; + } else { + PyErr_Format(PyExc_KeyError, "Key not present in map"); + return -1; + } + } +} + +PyObject* GetIterator(PyObject *_self) { + ScalarMapContainer* self = GetMap(_self); + + ScopedPyObjectPtr obj(PyType_GenericAlloc(&ScalarMapIterator_Type, 0)); + if (obj == NULL) { + return PyErr_Format(PyExc_KeyError, "Could not allocate iterator"); + } + + ScalarMapIterator* iter = GetIter(obj.get()); + + Py_INCREF(self); + iter->container = self; + iter->version = self->version; + iter->dict = PyDict_New(); + if (iter->dict == NULL) { + return PyErr_Format(PyExc_RuntimeError, + "Could not allocate dict for iterator."); + } + + // Build the entire map into a dict right now. Start from the beginning so + // that later entries win in the case of duplicates. + Message* message = self->message; + const Reflection* reflection = message->GetReflection(); + + // Right now the Reflection API doesn't support map lookup, so we implement it + // via linear search. We need to search from the end because the underlying + // representation can have duplicates if a user calls MergeFrom(); the last + // one needs to win. + // + // TODO(haberman): add lookup API to Reflection API. + size_t size = + reflection->FieldSize(*message, self->parent_field_descriptor); + for (int i = 0; i < size; i++) { + Message* entry = reflection->MutableRepeatedMessage( + message, self->parent_field_descriptor, i); + ScopedPyObjectPtr key( + cmessage::InternalGetScalar(entry, self->key_field_descriptor)); + ScopedPyObjectPtr val( + cmessage::InternalGetScalar(entry, self->value_field_descriptor)); + if (PyDict_SetItem(iter->dict, key.get(), val.get()) < 0) { + return PyErr_Format(PyExc_RuntimeError, + "SetItem failed in iterator construction."); + } + } + + + iter->iter = PyObject_GetIter(iter->dict); + + + return obj.release(); +} + +PyObject* Clear(PyObject* _self) { + ScalarMapContainer* self = GetMap(_self); + cmessage::AssureWritable(self->parent); + Message* message = self->message; + const Reflection* reflection = message->GetReflection(); + + reflection->ClearField(message, self->parent_field_descriptor); + + Py_RETURN_NONE; +} + +PyObject* Contains(PyObject* _self, PyObject* key) { + ScalarMapContainer* self = GetMap(_self); + + Message* message = self->message; + const Reflection* reflection = message->GetReflection(); + + // Right now the Reflection API doesn't support map lookup, so we implement it + // via linear search. + // + // TODO(haberman): add lookup API to Reflection API. + size_t size = reflection->FieldSize(*message, self->parent_field_descriptor); + for (int i = size - 1; i >= 0; i--) { + const Message& entry = reflection->GetRepeatedMessage( + *message, self->parent_field_descriptor, i); + int matches = MapKeyMatches(self, &entry, key); + if (matches < 0) return NULL; + if (matches) { + Py_RETURN_TRUE; + } + } + + Py_RETURN_FALSE; +} + +PyObject* Get(PyObject* self, PyObject* args) { + PyObject* key; + PyObject* default_value = NULL; + if (PyArg_ParseTuple(args, "O|O", &key, &default_value) < 0) { + return NULL; + } + + ScopedPyObjectPtr is_present(Contains(self, key)); + if (is_present.get() == NULL) { + return NULL; + } + + if (PyObject_IsTrue(is_present.get())) { + return GetItem(self, key); + } else { + if (default_value != NULL) { + Py_INCREF(default_value); + return default_value; + } else { + Py_RETURN_NONE; + } + } +} + +static PyMappingMethods MpMethods = { + Length, // mp_length + GetItem, // mp_subscript + SetItem, // mp_ass_subscript +}; + +static void Dealloc(PyObject* _self) { + ScalarMapContainer* self = GetMap(_self); + self->owner.reset(); + Py_TYPE(_self)->tp_free(_self); +} + +static PyMethodDef Methods[] = { + { "__contains__", Contains, METH_O, + "Tests whether a key is a member of the map." }, + { "clear", (PyCFunction)Clear, METH_NOARGS, + "Removes all elements from the map." }, + { "get", Get, METH_VARARGS, + "Gets the value for the given key if present, or otherwise a default" }, + /* + { "__deepcopy__", (PyCFunction)DeepCopy, METH_VARARGS, + "Makes a deep copy of the class." }, + { "__reduce__", (PyCFunction)Reduce, METH_NOARGS, + "Outputs picklable representation of the repeated field." }, + */ + {NULL, NULL}, +}; + +} // namespace scalar_map_container + +namespace scalar_map_iterator { + +static void Dealloc(PyObject* _self) { + ScalarMapIterator* self = GetIter(_self); + Py_DECREF(self->dict); + Py_DECREF(self->iter); + Py_DECREF(self->container); + Py_TYPE(_self)->tp_free(_self); +} + +PyObject* IterNext(PyObject* _self) { + ScalarMapIterator* self = GetIter(_self); + + // This won't catch mutations to the map performed by MergeFrom(); no easy way + // to address that. + if (self->version != self->container->version) { + return PyErr_Format(PyExc_RuntimeError, + "Map modified during iteration."); + } + + return PyIter_Next(self->iter); +} + +} // namespace scalar_map_iterator + +PyTypeObject ScalarMapContainer_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + FULL_MODULE_NAME ".ScalarMapContainer", // tp_name + sizeof(ScalarMapContainer), // tp_basicsize + 0, // tp_itemsize + scalar_map_container::Dealloc, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + 0, // tp_repr + 0, // tp_as_number + 0, // tp_as_sequence + &scalar_map_container::MpMethods, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + 0, // tp_getattro + 0, // tp_setattro + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + "A scalar map container", // tp_doc + 0, // tp_traverse + 0, // tp_clear + 0, // tp_richcompare + 0, // tp_weaklistoffset + scalar_map_container::GetIterator, // tp_iter + 0, // tp_iternext + scalar_map_container::Methods, // tp_methods + 0, // tp_members + 0, // tp_getset + 0, // tp_base + 0, // tp_dict + 0, // tp_descr_get + 0, // tp_descr_set + 0, // tp_dictoffset + 0, // tp_init +}; + +PyTypeObject ScalarMapIterator_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + FULL_MODULE_NAME ".ScalarMapIterator", // tp_name + sizeof(ScalarMapIterator), // tp_basicsize + 0, // tp_itemsize + scalar_map_iterator::Dealloc, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + 0, // tp_repr + 0, // tp_as_number + 0, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + 0, // tp_getattro + 0, // tp_setattro + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + "A scalar map iterator", // tp_doc + 0, // tp_traverse + 0, // tp_clear + 0, // tp_richcompare + 0, // tp_weaklistoffset + PyObject_SelfIter, // tp_iter + scalar_map_iterator::IterNext, // tp_iternext + 0, // tp_methods + 0, // tp_members + 0, // tp_getset + 0, // tp_base + 0, // tp_dict + 0, // tp_descr_get + 0, // tp_descr_set + 0, // tp_dictoffset + 0, // tp_init +}; + +} // namespace python +} // namespace protobuf +} // namespace google diff --git a/python/google/protobuf/pyext/scalar_map_container.h b/python/google/protobuf/pyext/scalar_map_container.h new file mode 100644 index 00000000..254e6e98 --- /dev/null +++ b/python/google/protobuf/pyext/scalar_map_container.h @@ -0,0 +1,110 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef GOOGLE_PROTOBUF_PYTHON_CPP_SCALAR_MAP_CONTAINER_H__ +#define GOOGLE_PROTOBUF_PYTHON_CPP_SCALAR_MAP_CONTAINER_H__ + +#include + +#include +#ifndef _SHARED_PTR_H +#include +#endif + +#include + +namespace google { +namespace protobuf { + +class Message; + +using internal::shared_ptr; + +namespace python { + +struct CMessage; + +struct ScalarMapContainer { + PyObject_HEAD; + + // This is the top-level C++ Message object that owns the whole + // proto tree. Every Python ScalarMapContainer holds a + // reference to it in order to keep it alive as long as there's a + // Python object that references any part of the tree. + shared_ptr owner; + + // Pointer to the C++ Message that contains this container. The + // ScalarMapContainer does not own this pointer. + Message* message; + + // Weak reference to a parent CMessage object (i.e. may be NULL.) + // + // Used to make sure all ancestors are also mutable when first + // modifying the container. + CMessage* parent; + + // Pointer to the parent's descriptor that describes this + // field. Used together with the parent's message when making a + // default message instance mutable. + // The pointer is owned by the global DescriptorPool. + const FieldDescriptor* parent_field_descriptor; + const FieldDescriptor* key_field_descriptor; + const FieldDescriptor* value_field_descriptor; + + // We bump this whenever we perform a mutation, to invalidate existing + // iterators. + uint64 version; +}; + +extern PyTypeObject ScalarMapContainer_Type; +extern PyTypeObject ScalarMapIterator_Type; + +namespace scalar_map_container { + +// Builds a ScalarMapContainer object, from a parent message and a +// field descriptor. +extern PyObject *NewContainer( + CMessage* parent, const FieldDescriptor* parent_field_descriptor); + +// Releases the messages in the container to a new message. +// +// Returns 0 on success, -1 on failure. +int Release(ScalarMapContainer* self); + +// Set the owner field of self and any children of self. +void SetOwner(ScalarMapContainer* self, + const shared_ptr& new_owner); + +} // namespace scalar_map_container +} // namespace python +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_PYTHON_CPP_SCALAR_MAP_CONTAINER_H__ diff --git a/python/google/protobuf/reflection.py b/python/google/protobuf/reflection.py index 55e653a0..82fca661 100755 --- a/python/google/protobuf/reflection.py +++ b/python/google/protobuf/reflection.py @@ -144,7 +144,6 @@ class GeneratedProtocolMessageType(type): _InitMessage(descriptor, cls) superclass = super(GeneratedProtocolMessageType, cls) superclass.__init__(name, bases, dictionary) - setattr(descriptor, '_concrete_class', cls) def ParseMessage(descriptor, byte_str): diff --git a/python/google/protobuf/text_format.py b/python/google/protobuf/text_format.py index a47ce3e3..8cbd6822 100755 --- a/python/google/protobuf/text_format.py +++ b/python/google/protobuf/text_format.py @@ -100,6 +100,10 @@ def MessageToString(message, as_utf8=False, as_one_line=False, return result.rstrip() return result +def _IsMapEntry(field): + return (field.type == descriptor.FieldDescriptor.TYPE_MESSAGE and + field.message_type.has_options and + field.message_type.GetOptions().map_entry) def PrintMessage(message, out, indent=0, as_utf8=False, as_one_line=False, pointy_brackets=False, use_index_order=False, @@ -108,7 +112,19 @@ def PrintMessage(message, out, indent=0, as_utf8=False, as_one_line=False, if use_index_order: fields.sort(key=lambda x: x[0].index) for field, value in fields: - if field.label == descriptor.FieldDescriptor.LABEL_REPEATED: + if _IsMapEntry(field): + for key in value: + # This is slow for maps with submessage entires because it copies the + # entire tree. Unfortunately this would take significant refactoring + # of this file to work around. + # + # TODO(haberman): refactor and optimize if this becomes an issue. + entry_submsg = field.message_type._concrete_class( + key=key, value=value[key]) + PrintField(field, entry_submsg, out, indent, as_utf8, as_one_line, + pointy_brackets=pointy_brackets, + use_index_order=use_index_order, float_format=float_format) + elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED: for element in value: PrintField(field, element, out, indent, as_utf8, as_one_line, pointy_brackets=pointy_brackets, @@ -367,6 +383,7 @@ def _MergeField(tokenizer, message, allow_multiple_scalars): message_descriptor.full_name, name)) if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: + is_map_entry = _IsMapEntry(field) tokenizer.TryConsume(':') if tokenizer.TryConsume('<'): @@ -378,6 +395,8 @@ def _MergeField(tokenizer, message, allow_multiple_scalars): if field.label == descriptor.FieldDescriptor.LABEL_REPEATED: if field.is_extension: sub_message = message.Extensions[field].add() + elif is_map_entry: + sub_message = field.message_type._concrete_class() else: sub_message = getattr(message, field.name).add() else: @@ -391,6 +410,14 @@ def _MergeField(tokenizer, message, allow_multiple_scalars): if tokenizer.AtEnd(): raise tokenizer.ParseErrorPreviousToken('Expected "%s".' % (end_token)) _MergeField(tokenizer, sub_message, allow_multiple_scalars) + + if is_map_entry: + value_cpptype = field.message_type.fields_by_name['value'].cpp_type + if value_cpptype == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: + value = getattr(message, field.name)[sub_message.key] + value.MergeFrom(sub_message.value) + else: + getattr(message, field.name)[sub_message.key] = sub_message.value else: _MergeScalarField(tokenizer, message, field, allow_multiple_scalars) @@ -701,13 +728,16 @@ class _Tokenizer(object): String literals (whether bytes or text) can come in multiple adjacent tokens which are automatically concatenated, like in C or Python. This method only consumes one token. + + Raises: + ParseError: When the wrong format data is found. """ text = self.token if len(text) < 1 or text[0] not in ('\'', '"'): - raise self._ParseError('Expected string but found: "%r"' % text) + raise self._ParseError('Expected string but found: %r' % (text,)) if len(text) < 2 or text[-1] != text[0]: - raise self._ParseError('String missing ending quote.') + raise self._ParseError('String missing ending quote: %r' % (text,)) try: result = text_encoding.CUnescape(text[1:-1]) diff --git a/python/setup.py b/python/setup.py index a1365fba..5c321f50 100755 --- a/python/setup.py +++ b/python/setup.py @@ -91,6 +91,7 @@ def GenerateUnittestProtos(): if not os.path.exists("../.git"): return + generate_proto("../src/google/protobuf/map_unittest.proto") generate_proto("../src/google/protobuf/unittest.proto") generate_proto("../src/google/protobuf/unittest_custom_options.proto") generate_proto("../src/google/protobuf/unittest_import.proto") diff --git a/ruby/tests/generated_code.proto b/ruby/tests/generated_code.proto index b1d63232..42d82a6b 100644 --- a/ruby/tests/generated_code.proto +++ b/ruby/tests/generated_code.proto @@ -3,17 +3,17 @@ 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; + int32 optional_int32 = 1; + int64 optional_int64 = 2; + uint32 optional_uint32 = 3; + uint64 optional_uint64 = 4; + bool optional_bool = 5; + double optional_double = 6; + float optional_float = 7; + string optional_string = 8; + bytes optional_bytes = 9; + TestEnum optional_enum = 10; + TestMessage optional_msg = 11; repeated int32 repeated_int32 = 21; repeated int64 repeated_int64 = 22; @@ -53,10 +53,10 @@ message TestMessage { map map_string_bool = 70; message NestedMessage { - optional int32 foo = 1; + int32 foo = 1; } - optional NestedMessage nested_message = 80; + NestedMessage nested_message = 80; } enum TestEnum { diff --git a/src/Makefile.am b/src/Makefile.am index fd10b789..2ecf6028 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -88,6 +88,7 @@ nobase_include_HEADERS = \ google/protobuf/stubs/type_traits.h \ google/protobuf/any.pb.h \ google/protobuf/api.pb.h \ + google/protobuf/any.h \ google/protobuf/arena.h \ google/protobuf/arenastring.h \ google/protobuf/descriptor_database.h \ @@ -186,6 +187,7 @@ libprotobuf_la_SOURCES = \ $(libprotobuf_lite_la_SOURCES) \ google/protobuf/any.pb.cc \ google/protobuf/api.pb.cc \ + google/protobuf/any.cc \ google/protobuf/descriptor.cc \ google/protobuf/descriptor_database.cc \ google/protobuf/descriptor.pb.cc \ @@ -264,6 +266,8 @@ libprotoc_la_SOURCES = \ google/protobuf/compiler/java/java_enum.cc \ google/protobuf/compiler/java/java_enum_field.cc \ google/protobuf/compiler/java/java_enum_field.h \ + google/protobuf/compiler/java/java_enum_field_lite.cc \ + google/protobuf/compiler/java/java_enum_field_lite.h \ google/protobuf/compiler/java/java_enum.h \ google/protobuf/compiler/java/java_extension.cc \ google/protobuf/compiler/java/java_extension.h \ @@ -278,22 +282,38 @@ libprotoc_la_SOURCES = \ google/protobuf/compiler/java/java_helpers.h \ google/protobuf/compiler/java/java_lazy_message_field.cc \ google/protobuf/compiler/java/java_lazy_message_field.h \ + google/protobuf/compiler/java/java_lazy_message_field_lite.cc\ + google/protobuf/compiler/java/java_lazy_message_field_lite.h \ google/protobuf/compiler/java/java_map_field.cc \ google/protobuf/compiler/java/java_map_field.h \ + google/protobuf/compiler/java/java_map_field_lite.cc \ + google/protobuf/compiler/java/java_map_field_lite.h \ google/protobuf/compiler/java/java_message.cc \ + google/protobuf/compiler/java/java_message_lite.cc \ + google/protobuf/compiler/java/java_message_builder.cc \ + google/protobuf/compiler/java/java_message_builder_lite.cc \ google/protobuf/compiler/java/java_message_field.cc \ google/protobuf/compiler/java/java_message_field.h \ + google/protobuf/compiler/java/java_message_field_lite.cc \ + google/protobuf/compiler/java/java_message_field_lite.h \ google/protobuf/compiler/java/java_message.h \ + google/protobuf/compiler/java/java_message_lite.h \ + google/protobuf/compiler/java/java_message_builder.h \ + google/protobuf/compiler/java/java_message_builder_lite.h \ google/protobuf/compiler/java/java_name_resolver.cc \ google/protobuf/compiler/java/java_name_resolver.h \ google/protobuf/compiler/java/java_primitive_field.cc \ google/protobuf/compiler/java/java_primitive_field.h \ + google/protobuf/compiler/java/java_primitive_field_lite.cc \ + google/protobuf/compiler/java/java_primitive_field_lite.h \ google/protobuf/compiler/java/java_shared_code_generator.cc \ google/protobuf/compiler/java/java_shared_code_generator.h \ google/protobuf/compiler/java/java_service.cc \ google/protobuf/compiler/java/java_service.h \ google/protobuf/compiler/java/java_string_field.cc \ google/protobuf/compiler/java/java_string_field.h \ + google/protobuf/compiler/java/java_string_field_lite.cc \ + google/protobuf/compiler/java/java_string_field_lite.h \ google/protobuf/compiler/java/java_doc_comment.cc \ google/protobuf/compiler/java/java_doc_comment.h \ google/protobuf/compiler/javanano/javanano_enum.cc \ @@ -381,6 +401,7 @@ protoc_SOURCES = google/protobuf/compiler/main.cc # Tests ============================================================== protoc_inputs = \ + google/protobuf/any_test.proto \ google/protobuf/map_lite_unittest.proto \ google/protobuf/map_proto2_unittest.proto \ google/protobuf/map_unittest.proto \ @@ -419,6 +440,7 @@ EXTRA_DIST = \ google/protobuf/testdata/golden_message_proto3 \ google/protobuf/testdata/golden_packed_fields_message \ google/protobuf/testdata/bad_utf8_string \ + google/protobuf/testdata/map_test_data.txt \ google/protobuf/testdata/text_format_unittest_data.txt \ google/protobuf/testdata/text_format_unittest_data_oneof_implemented.txt \ google/protobuf/testdata/text_format_unittest_data_pointy.txt \ @@ -443,6 +465,8 @@ protoc_lite_outputs = \ protoc_outputs = \ $(protoc_lite_outputs) \ + google/protobuf/any_test.pb.cc \ + google/protobuf/any_test.pb.h \ google/protobuf/map_proto2_unittest.pb.cc \ google/protobuf/map_proto2_unittest.pb.h \ google/protobuf/map_unittest.pb.cc \ @@ -543,6 +567,7 @@ protobuf_test_SOURCES = \ google/protobuf/stubs/stringprintf_unittest.cc \ google/protobuf/stubs/template_util_unittest.cc \ google/protobuf/stubs/type_traits_unittest.cc \ + google/protobuf/any_test.cc \ google/protobuf/arenastring_unittest.cc \ google/protobuf/arena_unittest.cc \ google/protobuf/descriptor_database_unittest.cc \ @@ -604,6 +629,8 @@ nodist_protobuf_lazy_descriptor_test_SOURCES = $(protoc_outputs) protobuf_lite_test_LDADD = $(PTHREAD_LIBS) libprotobuf-lite.la protobuf_lite_test_CXXFLAGS = $(NO_OPT_CXXFLAGS) protobuf_lite_test_SOURCES = \ + google/protobuf/arena_test_util.cc \ + google/protobuf/arena_test_util.h \ google/protobuf/lite_unittest.cc \ google/protobuf/map_lite_test_util.cc \ google/protobuf/map_lite_test_util.h \ diff --git a/src/google/protobuf/any.cc b/src/google/protobuf/any.cc new file mode 100644 index 00000000..c66fdfad --- /dev/null +++ b/src/google/protobuf/any.cc @@ -0,0 +1,100 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include + +namespace google { +namespace protobuf { +namespace internal { + +namespace { +string GetTypeUrl(const Descriptor* message) { + return string(kTypeGoogleApisComPrefix) + message->full_name(); +} + +} // namespace + +const char kAnyFullTypeName[] = "google.protobuf.Any"; +const char kTypeGoogleApisComPrefix[] = "type.googleapis.com/"; + +AnyMetadata::AnyMetadata(UrlType* type_url, ValueType* value) + : type_url_(type_url), value_(value) { +} + +void AnyMetadata::PackFrom(const Message& message) { + type_url_->SetNoArena(&::google::protobuf::internal::GetEmptyString(), + GetTypeUrl(message.GetDescriptor())); + message.SerializeToString(value_->MutableNoArena( + &::google::protobuf::internal::GetEmptyStringAlreadyInited())); +} + +bool AnyMetadata::UnpackTo(Message* message) const { + if (!InternalIs(message->GetDescriptor())) { + return false; + } + return message->ParseFromString( + value_->GetNoArena(&::google::protobuf::internal::GetEmptyString())); +} + +bool AnyMetadata::InternalIs(const Descriptor* descriptor) const { + return type_url_->GetNoArena( + &::google::protobuf::internal::GetEmptyString()) == + GetTypeUrl(descriptor); +} + +bool ParseAnyTypeUrl(const string& type_url, string* full_type_name) { + const int prefix_len = strlen(kTypeGoogleApisComPrefix); + if (strncmp(type_url.c_str(), kTypeGoogleApisComPrefix, prefix_len) == 0) { + full_type_name->assign(type_url.data() + prefix_len, + type_url.size() - prefix_len); + return true; + } + return true; +} + + +bool GetAnyFieldDescriptors(const Message& message, + const FieldDescriptor** type_url_field, + const FieldDescriptor** value_field) { + const Descriptor* descriptor = message.GetDescriptor(); + if (descriptor->full_name() != kAnyFullTypeName) { + return false; + } + *type_url_field = descriptor->FindFieldByNumber(1); + *value_field = descriptor->FindFieldByNumber(2); + return (*type_url_field != NULL && + (*type_url_field)->type() == FieldDescriptor::TYPE_STRING && + *value_field != NULL && + (*value_field)->type() == FieldDescriptor::TYPE_BYTES); +} + +} // namespace internal +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/any.h b/src/google/protobuf/any.h new file mode 100644 index 00000000..757b45aa --- /dev/null +++ b/src/google/protobuf/any.h @@ -0,0 +1,90 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef GOOGLE_PROTOBUF_ANY_H__ +#define GOOGLE_PROTOBUF_ANY_H__ + +#include + +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace internal { + +// Helper class used to implement google::protobuf::Any. +class AnyMetadata { + typedef ArenaStringPtr UrlType; + typedef ArenaStringPtr ValueType; + public: + // AnyMetadata does not take ownership of "type_url" and "value". + AnyMetadata(UrlType* type_url, ValueType* value); + + void PackFrom(const Message& message); + + bool UnpackTo(Message* message) const; + + template + bool Is() const { + return InternalIs(T::default_instance().GetDescriptor()); + } + + private: + bool InternalIs(const Descriptor* message) const; + + UrlType* type_url_; + ValueType* value_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(AnyMetadata); +}; + +extern const char kAnyFullTypeName[]; // "google.protobuf.Any". +extern const char kTypeGoogleApisComPrefix[]; // "type.googleapis.com/". + +// Get the proto type name from Any::type_url value. For example, passing +// "type.googleapis.com/rpc.QueryOrigin" will return "rpc.QueryOrigin" in +// *full_type_name. Returns false if type_url does not start with +// "type.googleapis.com". +bool ParseAnyTypeUrl(const string& type_url, string* full_type_name); + +// See if message is of type google.protobuf.Any, if so, return the descriptors +// for "type_url" and "value" fields. +bool GetAnyFieldDescriptors(const Message& message, + const FieldDescriptor** type_url_field, + const FieldDescriptor** value_field); + +} // namespace internal +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_ANY_H__ diff --git a/src/google/protobuf/any.pb.cc b/src/google/protobuf/any.pb.cc index 75cc8754..2c492b04 100644 --- a/src/google/protobuf/any.pb.cc +++ b/src/google/protobuf/any.pb.cc @@ -111,13 +111,21 @@ static void MergeFromFail(int line) { // =================================================================== +void Any::PackFrom(const ::google::protobuf::Message& message) { + _any_metadata_.PackFrom(message); +} + +bool Any::UnpackTo(::google::protobuf::Message* message) const { + return _any_metadata_.UnpackTo(message); +} + #ifndef _MSC_VER const int Any::kTypeUrlFieldNumber; const int Any::kValueFieldNumber; #endif // !_MSC_VER Any::Any() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL), _any_metadata_(&type_url_, &value_) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.Any) } @@ -128,7 +136,8 @@ void Any::InitAsDefaultInstance() { Any::Any(const Any& from) : ::google::protobuf::Message(), - _internal_metadata_(NULL) { + _internal_metadata_(NULL), + _any_metadata_(&type_url_, &value_) { SharedCtor(); MergeFrom(from); // @@protoc_insertion_point(copy_constructor:google.protobuf.Any) @@ -316,9 +325,9 @@ int Any::ByteSize() const { void Any::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const Any* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const Any* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -378,7 +387,7 @@ void Any::InternalSwap(Any* other) { // Any // optional string type_url = 1; - void Any::clear_type_url() { +void Any::clear_type_url() { type_url_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } const ::std::string& Any::type_url() const { @@ -421,7 +430,7 @@ void Any::InternalSwap(Any* other) { } // optional bytes value = 2; - void Any::clear_value() { +void Any::clear_value() { value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } const ::std::string& Any::value() const { diff --git a/src/google/protobuf/any.pb.h b/src/google/protobuf/any.pb.h index b2c238c1..c324c4af 100644 --- a/src/google/protobuf/any.pb.h +++ b/src/google/protobuf/any.pb.h @@ -27,6 +27,7 @@ #include #include #include +#include "google/protobuf/any.h" // @@protoc_insertion_point(includes) namespace google { @@ -56,6 +57,14 @@ class LIBPROTOBUF_EXPORT Any : public ::google::protobuf::Message { static const ::google::protobuf::Descriptor* descriptor(); static const Any& default_instance(); + // implements Any ----------------------------------------------- + + void PackFrom(const ::google::protobuf::Message& message); + bool UnpackTo(::google::protobuf::Message* message) const; + template bool Is() const { + return _any_metadata_.Is(); + } + void Swap(Any* other); // implements Message ---------------------------------------------- @@ -127,6 +136,7 @@ class LIBPROTOBUF_EXPORT Any : public ::google::protobuf::Message { ::google::protobuf::internal::ArenaStringPtr type_url_; ::google::protobuf::internal::ArenaStringPtr value_; mutable int _cached_size_; + ::google::protobuf::internal::AnyMetadata _any_metadata_; friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fany_2eproto(); friend void protobuf_AssignDesc_google_2fprotobuf_2fany_2eproto(); friend void protobuf_ShutdownFile_google_2fprotobuf_2fany_2eproto(); diff --git a/src/google/protobuf/any_test.cc b/src/google/protobuf/any_test.cc new file mode 100644 index 00000000..1bfaa63d --- /dev/null +++ b/src/google/protobuf/any_test.cc @@ -0,0 +1,89 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include + +namespace google { +namespace protobuf { +namespace { + +TEST(AnyTest, TestPackAndUnpack) { + protobuf_unittest::TestAny submessage; + submessage.set_int32_value(12345); + protobuf_unittest::TestAny message; + message.mutable_any_value()->PackFrom(submessage); + + string data = message.SerializeAsString(); + + ASSERT_TRUE(message.ParseFromString(data)); + EXPECT_TRUE(message.has_any_value()); + ASSERT_TRUE(message.any_value().UnpackTo(&submessage)); + EXPECT_EQ(12345, submessage.int32_value()); +} + +TEST(AnyTest, TestPackAndUnpackAny) { + // We can pack a Any message inside another Any message. + protobuf_unittest::TestAny submessage; + submessage.set_int32_value(12345); + google::protobuf::Any any; + any.PackFrom(submessage); + protobuf_unittest::TestAny message; + message.mutable_any_value()->PackFrom(any); + + string data = message.SerializeAsString(); + + ASSERT_TRUE(message.ParseFromString(data)); + EXPECT_TRUE(message.has_any_value()); + ASSERT_TRUE(message.any_value().UnpackTo(&any)); + ASSERT_TRUE(any.UnpackTo(&submessage)); + EXPECT_EQ(12345, submessage.int32_value()); +} + +TEST(AnyTest, TestIs) { + protobuf_unittest::TestAny submessage; + submessage.set_int32_value(12345); + google::protobuf::Any any; + any.PackFrom(submessage); + ASSERT_TRUE(any.ParseFromString(any.SerializeAsString())); + EXPECT_TRUE(any.Is()); + EXPECT_FALSE(any.Is()); + + protobuf_unittest::TestAny message; + message.mutable_any_value()->PackFrom(any); + ASSERT_TRUE(message.ParseFromString(message.SerializeAsString())); + EXPECT_FALSE(message.any_value().Is()); + EXPECT_TRUE(message.any_value().Is()); +} + +} // namespace +} // namespace protobuf + +} // namespace google diff --git a/src/google/protobuf/any_test.proto b/src/google/protobuf/any_test.proto new file mode 100644 index 00000000..0c5b30ba --- /dev/null +++ b/src/google/protobuf/any_test.proto @@ -0,0 +1,41 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package protobuf_unittest; + +import "google/protobuf/any.proto"; + +message TestAny { + int32 int32_value = 1; + google.protobuf.Any any_value = 2; + repeated google.protobuf.Any repeated_any_value = 3; +} diff --git a/src/google/protobuf/api.pb.cc b/src/google/protobuf/api.pb.cc index b34ce803..434320dc 100644 --- a/src/google/protobuf/api.pb.cc +++ b/src/google/protobuf/api.pb.cc @@ -162,7 +162,7 @@ const int Api::kSourceContextFieldNumber; #endif // !_MSC_VER Api::Api() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.Api) } @@ -230,7 +230,7 @@ Api* Api::New(::google::protobuf::Arena* arena) const { void Api::Clear() { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); version_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); - if (source_context_ != NULL) delete source_context_; + if (GetArenaNoVirtual() == NULL && source_context_ != NULL) delete source_context_; source_context_ = NULL; methods_.Clear(); options_.Clear(); @@ -266,26 +266,31 @@ bool Api::MergePartialFromCodedStream( case 2: { if (tag == 18) { parse_methods: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_methods: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_methods())); } else { goto handle_unusual; } - if (input->ExpectTag(18)) goto parse_methods; - if (input->ExpectTag(26)) goto parse_options; + if (input->ExpectTag(18)) goto parse_loop_methods; + if (input->ExpectTag(26)) goto parse_loop_options; + input->UnsafeDecrementRecursionDepth(); break; } // repeated .google.protobuf.Option options = 3; case 3: { if (tag == 26) { - parse_options: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_options: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_options())); } else { goto handle_unusual; } - if (input->ExpectTag(26)) goto parse_options; + if (input->ExpectTag(26)) goto parse_loop_options; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectTag(34)) goto parse_version; break; } @@ -483,9 +488,9 @@ int Api::ByteSize() const { void Api::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const Api* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const Api* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -553,7 +558,7 @@ void Api::InternalSwap(Api* other) { // Api // optional string name = 1; - void Api::clear_name() { +void Api::clear_name() { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } const ::std::string& Api::name() const { @@ -596,10 +601,10 @@ void Api::InternalSwap(Api* other) { } // repeated .google.protobuf.Method methods = 2; - int Api::methods_size() const { +int Api::methods_size() const { return methods_.size(); } - void Api::clear_methods() { +void Api::clear_methods() { methods_.Clear(); } const ::google::protobuf::Method& Api::methods(int index) const { @@ -626,10 +631,10 @@ Api::mutable_methods() { } // repeated .google.protobuf.Option options = 3; - int Api::options_size() const { +int Api::options_size() const { return options_.size(); } - void Api::clear_options() { +void Api::clear_options() { options_.Clear(); } const ::google::protobuf::Option& Api::options(int index) const { @@ -656,7 +661,7 @@ Api::mutable_options() { } // optional string version = 4; - void Api::clear_version() { +void Api::clear_version() { version_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } const ::std::string& Api::version() const { @@ -699,11 +704,11 @@ Api::mutable_options() { } // optional .google.protobuf.SourceContext source_context = 5; - bool Api::has_source_context() const { +bool Api::has_source_context() const { return !_is_default_instance_ && source_context_ != NULL; } - void Api::clear_source_context() { - if (source_context_ != NULL) delete source_context_; +void Api::clear_source_context() { + if (GetArenaNoVirtual() == NULL && source_context_ != NULL) delete source_context_; source_context_ = NULL; } const ::google::protobuf::SourceContext& Api::source_context() const { @@ -749,7 +754,7 @@ const int Method::kOptionsFieldNumber; #endif // !_MSC_VER Method::Method() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.Method) } @@ -929,12 +934,15 @@ bool Method::MergePartialFromCodedStream( case 6: { if (tag == 50) { parse_options: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_options: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_options())); } else { goto handle_unusual; } - if (input->ExpectTag(50)) goto parse_options; + if (input->ExpectTag(50)) goto parse_loop_options; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectAtEnd()) goto success; break; } @@ -1119,9 +1127,9 @@ int Method::ByteSize() const { void Method::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const Method* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const Method* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -1196,7 +1204,7 @@ void Method::InternalSwap(Method* other) { // Method // optional string name = 1; - void Method::clear_name() { +void Method::clear_name() { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } const ::std::string& Method::name() const { @@ -1239,7 +1247,7 @@ void Method::InternalSwap(Method* other) { } // optional string request_type_url = 2; - void Method::clear_request_type_url() { +void Method::clear_request_type_url() { request_type_url_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } const ::std::string& Method::request_type_url() const { @@ -1282,7 +1290,7 @@ void Method::InternalSwap(Method* other) { } // optional bool request_streaming = 3; - void Method::clear_request_streaming() { +void Method::clear_request_streaming() { request_streaming_ = false; } bool Method::request_streaming() const { @@ -1296,7 +1304,7 @@ void Method::InternalSwap(Method* other) { } // optional string response_type_url = 4; - void Method::clear_response_type_url() { +void Method::clear_response_type_url() { response_type_url_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } const ::std::string& Method::response_type_url() const { @@ -1339,7 +1347,7 @@ void Method::InternalSwap(Method* other) { } // optional bool response_streaming = 5; - void Method::clear_response_streaming() { +void Method::clear_response_streaming() { response_streaming_ = false; } bool Method::response_streaming() const { @@ -1353,10 +1361,10 @@ void Method::InternalSwap(Method* other) { } // repeated .google.protobuf.Option options = 6; - int Method::options_size() const { +int Method::options_size() const { return options_.size(); } - void Method::clear_options() { +void Method::clear_options() { options_.Clear(); } const ::google::protobuf::Option& Method::options(int index) const { diff --git a/src/google/protobuf/api.pb.h b/src/google/protobuf/api.pb.h index f459af0f..985adc5d 100644 --- a/src/google/protobuf/api.pb.h +++ b/src/google/protobuf/api.pb.h @@ -466,7 +466,7 @@ inline bool Api::has_source_context() const { return !_is_default_instance_ && source_context_ != NULL; } inline void Api::clear_source_context() { - if (source_context_ != NULL) delete source_context_; + if (GetArenaNoVirtual() == NULL && source_context_ != NULL) delete source_context_; source_context_ = NULL; } inline const ::google::protobuf::SourceContext& Api::source_context() const { @@ -690,6 +690,8 @@ Method::mutable_options() { } #endif // !PROTOBUF_INLINE_NOT_IN_HEADERS +// ------------------------------------------------------------------- + // @@protoc_insertion_point(namespace_scope) diff --git a/src/google/protobuf/arena.cc b/src/google/protobuf/arena.cc index f7059d26..96009645 100755 --- a/src/google/protobuf/arena.cc +++ b/src/google/protobuf/arena.cc @@ -29,7 +29,6 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include -#include #ifdef ADDRESS_SANITIZER #include @@ -155,10 +154,16 @@ void Arena::AddListNode(void* elem, void (*cleanup)(void*)) { reinterpret_cast(node))); } -void* Arena::AllocateAligned(size_t n) { +void* Arena::AllocateAligned(const std::type_info* allocated, size_t n) { // Align n to next multiple of 8 (from Hacker's Delight, Chapter 3.) n = (n + 7) & -8; + // Monitor allocation if needed. + if (GOOGLE_PREDICT_FALSE(hooks_cookie_ != NULL) && + options_.on_arena_allocation != NULL) { + options_.on_arena_allocation(allocated, n, hooks_cookie_); + } + // If this thread already owns a block in this arena then try to use that. // This fast path optimizes the case where multiple threads allocate from the // same arena. diff --git a/src/google/protobuf/arena.h b/src/google/protobuf/arena.h index b48bef92..4adcd677 100644 --- a/src/google/protobuf/arena.h +++ b/src/google/protobuf/arena.h @@ -28,12 +28,12 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// This header is logically internal, but is made public because it is used -// from protocol-compiler-generated code, which may reside in other components. - #ifndef GOOGLE_PROTOBUF_ARENA_H__ #define GOOGLE_PROTOBUF_ARENA_H__ +#if __cplusplus >= 201103L +#include +#endif #include #include @@ -113,10 +113,13 @@ struct ArenaOptions { void (*on_arena_reset)(Arena* arena, void* cookie, uint64 space_used); void (*on_arena_destruction)(Arena* arena, void* cookie, uint64 space_used); - // type_name is promised to be a static string - its lifetime extends to - // match program's lifetime. - void (*on_arena_allocation)(const char* type_name, uint64 alloc_size, - Arena* arena, void* cookie); + // type_info is promised to be static - its lifetime extends to + // match program's lifetime (It is given by typeid operator). + // Note: typeid(void) will be passed as allocated_type every time we + // intentionally want to avoid monitoring an allocation. (i.e. internal + // allocations for managing the arena) + void (*on_arena_allocation)(const std::type_info* allocated_type, + uint64 alloc_size, void* cookie); ArenaOptions() : start_block_size(kDefaultStartBlockSize), @@ -137,6 +140,14 @@ struct ArenaOptions { static const size_t kDefaultMaxBlockSize = 8192; }; +// Support for non-RTTI environments. (The metrics hooks API uses type +// information.) +#ifndef GOOGLE_PROTOBUF_NO_RTTI +#define RTTI_TYPE_ID(type) (&typeid(type)) +#else +#define RTTI_TYPE_ID(type) (NULL) +#endif + // Arena allocator. Arena allocation replaces ordinary (heap-based) allocation // with new/delete, and improves performance by aggregating allocations into // larger blocks and freeing allocations all at once. Protocol messages are @@ -146,6 +157,44 @@ struct ArenaOptions { // This is a thread-safe implementation: multiple threads may allocate from the // arena concurrently. Destruction is not thread-safe and the destructing // thread must synchronize with users of the arena first. +// +// An arena provides two allocation interfaces: CreateMessage, which works +// for arena-enabled proto2 message types as well as other types that satisfy +// the appropriate protocol (described below), and Create, which works for +// any arbitrary type T. CreateMessage is better when the type T supports it, +// because this interface (i) passes the arena pointer to the created object so +// that its sub-objects and internal allocations can use the arena too, and (ii) +// elides the object's destructor call when possible. Create does not place +// any special requirements on the type T, and will invoke the object's +// destructor when the arena is destroyed. +// +// The arena message allocation protocol, required by CreateMessage, is as +// follows: +// +// - The type T must have (at least) two constructors: a constructor with no +// arguments, called when a T is allocated on the heap; and a constructor with +// a google::protobuf::Arena* argument, called when a T is allocated on an arena. If the +// second constructor is called with a NULL arena pointer, it must be +// equivalent to invoking the first (no-argument) constructor. +// +// - The type T must have a particular type trait: a nested type +// |InternalArenaConstructable_|. This is usually a typedef to |void|. If no +// such type trait exists, then the instantiation CreateMessage will fail +// to compile. +// +// - The type T *may* have the type trait |DestructorSkippable_|. If this type +// trait is present in the type, then its destructor will not be called if and +// only if it was passed a non-NULL arena pointer. If this type trait is not +// present on the type, then its destructor is always called when the +// containing arena is destroyed. +// +// - One- and two-user-argument forms of CreateMessage() also exist that +// forward these constructor arguments to T's constructor: for example, +// CreateMessage(Arena*, arg1, arg2) forwards to a constructor T(Arena*, +// arg1, arg2). +// +// This protocol is implemented by all arena-enabled proto2 message classes as +// well as RepeatedPtrField. class LIBPROTOBUF_EXPORT Arena { public: // Arena constructor taking custom options. See ArenaOptions below for @@ -172,8 +221,10 @@ class LIBPROTOBUF_EXPORT Arena { // compilation error will occur. // // RepeatedField and RepeatedPtrField may also be instantiated directly on an - // arena with this method: they act as "arena-capable message types" for the - // purposes of the Arena API. + // arena with this method. + // + // This function also accepts any type T that satisfies the arena message + // allocation protocol, documented above. template GOOGLE_ATTRIBUTE_ALWAYS_INLINE static T* CreateMessage(::google::protobuf::Arena* arena) { if (arena == NULL) { @@ -183,17 +234,55 @@ class LIBPROTOBUF_EXPORT Arena { } } + // One-argument form of CreateMessage. This is useful for constructing objects + // that implement the arena message construction protocol described above but + // take additional constructor arguments. + template GOOGLE_ATTRIBUTE_ALWAYS_INLINE + static T* CreateMessage(::google::protobuf::Arena* arena, const Arg& arg) { + if (arena == NULL) { + return new T(NULL, arg); + } else { + return arena->CreateMessageInternal(static_cast(0), + arg); + } + } + + // Two-argument form of CreateMessage. This is useful for constructing objects + // that implement the arena message construction protocol described above but + // take additional constructor arguments. + template GOOGLE_ATTRIBUTE_ALWAYS_INLINE + static T* CreateMessage(::google::protobuf::Arena* arena, + const Arg1& arg1, + const Arg2& arg2) { + if (arena == NULL) { + return new T(NULL, arg1, arg2); + } else { + return arena->CreateMessageInternal(static_cast(0), + arg1, arg2); + } + } + // API to create any objects on the arena. Note that only the object will // be created on the arena; the underlying ptrs (in case of a proto2 message) // will be still heap allocated. Proto messages should usually be allocated // with CreateMessage() instead. + // + // Note that even if T satisfies the arena message construction protocol + // (InternalArenaConstructable_ trait and optional DestructorSkippable_ + // trait), as described above, this function does not follow the protocol; + // instead, it treats T as a black-box type, just as if it did not have these + // traits. Specifically, T's constructor arguments will always be only those + // passed to Create() -- no additional arena pointer is implicitly added. + // Furthermore, the destructor will always be called at arena destruction time + // (unless the destructor is trivial). Hence, from T's point of view, it is as + // if the object were allocated on the heap (except that the underlying memory + // is obtained from the arena). template GOOGLE_ATTRIBUTE_ALWAYS_INLINE static T* Create(::google::protobuf::Arena* arena) { if (arena == NULL) { return new T(); } else { - return arena->CreateInternal( - SkipDeleteList(static_cast(0))); + return arena->CreateInternal(google::protobuf::internal::has_trivial_destructor::value); } } @@ -203,7 +292,7 @@ class LIBPROTOBUF_EXPORT Arena { if (arena == NULL) { return new T(arg); } else { - return arena->CreateInternal(SkipDeleteList(static_cast(0)), + return arena->CreateInternal(google::protobuf::internal::has_trivial_destructor::value, arg); } } @@ -214,9 +303,8 @@ class LIBPROTOBUF_EXPORT Arena { if (arena == NULL) { return new T(arg1, arg2); } else { - return arena->CreateInternal(SkipDeleteList(static_cast(0)), - arg1, - arg2); + return arena->CreateInternal(google::protobuf::internal::has_trivial_destructor::value, + arg1, arg2); } } @@ -229,10 +317,8 @@ class LIBPROTOBUF_EXPORT Arena { if (arena == NULL) { return new T(arg1, arg2, arg3); } else { - return arena->CreateInternal(SkipDeleteList(static_cast(0)), - arg1, - arg2, - arg3); + return arena->CreateInternal(google::protobuf::internal::has_trivial_destructor::value, + arg1, arg2, arg3); } } @@ -246,20 +332,95 @@ class LIBPROTOBUF_EXPORT Arena { if (arena == NULL) { return new T(arg1, arg2, arg3, arg4); } else { - return arena->CreateInternal(SkipDeleteList(static_cast(0)), - arg1, - arg2, - arg3, - arg4); + return arena->CreateInternal(google::protobuf::internal::has_trivial_destructor::value, + arg1, arg2, arg3, arg4); } } - // Create an array of object type T on the arena. Type T must have a trivial - // constructor, as it will not be invoked when created on the arena. + // Version of the above with five constructor arguments for the created + // object. + template + GOOGLE_ATTRIBUTE_ALWAYS_INLINE static T* Create(::google::protobuf::Arena* arena, + const Arg1& arg1, const Arg2& arg2, + const Arg3& arg3, const Arg4& arg4, + const Arg5& arg5) { + if (arena == NULL) { + return new T(arg1, arg2, arg3, arg4, arg5); + } else { + return arena->CreateInternal(google::protobuf::internal::has_trivial_destructor::value, + arg1, arg2, arg3, arg4, arg5); + } + } + + // Version of the above with six constructor arguments for the created + // object. + template + GOOGLE_ATTRIBUTE_ALWAYS_INLINE static T* Create(::google::protobuf::Arena* arena, + const Arg1& arg1, const Arg2& arg2, + const Arg3& arg3, const Arg4& arg4, + const Arg5& arg5, const Arg6& arg6) { + if (arena == NULL) { + return new T(arg1, arg2, arg3, arg4, arg5, arg6); + } else { + return arena->CreateInternal(google::protobuf::internal::has_trivial_destructor::value, + arg1, arg2, arg3, arg4, arg5, arg6); + } + } + + // Version of the above with seven constructor arguments for the created + // object. + template + GOOGLE_ATTRIBUTE_ALWAYS_INLINE static T* Create(::google::protobuf::Arena* arena, + const Arg1& arg1, const Arg2& arg2, + const Arg3& arg3, const Arg4& arg4, + const Arg5& arg5, const Arg6& arg6, + const Arg7& arg7) { + if (arena == NULL) { + return new T(arg1, arg2, arg3, arg4, arg5, arg6, arg7); + } else { + return arena->CreateInternal(google::protobuf::internal::has_trivial_destructor::value, + arg1, arg2, arg3, arg4, arg5, arg6, arg7); + } + } + + // Version of the above with eight constructor arguments for the created + // object. + template + GOOGLE_ATTRIBUTE_ALWAYS_INLINE static T* Create(::google::protobuf::Arena* arena, + const Arg1& arg1, const Arg2& arg2, + const Arg3& arg3, const Arg4& arg4, + const Arg5& arg5, const Arg6& arg6, + const Arg7& arg7, const Arg8& arg8) { + if (arena == NULL) { + return new T(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); + } else { + return arena->CreateInternal( + google::protobuf::internal::has_trivial_destructor::value, + arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); + } + } + + // Create an array of object type T on the arena *without* invoking the + // constructor of T. If `arena` is null, then the return value should be freed + // with `delete[] x;` (or `::operator delete[](x);`). + // To ensure safe uses, this function checks at compile time + // (when compiled as C++11) that T is trivially default-constructible and + // trivially destructible. template GOOGLE_ATTRIBUTE_ALWAYS_INLINE static T* CreateArray(::google::protobuf::Arena* arena, size_t num_elements) { +#if __cplusplus >= 201103L + static_assert(std::is_trivially_default_constructible::value, + "CreateArray requires a trivially constructible type"); + static_assert(std::is_trivially_destructible::value, + "CreateArray requires a trivially destructible type"); +#endif if (arena == NULL) { - return new T[num_elements]; + return static_cast(::operator new[](num_elements * sizeof(T))); } else { return arena->CreateInternalRawArray(num_elements); } @@ -374,27 +535,26 @@ class LIBPROTOBUF_EXPORT Arena { // wrap them in static functions. static ThreadCache& thread_cache(); #elif defined(GOOGLE_PROTOBUF_OS_ANDROID) || defined(GOOGLE_PROTOBUF_OS_IPHONE) - // Android ndk does not support __thread keyword so we use a custom thread + // Android ndk does not support GOOGLE_THREAD_LOCAL keyword so we use a custom thread // local storage class we implemented. - // iOS also does not support the __thread keyword. + // iOS also does not support the GOOGLE_THREAD_LOCAL keyword. static ThreadCache& thread_cache(); #else static GOOGLE_THREAD_LOCAL ThreadCache thread_cache_; static ThreadCache& thread_cache() { return thread_cache_; } #endif - // SFINAE for skipping addition to delete list for a Type. This is mainly to - // skip proto2/proto1 message objects with cc_enable_arenas=true from being - // part of the delete list. Also, note, compiler will optimize out the branch - // in CreateInternal. - // + // SFINAE for skipping addition to delete list for a message type when created + // with CreateMessage. This is mainly to skip proto2/proto1 message objects + // with cc_enable_arenas=true from being part of the delete list. Also, note, + // compiler will optimize out the branch in CreateInternal. template static inline bool SkipDeleteList(typename T::DestructorSkippable_*) { return true; } - // For non message objects, we skip addition to delete list if the object has - // a trivial destructor. + // For message objects that don't have the DestructorSkippable_ trait, we + // always add to the delete list. template static inline bool SkipDeleteList(...) { return google::protobuf::internal::has_trivial_destructor::value; @@ -419,14 +579,15 @@ class LIBPROTOBUF_EXPORT Arena { // Just allocate the required size for the given type assuming the // type has a trivial constructor. template GOOGLE_ATTRIBUTE_ALWAYS_INLINE - inline T* CreateInternalRawArray(uint32 num_elements) { - return static_cast(AllocateAligned(sizeof(T) * num_elements)); + inline T* CreateInternalRawArray(size_t num_elements) { + return static_cast( + AllocateAligned(RTTI_TYPE_ID(T), sizeof(T) * num_elements)); } template GOOGLE_ATTRIBUTE_ALWAYS_INLINE inline T* CreateInternal( bool skip_explicit_ownership) { - T* t = new (AllocateAligned(sizeof(T))) T(); + T* t = new (AllocateAligned(RTTI_TYPE_ID(T), sizeof(T))) T(); if (!skip_explicit_ownership) { AddListNode(t, &internal::arena_destruct_object); } @@ -436,7 +597,7 @@ class LIBPROTOBUF_EXPORT Arena { template GOOGLE_ATTRIBUTE_ALWAYS_INLINE inline T* CreateInternal( bool skip_explicit_ownership, const Arg& arg) { - T* t = new (AllocateAligned(sizeof(T))) T(arg); + T* t = new (AllocateAligned(RTTI_TYPE_ID(T), sizeof(T))) T(arg); if (!skip_explicit_ownership) { AddListNode(t, &internal::arena_destruct_object); } @@ -446,7 +607,7 @@ class LIBPROTOBUF_EXPORT Arena { template GOOGLE_ATTRIBUTE_ALWAYS_INLINE inline T* CreateInternal( bool skip_explicit_ownership, const Arg1& arg1, const Arg2& arg2) { - T* t = new (AllocateAligned(sizeof(T))) T(arg1, arg2); + T* t = new (AllocateAligned(RTTI_TYPE_ID(T), sizeof(T))) T(arg1, arg2); if (!skip_explicit_ownership) { AddListNode(t, &internal::arena_destruct_object); } @@ -458,7 +619,8 @@ class LIBPROTOBUF_EXPORT Arena { const Arg1& arg1, const Arg2& arg2, const Arg3& arg3) { - T* t = new (AllocateAligned(sizeof(T))) T(arg1, arg2, arg3); + T* t = new (AllocateAligned(RTTI_TYPE_ID(T), sizeof(T))) + T(arg1, arg2, arg3); if (!skip_explicit_ownership) { AddListNode(t, &internal::arena_destruct_object); } @@ -472,7 +634,79 @@ class LIBPROTOBUF_EXPORT Arena { const Arg2& arg2, const Arg3& arg3, const Arg4& arg4) { - T* t = new (AllocateAligned(sizeof(T))) T(arg1, arg2, arg3, arg4); + T* t = new (AllocateAligned(RTTI_TYPE_ID(T), sizeof(T))) + T(arg1, arg2, arg3, arg4); + if (!skip_explicit_ownership) { + AddListNode(t, &internal::arena_destruct_object); + } + return t; + } + + template + GOOGLE_ATTRIBUTE_ALWAYS_INLINE inline T* CreateInternal(bool skip_explicit_ownership, + const Arg1& arg1, + const Arg2& arg2, + const Arg3& arg3, + const Arg4& arg4, + const Arg5& arg5) { + T* t = new (AllocateAligned(RTTI_TYPE_ID(T), sizeof(T))) + T(arg1, arg2, arg3, arg4, arg5); + if (!skip_explicit_ownership) { + AddListNode(t, &internal::arena_destruct_object); + } + return t; + } + + template + GOOGLE_ATTRIBUTE_ALWAYS_INLINE inline T* CreateInternal(bool skip_explicit_ownership, + const Arg1& arg1, + const Arg2& arg2, + const Arg3& arg3, + const Arg4& arg4, + const Arg5& arg5, + const Arg6& arg6) { + T* t = new (AllocateAligned(RTTI_TYPE_ID(T), sizeof(T))) + T(arg1, arg2, arg3, arg4, arg5, arg6); + if (!skip_explicit_ownership) { + AddListNode(t, &internal::arena_destruct_object); + } + return t; + } + + template + GOOGLE_ATTRIBUTE_ALWAYS_INLINE inline T* CreateInternal(bool skip_explicit_ownership, + const Arg1& arg1, + const Arg2& arg2, + const Arg3& arg3, + const Arg4& arg4, + const Arg5& arg5, + const Arg6& arg6, + const Arg7& arg7) { + T* t = new (AllocateAligned(RTTI_TYPE_ID(T), sizeof(T))) + T(arg1, arg2, arg3, arg4, arg5, arg6, arg7); + if (!skip_explicit_ownership) { + AddListNode(t, &internal::arena_destruct_object); + } + return t; + } + + template + GOOGLE_ATTRIBUTE_ALWAYS_INLINE inline T* CreateInternal(bool skip_explicit_ownership, + const Arg1& arg1, + const Arg2& arg2, + const Arg3& arg3, + const Arg4& arg4, + const Arg5& arg5, + const Arg6& arg6, + const Arg7& arg7, + const Arg8& arg8) { + T* t = new (AllocateAligned(RTTI_TYPE_ID(T), sizeof(T))) + T(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); if (!skip_explicit_ownership) { AddListNode(t, &internal::arena_destruct_object); } @@ -485,6 +719,20 @@ class LIBPROTOBUF_EXPORT Arena { this); } + template GOOGLE_ATTRIBUTE_ALWAYS_INLINE + inline T* CreateMessageInternal(typename T::InternalArenaConstructable_*, + const Arg& arg) { + return CreateInternal(SkipDeleteList(static_cast(0)), + this, arg); + } + + template GOOGLE_ATTRIBUTE_ALWAYS_INLINE + inline T* CreateMessageInternal(typename T::InternalArenaConstructable_*, + const Arg1& arg1, const Arg2& arg2) { + return CreateInternal(SkipDeleteList(static_cast(0)), + this, arg1, arg2); + } + // CreateInArenaStorage is used to implement map field. Without it, // google::protobuf::Map need to call generated message's protected arena constructor, // which needs to declare google::protobuf::Map as friend of generated message. @@ -536,8 +784,15 @@ class LIBPROTOBUF_EXPORT Arena { return NULL; } + // Allocate and also optionally call on_arena_allocation callback with the + // allocated type info when the hooks are in place in ArenaOptions and + // the cookie is not null. + void* AllocateAligned(const std::type_info* allocated, size_t n); - void* AllocateAligned(size_t size); + // Allocate an internal allocation, avoiding optional typed monitoring. + GOOGLE_ATTRIBUTE_ALWAYS_INLINE inline void* AllocateAligned(size_t n) { + return AllocateAligned(NULL, n); + } void Init(); @@ -596,6 +851,9 @@ class LIBPROTOBUF_EXPORT Arena { GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Arena); }; +// Defined above for supporting environments without RTTI. +#undef RTTI_TYPE_ID + template const typename Arena::is_arena_constructable::type Arena::is_arena_constructable::value = diff --git a/src/google/protobuf/arena_nc_test.py b/src/google/protobuf/arena_nc_test.py index f390df36..87a69b2a 100644 --- a/src/google/protobuf/arena_nc_test.py +++ b/src/google/protobuf/arena_nc_test.py @@ -35,6 +35,7 @@ import unittest from google3.testing.pybase import fake_target_util +import unittest class ArenaNcTest(unittest.TestCase): diff --git a/src/google/protobuf/arena_test_util.h b/src/google/protobuf/arena_test_util.h index 7db7a90e..690cc706 100644 --- a/src/google/protobuf/arena_test_util.h +++ b/src/google/protobuf/arena_test_util.h @@ -31,6 +31,7 @@ #ifndef GOOGLE_PROTOBUF_ARENA_TEST_UTIL_H__ #define GOOGLE_PROTOBUF_ARENA_TEST_UTIL_H__ + namespace google { namespace protobuf { namespace internal { diff --git a/src/google/protobuf/arena_unittest.cc b/src/google/protobuf/arena_unittest.cc index d9b198e0..873e85ae 100644 --- a/src/google/protobuf/arena_unittest.cc +++ b/src/google/protobuf/arena_unittest.cc @@ -39,6 +39,7 @@ #include #endif #include +#include #include #include @@ -47,11 +48,14 @@ #include #include #include +#include +#include #include #include #include #include #include +#include #include #include @@ -125,6 +129,29 @@ class MustBeConstructedWithOneThroughFour { GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MustBeConstructedWithOneThroughFour); }; +// A class that takes eight different types as constructor arguments. +class MustBeConstructedWithOneThroughEight { + public: + MustBeConstructedWithOneThroughEight( + int one, const char* two, const string& three, + const PleaseDontCopyMe* four, int five, const char* six, + const string& seven, const string& eight) + : one_(one), two_(two), three_(three), four_(four), five_(five), + six_(six), seven_(seven), eight_(eight) {} + + int one_; + const char* const two_; + string three_; + const PleaseDontCopyMe* four_; + int five_; + const char* const six_; + string seven_; + string eight_; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MustBeConstructedWithOneThroughEight); +}; + } // namespace TEST(ArenaTest, ArenaConstructable) { @@ -156,7 +183,7 @@ TEST(ArenaTest, BasicCreate) { EXPECT_EQ(2, notifier.GetCount()); } -TEST(ArenaTest, CreateWithManyConstructorArguments) { +TEST(ArenaTest, CreateWithFourConstructorArguments) { Arena arena; const string three("3"); const PleaseDontCopyMe four(4); @@ -170,6 +197,26 @@ TEST(ArenaTest, CreateWithManyConstructorArguments) { ASSERT_EQ(4, new_object->four_->value()); } +TEST(ArenaTest, CreateWithEightConstructorArguments) { + Arena arena; + const string three("3"); + const PleaseDontCopyMe four(4); + const string seven("7"); + const string eight("8"); + const MustBeConstructedWithOneThroughEight* new_object = + Arena::Create( + &arena, 1, "2", three, &four, 5, "6", seven, eight); + EXPECT_TRUE(new_object != NULL); + ASSERT_EQ(1, new_object->one_); + ASSERT_STREQ("2", new_object->two_); + ASSERT_EQ("3", new_object->three_); + ASSERT_EQ(4, new_object->four_->value()); + ASSERT_EQ(5, new_object->five_); + ASSERT_STREQ("6", new_object->six_); + ASSERT_EQ("7", new_object->seven_); + ASSERT_EQ("8", new_object->eight_); +} + TEST(ArenaTest, InitialBlockTooSmall) { // Construct a small (64 byte) initial block of memory to be used by the // arena allocator; then, allocate an object which will not fit in the @@ -1113,6 +1160,19 @@ TEST(ArenaTest, GetArenaShouldReturnNullForNonArenaAllocatedMessages) { EXPECT_EQ(NULL, Arena::GetArena(const_pointer_to_message)); } +TEST(ArenaTest, UnsafeSetAllocatedOnArena) { + ::google::protobuf::Arena arena; + TestAllTypes* message = Arena::CreateMessage(&arena); + EXPECT_FALSE(message->has_optional_string()); + + string owned_string = "test with long enough content to heap-allocate"; + message->unsafe_arena_set_allocated_optional_string(&owned_string); + EXPECT_TRUE(message->has_optional_string()); + + message->unsafe_arena_set_allocated_optional_string(NULL); + EXPECT_FALSE(message->has_optional_string()); +} + // A helper utility class to only contain static hook functions, some // counters to be used to verify the counters have been called and a cookie // value to be verified. @@ -1124,6 +1184,13 @@ class ArenaHooksTestUtil { return static_cast(cookie); } + static void on_allocation(const std::type_info* /*unused*/, uint64 alloc_size, + void* cookie) { + ++num_allocations; + int cookie_value = *static_cast(cookie); + EXPECT_EQ(kCookieValue, cookie_value); + } + static void on_reset(::google::protobuf::Arena* arena, void* cookie, uint64 space_used) { ++num_reset; @@ -1141,10 +1208,12 @@ class ArenaHooksTestUtil { static const int kCookieValue = 999; static uint32 num_init; + static uint32 num_allocations; static uint32 num_reset; static uint32 num_destruct; }; uint32 ArenaHooksTestUtil::num_init = 0; +uint32 ArenaHooksTestUtil::num_allocations = 0; uint32 ArenaHooksTestUtil::num_reset = 0; uint32 ArenaHooksTestUtil::num_destruct = 0; const int ArenaHooksTestUtil::kCookieValue; @@ -1153,6 +1222,7 @@ const int ArenaHooksTestUtil::kCookieValue; TEST(ArenaTest, ArenaHooksSanity) { ::google::protobuf::ArenaOptions options; options.on_arena_init = ArenaHooksTestUtil::on_init; + options.on_arena_allocation = ArenaHooksTestUtil::on_allocation; options.on_arena_reset = ArenaHooksTestUtil::on_reset; options.on_arena_destruction = ArenaHooksTestUtil::on_destruction; @@ -1160,6 +1230,9 @@ TEST(ArenaTest, ArenaHooksSanity) { { ::google::protobuf::Arena arena(options); EXPECT_EQ(1, ArenaHooksTestUtil::num_init); + EXPECT_EQ(0, ArenaHooksTestUtil::num_allocations); + ::google::protobuf::Arena::Create(&arena); + EXPECT_EQ(1, ArenaHooksTestUtil::num_allocations); arena.Reset(); arena.Reset(); diff --git a/src/google/protobuf/compiler/code_generator.h b/src/google/protobuf/compiler/code_generator.h index 321a8ccd..b989f151 100644 --- a/src/google/protobuf/compiler/code_generator.h +++ b/src/google/protobuf/compiler/code_generator.h @@ -79,6 +79,37 @@ class LIBPROTOC_EXPORT CodeGenerator { GeneratorContext* generator_context, string* error) const = 0; + // Generates code for all given proto files, generating one or more files in + // the given output directory. + // + // This method should be called instead of |Generate()| when + // |HasGenerateAll()| returns |true|. It is used to emulate legacy semantics + // when more than one `.proto` file is specified on one compiler invocation. + // + // WARNING: Please do not use unless legacy semantics force the code generator + // to produce a single output file for all input files, or otherwise require + // an examination of all input files first. The canonical code generator + // design produces one output file per input .proto file, and we do not wish + // to encourage alternate designs. + // + // A parameter is given as passed on the command line, as in |Generate()| + // above. + // + // Returns true if successful. Otherwise, sets *error to a description of + // the problem (e.g. "invalid parameter") and returns false. + virtual bool GenerateAll(const vector& files, + const string& parameter, + GeneratorContext* generator_context, + string* error) const { + *error = "Unimplemented GenerateAll() method."; + return false; + } + + // Returns true if the code generator expects to receive all FileDescriptors + // at once (via |GenerateAll()|), rather than one at a time (via + // |Generate()|). This is required to implement legacy semantics. + virtual bool HasGenerateAll() const { return false; } + private: GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CodeGenerator); }; diff --git a/src/google/protobuf/compiler/command_line_interface.cc b/src/google/protobuf/compiler/command_line_interface.cc index 567238ae..291fb054 100644 --- a/src/google/protobuf/compiler/command_line_interface.cc +++ b/src/google/protobuf/compiler/command_line_interface.cc @@ -898,12 +898,14 @@ CommandLineInterface::ParseArguments(int argc, const char* const argv[]) { return PARSE_ARGUMENT_FAIL; } if (mode_ != MODE_COMPILE && !dependency_out_name_.empty()) { - cerr << "Can only use --dependency_out=FILE when generating code." << endl; + std::cerr << "Can only use --dependency_out=FILE when generating code." + << std::endl; return PARSE_ARGUMENT_FAIL; } if (!dependency_out_name_.empty() && input_files_.size() > 1) { - cerr << "Can only process one input file when using --dependency_out=FILE." - << endl; + std::cerr + << "Can only process one input file when using --dependency_out=FILE." + << std::endl; return PARSE_ARGUMENT_FAIL; } if (imports_in_descriptor_set_ && descriptor_set_name_.empty()) { @@ -1054,11 +1056,11 @@ CommandLineInterface::InterpretArgument(const string& name, } else if (name == "--dependency_out") { if (!dependency_out_name_.empty()) { - cerr << name << " may only be passed once." << endl; + std::cerr << name << " may only be passed once." << std::endl; return PARSE_ARGUMENT_FAIL; } if (value.empty()) { - cerr << name << " requires a non-empty value." << endl; + std::cerr << name << " requires a non-empty value." << std::endl; return PARSE_ARGUMENT_FAIL; } dependency_out_name_ = value; @@ -1272,7 +1274,8 @@ void CommandLineInterface::PrintHelpText() { " defined in the given proto files. Groups share\n" " the same field number space with the parent \n" " message. Extension ranges are counted as \n" -" occupied fields numbers." << std::endl; +" occupied fields numbers." + << std::endl; if (!plugin_prefix_.empty()) { std::cerr << " --plugin=EXECUTABLE Specifies a plugin executable to use.\n" @@ -1327,13 +1330,23 @@ bool CommandLineInterface::GenerateOutput( } parameters.append(generator_parameters_[output_directive.name]); } - for (int i = 0; i < parsed_files.size(); i++) { - if (!output_directive.generator->Generate(parsed_files[i], parameters, - generator_context, &error)) { - // Generator returned an error. - std::cerr << output_directive.name << ": " << parsed_files[i]->name() - << ": " << error << std::endl; - return false; + if (output_directive.generator->HasGenerateAll()) { + if (!output_directive.generator->GenerateAll( + parsed_files, parameters, generator_context, &error)) { + // Generator returned an error. + std::cerr << output_directive.name << ": " + << ": " << error << std::endl; + return false; + } + } else { + for (int i = 0; i < parsed_files.size(); i++) { + if (!output_directive.generator->Generate(parsed_files[i], parameters, + generator_context, &error)) { + // Generator returned an error. + std::cerr << output_directive.name << ": " << parsed_files[i]->name() + << ": " << error << std::endl; + return false; + } } } } @@ -1403,7 +1416,8 @@ bool CommandLineInterface::GenerateDependencyManifestFile( printer.Print(" $disk_file$", "disk_file", disk_file); if (i < file_set.file_size() - 1) printer.Print("\\\n"); } else { - cerr << "Unable to identify path for file " << virtual_file << endl; + std::cerr << "Unable to identify path for file " << virtual_file + << std::endl; return false; } } @@ -1673,6 +1687,10 @@ void GatherOccupiedFieldRanges(const Descriptor* descriptor, ranges->insert(FieldRange(descriptor->extension_range(i)->start, descriptor->extension_range(i)->end)); } + for (int i = 0; i < descriptor->reserved_range_count(); ++i) { + ranges->insert(FieldRange(descriptor->reserved_range(i)->start, + descriptor->reserved_range(i)->end)); + } // Handle the nested messages/groups in declaration order to make it // post-order strict. for (int i = 0; i < descriptor->nested_type_count(); ++i) { diff --git a/src/google/protobuf/compiler/command_line_interface_unittest.cc b/src/google/protobuf/compiler/command_line_interface_unittest.cc index e284c791..23d67e2b 100644 --- a/src/google/protobuf/compiler/command_line_interface_unittest.cc +++ b/src/google/protobuf/compiler/command_line_interface_unittest.cc @@ -115,10 +115,15 @@ class CommandLineInterfaceTest : public testing::Test { // Create a subdirectory within temp_directory_. void CreateTempDir(const string& name); +#ifdef PROTOBUF_OPENSOURCE // Change working directory to temp directory. void SwitchToTempDirectory() { File::ChangeWorkingDirectory(temp_directory_); } +#else // !PROTOBUF_OPENSOURCE + // TODO(teboring): Figure out how to change and get working directory in + // google3. +#endif // !PROTOBUF_OPENSOURCE void SetInputsAreProtoPathRelative(bool enable) { cli_.SetInputsAreProtoPathRelative(enable); @@ -986,6 +991,7 @@ TEST_F(CommandLineInterfaceTest, WriteDependencyManifestFileGivenTwoInputs) { "Can only process one input file when using --dependency_out=FILE.\n"); } +#ifdef PROTOBUF_OPENSOURCE TEST_F(CommandLineInterfaceTest, WriteDependencyManifestFile) { CreateTempFile("foo.proto", "syntax = \"proto2\";\n" @@ -1011,6 +1017,10 @@ TEST_F(CommandLineInterfaceTest, WriteDependencyManifestFile) { File::ChangeWorkingDirectory(current_working_directory); } +#else // !PROTOBUF_OPENSOURCE +// TODO(teboring): Figure out how to change and get working directory in +// google3. +#endif // !PROTOBUF_OPENSOURCE TEST_F(CommandLineInterfaceTest, WriteDependencyManifestFileForAbsolutePath) { CreateTempFile("foo.proto", diff --git a/src/google/protobuf/compiler/cpp/cpp_enum.cc b/src/google/protobuf/compiler/cpp/cpp_enum.cc index 3eb20ab1..70d3a600 100644 --- a/src/google/protobuf/compiler/cpp/cpp_enum.cc +++ b/src/google/protobuf/compiler/cpp/cpp_enum.cc @@ -70,12 +70,23 @@ EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor, EnumGenerator::~EnumGenerator() {} +void EnumGenerator::GenerateForwardDeclaration(io::Printer* printer) { + if (!options_.proto_h) { + return; + } + map vars; + vars["classname"] = classname_; + printer->Print(vars, "enum $classname$ : int;\n"); + printer->Print(vars, "bool $classname$_IsValid(int value);\n"); +} + void EnumGenerator::GenerateDefinition(io::Printer* printer) { map vars; vars["classname"] = classname_; vars["short_name"] = descriptor_->name(); + vars["enumbase"] = classname_ + (options_.proto_h ? " : int" : ""); - printer->Print(vars, "enum $classname$ {\n"); + printer->Print(vars, "enum $enumbase$ {\n"); printer->Indent(); const EnumValueDescriptor* min_value = descriptor_->value(0); @@ -90,7 +101,6 @@ void EnumGenerator::GenerateDefinition(io::Printer* printer) { vars["prefix"] = (descriptor_->containing_type() == NULL) ? "" : classname_ + "_"; - if (i > 0) printer->Print(",\n"); printer->Print(vars, "$prefix$$name$ = $number$"); diff --git a/src/google/protobuf/compiler/cpp/cpp_enum.h b/src/google/protobuf/compiler/cpp/cpp_enum.h index 1ebd7cf7..3e930856 100644 --- a/src/google/protobuf/compiler/cpp/cpp_enum.h +++ b/src/google/protobuf/compiler/cpp/cpp_enum.h @@ -60,6 +60,12 @@ class EnumGenerator { // Header stuff. + // Generate header code to forward-declare the enum. This is for use when + // generating other .proto.h files. This code should be placed within the + // enum's package namespace, but NOT within any class, even for nested + // enums. + void GenerateForwardDeclaration(io::Printer* printer); + // Generate header code defining the enum. This code should be placed // within the enum's package namespace, but NOT within any class, even for // nested enums. diff --git a/src/google/protobuf/compiler/cpp/cpp_enum_field.cc b/src/google/protobuf/compiler/cpp/cpp_enum_field.cc index 74989703..965327b1 100644 --- a/src/google/protobuf/compiler/cpp/cpp_enum_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_enum_field.cc @@ -242,7 +242,7 @@ void RepeatedEnumFieldGenerator:: GeneratePrivateMembers(io::Printer* printer) const { printer->Print(variables_, "::google::protobuf::RepeatedField $name$_;\n"); - if (descriptor_->options().packed() + if (descriptor_->is_packed() && HasGeneratedMethods(descriptor_->file())) { printer->Print(variables_, "mutable int _$name$_cached_byte_size_;\n"); @@ -352,7 +352,7 @@ GenerateMergeFromCodedStream(io::Printer* printer) const { void RepeatedEnumFieldGenerator:: GenerateMergeFromCodedStreamWithPacking(io::Printer* printer) const { - if (!descriptor_->options().packed()) { + if (!descriptor_->is_packed()) { // This path is rarely executed, so we use a non-inlined implementation. if (HasPreservingUnknownEnumSemantics(descriptor_->file())) { printer->Print(variables_, @@ -419,7 +419,7 @@ GenerateMergeFromCodedStreamWithPacking(io::Printer* printer) const { void RepeatedEnumFieldGenerator:: GenerateSerializeWithCachedSizes(io::Printer* printer) const { - if (descriptor_->options().packed()) { + if (descriptor_->is_packed()) { // Write the tag and the size. printer->Print(variables_, "if (this->$name$_size() > 0) {\n" @@ -432,7 +432,7 @@ GenerateSerializeWithCachedSizes(io::Printer* printer) const { } printer->Print(variables_, "for (int i = 0; i < this->$name$_size(); i++) {\n"); - if (descriptor_->options().packed()) { + if (descriptor_->is_packed()) { printer->Print(variables_, " ::google::protobuf::internal::WireFormatLite::WriteEnumNoTag(\n" " this->$name$(i), output);\n"); @@ -446,7 +446,7 @@ GenerateSerializeWithCachedSizes(io::Printer* printer) const { void RepeatedEnumFieldGenerator:: GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const { - if (descriptor_->options().packed()) { + if (descriptor_->is_packed()) { // Write the tag and the size. printer->Print(variables_, "if (this->$name$_size() > 0) {\n" @@ -460,7 +460,7 @@ GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const { } printer->Print(variables_, "for (int i = 0; i < this->$name$_size(); i++) {\n"); - if (descriptor_->options().packed()) { + if (descriptor_->is_packed()) { printer->Print(variables_, " target = ::google::protobuf::internal::WireFormatLite::WriteEnumNoTagToArray(\n" " this->$name$(i), target);\n"); @@ -484,7 +484,7 @@ GenerateByteSize(io::Printer* printer) const { " this->$name$(i));\n" "}\n"); - if (descriptor_->options().packed()) { + if (descriptor_->is_packed()) { printer->Print(variables_, "if (data_size > 0) {\n" " total_size += $tag_size$ +\n" diff --git a/src/google/protobuf/compiler/cpp/cpp_field.h b/src/google/protobuf/compiler/cpp/cpp_field.h index cd2b6b75..1d7f8233 100644 --- a/src/google/protobuf/compiler/cpp/cpp_field.h +++ b/src/google/protobuf/compiler/cpp/cpp_field.h @@ -82,12 +82,38 @@ class FieldGenerator { // implementation is empty. virtual void GenerateStaticMembers(io::Printer* /*printer*/) const {} + // Generate prototypes for accessors that will manipulate imported + // messages inline. These are for .proto.h headers. + // + // In .proto.h mode, the headers of imports are not #included. However, + // functions that manipulate the imported message types need access to + // the class definition of the imported message, meaning that the headers + // must be #included. To get around this, functions that manipulate + // imported message objects are defined as dependent functions in a base + // template class. By making them dependent template functions, the + // function templates will not be instantiated until they are called, so + // we can defer to those translation units to #include the necessary + // generated headers. + // + // See: + // http://en.cppreference.com/w/cpp/language/class_template#Implicit_instantiation + // + // Most field types don't need this, so the default implementation is empty. + virtual void GenerateDependentAccessorDeclarations( + io::Printer* printer) const {} + // Generate prototypes for all of the accessor functions related to this // field. These are placed inside the class definition. virtual void GenerateAccessorDeclarations(io::Printer* printer) const = 0; + // Generate inline definitions of depenent accessor functions for this field. + // These are placed inside the header after all class definitions. + virtual void GenerateDependentInlineAccessorDefinitions( + io::Printer* printer) const {} + // Generate inline definitions of accessor functions for this field. // These are placed inside the header after all class definitions. + // In non-.proto.h mode, this generates dependent accessor functions as well. virtual void GenerateInlineAccessorDefinitions( io::Printer* printer, bool is_inline) const = 0; diff --git a/src/google/protobuf/compiler/cpp/cpp_file.cc b/src/google/protobuf/compiler/cpp/cpp_file.cc index a98c7d92..b997a51a 100644 --- a/src/google/protobuf/compiler/cpp/cpp_file.cc +++ b/src/google/protobuf/compiler/cpp/cpp_file.cc @@ -94,113 +94,10 @@ FileGenerator::FileGenerator(const FileDescriptor* file, const Options& options) FileGenerator::~FileGenerator() {} void FileGenerator::GenerateHeader(io::Printer* printer) { - string filename_identifier = FilenameIdentifier(file_->name()); - - // Generate top of header. - printer->Print( - "// Generated by the protocol buffer compiler. DO NOT EDIT!\n" - "// source: $filename$\n" - "\n" - "#ifndef PROTOBUF_$filename_identifier$__INCLUDED\n" - "#define PROTOBUF_$filename_identifier$__INCLUDED\n" - "\n" - "#include \n" - "\n", - "filename", file_->name(), - "filename_identifier", filename_identifier); - - - printer->Print( - "#include \n" - "\n"); - - // Verify the protobuf library header version is compatible with the protoc - // version before going any further. - printer->Print( - "#if GOOGLE_PROTOBUF_VERSION < $min_header_version$\n" - "#error This file was generated by a newer version of protoc which is\n" - "#error incompatible with your Protocol Buffer headers. Please update\n" - "#error your headers.\n" - "#endif\n" - "#if $protoc_version$ < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION\n" - "#error This file was generated by an older version of protoc which is\n" - "#error incompatible with your Protocol Buffer headers. Please\n" - "#error regenerate this file with a newer version of protoc.\n" - "#endif\n" - "\n", - "min_header_version", - SimpleItoa(protobuf::internal::kMinHeaderVersionForProtoc), - "protoc_version", SimpleItoa(GOOGLE_PROTOBUF_VERSION)); - - // OK, it's now safe to #include other files. - printer->Print( - "#include \n" - "#include \n" - "#include \n"); - if (UseUnknownFieldSet(file_)) { - printer->Print( - "#include \n"); - } - if (file_->message_type_count() > 0) { - if (HasDescriptorMethods(file_)) { - printer->Print( - "#include \n"); - } else { - printer->Print( - "#include \n"); - } - } - printer->Print( - "#include \n" - "#include \n"); - if (HasMapFields(file_)) { - printer->Print( - "#include \n"); - if (HasDescriptorMethods(file_)) { - printer->Print( - "#include \n"); - } else { - printer->Print( - "#include \n"); - } - } - - if (HasEnumDefinitions(file_)) { - if (HasDescriptorMethods(file_)) { - printer->Print( - "#include \n"); - } else { - printer->Print( - "#include \n"); - } - } - - if (HasGenericServices(file_)) { - printer->Print( - "#include \n"); - } - - if (UseUnknownFieldSet(file_) && file_->message_type_count() > 0) { - printer->Print( - "#include \n"); - } - - - set public_import_names; - for (int i = 0; i < file_->public_dependency_count(); i++) { - public_import_names.insert(file_->public_dependency(i)->name()); - } - - for (int i = 0; i < file_->dependency_count(); i++) { - const string& name = file_->dependency(i)->name(); - bool public_import = (public_import_names.count(name) != 0); + GenerateTopHeaderGuard(printer); - - printer->Print( - "#include \"$dependency$.pb.h\"$iwyu$\n", - "dependency", StripProto(name), - "iwyu", (public_import) ? " // IWYU pragma: export" : ""); - } + GenerateLibraryIncludes(printer); + GenerateDependencyIncludes(printer); printer->Print( "// @@protoc_insertion_point(includes)\n"); @@ -210,132 +107,44 @@ void FileGenerator::GenerateHeader(io::Printer* printer) { // Open namespace. GenerateNamespaceOpeners(printer); - // Forward-declare the AddDescriptors, AssignDescriptors, and ShutdownFile - // functions, so that we can declare them to be friends of each class. - printer->Print( - "\n" - "// Internal implementation detail -- do not call these.\n" - "void $dllexport_decl$$adddescriptorsname$();\n", - "adddescriptorsname", GlobalAddDescriptorsName(file_->name()), - "dllexport_decl", - options_.dllexport_decl.empty() ? "" : options_.dllexport_decl + " "); - - printer->Print( - // Note that we don't put dllexport_decl on these because they are only - // called by the .pb.cc file in which they are defined. - "void $assigndescriptorsname$();\n" - "void $shutdownfilename$();\n" - "\n", - "assigndescriptorsname", GlobalAssignDescriptorsName(file_->name()), - "shutdownfilename", GlobalShutdownFileName(file_->name())); - - // Generate forward declarations of classes. - for (int i = 0; i < file_->message_type_count(); i++) { - message_generators_[i]->GenerateForwardDeclaration(printer); - } + GenerateGlobalStateFunctionDeclarations(printer); + GenerateMessageForwardDeclarations(printer); printer->Print("\n"); - // Generate enum definitions. - for (int i = 0; i < file_->message_type_count(); i++) { - message_generators_[i]->GenerateEnumDefinitions(printer); - } - for (int i = 0; i < file_->enum_type_count(); i++) { - enum_generators_[i]->GenerateDefinition(printer); - } + GenerateEnumDefinitions(printer); printer->Print(kThickSeparator); printer->Print("\n"); - // Generate class definitions. - for (int i = 0; i < file_->message_type_count(); i++) { - if (i > 0) { - printer->Print("\n"); - printer->Print(kThinSeparator); - printer->Print("\n"); - } - message_generators_[i]->GenerateClassDefinition(printer); - } + GenerateMessageDefinitions(printer); printer->Print("\n"); printer->Print(kThickSeparator); printer->Print("\n"); - if (HasGenericServices(file_)) { - // Generate service definitions. - for (int i = 0; i < file_->service_count(); i++) { - if (i > 0) { - printer->Print("\n"); - printer->Print(kThinSeparator); - printer->Print("\n"); - } - service_generators_[i]->GenerateDeclarations(printer); - } - - printer->Print("\n"); - printer->Print(kThickSeparator); - printer->Print("\n"); - } + GenerateServiceDefinitions(printer); - // Declare extension identifiers. - for (int i = 0; i < file_->extension_count(); i++) { - extension_generators_[i]->GenerateDeclaration(printer); - } + GenerateExtensionIdentifiers(printer); printer->Print("\n"); printer->Print(kThickSeparator); printer->Print("\n"); - printer->Print("#if !PROTOBUF_INLINE_NOT_IN_HEADERS\n"); - // Generate class inline methods. - for (int i = 0; i < file_->message_type_count(); i++) { - if (i > 0) { - printer->Print(kThinSeparator); - printer->Print("\n"); - } - message_generators_[i]->GenerateInlineMethods(printer, - /* is_inline = */ true); - } - printer->Print("#endif // !PROTOBUF_INLINE_NOT_IN_HEADERS\n"); - - printer->Print( - "\n" - "// @@protoc_insertion_point(namespace_scope)\n"); + GenerateInlineFunctionDefinitions(printer); // Close up namespace. GenerateNamespaceClosers(printer); - // Emit GetEnumDescriptor specializations into google::protobuf namespace: - if (HasEnumDefinitions(file_)) { - // The SWIG conditional is to avoid a null-pointer dereference - // (bug 1984964) in swig-1.3.21 resulting from the following syntax: - // namespace X { void Y(); } - // which appears in GetEnumDescriptor() specializations. - printer->Print( - "\n" - "#ifndef SWIG\n" - "namespace google {\nnamespace protobuf {\n" - "\n"); - for (int i = 0; i < file_->message_type_count(); i++) { - message_generators_[i]->GenerateGetEnumDescriptorSpecializations(printer); - } - for (int i = 0; i < file_->enum_type_count(); i++) { - enum_generators_[i]->GenerateGetEnumDescriptorSpecializations(printer); - } - printer->Print( - "\n" - "} // namespace protobuf\n} // namespace google\n" - "#endif // SWIG\n"); - } + // We need to specialize some templates in the ::google::protobuf namespace: + GenerateProto2NamespaceEnumSpecializations(printer); printer->Print( "\n" "// @@protoc_insertion_point(global_scope)\n" "\n"); - printer->Print( - "#endif // PROTOBUF_$filename_identifier$__INCLUDED\n", - "filename_identifier", filename_identifier); + GenerateBottomHeaderGuard(printer); } void FileGenerator::GenerateSource(io::Printer* printer) { @@ -707,6 +516,294 @@ void FileGenerator::GenerateNamespaceClosers(io::Printer* printer) { } } +void FileGenerator::GenerateTopHeaderGuard(io::Printer* printer) { + string filename_identifier = FilenameIdentifier(file_->name()); + // Generate top of header. + printer->Print( + "// Generated by the protocol buffer compiler. DO NOT EDIT!\n" + "// source: $filename$\n" + "\n" + "#ifndef PROTOBUF_$filename_identifier$__INCLUDED\n" + "#define PROTOBUF_$filename_identifier$__INCLUDED\n" + "\n" + "#include \n" + "\n", + "filename", file_->name(), + "filename_identifier", filename_identifier); +} + +void FileGenerator::GenerateBottomHeaderGuard(io::Printer* printer) { + string filename_identifier = FilenameIdentifier(file_->name()); + printer->Print( + "#endif // PROTOBUF_$filename_identifier$__INCLUDED\n", + "filename_identifier", filename_identifier); +} + +void FileGenerator::GenerateLibraryIncludes(io::Printer* printer) { + + printer->Print( + "#include \n" + "\n"); + + // Verify the protobuf library header version is compatible with the protoc + // version before going any further. + printer->Print( + "#if GOOGLE_PROTOBUF_VERSION < $min_header_version$\n" + "#error This file was generated by a newer version of protoc which is\n" + "#error incompatible with your Protocol Buffer headers. Please update\n" + "#error your headers.\n" + "#endif\n" + "#if $protoc_version$ < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION\n" + "#error This file was generated by an older version of protoc which is\n" + "#error incompatible with your Protocol Buffer headers. Please\n" + "#error regenerate this file with a newer version of protoc.\n" + "#endif\n" + "\n", + "min_header_version", + SimpleItoa(protobuf::internal::kMinHeaderVersionForProtoc), + "protoc_version", SimpleItoa(GOOGLE_PROTOBUF_VERSION)); + + // OK, it's now safe to #include other files. + printer->Print( + "#include \n" + "#include \n" + "#include \n"); + if (UseUnknownFieldSet(file_)) { + printer->Print( + "#include \n"); + } + if (file_->message_type_count() > 0) { + if (HasDescriptorMethods(file_)) { + printer->Print( + "#include \n"); + } else { + printer->Print( + "#include \n"); + } + } + printer->Print( + "#include \n" + "#include \n"); + if (HasMapFields(file_)) { + printer->Print( + "#include \n"); + if (HasDescriptorMethods(file_)) { + printer->Print( + "#include \n"); + } else { + printer->Print( + "#include \n"); + } + } + + if (HasEnumDefinitions(file_)) { + if (HasDescriptorMethods(file_)) { + printer->Print( + "#include \n"); + } else { + printer->Print( + "#include \n"); + } + } + + if (HasGenericServices(file_)) { + printer->Print( + "#include \n"); + } + + if (UseUnknownFieldSet(file_) && file_->message_type_count() > 0) { + printer->Print( + "#include \n"); + } + + + if (IsAnyMessage(file_)) { + printer->Print( + "#include \"google/protobuf/any.h\"\n"); + } +} + +void FileGenerator::GenerateDependencyIncludes(io::Printer* printer) { + set public_import_names; + for (int i = 0; i < file_->public_dependency_count(); i++) { + public_import_names.insert(file_->public_dependency(i)->name()); + } + + for (int i = 0; i < file_->dependency_count(); i++) { + const string& name = file_->dependency(i)->name(); + bool public_import = (public_import_names.count(name) != 0); + + + printer->Print( + "#include \"$dependency$.pb.h\"$iwyu$\n", + "dependency", StripProto(name), + "iwyu", (public_import) ? " // IWYU pragma: export" : ""); + } +} + +void FileGenerator::GenerateGlobalStateFunctionDeclarations( + io::Printer* printer) { + // Forward-declare the AddDescriptors, AssignDescriptors, and ShutdownFile + // functions, so that we can declare them to be friends of each class. + printer->Print( + "\n" + "// Internal implementation detail -- do not call these.\n" + "void $dllexport_decl$$adddescriptorsname$();\n", + "adddescriptorsname", GlobalAddDescriptorsName(file_->name()), + "dllexport_decl", + options_.dllexport_decl.empty() ? "" : options_.dllexport_decl + " "); + + printer->Print( + // Note that we don't put dllexport_decl on these because they are only + // called by the .pb.cc file in which they are defined. + "void $assigndescriptorsname$();\n" + "void $shutdownfilename$();\n" + "\n", + "assigndescriptorsname", GlobalAssignDescriptorsName(file_->name()), + "shutdownfilename", GlobalShutdownFileName(file_->name())); +} + +void FileGenerator::GenerateMessageForwardDeclarations(io::Printer* printer) { + // Generate forward declarations of classes. + for (int i = 0; i < file_->message_type_count(); i++) { + message_generators_[i]->GenerateMessageForwardDeclaration(printer); + } +} + +void FileGenerator::GenerateMessageDefinitions(io::Printer* printer) { + // Generate class definitions. + for (int i = 0; i < file_->message_type_count(); i++) { + if (i > 0) { + printer->Print("\n"); + printer->Print(kThinSeparator); + printer->Print("\n"); + } + message_generators_[i]->GenerateClassDefinition(printer); + } +} + +void FileGenerator::GenerateEnumDefinitions(io::Printer* printer) { + // Generate enum definitions. + for (int i = 0; i < file_->message_type_count(); i++) { + message_generators_[i]->GenerateEnumDefinitions(printer); + } + for (int i = 0; i < file_->enum_type_count(); i++) { + enum_generators_[i]->GenerateDefinition(printer); + } +} + +void FileGenerator::GenerateServiceDefinitions(io::Printer* printer) { + if (HasGenericServices(file_)) { + // Generate service definitions. + for (int i = 0; i < file_->service_count(); i++) { + if (i > 0) { + printer->Print("\n"); + printer->Print(kThinSeparator); + printer->Print("\n"); + } + service_generators_[i]->GenerateDeclarations(printer); + } + + printer->Print("\n"); + printer->Print(kThickSeparator); + printer->Print("\n"); + } +} + +void FileGenerator::GenerateExtensionIdentifiers(io::Printer* printer) { + // Declare extension identifiers. + for (int i = 0; i < file_->extension_count(); i++) { + extension_generators_[i]->GenerateDeclaration(printer); + } +} + +void FileGenerator::GenerateInlineFunctionDefinitions(io::Printer* printer) { + // An aside about inline functions in .proto.h mode: + // + // The PROTOBUF_INLINE_NOT_IN_HEADERS symbol controls conditionally + // moving much of the inline functions to the .pb.cc file, which can be a + // significant performance benefit for compilation time, at the expense + // of non-inline function calls. + // + // However, in .proto.h mode, the definition of the internal dependent + // base class must remain in the header, and can never be out-lined. The + // dependent base class also needs access to has-bit manipuation + // functions, so the has-bit functions must be unconditionally inlined in + // proto_h mode. + // + // This gives us three flavors of functions: + // + // 1. Functions on the message not used by the internal dependent base + // class: in .proto.h mode, only some functions are defined on the + // message class; others are defined on the dependent base class. + // These are guarded and can be out-lined. These are generated by + // GenerateInlineMethods, and include has_* bit functions in + // non-proto_h mode. + // + // 2. Functions on the internal dependent base class: these functions + // are dependent on a template parameter, so they always need to + // remain in the header. + // + // 3. Functions on the message that are used by the dependent base: the + // dependent base class down casts itself to the message + // implementation class to access these functions (the has_* bit + // manipulation functions). Unlike #1, these functions must + // unconditionally remain in the header. These are emitted by + // GenerateDependentInlineMethods, even though they are not actually + // dependent. + + printer->Print("#if !PROTOBUF_INLINE_NOT_IN_HEADERS\n"); + // Generate class inline methods. + for (int i = 0; i < file_->message_type_count(); i++) { + if (i > 0) { + printer->Print(kThinSeparator); + printer->Print("\n"); + } + message_generators_[i]->GenerateInlineMethods(printer, + /* is_inline = */ true); + } + printer->Print("#endif // !PROTOBUF_INLINE_NOT_IN_HEADERS\n"); + + for (int i = 0; i < file_->message_type_count(); i++) { + if (i > 0) { + printer->Print(kThinSeparator); + printer->Print("\n"); + } + // Methods of the dependent base class must always be inline in the header. + message_generators_[i]->GenerateDependentInlineMethods(printer); + } + + printer->Print( + "\n" + "// @@protoc_insertion_point(namespace_scope)\n"); +} + +void FileGenerator::GenerateProto2NamespaceEnumSpecializations( + io::Printer* printer) { + // Emit GetEnumDescriptor specializations into google::protobuf namespace: + if (HasEnumDefinitions(file_)) { + // The SWIG conditional is to avoid a null-pointer dereference + // (bug 1984964) in swig-1.3.21 resulting from the following syntax: + // namespace X { void Y(); } + // which appears in GetEnumDescriptor() specializations. + printer->Print( + "\n" + "#ifndef SWIG\n" + "namespace google {\nnamespace protobuf {\n" + "\n"); + for (int i = 0; i < file_->message_type_count(); i++) { + message_generators_[i]->GenerateGetEnumDescriptorSpecializations(printer); + } + for (int i = 0; i < file_->enum_type_count(); i++) { + enum_generators_[i]->GenerateGetEnumDescriptorSpecializations(printer); + } + printer->Print( + "\n" + "} // namespace protobuf\n} // namespace google\n" + "#endif // SWIG\n"); + } +} + } // namespace cpp } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/cpp/cpp_file.h b/src/google/protobuf/compiler/cpp/cpp_file.h index 0e06547d..e68f67bb 100644 --- a/src/google/protobuf/compiler/cpp/cpp_file.h +++ b/src/google/protobuf/compiler/cpp/cpp_file.h @@ -80,6 +80,35 @@ class FileGenerator { void GenerateNamespaceOpeners(io::Printer* printer); void GenerateNamespaceClosers(io::Printer* printer); + // Generates top or bottom of a header file. + void GenerateTopHeaderGuard(io::Printer* printer); + void GenerateBottomHeaderGuard(io::Printer* printer); + + // Generates #include directives. + void GenerateLibraryIncludes(io::Printer* printer); + void GenerateDependencyIncludes(io::Printer* printer); + + // Generates a couple of different pieces before definitions: + void GenerateGlobalStateFunctionDeclarations(io::Printer* printer); + + // Generates types for classes. + void GenerateMessageForwardDeclarations(io::Printer* printer); + void GenerateMessageDefinitions(io::Printer* printer); + + // Generates enum definitions. + void GenerateEnumDefinitions(io::Printer* printer); + + // Generates generic service definitions. + void GenerateServiceDefinitions(io::Printer* printer); + + // Generates extension identifiers. + void GenerateExtensionIdentifiers(io::Printer* printer); + + // Generates inline function defintions. + void GenerateInlineFunctionDefinitions(io::Printer* printer); + + void GenerateProto2NamespaceEnumSpecializations(io::Printer* printer); + const FileDescriptor* file_; google::protobuf::scoped_array > message_generators_; diff --git a/src/google/protobuf/compiler/cpp/cpp_generator.cc b/src/google/protobuf/compiler/cpp/cpp_generator.cc index c999b93f..99416372 100644 --- a/src/google/protobuf/compiler/cpp/cpp_generator.cc +++ b/src/google/protobuf/compiler/cpp/cpp_generator.cc @@ -82,6 +82,7 @@ bool CppGenerator::Generate(const FileDescriptor* file, // } // FOO_EXPORT is a macro which should expand to __declspec(dllexport) or // __declspec(dllimport) depending on what is being compiled. + // Options file_options; for (int i = 0; i < options.size(); i++) { diff --git a/src/google/protobuf/compiler/cpp/cpp_helpers.cc b/src/google/protobuf/compiler/cpp/cpp_helpers.cc index 4e7155c3..0f3688d0 100644 --- a/src/google/protobuf/compiler/cpp/cpp_helpers.cc +++ b/src/google/protobuf/compiler/cpp/cpp_helpers.cc @@ -51,6 +51,9 @@ namespace cpp { namespace { +static const char kAnyMessageName[] = "Any"; +static const char kAnyProtoFile[] = "google/protobuf/any.proto"; + string DotsToUnderscores(const string& name) { return StringReplace(name, ".", "_", true); } @@ -162,6 +165,10 @@ string ClassName(const EnumDescriptor* enum_descriptor, bool qualified) { } +string DependentBaseClassTemplateName(const Descriptor* descriptor) { + return ClassName(descriptor, false) + "_InternalBase"; +} + string SuperClassName(const Descriptor* descriptor) { return HasDescriptorMethods(descriptor->file()) ? "::google::protobuf::Message" : "::google::protobuf::MessageLite"; @@ -200,6 +207,47 @@ string FieldConstantName(const FieldDescriptor *field) { return result; } +bool IsFieldDependent(const FieldDescriptor* field) { + if (field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) { + return false; + } + if (field->containing_oneof() != NULL) { + // Oneof fields will always be dependent. + // + // This is a unique case for field codegen. Field generators are + // responsible for generating all the field-specific accessor + // functions, except for the clear_*() function; instead, field + // generators produce inline clearing code. + // + // For non-oneof fields, the Message class uses the inline clearing + // code to define the field's clear_*() function, as well as in the + // destructor. For oneof fields, the Message class generates a much + // more complicated clear_*() function, which clears only the oneof + // member that is set, in addition to clearing methods for each of the + // oneof members individually. + // + // Since oneofs do not have their own generator class, the Message code + // generation logic would be significantly complicated in order to + // split dependent and non-dependent manipulation logic based on + // whether the oneof truly needs to be dependent; so, for oneof fields, + // we just assume it (and its constituents) should be manipulated by a + // dependent base class function. + // + // This is less precise than how dependent message-typed fields are + // handled, but the cost is limited to only the generated code for the + // oneof field, which seems like an acceptable tradeoff. + return true; + } + if (field->file() == field->message_type()->file()) { + return false; + } + return true; +} + +string DependentTypeName(const FieldDescriptor* field) { + return "InternalBase_" + field->name() + "_T"; +} + string FieldMessageTypeName(const FieldDescriptor* field) { // Note: The Google-internal version of Protocol Buffers uses this function // as a hook point for hacks to support legacy code. @@ -360,7 +408,7 @@ string FilenameIdentifier(const string& filename) { } else { // Not alphanumeric. To avoid any possibility of name conflicts we // use the hex code for the character. - StrAppend(&result, "_", ToHex(static_cast(filename[i]))); + StrAppend(&result, "_", strings::Hex(static_cast(filename[i]))); } } return result; @@ -521,6 +569,15 @@ FieldOptions::CType EffectiveStringCType(const FieldDescriptor* field) { } +bool IsAnyMessage(const FileDescriptor* descriptor) { + return descriptor->name() == kAnyProtoFile; +} + +bool IsAnyMessage(const Descriptor* descriptor) { + return descriptor->name() == kAnyMessageName && + descriptor->file()->name() == kAnyProtoFile; +} + } // namespace cpp } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/cpp/cpp_helpers.h b/src/google/protobuf/compiler/cpp/cpp_helpers.h index 284fa2c1..4bbf8303 100644 --- a/src/google/protobuf/compiler/cpp/cpp_helpers.h +++ b/src/google/protobuf/compiler/cpp/cpp_helpers.h @@ -66,6 +66,10 @@ extern const char kThinSeparator[]; string ClassName(const Descriptor* descriptor, bool qualified); string ClassName(const EnumDescriptor* enum_descriptor, bool qualified); +// Name of the CRTP class template (for use with proto_h). +// This is a class name, like "ProtoName_InternalBase". +string DependentBaseClassTemplateName(const Descriptor* descriptor); + string SuperClassName(const Descriptor* descriptor); // Get the (unqualified) name that should be used for this field in C++ code. @@ -88,6 +92,20 @@ inline const Descriptor* FieldScope(const FieldDescriptor* field) { field->extension_scope() : field->containing_type(); } +// Returns true if the given 'field_descriptor' has a message type that is +// a dependency of the file where the field is defined (i.e., the field +// type is defined in a different file than the message holding the field). +// +// This only applies to Message-typed fields. Enum-typed fields may refer +// to an enum in a dependency; however, enums are specified and +// forward-declared with an enum-base, so the definition is not required to +// manipulate the field value. +bool IsFieldDependent(const FieldDescriptor* field_descriptor); + +// Returns the name that should be used for forcing dependent lookup from a +// dependent base class. +string DependentTypeName(const FieldDescriptor* field); + // Returns the fully-qualified type name field->message_type(). Usually this // is just ClassName(field->message_type(), true); string FieldMessageTypeName(const FieldDescriptor* field); @@ -242,6 +260,9 @@ inline bool SupportsArenas(const FieldDescriptor* field) { return SupportsArenas(field->file()); } +bool IsAnyMessage(const FileDescriptor* descriptor); +bool IsAnyMessage(const Descriptor* descriptor); + } // namespace cpp } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/cpp/cpp_map_field.cc b/src/google/protobuf/compiler/cpp/cpp_map_field.cc index 8c38db2b..0ff0d27c 100644 --- a/src/google/protobuf/compiler/cpp/cpp_map_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_map_field.cc @@ -244,7 +244,8 @@ GenerateSerializeWithCachedSizes(io::Printer* printer) const { "{\n" " ::google::protobuf::scoped_ptr<$map_classname$> entry;\n" " for (::google::protobuf::Map< $key_cpp$, $val_cpp$ >::const_iterator\n" - " it = $name$().begin(); it != $name$().end(); ++it) {\n"); + " it = this->$name$().begin();\n" + " it != this->$name$().end(); ++it) {\n"); // If entry is allocated by arena, its desctructor should be avoided. if (SupportsArenas(descriptor_)) { @@ -277,7 +278,8 @@ GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const { "{\n" " ::google::protobuf::scoped_ptr<$map_classname$> entry;\n" " for (::google::protobuf::Map< $key_cpp$, $val_cpp$ >::const_iterator\n" - " it = $name$().begin(); it != $name$().end(); ++it) {\n"); + " it = this->$name$().begin();\n" + " it != this->$name$().end(); ++it) {\n"); // If entry is allocated by arena, its desctructor should be avoided. if (SupportsArenas(descriptor_)) { @@ -312,7 +314,8 @@ GenerateByteSize(io::Printer* printer) const { "{\n" " ::google::protobuf::scoped_ptr<$map_classname$> entry;\n" " for (::google::protobuf::Map< $key_cpp$, $val_cpp$ >::const_iterator\n" - " it = $name$().begin(); it != $name$().end(); ++it) {\n"); + " it = this->$name$().begin();\n" + " it != this->$name$().end(); ++it) {\n"); // If entry is allocated by arena, its desctructor should be avoided. if (SupportsArenas(descriptor_)) { diff --git a/src/google/protobuf/compiler/cpp/cpp_message.cc b/src/google/protobuf/compiler/cpp/cpp_message.cc index 98929b1e..212fe2e1 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message.cc +++ b/src/google/protobuf/compiler/cpp/cpp_message.cc @@ -64,10 +64,15 @@ using internal::WireFormatLite; namespace { -void PrintFieldComment(io::Printer* printer, const FieldDescriptor* field) { - // Print the field's proto-syntax definition as a comment. We don't want to - // print group bodies so we cut off after the first line. - string def = field->DebugString(); +template +void PrintFieldComment(io::Printer* printer, const T* field) { + // Print the field's (or oneof's) proto-syntax definition as a comment. + // We don't want to print group bodies so we cut off after the first + // line. + DebugStringOptions options; + options.elide_group_body = true; + options.elide_oneof_body = true; + string def = field->DebugStringWithOptions(options); printer->Print("// $def$\n", "def", def.substr(0, def.find_first_of('\n'))); } @@ -280,6 +285,10 @@ void OptimizePadding(vector* fields) { } } +string MessageTypeProtoName(const FieldDescriptor* field) { + return field->message_type()->full_name(); +} + // Emits an if-statement with a condition that evaluates to true if |field| is // considered non-default (will be sent over the wire), for message types // without true field presence. Should only be called if @@ -379,7 +388,8 @@ MessageGenerator::MessageGenerator(const Descriptor* descriptor, enum_generators_( new google::protobuf::scoped_ptr[descriptor->enum_type_count()]), extension_generators_(new google::protobuf::scoped_ptr< - ExtensionGenerator>[descriptor->extension_count()]) { + ExtensionGenerator>[descriptor->extension_count()]), + use_dependent_base_(false) { for (int i = 0; i < descriptor->nested_type_count(); i++) { nested_generators_[i].reset( @@ -401,13 +411,16 @@ MessageGenerator::MessageGenerator(const Descriptor* descriptor, if (descriptor->field(i)->is_required()) { ++num_required_fields_; } + if (options.proto_h && IsFieldDependent(descriptor->field(i))) { + use_dependent_base_ = true; + } } } MessageGenerator::~MessageGenerator() {} void MessageGenerator:: -GenerateForwardDeclaration(io::Printer* printer) { +GenerateMessageForwardDeclaration(io::Printer* printer) { printer->Print("class $classname$;\n", "classname", classname_); @@ -416,7 +429,17 @@ GenerateForwardDeclaration(io::Printer* printer) { // message cannot be a top level class, we just need to avoid calling // GenerateForwardDeclaration here. if (IsMapEntryMessage(descriptor_->nested_type(i))) continue; - nested_generators_[i]->GenerateForwardDeclaration(printer); + nested_generators_[i]->GenerateMessageForwardDeclaration(printer); + } +} + +void MessageGenerator:: +GenerateEnumForwardDeclaration(io::Printer* printer) { + for (int i = 0; i < descriptor_->nested_type_count(); i++) { + nested_generators_[i]->GenerateEnumForwardDeclaration(printer); + } + for (int i = 0; i < descriptor_->enum_type_count(); i++) { + enum_generators_[i]->GenerateForwardDeclaration(printer); } } @@ -441,6 +464,35 @@ GenerateGetEnumDescriptorSpecializations(io::Printer* printer) { } } +void MessageGenerator:: +GenerateDependentFieldAccessorDeclarations(io::Printer* printer) { + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + + PrintFieldComment(printer, field); + + map vars; + SetCommonFieldVariables(field, &vars, options_); + + if (use_dependent_base_ && IsFieldDependent(field)) { + // If the message is dependent, the inline clear_*() method will need + // to delete the message type, so it must be in the dependent base + // class. (See also GenerateFieldAccessorDeclarations.) + printer->Print(vars, "void clear_$name$()$deprecation$;\n"); + } + // Generate type-specific accessor declarations. + field_generators_.get(field).GenerateDependentAccessorDeclarations(printer); + printer->Print("\n"); + } + for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { + const OneofDescriptor* oneof = descriptor_->oneof_decl(i); + PrintFieldComment(printer, oneof); + printer->Print( + "void clear_$oneof_name$();\n", + "oneof_name", oneof->name()); + } +} + void MessageGenerator:: GenerateFieldAccessorDeclarations(io::Printer* printer) { for (int i = 0; i < descriptor_->field_count(); i++) { @@ -452,6 +504,19 @@ GenerateFieldAccessorDeclarations(io::Printer* printer) { SetCommonFieldVariables(field, &vars, options_); vars["constant_name"] = FieldConstantName(field); + bool dependent_field = use_dependent_base_ && IsFieldDependent(field); + if (dependent_field) { + // If this field is dependent, the dependent base class determines + // the message type from the derived class (which is a template + // parameter). This typedef is for that: + printer->Print( + "private:\n" + "typedef $field_type$ $dependent_type$;\n" + "public:\n", + "field_type", FieldMessageTypeName(field), + "dependent_type", DependentTypeName(field)); + } + if (field->is_repeated()) { printer->Print(vars, "int $name$_size() const$deprecation$;\n"); } else if (HasHasMethod(field)) { @@ -463,7 +528,11 @@ GenerateFieldAccessorDeclarations(io::Printer* printer) { "public:\n"); } - printer->Print(vars, "void clear_$name$()$deprecation$;\n"); + if (!dependent_field) { + // If this field is dependent, then its clear_() method is in the + // depenent base class. (See also GenerateDependentAccessorDeclarations.) + printer->Print(vars, "void clear_$name$()$deprecation$;\n"); + } printer->Print(vars, "static const int $constant_name$ = $number$;\n"); // Generate type-specific accessor declarations. @@ -489,6 +558,188 @@ GenerateFieldAccessorDeclarations(io::Printer* printer) { } } +void MessageGenerator:: +GenerateDependentFieldAccessorDefinitions(io::Printer* printer) { + if (!use_dependent_base_) return; + + printer->Print("// $classname$\n\n", "classname", + DependentBaseClassTemplateName(descriptor_)); + + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + + PrintFieldComment(printer, field); + + // These functions are not really dependent: they are part of the + // (non-dependent) derived class. However, they need to live outside + // any #ifdef guards, so we treat them as if they were dependent. + // + // See the comment in FileGenerator::GenerateInlineFunctionDefinitions + // for a more complete explanation. + if (use_dependent_base_ && IsFieldDependent(field)) { + map vars; + SetCommonFieldVariables(field, &vars, options_); + vars["inline"] = "inline "; + if (field->containing_oneof()) { + vars["field_name"] = UnderscoresToCamelCase(field->name(), true); + vars["oneof_name"] = field->containing_oneof()->name(); + vars["oneof_index"] = SimpleItoa(field->containing_oneof()->index()); + GenerateOneofMemberHasBits(field, vars, printer); + } else if (!field->is_repeated()) { + // There will be no header guard, so this always has to be inline. + GenerateSingularFieldHasBits(field, vars, printer); + } + // vars needed for clear_(), which is in the dependent base: + // (See also GenerateDependentFieldAccessorDeclarations.) + vars["tmpl"] = "template\n"; + vars["dependent_classname"] = + DependentBaseClassTemplateName(descriptor_) + ""; + vars["this_message"] = "reinterpret_cast(this)->"; + vars["this_const_message"] = "reinterpret_cast(this)->"; + GenerateFieldClear(field, vars, printer); + } + + // Generate type-specific accessors. + field_generators_.get(field) + .GenerateDependentInlineAccessorDefinitions(printer); + + printer->Print("\n"); + } + + // Generate has_$name$() and clear_has_$name$() functions for oneofs + // Similar to other has-bits, these must always be in the header if we + // are using a dependent base class. + GenerateOneofHasBits(printer, true /* is_inline */); +} + +void MessageGenerator:: +GenerateSingularFieldHasBits(const FieldDescriptor* field, + map vars, + io::Printer* printer) { + if (HasFieldPresence(descriptor_->file())) { + // N.B.: without field presence, we do not use has-bits or generate + // has_$name$() methods. + vars["has_array_index"] = SimpleItoa(field->index() / 32); + vars["has_mask"] = StrCat(strings::Hex(1u << (field->index() % 32), + strings::Hex::ZERO_PAD_8)); + printer->Print(vars, + "$inline$" + "bool $classname$::has_$name$() const {\n" + " return (_has_bits_[$has_array_index$] & 0x$has_mask$u) != 0;\n" + "}\n" + "$inline$" + "void $classname$::set_has_$name$() {\n" + " _has_bits_[$has_array_index$] |= 0x$has_mask$u;\n" + "}\n" + "$inline$" + "void $classname$::clear_has_$name$() {\n" + " _has_bits_[$has_array_index$] &= ~0x$has_mask$u;\n" + "}\n"); + } else { + // Message fields have a has_$name$() method. + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + bool is_lazy = false; + if (is_lazy) { + printer->Print(vars, + "$inline$" + "bool $classname$::has_$name$() const {\n" + " return !$name$_.IsCleared();\n" + "}\n"); + } else { + printer->Print(vars, + "$inline$" + "bool $classname$::has_$name$() const {\n" + " return !_is_default_instance_ && $name$_ != NULL;\n" + "}\n"); + } + } + } +} + +void MessageGenerator:: +GenerateOneofHasBits(io::Printer* printer, bool is_inline) { + for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { + map vars; + vars["oneof_name"] = descriptor_->oneof_decl(i)->name(); + vars["oneof_index"] = SimpleItoa(descriptor_->oneof_decl(i)->index()); + vars["cap_oneof_name"] = + ToUpper(descriptor_->oneof_decl(i)->name()); + vars["classname"] = classname_; + vars["inline"] = (is_inline ? "inline " : ""); + printer->Print( + vars, + "$inline$" + "bool $classname$::has_$oneof_name$() const {\n" + " return $oneof_name$_case() != $cap_oneof_name$_NOT_SET;\n" + "}\n" + "$inline$" + "void $classname$::clear_has_$oneof_name$() {\n" + " _oneof_case_[$oneof_index$] = $cap_oneof_name$_NOT_SET;\n" + "}\n"); + } +} + +void MessageGenerator:: +GenerateOneofMemberHasBits(const FieldDescriptor* field, + const map& vars, + io::Printer* printer) { + // Singular field in a oneof + // N.B.: Without field presence, we do not use has-bits or generate + // has_$name$() methods, but oneofs still have set_has_$name$(). + // Oneofs also have has_$name$() but only as a private helper + // method, so that generated code is slightly cleaner (vs. comparing + // _oneof_case_[index] against a constant everywhere). + printer->Print(vars, + "$inline$" + "bool $classname$::has_$name$() const {\n" + " return $oneof_name$_case() == k$field_name$;\n" + "}\n"); + printer->Print(vars, + "$inline$" + "void $classname$::set_has_$name$() {\n" + " _oneof_case_[$oneof_index$] = k$field_name$;\n" + "}\n"); +} + +void MessageGenerator:: +GenerateFieldClear(const FieldDescriptor* field, + const map& vars, + io::Printer* printer) { + // Generate clear_$name$() (See GenerateFieldAccessorDeclarations and + // GenerateDependentFieldAccessorDeclarations, $dependent_classname$ is + // set by the Generate*Definitions functions.) + printer->Print(vars, + "$tmpl$" + "$inline$" + "void $dependent_classname$::clear_$name$() {\n"); + + printer->Indent(); + + if (field->containing_oneof()) { + // Clear this field only if it is the active field in this oneof, + // otherwise ignore + printer->Print(vars, + "if ($this_message$has_$name$()) {\n"); + printer->Indent(); + field_generators_.get(field).GenerateClearingCode(printer); + printer->Print(vars, + "$this_message$clear_has_$oneof_name$();\n"); + printer->Outdent(); + printer->Print("}\n"); + } else { + field_generators_.get(field).GenerateClearingCode(printer); + if (HasFieldPresence(descriptor_->file())) { + if (!field->is_repeated()) { + printer->Print(vars, + "$this_message$clear_has_$name$();\n"); + } + } + } + + printer->Outdent(); + printer->Print("}\n"); +} + void MessageGenerator:: GenerateFieldAccessorDefinitions(io::Printer* printer, bool is_inline) { printer->Print("// $classname$\n\n", "classname", classname_); @@ -500,101 +751,37 @@ GenerateFieldAccessorDefinitions(io::Printer* printer, bool is_inline) { map vars; SetCommonFieldVariables(field, &vars, options_); - vars["inline"] = is_inline ? "inline" : ""; + vars["inline"] = is_inline ? "inline " : ""; // Generate has_$name$() or $name$_size(). if (field->is_repeated()) { printer->Print(vars, - "$inline$ int $classname$::$name$_size() const {\n" + "$inline$" + "int $classname$::$name$_size() const {\n" " return $name$_.size();\n" "}\n"); } else if (field->containing_oneof()) { - // Singular field in a oneof - // N.B.: Without field presence, we do not use has-bits or generate - // has_$name$() methods, but oneofs still have set_has_$name$(). - // Oneofs also have has_$name$() but only as a private helper - // method, so that generated code is slightly cleaner (vs. comparing - // _oneof_case_[index] against a constant everywhere). vars["field_name"] = UnderscoresToCamelCase(field->name(), true); vars["oneof_name"] = field->containing_oneof()->name(); vars["oneof_index"] = SimpleItoa(field->containing_oneof()->index()); - printer->Print(vars, - "$inline$ bool $classname$::has_$name$() const {\n" - " return $oneof_name$_case() == k$field_name$;\n" - "}\n"); - printer->Print(vars, - "$inline$ void $classname$::set_has_$name$() {\n" - " _oneof_case_[$oneof_index$] = k$field_name$;\n" - "}\n"); + if (!use_dependent_base_ || !IsFieldDependent(field)) { + GenerateOneofMemberHasBits(field, vars, printer); + } } else { // Singular field. - if (HasFieldPresence(descriptor_->file())) { - // N.B.: without field presence, we do not use has-bits or generate - // has_$name$() methods. - char buffer[kFastToBufferSize]; - vars["has_array_index"] = SimpleItoa(field->index() / 32); - vars["has_mask"] = FastHex32ToBuffer(1u << (field->index() % 32), - buffer); - printer->Print(vars, - "$inline$ bool $classname$::has_$name$() const {\n" - " return (_has_bits_[$has_array_index$] & 0x$has_mask$u) != 0;\n" - "}\n" - "$inline$ void $classname$::set_has_$name$() {\n" - " _has_bits_[$has_array_index$] |= 0x$has_mask$u;\n" - "}\n" - "$inline$ void $classname$::clear_has_$name$() {\n" - " _has_bits_[$has_array_index$] &= ~0x$has_mask$u;\n" - "}\n" - ); - } else { - // Message fields have a has_$name$() method. - if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { - bool is_lazy = false; - if (is_lazy) { - printer->Print(vars, - "$inline$ bool $classname$::has_$name$() const {\n" - " return !$name$_.IsCleared();\n" - "}\n"); - } else { - printer->Print(vars, - "$inline$ bool $classname$::has_$name$() const {\n" - " return !_is_default_instance_ && $name$_ != NULL;\n" - "}\n"); - } - } + if (!use_dependent_base_ || !IsFieldDependent(field)) { + GenerateSingularFieldHasBits(field, vars, printer); } } - // Generate clear_$name$() - printer->Print(vars, - "$inline$ void $classname$::clear_$name$() {\n"); - - printer->Indent(); - - if (field->containing_oneof()) { - // Clear this field only if it is the active field in this oneof, - // otherwise ignore - printer->Print(vars, - "if (has_$name$()) {\n"); - printer->Indent(); - field_generators_.get(field).GenerateClearingCode(printer); - printer->Print(vars, - "clear_has_$oneof_name$();\n"); - printer->Outdent(); - printer->Print("}\n"); - } else { - field_generators_.get(field).GenerateClearingCode(printer); - if (HasFieldPresence(descriptor_->file())) { - if (!field->is_repeated()) { - printer->Print(vars, - "clear_has_$name$();\n"); - } - } + if (!use_dependent_base_ || !IsFieldDependent(field)) { + vars["tmpl"] = ""; + vars["dependent_classname"] = vars["classname"]; + vars["this_message"] = ""; + vars["this_const_message"] = ""; + GenerateFieldClear(field, vars, printer); } - printer->Outdent(); - printer->Print("}\n"); - // Generate type-specific accessors. field_generators_.get(field).GenerateInlineAccessorDefinitions(printer, is_inline); @@ -602,23 +789,11 @@ GenerateFieldAccessorDefinitions(io::Printer* printer, bool is_inline) { printer->Print("\n"); } - // Generate has_$name$() and clear_has_$name$() functions for oneofs - for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { - map vars; - vars["oneof_name"] = descriptor_->oneof_decl(i)->name(); - vars["oneof_index"] = SimpleItoa(descriptor_->oneof_decl(i)->index()); - vars["cap_oneof_name"] = - ToUpper(descriptor_->oneof_decl(i)->name()); - vars["classname"] = classname_; - vars["inline"] = is_inline ? "inline" : ""; - printer->Print( - vars, - "$inline$ bool $classname$::has_$oneof_name$() const {\n" - " return $oneof_name$_case() != $cap_oneof_name$_NOT_SET;\n" - "}\n" - "$inline$ void $classname$::clear_has_$oneof_name$() {\n" - " _oneof_case_[$oneof_index$] = $cap_oneof_name$_NOT_SET;\n" - "}\n"); + if (!use_dependent_base_) { + // Generate has_$name$() and clear_has_$name$() functions for oneofs + // If we aren't using a dependent base, they can be with the other functions + // that are #ifdef-guarded. + GenerateOneofHasBits(printer, is_inline); } } @@ -647,6 +822,34 @@ static bool CanClearByZeroing(const FieldDescriptor* field) { } } +void MessageGenerator:: +GenerateDependentBaseClassDefinition(io::Printer* printer) { + if (!use_dependent_base_) { + return; + } + + map vars; + vars["classname"] = DependentBaseClassTemplateName(descriptor_); + vars["superclass"] = SuperClassName(descriptor_); + + printer->Print(vars, + "template \n" + "class $classname$ : public $superclass$ {\n" + " public:\n"); + printer->Indent(); + + printer->Print(vars, + "$classname$() {}\n" + "virtual ~$classname$() {}\n" + "\n"); + + // Generate dependent accessor methods for all fields. + GenerateDependentFieldAccessorDeclarations(printer); + + printer->Outdent(); + printer->Print("};\n"); +} + void MessageGenerator:: GenerateClassDefinition(io::Printer* printer) { for (int i = 0; i < descriptor_->nested_type_count(); i++) { @@ -660,6 +863,11 @@ GenerateClassDefinition(io::Printer* printer) { printer->Print("\n"); } + if (use_dependent_base_) { + GenerateDependentBaseClassDefinition(printer); + printer->Print("\n"); + } + map vars; vars["classname"] = classname_; vars["field_count"] = SimpleItoa(descriptor_->field_count()); @@ -669,11 +877,18 @@ GenerateClassDefinition(io::Printer* printer) { } else { vars["dllexport"] = options_.dllexport_decl + " "; } - vars["superclass"] = SuperClassName(descriptor_); - + if (use_dependent_base_) { + vars["superclass"] = + DependentBaseClassTemplateName(descriptor_) + "<" + classname_ + ">"; + } else { + vars["superclass"] = SuperClassName(descriptor_); + } printer->Print(vars, - "class $dllexport$$classname$ : public $superclass$ {\n" - " public:\n"); + "class $dllexport$$classname$ : public $superclass$ {\n"); + if (use_dependent_base_) { + printer->Print(vars, " friend class $superclass$;\n"); + } + printer->Print(" public:\n"); printer->Indent(); printer->Print(vars, @@ -782,6 +997,19 @@ GenerateClassDefinition(io::Printer* printer) { printer->Print(vars, "void UnsafeArenaSwap($classname$* other);\n"); } + + if (IsAnyMessage(descriptor_)) { + printer->Print(vars, + "// implements Any -----------------------------------------------\n" + "\n" + "void PackFrom(const ::google::protobuf::Message& message);\n" + "bool UnpackTo(::google::protobuf::Message* message) const;\n" + "template bool Is() const {\n" + " return _any_metadata_.Is();\n" + "}\n" + "\n"); + } + printer->Print(vars, "void Swap($classname$* other);\n" "\n" @@ -973,11 +1201,18 @@ GenerateClassDefinition(io::Printer* printer) { // Generate oneof function declarations for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { - printer->Print( - "inline bool has_$oneof_name$() const;\n" - "void clear_$oneof_name$();\n" - "inline void clear_has_$oneof_name$();\n\n", - "oneof_name", descriptor_->oneof_decl(i)->name()); + if (use_dependent_base_) { + printer->Print( + "inline bool has_$oneof_name$() const;\n" + "inline void clear_has_$oneof_name$();\n\n", + "oneof_name", descriptor_->oneof_decl(i)->name()); + } else { + printer->Print( + "inline bool has_$oneof_name$() const;\n" + "void clear_$oneof_name$();\n" + "inline void clear_has_$oneof_name$();\n\n", + "oneof_name", descriptor_->oneof_decl(i)->name()); + } } if (HasGeneratedMethods(descriptor_->file()) && @@ -1139,6 +1374,12 @@ GenerateClassDefinition(io::Printer* printer) { "\n"); } + // Generate _any_metadata_ for the Any type. + if (IsAnyMessage(descriptor_)) { + printer->Print(vars, + "::google::protobuf::internal::AnyMetadata _any_metadata_;\n"); + } + // Declare AddDescriptors(), BuildDescriptors(), and ShutdownFile() as // friends so that they can access private static variables like // default_instance_ and reflection_. @@ -1171,6 +1412,21 @@ GenerateClassDefinition(io::Printer* printer) { GOOGLE_DCHECK(!need_to_emit_cached_size); } +void MessageGenerator:: +GenerateDependentInlineMethods(io::Printer* printer) { + for (int i = 0; i < descriptor_->nested_type_count(); i++) { + // map entry message doesn't need inline methods. Since map entry message + // cannot be a top level class, we just need to avoid calling + // GenerateInlineMethods here. + if (IsMapEntryMessage(descriptor_->nested_type(i))) continue; + nested_generators_[i]->GenerateDependentInlineMethods(printer); + printer->Print(kThinSeparator); + printer->Print("\n"); + } + + GenerateDependentFieldAccessorDefinitions(printer); +} + void MessageGenerator:: GenerateInlineMethods(io::Printer* printer, bool is_inline) { for (int i = 0; i < descriptor_->nested_type_count(); i++) { @@ -1196,7 +1452,8 @@ GenerateInlineMethods(io::Printer* printer, bool is_inline) { vars["inline"] = is_inline ? "inline " : ""; printer->Print( vars, - "$inline$$class_name$::$camel_oneof_name$Case $class_name$::" + "$inline$" + "$class_name$::$camel_oneof_name$Case $class_name$::" "$oneof_name$_case() const {\n" " return $class_name$::$camel_oneof_name$Case(" "_oneof_case_[$oneof_index$]);\n" @@ -1494,6 +1751,19 @@ GenerateShutdownCode(io::Printer* printer) { void MessageGenerator:: GenerateClassMethods(io::Printer* printer) { + if (IsAnyMessage(descriptor_)) { + printer->Print( + "void $classname$::PackFrom(const ::google::protobuf::Message& message) {\n" + " _any_metadata_.PackFrom(message);\n" + "}\n" + "\n" + "bool $classname$::UnpackTo(::google::protobuf::Message* message) const {\n" + " return _any_metadata_.UnpackTo(message);\n" + "}\n" + "\n", + "classname", classname_); + } + for (int i = 0; i < descriptor_->enum_type_count(); i++) { enum_generators_[i]->GenerateMethods(printer); } @@ -1767,7 +2037,7 @@ GenerateArenaDestructorCode(io::Printer* printer) { if (need_registration) { printer->Print( "inline void $classname$::RegisterArenaDtor(::google::protobuf::Arena* arena) {\n" - " if (arena != NULL) {" + " if (arena != NULL) {\n" " arena->OwnCustomDestructor(this, &$classname$::ArenaDtor);\n" " }\n" "}\n", @@ -1782,18 +2052,23 @@ GenerateArenaDestructorCode(io::Printer* printer) { void MessageGenerator:: GenerateStructors(io::Printer* printer) { - string superclass = SuperClassName(descriptor_); - string initializer_with_arena; - if (UseUnknownFieldSet(descriptor_->file())) { - initializer_with_arena = "_internal_metadata_(arena)"; + string superclass; + if (use_dependent_base_) { + superclass = + DependentBaseClassTemplateName(descriptor_) + "<" + classname_ + ">"; } else { - initializer_with_arena = "_arena_ptr_(arena)"; + superclass = SuperClassName(descriptor_); } + string initializer_with_arena = superclass + "()"; + if (descriptor_->extension_range_count() > 0) { - initializer_with_arena = string("\n _extensions_(arena)") + - (!initializer_with_arena.empty() ? ", " : "") + initializer_with_arena; + initializer_with_arena += ",\n _extensions_(arena)"; + } + + if (UseUnknownFieldSet(descriptor_->file())) { + initializer_with_arena += ",\n _internal_metadata_(arena)"; } else { - initializer_with_arena = "\n " + initializer_with_arena; + initializer_with_arena += ",\n _arena_ptr_(arena)"; } // Initialize member variables with arena constructor. @@ -1804,16 +2079,21 @@ GenerateStructors(io::Printer* printer) { FieldName(descriptor_->field(i)) + string("_(arena)"); } } - initializer_with_arena = superclass + "()" + - (!initializer_with_arena.empty() ? "," : " ") + initializer_with_arena; + + if (IsAnyMessage(descriptor_)) { + initializer_with_arena += ",\n _any_metadata_(&type_url, &value_)"; + } string initializer_null; initializer_null = (UseUnknownFieldSet(descriptor_->file()) ? - ", _internal_metadata_(NULL) " : ", _arena_ptr_(NULL)"); + ", _internal_metadata_(NULL)" : ", _arena_ptr_(NULL)"); + if (IsAnyMessage(descriptor_)) { + initializer_null += ", _any_metadata_(&type_url_, &value_)"; + } printer->Print( "$classname$::$classname$()\n" - " : $superclass$() $initializer$ {\n" + " : $superclass$()$initializer$ {\n" " SharedCtor();\n" " // @@protoc_insertion_point(constructor:$full_name$)\n" "}\n", @@ -1894,10 +2174,14 @@ GenerateStructors(io::Printer* printer) { "full_name", descriptor_->full_name()); if (UseUnknownFieldSet(descriptor_->file())) { printer->Print( - ",\n _internal_metadata_(NULL) {\n"); + ",\n _internal_metadata_(NULL)"); } else if (!UseUnknownFieldSet(descriptor_->file())) { - printer->Print(",\n _arena_ptr_(NULL) {\n"); + printer->Print(",\n _arena_ptr_(NULL)"); } + if (IsAnyMessage(descriptor_)) { + printer->Print(",\n _any_metadata_(&type_url_, &value_)"); + } + printer->Print(" {\n"); printer->Print( " SharedCtor();\n" " MergeFrom(from);\n" @@ -2124,7 +2408,11 @@ GenerateClear(io::Printer* printer) { have_enclosing_if = true; } - field_generators_.get(field).GenerateClearingCode(printer); + if (use_dependent_base_ && IsFieldDependent(field)) { + printer->Print("clear_$name$();\n", "name", fieldname); + } else { + field_generators_.get(field).GenerateClearingCode(printer); + } if (have_enclosing_if) { printer->Outdent(); @@ -2147,7 +2435,11 @@ GenerateClear(io::Printer* printer) { const FieldDescriptor* field = descriptor_->field(i); if (field->is_repeated()) { - field_generators_.get(field).GenerateClearingCode(printer); + if (use_dependent_base_ && IsFieldDependent(field)) { + printer->Print("clear_$name$();\n", "name", FieldName(field)); + } else { + field_generators_.get(field).GenerateClearingCode(printer); + } } } @@ -2184,19 +2476,38 @@ void MessageGenerator:: GenerateOneofClear(io::Printer* printer) { // Generated function clears the active field and union case (e.g. foo_case_). for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { - printer->Print( - "void $classname$::clear_$oneofname$() {\n", - "classname", classname_, - "oneofname", descriptor_->oneof_decl(i)->name()); + map oneof_vars; + oneof_vars["classname"] = classname_; + oneof_vars["oneofname"] = descriptor_->oneof_decl(i)->name(); + string message_class; + + if (use_dependent_base_) { + oneof_vars["tmpl"] = "template\n"; + oneof_vars["inline"] = "inline "; + oneof_vars["dependent_classname"] = + DependentBaseClassTemplateName(descriptor_) + ""; + oneof_vars["this_message"] = "reinterpret_cast(this)->"; + message_class = "T::"; + } else { + oneof_vars["tmpl"] = ""; + oneof_vars["inline"] = ""; + oneof_vars["dependent_classname"] = classname_; + oneof_vars["this_message"] = ""; + } + + printer->Print(oneof_vars, + "$tmpl$" + "$inline$" + "void $dependent_classname$::clear_$oneofname$() {\n"); printer->Indent(); - printer->Print( - "switch($oneofname$_case()) {\n", - "oneofname", descriptor_->oneof_decl(i)->name()); + printer->Print(oneof_vars, + "switch($this_message$$oneofname$_case()) {\n"); printer->Indent(); for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) { const FieldDescriptor* field = descriptor_->oneof_decl(i)->field(j); printer->Print( - "case k$field_name$: {\n", + "case $message_class$k$field_name$: {\n", + "message_class", message_class, "field_name", UnderscoresToCamelCase(field->name(), true)); printer->Indent(); // We clear only allocated objects in oneofs @@ -2213,16 +2524,20 @@ GenerateOneofClear(io::Printer* printer) { "}\n"); } printer->Print( - "case $cap_oneof_name$_NOT_SET: {\n" + "case $message_class$$cap_oneof_name$_NOT_SET: {\n" " break;\n" "}\n", + "message_class", message_class, "cap_oneof_name", ToUpper(descriptor_->oneof_decl(i)->name())); printer->Outdent(); printer->Print( "}\n" - "_oneof_case_[$oneof_index$] = $cap_oneof_name$_NOT_SET;\n", + "$this_message$_oneof_case_[$oneof_index$] = " + "$message_class$$cap_oneof_name$_NOT_SET;\n", + "this_message", oneof_vars["this_message"], "oneof_index", SimpleItoa(i), + "message_class", message_class, "cap_oneof_name", ToUpper(descriptor_->oneof_decl(i)->name())); printer->Outdent(); @@ -2333,9 +2648,9 @@ GenerateMergeFrom(io::Printer* printer) { // system, as the GOOGLE_CHECK above ensured that we have the same descriptor // for each message. printer->Print( - "const $classname$* source =\n" - " ::google::protobuf::internal::dynamic_cast_if_available(\n" - " &from);\n" + "const $classname$* source = \n" + " ::google::protobuf::internal::DynamicCastToGenerated(\n" + " &from);\n" "if (source == NULL) {\n" " ::google::protobuf::internal::ReflectionOps::Merge(from, this);\n" "} else {\n" @@ -2585,8 +2900,24 @@ GenerateMergeFromCodedStream(io::Printer* printer) { printer->Indent(); + // Find repeated messages and groups now, to simplify what follows. + hash_set fields_with_parse_loop; + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = ordered_fields[i]; + if (field->is_repeated() && + (field->type() == FieldDescriptor::TYPE_MESSAGE || + field->type() == FieldDescriptor::TYPE_GROUP)) { + fields_with_parse_loop.insert(i); + } + } + + // need_label is true if we generated "goto parse_$name$" while handling the + // previous field. + bool need_label = false; for (int i = 0; i < descriptor_->field_count(); i++) { const FieldDescriptor* field = ordered_fields[i]; + const bool loops = fields_with_parse_loop.count(i) > 0; + const bool next_field_loops = fields_with_parse_loop.count(i + 1) > 0; PrintFieldComment(printer, field); @@ -2600,9 +2931,16 @@ GenerateMergeFromCodedStream(io::Printer* printer) { printer->Print("if (tag == $commontag$) {\n", "commontag", SimpleItoa(WireFormat::MakeTag(field))); - if (i > 0 || (field->is_repeated() && !field->options().packed())) { + if (need_label || + (field->is_repeated() && !field->options().packed() && !loops)) { + printer->Print( + " parse_$name$:\n", + "name", field->name()); + } + if (loops) { printer->Print( - " parse_$name$:\n", + " DO_(input->IncrementRecursionDepth());\n" + " parse_loop_$name$:\n", "name", field->name()); } @@ -2644,26 +2982,53 @@ GenerateMergeFromCodedStream(io::Printer* printer) { // switch() is slow since it can't be predicted well. Insert some if()s // here that attempt to predict the next tag. - if (field->is_repeated() && !field->options().packed()) { - // Expect repeats of this field. + // For non-packed repeated fields, expect the same tag again. + if (loops) { + printer->Print( + "if (input->ExpectTag($tag$)) goto parse_loop_$name$;\n", + "tag", SimpleItoa(WireFormat::MakeTag(field)), + "name", field->name()); + } else if (field->is_repeated() && !field->options().packed()) { printer->Print( "if (input->ExpectTag($tag$)) goto parse_$name$;\n", "tag", SimpleItoa(WireFormat::MakeTag(field)), "name", field->name()); } - if (i + 1 < descriptor_->field_count()) { - // Expect the next field in order. - const FieldDescriptor* next_field = ordered_fields[i + 1]; - printer->Print( - "if (input->ExpectTag($next_tag$)) goto parse_$next_name$;\n", - "next_tag", SimpleItoa(WireFormat::MakeTag(next_field)), - "next_name", next_field->name()); - } else { - // Expect EOF. - // TODO(kenton): Expect group end-tag? + // Have we emitted "if (input->ExpectTag($next_tag$)) ..." yet? + bool emitted_goto_next_tag = false; + + // For repeated messages/groups, we need to decrement recursion depth, + // unless the next tag is also for a repeated message/group. + if (loops) { + if (next_field_loops) { + const FieldDescriptor* next_field = ordered_fields[i + 1]; + printer->Print( + "if (input->ExpectTag($next_tag$)) goto parse_loop_$next_name$;\n", + "next_tag", SimpleItoa(WireFormat::MakeTag(next_field)), + "next_name", next_field->name()); + emitted_goto_next_tag = true; + } printer->Print( - "if (input->ExpectAtEnd()) goto success;\n"); + "input->UnsafeDecrementRecursionDepth();\n"); + } + + // If there are more fields, expect the next one. + need_label = false; + if (!emitted_goto_next_tag) { + if (i + 1 == descriptor_->field_count()) { + // Expect EOF. + // TODO(kenton): Expect group end-tag? + printer->Print( + "if (input->ExpectAtEnd()) goto success;\n"); + } else { + const FieldDescriptor* next_field = ordered_fields[i + 1]; + printer->Print( + "if (input->ExpectTag($next_tag$)) goto parse_$next_name$;\n", + "next_tag", SimpleItoa(WireFormat::MakeTag(next_field)), + "next_name", next_field->name()); + need_label = true; + } } printer->Print( @@ -2999,9 +3364,7 @@ static string ConditionalToCheckBitmasks(const vector& masks) { vector parts; for (int i = 0; i < masks.size(); i++) { if (masks[i] == 0) continue; - char buffer[kFastToBufferSize]; - FastHex32ToBuffer(masks[i], buffer); - string m = StrCat("0x", buffer); + string m = StrCat("0x", strings::Hex(masks[i], strings::Hex::ZERO_PAD_8)); // Each xor evaluates to 0 if the expected bits are present. parts.push_back(StrCat("((_has_bits_[", i, "] & ", m, ") ^ ", m, ")")); } @@ -3293,11 +3656,10 @@ GenerateIsInitialized(io::Printer* printer) { } if (mask != 0) { - char buffer[kFastToBufferSize]; printer->Print( "if ((_has_bits_[$i$] & 0x$mask$) != 0x$mask$) return false;\n", "i", SimpleItoa(i), - "mask", FastHex32ToBuffer(mask, buffer)); + "mask", StrCat(strings::Hex(mask, strings::Hex::ZERO_PAD_8))); } } } diff --git a/src/google/protobuf/compiler/cpp/cpp_message.h b/src/google/protobuf/compiler/cpp/cpp_message.h index ea96581d..23dad10c 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message.h +++ b/src/google/protobuf/compiler/cpp/cpp_message.h @@ -67,7 +67,8 @@ class MessageGenerator { // Header stuff. // Generate foward declarations for this class and all its nested types. - void GenerateForwardDeclaration(io::Printer* printer); + void GenerateMessageForwardDeclaration(io::Printer* printer); + void GenerateEnumForwardDeclaration(io::Printer* printer); // Generate definitions of all nested enums (must come before class // definitions because those classes use the enums definitions). @@ -84,6 +85,9 @@ class MessageGenerator { // file). void GenerateInlineMethods(io::Printer* printer, bool is_inline); + // Dependent methods are always inline. + void GenerateDependentInlineMethods(io::Printer* printer); + // Source file stuff. // Generate code which declares all the global descriptor pointers which @@ -115,7 +119,10 @@ class MessageGenerator { private: // Generate declarations and definitions of accessors for fields. + void GenerateDependentBaseClassDefinition(io::Printer* printer); + void GenerateDependentFieldAccessorDeclarations(io::Printer* printer); void GenerateFieldAccessorDeclarations(io::Printer* printer); + void GenerateDependentFieldAccessorDefinitions(io::Printer* printer); void GenerateFieldAccessorDefinitions(io::Printer* printer, bool is_inline); // Generate the field offsets array. @@ -158,6 +165,21 @@ class MessageGenerator { bool unbounded); + // Generates has_foo() functions and variables for singular field has-bits. + void GenerateSingularFieldHasBits(const FieldDescriptor* field, + map vars, + io::Printer* printer); + // Generates has_foo() functions and variables for oneof field has-bits. + void GenerateOneofHasBits(io::Printer* printer, bool is_inline); + // Generates has_foo_bar() functions for oneof members. + void GenerateOneofMemberHasBits(const FieldDescriptor* field, + const map& vars, + io::Printer* printer); + // Generates the clear_foo() method for a field. + void GenerateFieldClear(const FieldDescriptor* field, + const map& vars, + io::Printer* printer); + const Descriptor* descriptor_; string classname_; Options options_; @@ -168,6 +190,7 @@ class MessageGenerator { google::protobuf::scoped_array > extension_generators_; int num_required_fields_; bool uses_string_; + bool use_dependent_base_; GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageGenerator); }; diff --git a/src/google/protobuf/compiler/cpp/cpp_message_field.cc b/src/google/protobuf/compiler/cpp/cpp_message_field.cc index 467b6bf6..ba318d10 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_message_field.cc @@ -72,7 +72,8 @@ void SetMessageVariables(const FieldDescriptor* descriptor, MessageFieldGenerator:: MessageFieldGenerator(const FieldDescriptor* descriptor, const Options& options) - : descriptor_(descriptor) { + : descriptor_(descriptor), + dependent_field_(options.proto_h && IsFieldDependent(descriptor)) { SetMessageVariables(descriptor, &variables_, options); } @@ -83,6 +84,10 @@ GeneratePrivateMembers(io::Printer* printer) const { printer->Print(variables_, "$type$* $name$_;\n"); } +void MessageFieldGenerator:: +GenerateDependentAccessorDeclarations(io::Printer* printer) const { +} + void MessageFieldGenerator:: GenerateAccessorDeclarations(io::Printer* printer) const { if (SupportsArenas(descriptor_)) { @@ -144,6 +149,8 @@ void MessageFieldGenerator::GenerateNonInlineAccessorDefinitions( " return temp;\n" "}\n"); if (SupportsArenas(descriptor_->message_type())) { + // NOTE: the same logic is mirrored in weak_message_field.cc. Any + // arena-related semantics changes should be made in both places. printer->Print(variables_, "void $classname$::_slow_set_allocated_$name$(\n" " ::google::protobuf::Arena* message_arena, $type$** $name$) {\n" @@ -180,6 +187,10 @@ void MessageFieldGenerator::GenerateNonInlineAccessorDefinitions( } } +void MessageFieldGenerator:: +GenerateDependentInlineAccessorDefinitions(io::Printer* printer) const { +} + void MessageFieldGenerator:: GenerateInlineAccessorDefinitions(io::Printer* printer, bool is_inline) const { @@ -294,7 +305,7 @@ GenerateClearingCode(io::Printer* printer) const { // If we don't have has-bits, message presence is indicated only by ptr != // NULL. Thus on clear, we need to delete the object. printer->Print(variables_, - "if ($name$_ != NULL) delete $name$_;\n" + "if (GetArenaNoVirtual() == NULL && $name$_ != NULL) delete $name$_;\n" "$name$_ = NULL;\n"); } else { printer->Print(variables_, @@ -365,6 +376,10 @@ MessageOneofFieldGenerator(const FieldDescriptor* descriptor, MessageOneofFieldGenerator::~MessageOneofFieldGenerator() {} +void MessageOneofFieldGenerator:: +GenerateDependentInlineAccessorDefinitions(io::Printer* printer) const { +} + void MessageOneofFieldGenerator:: GenerateInlineAccessorDefinitions(io::Printer* printer, bool is_inline) const { @@ -559,6 +574,10 @@ GeneratePrivateMembers(io::Printer* printer) const { "::google::protobuf::RepeatedPtrField< $type$ > $name$_;\n"); } +void RepeatedMessageFieldGenerator:: +GenerateDependentAccessorDeclarations(io::Printer* printer) const { +} + void RepeatedMessageFieldGenerator:: GenerateAccessorDeclarations(io::Printer* printer) const { printer->Print(variables_, @@ -572,6 +591,10 @@ GenerateAccessorDeclarations(io::Printer* printer) const { " mutable_$name$()$deprecation$;\n"); } +void RepeatedMessageFieldGenerator:: +GenerateDependentInlineAccessorDefinitions(io::Printer* printer) const { +} + void RepeatedMessageFieldGenerator:: GenerateInlineAccessorDefinitions(io::Printer* printer, bool is_inline) const { @@ -627,11 +650,13 @@ void RepeatedMessageFieldGenerator:: GenerateMergeFromCodedStream(io::Printer* printer) const { if (descriptor_->type() == FieldDescriptor::TYPE_MESSAGE) { printer->Print(variables_, - "DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(\n" + "DO_(::google::protobuf::internal::WireFormatLite::" + "ReadMessageNoVirtualNoRecursionDepth(\n" " input, add_$name$()));\n"); } else { printer->Print(variables_, - "DO_(::google::protobuf::internal::WireFormatLite::ReadGroupNoVirtual(\n" + "DO_(::google::protobuf::internal::WireFormatLite::" + "ReadGroupNoVirtualNoRecursionDepth(\n" " $number$, input, add_$name$()));\n"); } } diff --git a/src/google/protobuf/compiler/cpp/cpp_message_field.h b/src/google/protobuf/compiler/cpp/cpp_message_field.h index c1704fc1..9ddf9643 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message_field.h +++ b/src/google/protobuf/compiler/cpp/cpp_message_field.h @@ -52,7 +52,9 @@ class MessageFieldGenerator : public FieldGenerator { // implements FieldGenerator --------------------------------------- void GeneratePrivateMembers(io::Printer* printer) const; + void GenerateDependentAccessorDeclarations(io::Printer* printer) const; void GenerateAccessorDeclarations(io::Printer* printer) const; + void GenerateDependentInlineAccessorDefinitions(io::Printer* printer) const; void GenerateInlineAccessorDefinitions(io::Printer* printer, bool is_inline) const; void GenerateNonInlineAccessorDefinitions(io::Printer* printer) const; @@ -67,6 +69,7 @@ class MessageFieldGenerator : public FieldGenerator { protected: const FieldDescriptor* descriptor_; + const bool dependent_field_; map variables_; private: @@ -80,6 +83,7 @@ class MessageOneofFieldGenerator : public MessageFieldGenerator { ~MessageOneofFieldGenerator(); // implements FieldGenerator --------------------------------------- + void GenerateDependentInlineAccessorDefinitions(io::Printer* printer) const; void GenerateInlineAccessorDefinitions(io::Printer* printer, bool is_inline) const; void GenerateNonInlineAccessorDefinitions(io::Printer* printer) const {} @@ -99,7 +103,9 @@ class RepeatedMessageFieldGenerator : public FieldGenerator { // implements FieldGenerator --------------------------------------- void GeneratePrivateMembers(io::Printer* printer) const; + void GenerateDependentAccessorDeclarations(io::Printer* printer) const; void GenerateAccessorDeclarations(io::Printer* printer) const; + void GenerateDependentInlineAccessorDefinitions(io::Printer* printer) const; void GenerateInlineAccessorDefinitions(io::Printer* printer, bool is_inline) const; void GenerateClearingCode(io::Printer* printer) const; diff --git a/src/google/protobuf/compiler/cpp/cpp_options.h b/src/google/protobuf/compiler/cpp/cpp_options.h index 0c99cff1..4463f200 100644 --- a/src/google/protobuf/compiler/cpp/cpp_options.h +++ b/src/google/protobuf/compiler/cpp/cpp_options.h @@ -41,12 +41,13 @@ namespace protobuf { namespace compiler { namespace cpp { -// Generator options: +// Generator options (see generator.cc for a description of each): struct Options { - Options() : safe_boundary_check(false) { + Options() : safe_boundary_check(false), proto_h(false) { } string dllexport_decl; bool safe_boundary_check; + bool proto_h; }; } // namespace cpp diff --git a/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc b/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc index 329eae32..9f929d37 100644 --- a/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc @@ -262,7 +262,7 @@ RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor, : descriptor_(descriptor) { SetPrimitiveVariables(descriptor, &variables_, options); - if (descriptor->options().packed()) { + if (descriptor->is_packed()) { variables_["packed_reader"] = "ReadPackedPrimitive"; variables_["repeated_reader"] = "ReadRepeatedPrimitiveNoInline"; } else { @@ -277,7 +277,7 @@ void RepeatedPrimitiveFieldGenerator:: GeneratePrivateMembers(io::Printer* printer) const { printer->Print(variables_, "::google::protobuf::RepeatedField< $type$ > $name$_;\n"); - if (descriptor_->options().packed() && HasGeneratedMethods(descriptor_->file())) { + if (descriptor_->is_packed() && HasGeneratedMethods(descriptor_->file())) { printer->Print(variables_, "mutable int _$name$_cached_byte_size_;\n"); } @@ -364,7 +364,7 @@ GenerateMergeFromCodedStreamWithPacking(io::Printer* printer) const { void RepeatedPrimitiveFieldGenerator:: GenerateSerializeWithCachedSizes(io::Printer* printer) const { - if (descriptor_->options().packed()) { + if (descriptor_->is_packed()) { // Write the tag and the size. printer->Print(variables_, "if (this->$name$_size() > 0) {\n" @@ -377,7 +377,7 @@ GenerateSerializeWithCachedSizes(io::Printer* printer) const { } printer->Print(variables_, "for (int i = 0; i < this->$name$_size(); i++) {\n"); - if (descriptor_->options().packed()) { + if (descriptor_->is_packed()) { printer->Print(variables_, " ::google::protobuf::internal::WireFormatLite::Write$declared_type$NoTag(\n" " this->$name$(i), output);\n"); @@ -391,7 +391,7 @@ GenerateSerializeWithCachedSizes(io::Printer* printer) const { void RepeatedPrimitiveFieldGenerator:: GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const { - if (descriptor_->options().packed()) { + if (descriptor_->is_packed()) { // Write the tag and the size. printer->Print(variables_, "if (this->$name$_size() > 0) {\n" @@ -405,7 +405,7 @@ GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const { } printer->Print(variables_, "for (int i = 0; i < this->$name$_size(); i++) {\n"); - if (descriptor_->options().packed()) { + if (descriptor_->is_packed()) { printer->Print(variables_, " target = ::google::protobuf::internal::WireFormatLite::\n" " Write$declared_type$NoTagToArray(this->$name$(i), target);\n"); @@ -435,7 +435,7 @@ GenerateByteSize(io::Printer* printer) const { "data_size = $fixed_size$ * this->$name$_size();\n"); } - if (descriptor_->options().packed()) { + if (descriptor_->is_packed()) { printer->Print(variables_, "if (data_size > 0) {\n" " total_size += $tag_size$ +\n" diff --git a/src/google/protobuf/compiler/cpp/cpp_string_field.cc b/src/google/protobuf/compiler/cpp/cpp_string_field.cc index 1a1bcd3d..1a3896a1 100644 --- a/src/google/protobuf/compiler/cpp/cpp_string_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_string_field.cc @@ -226,7 +226,6 @@ GenerateInlineAccessorDefinitions(io::Printer* printer, " } else {\n" " $clear_hasbit$\n" " }\n" - " $set_hasbit$\n" " $name$_.UnsafeArenaSetAllocated($default_variable$,\n" " $name$, GetArenaNoVirtual());\n" " // @@protoc_insertion_point(field_set_allocated:$full_name$)\n" diff --git a/src/google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto b/src/google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto index 9f63155b..4e25b2ea 100644 --- a/src/google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto +++ b/src/google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto @@ -131,23 +131,23 @@ message TestConflictingSymbolNamesExtension { // NO_PROTO3 } // NO_PROTO3 } // NO_PROTO3 -message TestConflictingEnumNames { - enum NestedConflictingEnum { - and = 1; - class = 2; - int = 3; - typedef = 4; - XOR = 5; - } - - optional NestedConflictingEnum conflicting_enum = 1; -} - -enum ConflictingEnum { - NOT_EQ = 1; - volatile = 2; - return = 3; -} +message TestConflictingEnumNames { // NO_PROTO3 + enum NestedConflictingEnum { // NO_PROTO3 + and = 1; // NO_PROTO3 + class = 2; // NO_PROTO3 + int = 3; // NO_PROTO3 + typedef = 4; // NO_PROTO3 + XOR = 5; // NO_PROTO3 + } // NO_PROTO3 + + optional NestedConflictingEnum conflicting_enum = 1; // NO_PROTO3 +} // NO_PROTO3 + +enum ConflictingEnum { // NO_PROTO3 + NOT_EQ = 1; // NO_PROTO3 + volatile = 2; // NO_PROTO3 + return = 3; // NO_PROTO3 +} // NO_PROTO3 message DummyMessage {} diff --git a/src/google/protobuf/compiler/cpp/cpp_unittest.cc b/src/google/protobuf/compiler/cpp/cpp_unittest.cc index 9d14a924..b11fb21a 100644 --- a/src/google/protobuf/compiler/cpp/cpp_unittest.cc +++ b/src/google/protobuf/compiler/cpp/cpp_unittest.cc @@ -941,6 +941,22 @@ TEST(GeneratedMessageTest, ExtensionConstantValues) { EXPECT_EQ(unittest::kRepeatedNestedEnumExtensionFieldNumber, 51); } +TEST(GeneratedMessageTest, ParseFromTruncated) { + const string long_string = string(128, 'q'); + FileDescriptorProto p; + p.add_extension()->set_name(long_string); + const string msg = p.SerializeAsString(); + int successful_count = 0; + for (int i = 0; i <= msg.size(); i++) { + if (p.ParseFromArray(msg.c_str(), i)) { + ++successful_count; + } + } + // We don't really care about how often we succeeded. + // As long as we didn't crash, we're happy. + EXPECT_GE(successful_count, 1); +} + // =================================================================== TEST(GeneratedEnumTest, EnumValuesAsSwitchCases) { diff --git a/src/google/protobuf/compiler/cpp/test_large_enum_value.proto b/src/google/protobuf/compiler/cpp/test_large_enum_value.proto new file mode 100644 index 00000000..cb6ca1b1 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/test_large_enum_value.proto @@ -0,0 +1,43 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Test that proto2 compiler can generate valid code when the enum value +// is INT_MAX. Note that this is a compile-only test and this proto is not +// referenced in any C++ code. +syntax = "proto2"; + +package protobuf_unittest; + +message TestLargeEnumValue { + enum EnumWithLargeValue { + VALUE_1 = 1; + VALUE_MAX = 0x7fffffff; + } +} diff --git a/src/google/protobuf/compiler/java/java_enum_field.cc b/src/google/protobuf/compiler/java/java_enum_field.cc index 71a2ba4b..39318a19 100644 --- a/src/google/protobuf/compiler/java/java_enum_field.cc +++ b/src/google/protobuf/compiler/java/java_enum_field.cc @@ -67,7 +67,13 @@ void SetEnumVariables(const FieldDescriptor* descriptor, (*variables)["default"] = ImmutableDefaultValue(descriptor, name_resolver); (*variables)["default_number"] = SimpleItoa( descriptor->default_value_enum()->number()); - (*variables)["tag"] = SimpleItoa(internal::WireFormat::MakeTag(descriptor)); + if (descriptor->is_packed()) { + (*variables)["tag"] = SimpleItoa(internal::WireFormatLite::MakeTag( + descriptor->number(), + internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED)); + } else { + (*variables)["tag"] = SimpleItoa(internal::WireFormat::MakeTag(descriptor)); + } (*variables)["tag_size"] = SimpleItoa( internal::WireFormat::TagSize(descriptor->number(), GetType(descriptor))); // TODO(birdo): Add @deprecated javadoc when generating javadoc is supported @@ -641,7 +647,7 @@ GenerateMembers(io::Printer* printer) const { "}\n"); } - if (descriptor_->options().packed() && + if (descriptor_->is_packed() && HasGeneratedMethods(descriptor_->containing_type())) { printer->Print(variables_, "private int $name$MemoizedSerializedSize;\n"); @@ -884,7 +890,7 @@ GenerateParsingDoneCode(io::Printer* printer) const { void RepeatedImmutableEnumFieldGenerator:: GenerateSerializationCode(io::Printer* printer) const { - if (descriptor_->options().packed()) { + if (descriptor_->is_packed()) { printer->Print(variables_, "if (get$capitalized_name$List().size() > 0) {\n" " output.writeRawVarint32($tag$);\n" @@ -915,7 +921,7 @@ GenerateSerializedSizeCode(io::Printer* printer) const { "}\n"); printer->Print( "size += dataSize;\n"); - if (descriptor_->options().packed()) { + if (descriptor_->is_packed()) { printer->Print(variables_, "if (!get$capitalized_name$List().isEmpty()) {" " size += $tag_size$;\n" @@ -928,7 +934,7 @@ GenerateSerializedSizeCode(io::Printer* printer) const { } // cache the data size for packed fields. - if (descriptor_->options().packed()) { + if (descriptor_->is_packed()) { printer->Print(variables_, "$name$MemoizedSerializedSize = dataSize;\n"); } diff --git a/src/google/protobuf/compiler/java/java_enum_field_lite.cc b/src/google/protobuf/compiler/java/java_enum_field_lite.cc new file mode 100644 index 00000000..697a07a7 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_enum_field_lite.cc @@ -0,0 +1,967 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +namespace { + +void SetEnumVariables(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + const FieldGeneratorInfo* info, + ClassNameResolver* name_resolver, + map* variables) { + SetCommonFieldVariables(descriptor, info, variables); + + (*variables)["type"] = + name_resolver->GetImmutableClassName(descriptor->enum_type()); + (*variables)["mutable_type"] = + name_resolver->GetMutableClassName(descriptor->enum_type()); + (*variables)["default"] = ImmutableDefaultValue(descriptor, name_resolver); + (*variables)["default_number"] = SimpleItoa( + descriptor->default_value_enum()->number()); + (*variables)["tag"] = SimpleItoa(internal::WireFormat::MakeTag(descriptor)); + (*variables)["tag_size"] = SimpleItoa( + internal::WireFormat::TagSize(descriptor->number(), GetType(descriptor))); + // TODO(birdo): Add @deprecated javadoc when generating javadoc is supported + // by the proto compiler + (*variables)["deprecation"] = descriptor->options().deprecated() + ? "@java.lang.Deprecated " : ""; + (*variables)["on_changed"] = + HasDescriptorMethods(descriptor->containing_type()) ? "onChanged();" : ""; + + if (SupportFieldPresence(descriptor->file())) { + // For singular messages and builders, one bit is used for the hasField bit. + (*variables)["get_has_field_bit_message"] = GenerateGetBit(messageBitIndex); + + // Note that these have a trailing ";". + (*variables)["set_has_field_bit_message"] = + GenerateSetBit(messageBitIndex) + ";"; + (*variables)["clear_has_field_bit_message"] = + GenerateClearBit(messageBitIndex) + ";"; + + (*variables)["is_field_present_message"] = GenerateGetBit(messageBitIndex); + } else { + (*variables)["set_has_field_bit_message"] = ""; + (*variables)["clear_has_field_bit_message"] = ""; + + (*variables)["is_field_present_message"] = + (*variables)["name"] + "_ != " + + (*variables)["default"] + ".getNumber()"; + } + + // For repeated builders, the underlying list tracks mutability state. + (*variables)["is_mutable"] = (*variables)["name"] + "_.isModifiable()"; + + (*variables)["get_has_field_bit_from_local"] = + GenerateGetBitFromLocal(builderBitIndex); + (*variables)["set_has_field_bit_to_local"] = + GenerateSetBitToLocal(messageBitIndex); + + if (SupportUnknownEnumValue(descriptor->file())) { + (*variables)["unknown"] = (*variables)["type"] + ".UNRECOGNIZED"; + } else { + (*variables)["unknown"] = (*variables)["default"]; + } +} + +} // namespace + +// =================================================================== + +ImmutableEnumFieldLiteGenerator:: +ImmutableEnumFieldLiteGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) + : descriptor_(descriptor), messageBitIndex_(messageBitIndex), + builderBitIndex_(builderBitIndex), + name_resolver_(context->GetNameResolver()) { + SetEnumVariables(descriptor, messageBitIndex, builderBitIndex, + context->GetFieldGeneratorInfo(descriptor), + name_resolver_, &variables_); +} + +ImmutableEnumFieldLiteGenerator::~ImmutableEnumFieldLiteGenerator() {} + +int ImmutableEnumFieldLiteGenerator::GetNumBitsForMessage() const { + return 1; +} + +int ImmutableEnumFieldLiteGenerator::GetNumBitsForBuilder() const { + return 0; +} + +void ImmutableEnumFieldLiteGenerator:: +GenerateInterfaceMembers(io::Printer* printer) const { + if (SupportFieldPresence(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$boolean has$capitalized_name$();\n"); + } + if (SupportUnknownEnumValue(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$int get$capitalized_name$Value();\n"); + } + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$$type$ get$capitalized_name$();\n"); +} + +void ImmutableEnumFieldLiteGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print(variables_, + "private int $name$_;\n"); + PrintExtraFieldInfo(variables_, printer); + if (SupportFieldPresence(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return $get_has_field_bit_message$;\n" + "}\n"); + } + if (SupportUnknownEnumValue(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public int get$capitalized_name$Value() {\n" + " return $name$_;\n" + "}\n"); + } + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$() {\n" + " $type$ result = $type$.valueOf($name$_);\n" + " return result == null ? $unknown$ : result;\n" + "}\n"); + + // Generate private setters for the builder to proxy into. + if (SupportUnknownEnumValue(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$Value(int value) {\n" + " $set_has_field_bit_message$" + " $name$_ = value;\n" + "}\n"); + } + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$($type$ value) {\n" + " if (value == null) {\n" + " throw new NullPointerException();\n" + " }\n" + " $set_has_field_bit_message$\n" + " $name$_ = value.getNumber();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void clear$capitalized_name$() {\n" + " $clear_has_field_bit_message$\n" + " $name$_ = $default_number$;\n" + "}\n"); +} + +void ImmutableEnumFieldLiteGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + if (SupportFieldPresence(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return instance.has$capitalized_name$();\n" + "}\n"); + } + if (SupportUnknownEnumValue(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public int get$capitalized_name$Value() {\n" + " return instance.get$capitalized_name$Value();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$Value(int value) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$Value(int value);\n" + " return this;\n" + "}\n"); + } + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$() {\n" + " return instance.get$capitalized_name$();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$($type$ value) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(value);\n" + " return this;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder clear$capitalized_name$() {\n" + " copyOnWrite();\n" + " instance.clear$capitalized_name$();\n" + " return this;\n" + "}\n"); +} + +void ImmutableEnumFieldLiteGenerator:: +GenerateFieldBuilderInitializationCode(io::Printer* printer) const { + // noop for enums +} + +void ImmutableEnumFieldLiteGenerator:: +GenerateInitializationCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_ = $default_number$;\n"); +} + +void ImmutableEnumFieldLiteGenerator:: +GenerateMergingCode(io::Printer* printer) const { + if (SupportFieldPresence(descriptor_->file())) { + printer->Print(variables_, + "if (other.has$capitalized_name$()) {\n" + " set$capitalized_name$(other.get$capitalized_name$());\n" + "}\n"); + } else if (SupportUnknownEnumValue(descriptor_->file())) { + printer->Print(variables_, + "if (other.$name$_ != $default_number$) {\n" + " set$capitalized_name$Value(other.get$capitalized_name$Value());\n" + "}\n"); + } else { + GOOGLE_LOG(FATAL) << "Can't reach here."; + } +} + +void ImmutableEnumFieldLiteGenerator:: +GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const { + // noop for scalars +} + +void ImmutableEnumFieldLiteGenerator:: +GenerateParsingCode(io::Printer* printer) const { + if (SupportUnknownEnumValue(descriptor_->file())) { + printer->Print(variables_, + "int rawValue = input.readEnum();\n" + "$set_has_field_bit_message$\n" + "$name$_ = rawValue;\n"); + } else { + printer->Print(variables_, + "int rawValue = input.readEnum();\n" + "$type$ value = $type$.valueOf(rawValue);\n" + "if (value == null) {\n"); + if (PreserveUnknownFields(descriptor_->containing_type())) { + printer->Print(variables_, + " unknownFields.mergeVarintField($number$, rawValue);\n"); + } + printer->Print(variables_, + "} else {\n" + " $set_has_field_bit_message$\n" + " $name$_ = rawValue;\n" + "}\n"); + } +} + +void ImmutableEnumFieldLiteGenerator:: +GenerateParsingDoneCode(io::Printer* printer) const { + // noop for enums +} + +void ImmutableEnumFieldLiteGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($is_field_present_message$) {\n" + " output.writeEnum($number$, $name$_);\n" + "}\n"); +} + +void ImmutableEnumFieldLiteGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($is_field_present_message$) {\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .computeEnumSize($number$, $name$_);\n" + "}\n"); +} + +void ImmutableEnumFieldLiteGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + printer->Print(variables_, + "result = result && $name$_ == other.$name$_;\n"); +} + +void ImmutableEnumFieldLiteGenerator:: +GenerateHashCode(io::Printer* printer) const { + printer->Print(variables_, + "hash = (37 * hash) + $constant_name$;\n" + "hash = (53 * hash) + $name$_;\n"); +} + +string ImmutableEnumFieldLiteGenerator::GetBoxedType() const { + return name_resolver_->GetImmutableClassName(descriptor_->enum_type()); +} + +// =================================================================== + +ImmutableEnumOneofFieldLiteGenerator:: +ImmutableEnumOneofFieldLiteGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) + : ImmutableEnumFieldLiteGenerator( + descriptor, messageBitIndex, builderBitIndex, context) { + const OneofGeneratorInfo* info = + context->GetOneofGeneratorInfo(descriptor->containing_oneof()); + SetCommonOneofVariables(descriptor, info, &variables_); +} + +ImmutableEnumOneofFieldLiteGenerator:: +~ImmutableEnumOneofFieldLiteGenerator() {} + +void ImmutableEnumOneofFieldLiteGenerator:: +GenerateMembers(io::Printer* printer) const { + PrintExtraFieldInfo(variables_, printer); + if (SupportFieldPresence(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return $has_oneof_case_message$;\n" + "}\n"); + } + if (SupportUnknownEnumValue(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public int get$capitalized_name$Value() {\n" + " if ($has_oneof_case_message$) {\n" + " return (java.lang.Integer) $oneof_name$_;\n" + " }\n" + " return $default_number$;\n" + "}\n"); + } + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$() {\n" + " if ($has_oneof_case_message$) {\n" + " $type$ result = $type$.valueOf((java.lang.Integer) $oneof_name$_);\n" + " return result == null ? $unknown$ : result;\n" + " }\n" + " return $default$;\n" + "}\n"); + + // Generate private setters for the builder to proxy into. + if (SupportUnknownEnumValue(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$Value(int value) {\n" + " $set_oneof_case_message$;\n" + " $oneof_name$_ = value;\n" + "}\n"); + } + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$($type$ value) {\n" + " if (value == null) {\n" + " throw new NullPointerException();\n" + " }\n" + " $set_oneof_case_message$;\n" + " $oneof_name$_ = value.getNumber();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void clear$capitalized_name$() {\n" + " if ($has_oneof_case_message$) {\n" + " $clear_oneof_case_message$;\n" + " $oneof_name$_ = null;\n" + " }\n" + "}\n"); +} + +void ImmutableEnumOneofFieldLiteGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + if (SupportFieldPresence(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return instance.has$capitalized_name$();\n" + "}\n"); + } + if (SupportUnknownEnumValue(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public int get$capitalized_name$Value() {\n" + " return instance.get$capitalized_name$Value();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$Value(int value) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$Value(value);\n" + " return this;\n" + "}\n"); + } + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$() {\n" + " return instance.get$capitalized_name$();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$($type$ value) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(value);\n" + " return this;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder clear$capitalized_name$() {\n" + " copyOnWrite();\n" + " instance.clear$capitalized_name$();\n" + " return this;\n" + "}\n"); +} + +void ImmutableEnumOneofFieldLiteGenerator:: +GenerateMergingCode(io::Printer* printer) const { + if (SupportUnknownEnumValue(descriptor_->file())) { + printer->Print(variables_, + "set$capitalized_name$Value(other.get$capitalized_name$Value());\n"); + } else { + printer->Print(variables_, + "set$capitalized_name$(other.get$capitalized_name$());\n"); + } +} + +void ImmutableEnumOneofFieldLiteGenerator:: +GenerateParsingCode(io::Printer* printer) const { + if (SupportUnknownEnumValue(descriptor_->file())) { + printer->Print(variables_, + "int rawValue = input.readEnum();\n" + "$set_oneof_case_message$;\n" + "$oneof_name$_ = rawValue;\n"); + } else { + printer->Print(variables_, + "int rawValue = input.readEnum();\n" + "$type$ value = $type$.valueOf(rawValue);\n" + "if (value == null) {\n"); + if (PreserveUnknownFields(descriptor_->containing_type())) { + printer->Print(variables_, + " unknownFields.mergeVarintField($number$, rawValue);\n"); + } + printer->Print(variables_, + "} else {\n" + " $set_oneof_case_message$;\n" + " $oneof_name$_ = rawValue;\n" + "}\n"); + } +} + +void ImmutableEnumOneofFieldLiteGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($has_oneof_case_message$) {\n" + " output.writeEnum($number$, ((java.lang.Integer) $oneof_name$_));\n" + "}\n"); +} + +void ImmutableEnumOneofFieldLiteGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($has_oneof_case_message$) {\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .computeEnumSize($number$, ((java.lang.Integer) $oneof_name$_));\n" + "}\n"); +} + +void ImmutableEnumOneofFieldLiteGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + if (SupportUnknownEnumValue(descriptor_->file())) { + printer->Print(variables_, + "result = result && get$capitalized_name$Value()\n" + " == other.get$capitalized_name$Value();\n"); + } else { + printer->Print(variables_, + "result = result && get$capitalized_name$()\n" + " .equals(other.get$capitalized_name$());\n"); + } +} + +void ImmutableEnumOneofFieldLiteGenerator:: +GenerateHashCode(io::Printer* printer) const { + if (SupportUnknownEnumValue(descriptor_->file())) { + printer->Print(variables_, + "hash = (37 * hash) + $constant_name$;\n" + "hash = (53 * hash) + get$capitalized_name$Value();\n"); + } else { + printer->Print(variables_, + "hash = (37 * hash) + $constant_name$;\n" + "hash = (53 * hash) + get$capitalized_name$().getNumber();\n"); + } +} + +// =================================================================== + +RepeatedImmutableEnumFieldLiteGenerator:: +RepeatedImmutableEnumFieldLiteGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) + : descriptor_(descriptor), messageBitIndex_(messageBitIndex), + builderBitIndex_(builderBitIndex), context_(context), + name_resolver_(context->GetNameResolver()) { + SetEnumVariables(descriptor, messageBitIndex, builderBitIndex, + context->GetFieldGeneratorInfo(descriptor), + name_resolver_, &variables_); +} + +RepeatedImmutableEnumFieldLiteGenerator:: +~RepeatedImmutableEnumFieldLiteGenerator() {} + +int RepeatedImmutableEnumFieldLiteGenerator::GetNumBitsForMessage() const { + return 0; +} + +int RepeatedImmutableEnumFieldLiteGenerator::GetNumBitsForBuilder() const { + return 0; +} + +void RepeatedImmutableEnumFieldLiteGenerator:: +GenerateInterfaceMembers(io::Printer* printer) const { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$java.util.List<$type$> get$capitalized_name$List();\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$int get$capitalized_name$Count();\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$$type$ get$capitalized_name$(int index);\n"); + if (SupportUnknownEnumValue(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$java.util.List\n" + "get$capitalized_name$ValueList();\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$int get$capitalized_name$Value(int index);\n"); + } +} + +void RepeatedImmutableEnumFieldLiteGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print(variables_, + // TODO(dweis): Switch to IntList? + "private com.google.protobuf.Internal.ProtobufList<\n" + " java.lang.Integer> $name$_;\n" + "private static final com.google.protobuf.Internal.ListAdapter.Converter<\n" + " java.lang.Integer, $type$> $name$_converter_ =\n" + " new com.google.protobuf.Internal.ListAdapter.Converter<\n" + " java.lang.Integer, $type$>() {\n" + " public $type$ convert(java.lang.Integer from) {\n" + " $type$ result = $type$.valueOf(from);\n" + " return result == null ? $unknown$ : result;\n" + " }\n" + " };\n"); + PrintExtraFieldInfo(variables_, printer); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.util.List<$type$> get$capitalized_name$List() {\n" + " return new com.google.protobuf.Internal.ListAdapter<\n" + " java.lang.Integer, $type$>($name$_, $name$_converter_);\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public int get$capitalized_name$Count() {\n" + " return $name$_.size();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$(int index) {\n" + " return $name$_converter_.convert($name$_.get(index));\n" + "}\n"); + if (SupportUnknownEnumValue(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.util.List\n" + "get$capitalized_name$ValueList() {\n" + " return $name$_;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public int get$capitalized_name$Value(int index) {\n" + " return $name$_.get(index);\n" + "}\n"); + } + + if (descriptor_->options().packed() && + HasGeneratedMethods(descriptor_->containing_type())) { + printer->Print(variables_, + "private int $name$MemoizedSerializedSize;\n"); + } + + // Generate private setters for the builder to proxy into. + printer->Print(variables_, + "private void ensure$capitalized_name$IsMutable() {\n" + " if (!$is_mutable$) {\n" + " $name$_ = newProtobufList($name$_);\n" + " }\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$(\n" + " int index, $type$ value) {\n" + " if (value == null) {\n" + " throw new NullPointerException();\n" + " }\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.set(index, value.getNumber());\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void add$capitalized_name$($type$ value) {\n" + " if (value == null) {\n" + " throw new NullPointerException();\n" + " }\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.add(value.getNumber());\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void addAll$capitalized_name$(\n" + " java.lang.Iterable values) {\n" + " ensure$capitalized_name$IsMutable();\n" + " for ($type$ value : values) {\n" + " $name$_.add(value.getNumber());\n" + " }\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void clear$capitalized_name$() {\n" + " $name$_ = emptyProtobufList();\n" + "}\n"); + + if (SupportUnknownEnumValue(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$Value(\n" + " int index, int value) {\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.set(index, value);\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void add$capitalized_name$Value(int value) {\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.add(value);\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void addAll$capitalized_name$Value(\n" + " java.lang.Iterable values) {\n" + " ensure$capitalized_name$IsMutable();\n" + " for (int value : values) {\n" + " $name$_.add(value);\n" + " }\n" + "}\n"); + } +} + +void RepeatedImmutableEnumFieldLiteGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.util.List<$type$> get$capitalized_name$List() {\n" + " return instance.get$capitalized_name$List();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public int get$capitalized_name$Count() {\n" + " return instance.get$capitalized_name$Count();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$(int index) {\n" + " return instance.get$capitalized_name$(index);\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$(\n" + " int index, $type$ value) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(index, value);\n" + " return this;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder add$capitalized_name$($type$ value) {\n" + " copyOnWrite();\n" + " instance.add$capitalized_name$(value);\n" + " return this;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder addAll$capitalized_name$(\n" + " java.lang.Iterable values) {\n" + " copyOnWrite();\n" + " instance.addAll$capitalized_name$(values);" + " return this;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder clear$capitalized_name$() {\n" + " copyOnWrite();\n" + " instance.clear$capitalized_name$();\n" + " return this;\n" + "}\n"); + + if (SupportUnknownEnumValue(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.util.List\n" + "get$capitalized_name$ValueList() {\n" + " return java.util.Collections.unmodifiableList(\n" + " instance.get$capitalized_name$ValueList());\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public int get$capitalized_name$Value(int index) {\n" + " return instance.get$capitalized_name$Value(index);\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$Value(\n" + " int index, int value) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$Value(index, value);\n" + " return this;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder add$capitalized_name$Value(int value) {\n" + " instance.add$capitalized_name$Value(value);\n" + " return this;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder addAll$capitalized_name$Value(\n" + " java.lang.Iterable values) {\n" + " copyOnWrite();\n" + " instance.addAll$capitalized_name$Value(values);\n" + " return this;\n" + "}\n"); + } +} + +void RepeatedImmutableEnumFieldLiteGenerator:: +GenerateFieldBuilderInitializationCode(io::Printer* printer) const { + // noop for enums +} + +void RepeatedImmutableEnumFieldLiteGenerator:: +GenerateInitializationCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_ = emptyProtobufList();\n"); +} + +void RepeatedImmutableEnumFieldLiteGenerator:: +GenerateMergingCode(io::Printer* printer) const { + // The code below does two optimizations: + // 1. If the other list is empty, there's nothing to do. This ensures we + // don't allocate a new array if we already have an immutable one. + // 2. If the other list is non-empty and our current list is empty, we can + // reuse the other list which is guaranteed to be immutable. + printer->Print(variables_, + "if (!other.$name$_.isEmpty()) {\n" + " if ($name$_.isEmpty()) {\n" + " $name$_ = other.$name$_;\n" + " } else {\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.addAll(other.$name$_);\n" + " }\n" + " $on_changed$\n" + "}\n"); +} + +void RepeatedImmutableEnumFieldLiteGenerator:: +GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const { + printer->Print(variables_, + "$name$_.makeImmutable();\n"); +} + +void RepeatedImmutableEnumFieldLiteGenerator:: +GenerateParsingCode(io::Printer* printer) const { + // Read and store the enum + if (SupportUnknownEnumValue(descriptor_->file())) { + printer->Print(variables_, + "int rawValue = input.readEnum();\n" + "if (!$is_mutable$) {\n" + " $name$_ = newProtobufList();\n" + "}\n" + "$name$_.add(rawValue);\n"); + } else { + printer->Print(variables_, + "int rawValue = input.readEnum();\n" + "$type$ value = $type$.valueOf(rawValue);\n" + "if (value == null) {\n"); + if (PreserveUnknownFields(descriptor_->containing_type())) { + printer->Print(variables_, + " unknownFields.mergeVarintField($number$, rawValue);\n"); + } + printer->Print(variables_, + "} else {\n" + " if (!$is_mutable$) {\n" + " $name$_ = newProtobufList();\n" + " }\n" + " $name$_.add(rawValue);\n" + "}\n"); + } +} + +void RepeatedImmutableEnumFieldLiteGenerator:: +GenerateParsingCodeFromPacked(io::Printer* printer) const { + // Wrap GenerateParsingCode's contents with a while loop. + + printer->Print(variables_, + "int length = input.readRawVarint32();\n" + "int oldLimit = input.pushLimit(length);\n" + "while(input.getBytesUntilLimit() > 0) {\n"); + printer->Indent(); + + GenerateParsingCode(printer); + + printer->Outdent(); + printer->Print(variables_, + "}\n" + "input.popLimit(oldLimit);\n"); +} + +void RepeatedImmutableEnumFieldLiteGenerator:: +GenerateParsingDoneCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($is_mutable$) {\n" + " $name$_.makeImmutable();\n" + "}\n"); +} + +void RepeatedImmutableEnumFieldLiteGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + if (descriptor_->options().packed()) { + printer->Print(variables_, + "if (get$capitalized_name$List().size() > 0) {\n" + " output.writeRawVarint32($tag$);\n" + " output.writeRawVarint32($name$MemoizedSerializedSize);\n" + "}\n" + "for (int i = 0; i < $name$_.size(); i++) {\n" + " output.writeEnumNoTag($name$_.get(i));\n" + "}\n"); + } else { + printer->Print(variables_, + "for (int i = 0; i < $name$_.size(); i++) {\n" + " output.writeEnum($number$, $name$_.get(i));\n" + "}\n"); + } +} + +void RepeatedImmutableEnumFieldLiteGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "{\n" + " int dataSize = 0;\n"); + printer->Indent(); + + printer->Print(variables_, + "for (int i = 0; i < $name$_.size(); i++) {\n" + " dataSize += com.google.protobuf.CodedOutputStream\n" + " .computeEnumSizeNoTag($name$_.get(i));\n" + "}\n"); + printer->Print( + "size += dataSize;\n"); + if (descriptor_->options().packed()) { + printer->Print(variables_, + "if (!get$capitalized_name$List().isEmpty()) {" + " size += $tag_size$;\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .computeRawVarint32Size(dataSize);\n" + "}"); + } else { + printer->Print(variables_, + "size += $tag_size$ * $name$_.size();\n"); + } + + // cache the data size for packed fields. + if (descriptor_->options().packed()) { + printer->Print(variables_, + "$name$MemoizedSerializedSize = dataSize;\n"); + } + + printer->Outdent(); + printer->Print("}\n"); +} + +void RepeatedImmutableEnumFieldLiteGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + printer->Print(variables_, + "result = result && $name$_.equals(other.$name$_);\n"); +} + +void RepeatedImmutableEnumFieldLiteGenerator:: +GenerateHashCode(io::Printer* printer) const { + printer->Print(variables_, + "if (get$capitalized_name$Count() > 0) {\n" + " hash = (37 * hash) + $constant_name$;\n" + " hash = (53 * hash) + $name$_.hashCode();\n" + "}\n"); +} + +string RepeatedImmutableEnumFieldLiteGenerator::GetBoxedType() const { + return name_resolver_->GetImmutableClassName(descriptor_->enum_type()); +} + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_enum_field_lite.h b/src/google/protobuf/compiler/java/java_enum_field_lite.h new file mode 100644 index 00000000..2c41c3e4 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_enum_field_lite.h @@ -0,0 +1,159 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_ENUM_FIELD_LITE_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_ENUM_FIELD_LITE_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { + namespace compiler { + namespace java { + class Context; // context.h + class ClassNameResolver; // name_resolver.h + } + } +} + +namespace protobuf { +namespace compiler { +namespace java { + +class ImmutableEnumFieldLiteGenerator : public ImmutableFieldLiteGenerator { + public: + explicit ImmutableEnumFieldLiteGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~ImmutableEnumFieldLiteGenerator(); + + // implements ImmutableFieldLiteGenerator ------------------------------------ + int GetNumBitsForMessage() const; + int GetNumBitsForBuilder() const; + void GenerateInterfaceMembers(io::Printer* printer) const; + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateInitializationCode(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateParsingDoneCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCode(io::Printer* printer) const; + + string GetBoxedType() const; + + protected: + const FieldDescriptor* descriptor_; + map variables_; + const int messageBitIndex_; + const int builderBitIndex_; + Context* context_; + ClassNameResolver* name_resolver_; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableEnumFieldLiteGenerator); +}; + +class ImmutableEnumOneofFieldLiteGenerator + : public ImmutableEnumFieldLiteGenerator { + public: + ImmutableEnumOneofFieldLiteGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~ImmutableEnumOneofFieldLiteGenerator(); + + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCode(io::Printer* printer) const; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableEnumOneofFieldLiteGenerator); +}; + +class RepeatedImmutableEnumFieldLiteGenerator + : public ImmutableFieldLiteGenerator { + public: + explicit RepeatedImmutableEnumFieldLiteGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~RepeatedImmutableEnumFieldLiteGenerator(); + + // implements ImmutableFieldLiteGenerator ------------------------------------ + int GetNumBitsForMessage() const; + int GetNumBitsForBuilder() const; + void GenerateInterfaceMembers(io::Printer* printer) const; + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateInitializationCode(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateParsingCodeFromPacked(io::Printer* printer) const; + void GenerateParsingDoneCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCode(io::Printer* printer) const; + + string GetBoxedType() const; + + private: + const FieldDescriptor* descriptor_; + map variables_; + const int messageBitIndex_; + const int builderBitIndex_; + Context* context_; + ClassNameResolver* name_resolver_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedImmutableEnumFieldLiteGenerator); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_ENUM_FIELD_LITE_H__ diff --git a/src/google/protobuf/compiler/java/java_field.cc b/src/google/protobuf/compiler/java/java_field.cc index af9978e2..3f0fa11f 100644 --- a/src/google/protobuf/compiler/java/java_field.cc +++ b/src/google/protobuf/compiler/java/java_field.cc @@ -42,12 +42,18 @@ #include #include #include +#include #include #include +#include #include +#include #include +#include #include +#include #include +#include #include #include #include @@ -133,6 +139,79 @@ ImmutableFieldGenerator* MakeImmutableGenerator( } } +ImmutableFieldLiteGenerator* MakeImmutableLiteGenerator( + const FieldDescriptor* field, int messageBitIndex, int builderBitIndex, + Context* context) { + if (field->is_repeated()) { + switch (GetJavaType(field)) { + case JAVATYPE_MESSAGE: + if (IsMapEntry(field->message_type())) { + return new ImmutableMapFieldLiteGenerator( + field, messageBitIndex, builderBitIndex, context); + } else { + if (IsLazy(field)) { + return new RepeatedImmutableLazyMessageFieldLiteGenerator( + field, messageBitIndex, builderBitIndex, context); + } else { + return new RepeatedImmutableMessageFieldLiteGenerator( + field, messageBitIndex, builderBitIndex, context); + } + } + case JAVATYPE_ENUM: + return new RepeatedImmutableEnumFieldLiteGenerator( + field, messageBitIndex, builderBitIndex, context); + case JAVATYPE_STRING: + return new RepeatedImmutableStringFieldLiteGenerator( + field, messageBitIndex, builderBitIndex, context); + default: + return new RepeatedImmutablePrimitiveFieldLiteGenerator( + field, messageBitIndex, builderBitIndex, context); + } + } else { + if (field->containing_oneof()) { + switch (GetJavaType(field)) { + case JAVATYPE_MESSAGE: + if (IsLazy(field)) { + return new ImmutableLazyMessageOneofFieldLiteGenerator( + field, messageBitIndex, builderBitIndex, context); + } else { + return new ImmutableMessageOneofFieldLiteGenerator( + field, messageBitIndex, builderBitIndex, context); + } + case JAVATYPE_ENUM: + return new ImmutableEnumOneofFieldLiteGenerator( + field, messageBitIndex, builderBitIndex, context); + case JAVATYPE_STRING: + return new ImmutableStringOneofFieldLiteGenerator( + field, messageBitIndex, builderBitIndex, context); + default: + return new ImmutablePrimitiveOneofFieldLiteGenerator( + field, messageBitIndex, builderBitIndex, context); + } + } else { + switch (GetJavaType(field)) { + case JAVATYPE_MESSAGE: + if (IsLazy(field)) { + return new ImmutableLazyMessageFieldLiteGenerator( + field, messageBitIndex, builderBitIndex, context); + } else { + return new ImmutableMessageFieldLiteGenerator( + field, messageBitIndex, builderBitIndex, context); + } + case JAVATYPE_ENUM: + return new ImmutableEnumFieldLiteGenerator( + field, messageBitIndex, builderBitIndex, context); + case JAVATYPE_STRING: + return new ImmutableStringFieldLiteGenerator( + field, messageBitIndex, builderBitIndex, context); + default: + return new ImmutablePrimitiveFieldLiteGenerator( + field, messageBitIndex, builderBitIndex, context); + } + } + } +} + static inline void ReportUnexpectedPackedFieldsCall(io::Printer* printer) { // Reaching here indicates a bug. Cases are: @@ -153,6 +232,13 @@ GenerateParsingCodeFromPacked(io::Printer* printer) const { ReportUnexpectedPackedFieldsCall(printer); } +ImmutableFieldLiteGenerator::~ImmutableFieldLiteGenerator() {} + +void ImmutableFieldLiteGenerator:: +GenerateParsingCodeFromPacked(io::Printer* printer) const { + ReportUnexpectedPackedFieldsCall(printer); +} + // =================================================================== template <> @@ -178,6 +264,28 @@ FieldGeneratorMap::FieldGeneratorMap( template<> FieldGeneratorMap::~FieldGeneratorMap() {} +template <> +FieldGeneratorMap::FieldGeneratorMap( + const Descriptor* descriptor, Context* context) + : descriptor_(descriptor), + field_generators_(new google::protobuf::scoped_ptr< + ImmutableFieldLiteGenerator>[descriptor->field_count()]) { + // Construct all the FieldGenerators and assign them bit indices for their + // bit fields. + int messageBitIndex = 0; + int builderBitIndex = 0; + for (int i = 0; i < descriptor->field_count(); i++) { + ImmutableFieldLiteGenerator* generator = MakeImmutableLiteGenerator( + descriptor->field(i), messageBitIndex, builderBitIndex, context); + field_generators_[i].reset(generator); + messageBitIndex += generator->GetNumBitsForMessage(); + builderBitIndex += generator->GetNumBitsForBuilder(); + } +} + +template<> +FieldGeneratorMap::~FieldGeneratorMap() {} + void SetCommonFieldVariables(const FieldDescriptor* descriptor, const FieldGeneratorInfo* info, diff --git a/src/google/protobuf/compiler/java/java_field.h b/src/google/protobuf/compiler/java/java_field.h index 1cf360c1..00f3c601 100644 --- a/src/google/protobuf/compiler/java/java_field.h +++ b/src/google/protobuf/compiler/java/java_field.h @@ -83,7 +83,6 @@ class ImmutableFieldGenerator { virtual void GenerateSerializedSizeCode(io::Printer* printer) const = 0; virtual void GenerateFieldBuilderInitializationCode(io::Printer* printer) const = 0; - virtual void GenerateStaticInitializationCode(io::Printer* printer) const {} virtual void GenerateEqualsCode(io::Printer* printer) const = 0; virtual void GenerateHashCode(io::Printer* printer) const = 0; @@ -94,6 +93,37 @@ class ImmutableFieldGenerator { GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableFieldGenerator); }; +class ImmutableFieldLiteGenerator { + public: + ImmutableFieldLiteGenerator() {} + virtual ~ImmutableFieldLiteGenerator(); + + virtual int GetNumBitsForMessage() const = 0; + virtual int GetNumBitsForBuilder() const = 0; + virtual void GenerateInterfaceMembers(io::Printer* printer) const = 0; + virtual void GenerateMembers(io::Printer* printer) const = 0; + virtual void GenerateBuilderMembers(io::Printer* printer) const = 0; + virtual void GenerateInitializationCode(io::Printer* printer) const = 0; + virtual void GenerateMergingCode(io::Printer* printer) const = 0; + virtual void GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) + const = 0; + virtual void GenerateParsingCode(io::Printer* printer) const = 0; + virtual void GenerateParsingCodeFromPacked(io::Printer* printer) const; + virtual void GenerateParsingDoneCode(io::Printer* printer) const = 0; + virtual void GenerateSerializationCode(io::Printer* printer) const = 0; + virtual void GenerateSerializedSizeCode(io::Printer* printer) const = 0; + virtual void GenerateFieldBuilderInitializationCode(io::Printer* printer) + const = 0; + + virtual void GenerateEqualsCode(io::Printer* printer) const = 0; + virtual void GenerateHashCode(io::Printer* printer) const = 0; + + virtual string GetBoxedType() const = 0; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableFieldLiteGenerator); +}; + // Convenience class which constructs FieldGenerators for a Descriptor. template diff --git a/src/google/protobuf/compiler/java/java_file.cc b/src/google/protobuf/compiler/java/java_file.cc index 4a1f4529..68b47ee1 100644 --- a/src/google/protobuf/compiler/java/java_file.cc +++ b/src/google/protobuf/compiler/java/java_file.cc @@ -35,10 +35,10 @@ #include #include -#include #ifndef _SHARED_PTR_H #include #endif +#include #include #include @@ -153,6 +153,12 @@ void CollectExtensions(const FileDescriptorProto& file_proto, } } +// Compare two field descriptors, returning true if the first should come +// before the second. +bool CompareFieldsByName(const FieldDescriptor *a, const FieldDescriptor *b) { + return a->full_name() < b->full_name(); +} + // Our static initialization methods can become very, very large. // So large that if we aren't careful we end up blowing the JVM's // 64K bytes of bytecode/method. Fortunately, since these static @@ -166,7 +172,6 @@ void MaybeRestartJavaMethod(io::Printer* printer, int *method_num, const char *chain_statement, const char *method_decl) { - // The goal here is to stay under 64K bytes of jvm bytecode/method, // since otherwise we hit a hardcoded limit in the jvm and javac will // then fail with the error "code too large". This limit lets our @@ -184,6 +189,7 @@ void MaybeRestartJavaMethod(io::Printer* printer, } } + } // namespace FileGenerator::FileGenerator(const FileDescriptor* file, bool immutable_api) diff --git a/src/google/protobuf/compiler/java/java_generator_factory.cc b/src/google/protobuf/compiler/java/java_generator_factory.cc index 2d1437f0..92ef851b 100644 --- a/src/google/protobuf/compiler/java/java_generator_factory.cc +++ b/src/google/protobuf/compiler/java/java_generator_factory.cc @@ -38,6 +38,7 @@ #include #include #include +#include #include namespace google { @@ -57,7 +58,12 @@ ImmutableGeneratorFactory::~ImmutableGeneratorFactory() {} MessageGenerator* ImmutableGeneratorFactory::NewMessageGenerator( const Descriptor* descriptor) const { - return new ImmutableMessageGenerator(descriptor, context_); + if (descriptor->file()->options().optimize_for() == + FileOptions::LITE_RUNTIME) { + return new ImmutableMessageLiteGenerator(descriptor, context_); + } else { + return new ImmutableMessageGenerator(descriptor, context_); + } } ExtensionGenerator* ImmutableGeneratorFactory::NewExtensionGenerator( diff --git a/src/google/protobuf/compiler/java/java_helpers.h b/src/google/protobuf/compiler/java/java_helpers.h index 62efbefa..96d2545f 100644 --- a/src/google/protobuf/compiler/java/java_helpers.h +++ b/src/google/protobuf/compiler/java/java_helpers.h @@ -196,12 +196,6 @@ inline bool HasDescriptorMethods(const FileDescriptor* descriptor) { FileOptions::LITE_RUNTIME; } -inline bool HasNestedBuilders(const Descriptor* descriptor) { - // The proto-lite version doesn't support nested builders. - return descriptor->file()->options().optimize_for() != - FileOptions::LITE_RUNTIME; -} - // Should we generate generic services for this file? inline bool HasGenericServices(const FileDescriptor *file) { return file->service_count() > 0 && @@ -330,6 +324,10 @@ inline bool IsMapEntry(const Descriptor* descriptor) { return descriptor->options().map_entry(); } +inline bool IsMapField(const FieldDescriptor* descriptor) { + return descriptor->is_map(); +} + inline bool PreserveUnknownFields(const Descriptor* descriptor) { return descriptor->file()->syntax() != FileDescriptor::SYNTAX_PROTO3; } diff --git a/src/google/protobuf/compiler/java/java_lazy_message_field.cc b/src/google/protobuf/compiler/java/java_lazy_message_field.cc index 6e29a40b..0de8cbe5 100644 --- a/src/google/protobuf/compiler/java/java_lazy_message_field.cc +++ b/src/google/protobuf/compiler/java/java_lazy_message_field.cc @@ -72,14 +72,12 @@ GenerateMembers(io::Printer* printer) const { printer->Print(variables_, "$deprecation$public $type$ get$capitalized_name$() {\n" " return ($type$) $name$_.getValue($type$.getDefaultInstance());\n" - "}\n"); - if (HasNestedBuilders(descriptor_->containing_type())) { - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder() {\n" - " return $name$_;\n" - "}\n"); - } + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder() {\n" + " return $name$_;\n" + "}\n"); } void ImmutableLazyMessageFieldGenerator:: @@ -92,14 +90,12 @@ GenerateBuilderMembers(io::Printer* printer) const { "private com.google.protobuf.LazyFieldLite $name$_ =\n" " new com.google.protobuf.LazyFieldLite();\n"); - if (HasNestedBuilders(descriptor_->containing_type())) { - printer->Print(variables_, - // If this builder is non-null, it is used and the other fields are - // ignored. - "private com.google.protobuf.SingleFieldBuilder<\n" - " $type$, $type$.Builder, $type$OrBuilder> $name$Builder_;" - "\n"); - } + printer->Print(variables_, + // If this builder is non-null, it is used and the other fields are + // ignored. + "private com.google.protobuf.SingleFieldBuilder<\n" + " $type$, $type$.Builder, $type$OrBuilder> $name$Builder_;" + "\n"); // The comments above the methods below are based on a hypothetical // field of type "Field" called "Field". @@ -179,39 +175,37 @@ GenerateBuilderMembers(io::Printer* printer) const { "$clear_has_field_bit_builder$;\n" "return this;\n"); - if (HasNestedBuilders(descriptor_->containing_type())) { - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public $type$.Builder get$capitalized_name$Builder() {\n" - " $set_has_field_bit_builder$;\n" - " $on_changed$\n" - " return get$capitalized_name$FieldBuilder().getBuilder();\n" - "}\n"); - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder() {\n" - " if ($name$Builder_ != null) {\n" - " return $name$Builder_.getMessageOrBuilder();\n" - " } else {\n" - " return $name$_;\n" - " }\n" - "}\n"); - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "private com.google.protobuf.SingleFieldBuilder<\n" - " $type$, $type$.Builder, $type$OrBuilder> \n" - " get$capitalized_name$FieldBuilder() {\n" - " if ($name$Builder_ == null) {\n" - " $name$Builder_ = new com.google.protobuf.SingleFieldBuilder<\n" - " $type$, $type$.Builder, $type$OrBuilder>(\n" - " $name$_,\n" - " getParentForChildren(),\n" - " isClean());\n" - " $name$_ = null;\n" - " }\n" - " return $name$Builder_;\n" - "}\n"); - } + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$.Builder get$capitalized_name$Builder() {\n" + " $set_has_field_bit_builder$;\n" + " $on_changed$\n" + " return get$capitalized_name$FieldBuilder().getBuilder();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder() {\n" + " if ($name$Builder_ != null) {\n" + " return $name$Builder_.getMessageOrBuilder();\n" + " } else {\n" + " return $name$_;\n" + " }\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private com.google.protobuf.SingleFieldBuilder<\n" + " $type$, $type$.Builder, $type$OrBuilder> \n" + " get$capitalized_name$FieldBuilder() {\n" + " if ($name$Builder_ == null) {\n" + " $name$Builder_ = new com.google.protobuf.SingleFieldBuilder<\n" + " $type$, $type$.Builder, $type$OrBuilder>(\n" + " $name$_,\n" + " getParentForChildren(),\n" + " isClean());\n" + " $name$_ = null;\n" + " }\n" + " return $name$Builder_;\n" + "}\n"); } @@ -538,14 +532,12 @@ GenerateBuilderMembers(io::Printer* printer) const { "}\n" "\n"); - if (HasNestedBuilders(descriptor_->containing_type())) { - printer->Print(variables_, - // If this builder is non-null, it is used and the other fields are - // ignored. - "private com.google.protobuf.RepeatedFieldBuilder<\n" - " $type$, $type$.Builder, $type$OrBuilder> $name$Builder_;\n" - "\n"); - } + printer->Print(variables_, + // If this builder is non-null, it is used and the other fields are + // ignored. + "private com.google.protobuf.RepeatedFieldBuilder<\n" + " $type$, $type$.Builder, $type$OrBuilder> $name$Builder_;\n" + "\n"); // The comments above the methods below are based on a hypothetical // repeated field of type "Field" called "RepeatedField". @@ -723,70 +715,68 @@ GenerateBuilderMembers(io::Printer* printer) const { "return this;\n"); - if (HasNestedBuilders(descriptor_->containing_type())) { - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public $type$.Builder get$capitalized_name$Builder(\n" - " int index) {\n" - " return get$capitalized_name$FieldBuilder().getBuilder(index);\n" - "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$.Builder get$capitalized_name$Builder(\n" + " int index) {\n" + " return get$capitalized_name$FieldBuilder().getBuilder(index);\n" + "}\n"); - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder(\n" - " int index) {\n" - " if ($name$Builder_ == null) {\n" - " return $name$_.get(index);" - " } else {\n" - " return $name$Builder_.getMessageOrBuilder(index);\n" - " }\n" - "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder(\n" + " int index) {\n" + " if ($name$Builder_ == null) {\n" + " return $name$_.get(index);" + " } else {\n" + " return $name$Builder_.getMessageOrBuilder(index);\n" + " }\n" + "}\n"); - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public java.util.List \n" - " get$capitalized_name$OrBuilderList() {\n" - " if ($name$Builder_ != null) {\n" - " return $name$Builder_.getMessageOrBuilderList();\n" - " } else {\n" - " return java.util.Collections.unmodifiableList($name$_);\n" - " }\n" - "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.util.List \n" + " get$capitalized_name$OrBuilderList() {\n" + " if ($name$Builder_ != null) {\n" + " return $name$Builder_.getMessageOrBuilderList();\n" + " } else {\n" + " return java.util.Collections.unmodifiableList($name$_);\n" + " }\n" + "}\n"); - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public $type$.Builder add$capitalized_name$Builder() {\n" - " return get$capitalized_name$FieldBuilder().addBuilder(\n" - " $type$.getDefaultInstance());\n" - "}\n"); - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public $type$.Builder add$capitalized_name$Builder(\n" - " int index) {\n" - " return get$capitalized_name$FieldBuilder().addBuilder(\n" - " index, $type$.getDefaultInstance());\n" - "}\n"); - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public java.util.List<$type$.Builder> \n" - " get$capitalized_name$BuilderList() {\n" - " return get$capitalized_name$FieldBuilder().getBuilderList();\n" - "}\n" - "private com.google.protobuf.RepeatedFieldBuilder<\n" - " $type$, $type$.Builder, $type$OrBuilder> \n" - " get$capitalized_name$FieldBuilder() {\n" - " if ($name$Builder_ == null) {\n" - " $name$Builder_ = new com.google.protobuf.RepeatedFieldBuilder<\n" - " $type$, $type$.Builder, $type$OrBuilder>(\n" - " $name$_,\n" - " $get_mutable_bit_builder$,\n" - " getParentForChildren(),\n" - " isClean());\n" - " $name$_ = null;\n" - " }\n" - " return $name$Builder_;\n" - "}\n"); - } + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$.Builder add$capitalized_name$Builder() {\n" + " return get$capitalized_name$FieldBuilder().addBuilder(\n" + " $type$.getDefaultInstance());\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$.Builder add$capitalized_name$Builder(\n" + " int index) {\n" + " return get$capitalized_name$FieldBuilder().addBuilder(\n" + " index, $type$.getDefaultInstance());\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.util.List<$type$.Builder> \n" + " get$capitalized_name$BuilderList() {\n" + " return get$capitalized_name$FieldBuilder().getBuilderList();\n" + "}\n" + "private com.google.protobuf.RepeatedFieldBuilder<\n" + " $type$, $type$.Builder, $type$OrBuilder> \n" + " get$capitalized_name$FieldBuilder() {\n" + " if ($name$Builder_ == null) {\n" + " $name$Builder_ = new com.google.protobuf.RepeatedFieldBuilder<\n" + " $type$, $type$.Builder, $type$OrBuilder>(\n" + " $name$_,\n" + " $get_mutable_bit_builder$,\n" + " getParentForChildren(),\n" + " isClean());\n" + " $name$_ = null;\n" + " }\n" + " return $name$Builder_;\n" + "}\n"); } void RepeatedImmutableLazyMessageFieldGenerator:: diff --git a/src/google/protobuf/compiler/java/java_lazy_message_field_lite.cc b/src/google/protobuf/compiler/java/java_lazy_message_field_lite.cc new file mode 100644 index 00000000..283ba1d3 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_lazy_message_field_lite.cc @@ -0,0 +1,708 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: niwasaki@google.com (Naoki Iwasaki) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +ImmutableLazyMessageFieldLiteGenerator:: +ImmutableLazyMessageFieldLiteGenerator( + const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) + : ImmutableMessageFieldLiteGenerator( + descriptor, messageBitIndex, builderBitIndex, context) { +} + +ImmutableLazyMessageFieldLiteGenerator:: +~ImmutableLazyMessageFieldLiteGenerator() {} + +void ImmutableLazyMessageFieldLiteGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print(variables_, + "private com.google.protobuf.LazyFieldLite $name$_ =\n" + " new com.google.protobuf.LazyFieldLite();\n"); + + PrintExtraFieldInfo(variables_, printer); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return $get_has_field_bit_message$;\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$() {\n" + " return ($type$) $name$_.getValue($type$.getDefaultInstance());\n" + "}\n"); + + // Field.Builder setField(Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$($type$ value) {\n" + " if (value == null) {\n" + " throw new NullPointerException();\n" + " }\n" + " $name$_.setValue(value);\n" + " $set_has_field_bit_message$;\n" + "}\n"); + + // Field.Builder setField(Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$(\n" + " $type$.Builder builderForValue) {\n" + " $name$_.setValue(builderForValue.build());\n" + " $set_has_field_bit_message$;\n" + "}\n"); + + // Field.Builder mergeField(Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void merge$capitalized_name$($type$ value) {\n" + " if ($get_has_field_bit_message$ &&\n" + " !$name$_.containsDefaultInstance()) {\n" + " $name$_.setValue(\n" + " $type$.newBuilder(\n" + " get$capitalized_name$()).mergeFrom(value).buildPartial());\n" + " } else {\n" + " $name$_.setValue(value);\n" + " }\n" + " $set_has_field_bit_message$;\n" + "}\n"); + + // Field.Builder clearField() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void clear$capitalized_name$() {\n" + " $name$_.clear();\n" + " $clear_has_field_bit_message$;\n" + "}\n"); +} + +void ImmutableLazyMessageFieldLiteGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + // The comments above the methods below are based on a hypothetical + // field of type "Field" called "Field". + + // boolean hasField() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return instance.has$capitalized_name$();\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$() {\n" + " return instance.get$capitalized_name$();\n" + "}\n"); + + // Field.Builder setField(Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$($type$ value) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(value);\n" + " return this;\n" + "}\n"); + + // Field.Builder setField(Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$(\n" + " $type$.Builder builderForValue) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(builderForValue);\n" + " return this;\n" + "}\n"); + + // Field.Builder mergeField(Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder merge$capitalized_name$($type$ value) {\n" + " copyOnWrite();\n" + " instance.merge$capitalized_name$(value);\n" + " return this;\n" + "}\n"); + + // Field.Builder clearField() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder clear$capitalized_name$() {\n" + " copyOnWrite();\n" + " instance.clear$capitalized_name$();\n" + " return this;\n" + "}\n"); +} + + +void ImmutableLazyMessageFieldLiteGenerator:: +GenerateInitializationCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_.clear();\n"); +} + +void ImmutableLazyMessageFieldLiteGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (other.has$capitalized_name$()) {\n" + " $name$_.merge(other.$name$_);\n" + " $set_has_field_bit_message$;\n" + "}\n"); +} + +void ImmutableLazyMessageFieldLiteGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "$name$_.setByteString(input.readBytes(), extensionRegistry);\n"); + printer->Print(variables_, + "$set_has_field_bit_message$;\n"); +} + +void ImmutableLazyMessageFieldLiteGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + // Do not de-serialize lazy fields. + printer->Print(variables_, + "if ($get_has_field_bit_message$) {\n" + " output.writeBytes($number$, $name$_.toByteString());\n" + "}\n"); +} + +void ImmutableLazyMessageFieldLiteGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($get_has_field_bit_message$) {\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .computeLazyFieldSize($number$, $name$_);\n" + "}\n"); +} + +// =================================================================== + +ImmutableLazyMessageOneofFieldLiteGenerator:: +ImmutableLazyMessageOneofFieldLiteGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) + : ImmutableLazyMessageFieldLiteGenerator( + descriptor, messageBitIndex, builderBitIndex, context) { + const OneofGeneratorInfo* info = + context->GetOneofGeneratorInfo(descriptor->containing_oneof()); + SetCommonOneofVariables(descriptor, info, &variables_); + variables_["lazy_type"] = "com.google.protobuf.LazyFieldLite"; +} + +ImmutableLazyMessageOneofFieldLiteGenerator:: +~ImmutableLazyMessageOneofFieldLiteGenerator() {} + +void ImmutableLazyMessageOneofFieldLiteGenerator:: +GenerateMembers(io::Printer* printer) const { + PrintExtraFieldInfo(variables_, printer); + WriteFieldDocComment(printer, descriptor_); + + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return $has_oneof_case_message$;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$() {\n" + " if ($has_oneof_case_message$) {\n" + " return ($type$) (($lazy_type$) $oneof_name$_).getValue(\n" + " $type$.getDefaultInstance());\n" + " }\n" + " return $type$.getDefaultInstance();\n" + "}\n"); + + // Field.Builder setField(Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$($type$ value) {\n" + " if (value == null) {\n" + " throw new NullPointerException();\n" + " }\n" + " if (!($has_oneof_case_message$)) {\n" + " $oneof_name$_ = new $lazy_type$();\n" + " $set_oneof_case_message$;\n" + " }\n" + " (($lazy_type$) $oneof_name$_).setValue(value);\n" + "}\n"); + + // Field.Builder setField(Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$(\n" + " $type$.Builder builderForValue) {\n" + " if (!($has_oneof_case_message$)) {\n" + " $oneof_name$_ = new $lazy_type$();\n" + " $set_oneof_case_message$;\n" + " }\n" + " (($lazy_type$) $oneof_name$_).setValue(builderForValue.build());\n" + "}\n"); + + // Field.Builder mergeField(Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void merge$capitalized_name$($type$ value) {\n" + " if ($has_oneof_case_message$ &&\n" + " !(($lazy_type$) $oneof_name$_).containsDefaultInstance()) {\n" + " (($lazy_type$) $oneof_name$_).setValue(\n" + " $type$.newBuilder(\n" + " get$capitalized_name$()).mergeFrom(value).buildPartial());\n" + " } else {\n" + " if (!($has_oneof_case_message$)) {\n" + " $oneof_name$_ = new $lazy_type$();\n" + " $set_oneof_case_message$;\n" + " }\n" + " (($lazy_type$) $oneof_name$_).setValue(value);\n" + " }\n" + "}\n"); + + // Field.Builder clearField() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void clear$capitalized_name$() {\n" + " if ($has_oneof_case_message$) {\n" + " $clear_oneof_case_message$;\n" + " $oneof_name$_ = null;\n" + " }\n" + "}\n"); +} + +void ImmutableLazyMessageOneofFieldLiteGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + // boolean hasField() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return instance.has$capitalized_name$();\n" + "}\n"); + + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$() {\n" + " return instance.get$capitalized_name$();\n" + "}\n"); + + // Field.Builder setField(Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$($type$ value) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(value);\n" + " return this;\n" + "}\n"); + + // Field.Builder setField(Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$(\n" + " $type$.Builder builderForValue) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(builderForValue);\n" + " return this;\n" + "}\n"); + + // Field.Builder mergeField(Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder merge$capitalized_name$($type$ value) {\n" + " copyOnWrite();\n" + " instance.merge$capitalized_name$(value);\n" + " return this;\n" + "}\n"); + + // Field.Builder clearField() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder clear$capitalized_name$() {\n" + " copyOnWrite();\n" + " instance.clear$capitalized_name$();\n" + " return this;\n" + "}\n"); +} + +void ImmutableLazyMessageOneofFieldLiteGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (!($has_oneof_case_message$)) {\n" + " $oneof_name$_ = new $lazy_type$();\n" + "}\n" + "(($lazy_type$) $oneof_name$_).merge(\n" + " ($lazy_type$) other.$oneof_name$_);\n" + "$set_oneof_case_message$;\n"); +} + +void ImmutableLazyMessageOneofFieldLiteGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (!($has_oneof_case_message$)) {\n" + " $oneof_name$_ = new $lazy_type$();\n" + "}\n" + "(($lazy_type$) $oneof_name$_).setByteString(\n" + " input.readBytes(), extensionRegistry);\n" + "$set_oneof_case_message$;\n"); +} + +void ImmutableLazyMessageOneofFieldLiteGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + // Do not de-serialize lazy fields. + printer->Print(variables_, + "if ($has_oneof_case_message$) {\n" + " output.writeBytes(\n" + " $number$, (($lazy_type$) $oneof_name$_).toByteString());\n" + "}\n"); +} + +void ImmutableLazyMessageOneofFieldLiteGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($has_oneof_case_message$) {\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .computeLazyFieldSize($number$, ($lazy_type$) $oneof_name$_);\n" + "}\n"); +} + +// =================================================================== + +RepeatedImmutableLazyMessageFieldLiteGenerator:: +RepeatedImmutableLazyMessageFieldLiteGenerator( + const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) + : RepeatedImmutableMessageFieldLiteGenerator( + descriptor, messageBitIndex, builderBitIndex, context) { +} + + +RepeatedImmutableLazyMessageFieldLiteGenerator:: +~RepeatedImmutableLazyMessageFieldLiteGenerator() {} + +void RepeatedImmutableLazyMessageFieldLiteGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print(variables_, + "private com.google.protobuf.Internal.ProtobufList<\n" + " com.google.protobuf.LazyFieldLite> $name$_;\n"); + PrintExtraFieldInfo(variables_, printer); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.util.List<$type$>\n" + " get$capitalized_name$List() {\n" + " java.util.List<$type$> list =\n" + " new java.util.ArrayList<$type$>($name$_.size());\n" + " for (com.google.protobuf.LazyFieldLite lf : $name$_) {\n" + " list.add(($type$) lf.getValue($type$.getDefaultInstance()));\n" + " }\n" + // TODO(dweis): Make this list immutable? + " return list;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.util.List\n" + " get$capitalized_name$OrBuilderList() {\n" + " return get$capitalized_name$List();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public int get$capitalized_name$Count() {\n" + " return $name$_.size();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$(int index) {\n" + " return ($type$)\n" + " $name$_.get(index).getValue($type$.getDefaultInstance());\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder(\n" + " int index) {\n" + " return ($type$OrBuilder)\n" + " $name$_.get(index).getValue($type$.getDefaultInstance());\n" + "}\n"); + + printer->Print(variables_, + "private void ensure$capitalized_name$IsMutable() {\n" + " if (!$is_mutable$) {\n" + " $name$_ = newProtobufList($name$_);\n" + " }\n" + "}\n" + "\n"); + + // Builder setRepeatedField(int index, Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$(\n" + " int index, $type$ value) {\n" + " if (value == null) {\n" + " throw new NullPointerException();\n" + " }\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.set(\n" + " index, com.google.protobuf.LazyFieldLite.fromValue(value));\n" + "}\n"); + + // Builder setRepeatedField(int index, Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$(\n" + " int index, $type$.Builder builderForValue) {\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.set(index, com.google.protobuf.LazyFieldLite.fromValue(\n" + " builderForValue.build()));\n" + "}\n"); + + // Builder addRepeatedField(Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void add$capitalized_name$($type$ value) {\n" + " if (value == null) {\n" + " throw new NullPointerException();\n" + " }\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.add(com.google.protobuf.LazyFieldLite.fromValue(value));\n" + "}\n"); + + // Builder addRepeatedField(int index, Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void add$capitalized_name$(\n" + " int index, $type$ value) {\n" + " if (value == null) {\n" + " throw new NullPointerException();\n" + " }\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.add(\n" + " index, com.google.protobuf.LazyFieldLite.fromValue(value));\n" + "}\n"); + + // Builder addRepeatedField(Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void add$capitalized_name$(\n" + " $type$.Builder builderForValue) {\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.add(com.google.protobuf.LazyFieldLite.fromValue(\n" + " builderForValue.build()));\n" + "}\n"); + + // Builder addRepeatedField(int index, Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void add$capitalized_name$(\n" + " int index, $type$.Builder builderForValue) {\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.add(index, com.google.protobuf.LazyFieldLite.fromValue(\n" + " builderForValue.build()));\n" + "}\n"); + + // Builder addAllRepeatedField(Iterable values) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void addAll$capitalized_name$(\n" + " java.lang.Iterable values) {\n" + " ensure$capitalized_name$IsMutable();\n" + " for (com.google.protobuf.MessageLite v : values) {\n" + " $name$_.add(com.google.protobuf.LazyFieldLite.fromValue(v));\n" + " }\n" + "}\n"); + + // Builder clearAllRepeatedField() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void clear$capitalized_name$() {\n" + " $name$_ = emptyProtobufList();\n" + "}\n"); + + // Builder removeRepeatedField(int index) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void remove$capitalized_name$(int index) {\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.remove(index);\n" + "}\n"); +} + +void RepeatedImmutableLazyMessageFieldLiteGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + // List getRepeatedFieldList() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.util.List<$type$> get$capitalized_name$List() {\n" + " return java.util.Collections.unmodifiableList(\n" + " instance.get$capitalized_name$List());\n" + "}\n"); + + // int getRepeatedFieldCount() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public int get$capitalized_name$Count() {\n" + " return instance.get$capitalized_name$Count();\n" + "}\n"); + + // Field getRepeatedField(int index) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$(int index) {\n" + " return instance.get$capitalized_name$(index);\n" + "}\n"); + + // Builder setRepeatedField(int index, Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$(\n" + " int index, $type$ value) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(index, value);\n" + " return this;\n" + "}\n"); + + // Builder setRepeatedField(int index, Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$(\n" + " int index, $type$.Builder builderForValue) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(index, builderForValue);\n" + " return this;\n" + "}\n"); + + // Builder addRepeatedField(Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder add$capitalized_name$($type$ value) {\n" + " copyOnWrite();\n" + " instance.add$capitalized_name$(value);\n" + " return this;\n" + "}\n"); + + // Builder addRepeatedField(int index, Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder add$capitalized_name$(\n" + " int index, $type$ value) {\n" + " copyOnWrite();\n" + " instance.add$capitalized_name$(index, value);\n" + " return this;\n" + "}\n"); + + // Builder addRepeatedField(Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder add$capitalized_name$(\n" + " $type$.Builder builderForValue) {\n" + " copyOnWrite();\n" + " instance.add$capitalized_name$(builderForValue);\n" + " return this;\n" + "}\n"); + + // Builder addRepeatedField(int index, Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder add$capitalized_name$(\n" + " int index, $type$.Builder builderForValue) {\n" + " copyOnWrite();\n" + " instance.add$capitalized_name$(index, builderForValue);\n" + " return this;\n" + "}\n"); + + // Builder addAllRepeatedField(Iterable values) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder addAll$capitalized_name$(\n" + " java.lang.Iterable values) {\n" + " copyOnWrite();\n" + " instance.addAll$capitalized_name$(values);\n" + " return this;\n" + "}\n"); + + // Builder clearAllRepeatedField() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder clear$capitalized_name$() {\n" + " copyOnWrite();\n" + " instance.clear$capitalized_name$();\n" + " return this;\n" + "}\n"); + + // Builder removeRepeatedField(int index) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder remove$capitalized_name$(int index) {\n" + " copyOnWrite();\n" + " instance.remove$capitalized_name$(index);\n" + " return this;\n" + "}\n"); +} + +void RepeatedImmutableLazyMessageFieldLiteGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (!$is_mutable$) {\n" + " $name$_ = newProtobufList();\n" + "}\n" + "$name$_.add(new com.google.protobuf.LazyFieldLite(\n" + " extensionRegistry, input.readBytes()));\n"); +} + +void RepeatedImmutableLazyMessageFieldLiteGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "for (int i = 0; i < $name$_.size(); i++) {\n" + " output.writeBytes($number$, $name$_.get(i).toByteString());\n" + "}\n"); +} + +void RepeatedImmutableLazyMessageFieldLiteGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "for (int i = 0; i < $name$_.size(); i++) {\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .computeLazyFieldSize($number$, $name$_.get(i));\n" + "}\n"); +} + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_lazy_message_field_lite.h b/src/google/protobuf/compiler/java/java_lazy_message_field_lite.h new file mode 100644 index 00000000..e85ec0f3 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_lazy_message_field_lite.h @@ -0,0 +1,118 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: niwasaki@google.com (Naoki Iwasaki) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_LAZY_MESSAGE_FIELD_LITE_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_LAZY_MESSAGE_FIELD_LITE_H__ + +#include +#include + +namespace google { +namespace protobuf { + namespace compiler { + namespace java { + class Context; // context.h + } + } +} + +namespace protobuf { +namespace compiler { +namespace java { + +class ImmutableLazyMessageFieldLiteGenerator + : public ImmutableMessageFieldLiteGenerator { + public: + explicit ImmutableLazyMessageFieldLiteGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~ImmutableLazyMessageFieldLiteGenerator(); + + // overroads ImmutableMessageFieldLiteGenerator ------------------------------ + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateInitializationCode(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableLazyMessageFieldLiteGenerator); +}; + +class ImmutableLazyMessageOneofFieldLiteGenerator + : public ImmutableLazyMessageFieldLiteGenerator { + public: + ImmutableLazyMessageOneofFieldLiteGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~ImmutableLazyMessageOneofFieldLiteGenerator(); + + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableLazyMessageOneofFieldLiteGenerator); +}; + +class RepeatedImmutableLazyMessageFieldLiteGenerator + : public RepeatedImmutableMessageFieldLiteGenerator { + public: + explicit RepeatedImmutableLazyMessageFieldLiteGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~RepeatedImmutableLazyMessageFieldLiteGenerator(); + + // overroads RepeatedImmutableMessageFieldLiteGenerator ---------------------- + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedImmutableLazyMessageFieldLiteGenerator); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_LAZY_MESSAGE_FIELD_LITE_H__ diff --git a/src/google/protobuf/compiler/java/java_map_field.cc b/src/google/protobuf/compiler/java/java_map_field.cc index cf1ef589..f25970e5 100644 --- a/src/google/protobuf/compiler/java/java_map_field.cc +++ b/src/google/protobuf/compiler/java/java_map_field.cc @@ -133,9 +133,11 @@ void SetMessageVariables(const FieldDescriptor* descriptor, (*variables)["set_mutable_bit_parser"] = GenerateSetBitMutableLocal(builderBitIndex); + (*variables)["default_entry"] = (*variables)["capitalized_name"] + + "DefaultEntryHolder.defaultEntry"; if (HasDescriptorMethods(descriptor->file())) { (*variables)["lite"] = ""; - (*variables)["map_field_parameter"] = (*variables)["name"] + "DefaultEntry"; + (*variables)["map_field_parameter"] = (*variables)["default_entry"]; (*variables)["descriptor"] = name_resolver->GetImmutableClassName(descriptor->file()) + ".internal_" + UniqueFileScopeIdentifier(descriptor->message_type()) + @@ -197,27 +199,21 @@ GenerateInterfaceMembers(io::Printer* printer) const { } } -void ImmutableMapFieldGenerator:: -GenerateStaticInitializationCode(io::Printer* printer) const { - printer->Print( - variables_, - "$name$DefaultEntry =\n" - " com.google.protobuf.MapEntry$lite$\n" - " .<$type_parameters$>newDefaultInstance(\n" - " $descriptor$\n" - " $key_wire_type$,\n" - " $key_default_value$,\n" - " $value_wire_type$,\n" - " $value_default_value$);\n" - "\n"); -} - void ImmutableMapFieldGenerator:: GenerateMembers(io::Printer* printer) const { printer->Print( variables_, - "private static final com.google.protobuf.MapEntry$lite$<\n" - " $type_parameters$> $name$DefaultEntry;\n"); + "private static final class $capitalized_name$DefaultEntryHolder {\n" + " static final com.google.protobuf.MapEntry$lite$<\n" + " $type_parameters$> defaultEntry =\n" + " com.google.protobuf.MapEntry$lite$\n" + " .<$type_parameters$>newDefaultInstance(\n" + " $descriptor$\n" + " $key_wire_type$,\n" + " $key_default_value$,\n" + " $value_wire_type$,\n" + " $value_default_value$);\n" + "}\n"); printer->Print( variables_, "private com.google.protobuf.MapField$lite$<\n" @@ -291,7 +287,10 @@ GenerateBuilderMembers(io::Printer* printer) const { " if ($name$_ == null) {\n" " $name$_ = com.google.protobuf.MapField$lite$.newMapField(\n" " $map_field_parameter$);\n" - " }\n" + " }\n" + " if (!$name$_.isMutable()) {\n" + " $name$_ = $name$_.copy();\n" + " }\n" " return $name$_;\n" "}\n"); if (GetJavaType(ValueField(descriptor_)) == JAVATYPE_ENUM) { @@ -381,10 +380,8 @@ void ImmutableMapFieldGenerator:: GenerateBuildingCode(io::Printer* printer) const { printer->Print( variables_, - // We do a copy of the map field to ensure that the built result is - // immutable. Implementation of this copy() method can do copy-on-write - // to defer this copy until further modifications are made on the field. - "result.$name$_ = internalGet$capitalized_name$().copy();\n"); + "result.$name$_ = internalGet$capitalized_name$();\n" + "result.$name$_.makeImmutable();\n"); } void ImmutableMapFieldGenerator:: @@ -402,7 +399,7 @@ GenerateParsingCode(io::Printer* printer) const { variables_, "com.google.protobuf.ByteString bytes = input.readBytes();\n" "com.google.protobuf.MapEntry$lite$<$type_parameters$>\n" - "$name$ = $name$DefaultEntry.getParserForType().parseFrom(bytes);\n"); + "$name$ = $default_entry$.getParserForType().parseFrom(bytes);\n"); printer->Print( variables_, "if ($value_enum_type$.valueOf($name$.getValue()) == null) {\n" @@ -415,7 +412,7 @@ GenerateParsingCode(io::Printer* printer) const { variables_, "com.google.protobuf.MapEntry$lite$<$type_parameters$>\n" "$name$ = input.readMessage(\n" - " $name$DefaultEntry.getParserForType(), extensionRegistry);\n" + " $default_entry$.getParserForType(), extensionRegistry);\n" "$name$_.getMutableMap().put($name$.getKey(), $name$.getValue());\n"); } } @@ -432,7 +429,7 @@ GenerateSerializationCode(io::Printer* printer) const { "for (java.util.Map.Entry<$type_parameters$> entry\n" " : internalGet$capitalized_name$().getMap().entrySet()) {\n" " com.google.protobuf.MapEntry$lite$<$type_parameters$>\n" - " $name$ = $name$DefaultEntry.newBuilderForType()\n" + " $name$ = $default_entry$.newBuilderForType()\n" " .setKey(entry.getKey())\n" " .setValue(entry.getValue())\n" " .build();\n" @@ -447,7 +444,7 @@ GenerateSerializedSizeCode(io::Printer* printer) const { "for (java.util.Map.Entry<$type_parameters$> entry\n" " : internalGet$capitalized_name$().getMap().entrySet()) {\n" " com.google.protobuf.MapEntry$lite$<$type_parameters$>\n" - " $name$ = $name$DefaultEntry.newBuilderForType()\n" + " $name$ = $default_entry$.newBuilderForType()\n" " .setKey(entry.getKey())\n" " .setValue(entry.getValue())\n" " .build();\n" diff --git a/src/google/protobuf/compiler/java/java_map_field.h b/src/google/protobuf/compiler/java/java_map_field.h index 3e6dd973..80a94f45 100644 --- a/src/google/protobuf/compiler/java/java_map_field.h +++ b/src/google/protobuf/compiler/java/java_map_field.h @@ -60,7 +60,6 @@ class ImmutableMapFieldGenerator : public ImmutableFieldGenerator { void GenerateSerializationCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const; void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; - void GenerateStaticInitializationCode(io::Printer* printer) const; void GenerateEqualsCode(io::Printer* printer) const; void GenerateHashCode(io::Printer* printer) const; diff --git a/src/google/protobuf/compiler/java/java_map_field_lite.cc b/src/google/protobuf/compiler/java/java_map_field_lite.cc new file mode 100644 index 00000000..ccc1b32e --- /dev/null +++ b/src/google/protobuf/compiler/java/java_map_field_lite.cc @@ -0,0 +1,461 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include + +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +namespace { + +const FieldDescriptor* KeyField(const FieldDescriptor* descriptor) { + GOOGLE_CHECK_EQ(FieldDescriptor::TYPE_MESSAGE, descriptor->type()); + const Descriptor* message = descriptor->message_type(); + GOOGLE_CHECK(message->options().map_entry()); + return message->FindFieldByName("key"); +} + +const FieldDescriptor* ValueField(const FieldDescriptor* descriptor) { + GOOGLE_CHECK_EQ(FieldDescriptor::TYPE_MESSAGE, descriptor->type()); + const Descriptor* message = descriptor->message_type(); + GOOGLE_CHECK(message->options().map_entry()); + return message->FindFieldByName("value"); +} + +string TypeName(const FieldDescriptor* field, + ClassNameResolver* name_resolver, + bool boxed) { + if (GetJavaType(field) == JAVATYPE_MESSAGE) { + return name_resolver->GetImmutableClassName(field->message_type()); + } else if (GetJavaType(field) == JAVATYPE_ENUM) { + return name_resolver->GetImmutableClassName(field->enum_type()); + } else { + return boxed ? BoxedPrimitiveTypeName(GetJavaType(field)) + : PrimitiveTypeName(GetJavaType(field)); + } +} + +string WireType(const FieldDescriptor* field) { + return "com.google.protobuf.WireFormat.FieldType." + + string(FieldTypeName(field->type())); +} + +void SetMessageVariables(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + const FieldGeneratorInfo* info, + ClassNameResolver* name_resolver, + map* variables) { + SetCommonFieldVariables(descriptor, info, variables); + + (*variables)["type"] = + name_resolver->GetImmutableClassName(descriptor->message_type()); + const FieldDescriptor* key = KeyField(descriptor); + const FieldDescriptor* value = ValueField(descriptor); + (*variables)["key_type"] = TypeName(key, name_resolver, false); + (*variables)["boxed_key_type"] = TypeName(key, name_resolver, true); + (*variables)["key_wire_type"] = WireType(key); + (*variables)["key_default_value"] = DefaultValue(key, true, name_resolver); + if (GetJavaType(value) == JAVATYPE_ENUM) { + // We store enums as Integers internally. + (*variables)["value_type"] = "int"; + (*variables)["boxed_value_type"] = "java.lang.Integer"; + (*variables)["value_wire_type"] = WireType(value); + (*variables)["value_default_value"] = + DefaultValue(value, true, name_resolver) + ".getNumber()"; + + (*variables)["value_enum_type"] = TypeName(value, name_resolver, false); + + if (SupportUnknownEnumValue(descriptor->file())) { + // Map unknown values to a special UNRECOGNIZED value if supported. + (*variables)["unrecognized_value"] = + (*variables)["value_enum_type"] + ".UNRECOGNIZED"; + } else { + // Map unknown values to the default value if we don't have UNRECOGNIZED. + (*variables)["unrecognized_value"] = + DefaultValue(value, true, name_resolver); + } + } else { + (*variables)["value_type"] = TypeName(value, name_resolver, false); + (*variables)["boxed_value_type"] = TypeName(value, name_resolver, true); + (*variables)["value_wire_type"] = WireType(value); + (*variables)["value_default_value"] = + DefaultValue(value, true, name_resolver); + } + (*variables)["type_parameters"] = + (*variables)["boxed_key_type"] + ", " + (*variables)["boxed_value_type"]; + // TODO(birdo): Add @deprecated javadoc when generating javadoc is supported + // by the proto compiler + (*variables)["deprecation"] = descriptor->options().deprecated() + ? "@java.lang.Deprecated " : ""; + (*variables)["on_changed"] = + HasDescriptorMethods(descriptor->containing_type()) ? "onChanged();" : ""; + + (*variables)["default_entry"] = (*variables)["capitalized_name"] + + "DefaultEntryHolder.defaultEntry"; + (*variables)["lite"] = "Lite"; + (*variables)["descriptor"] = ""; +} + +} // namespace + +ImmutableMapFieldLiteGenerator:: +ImmutableMapFieldLiteGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) + : descriptor_(descriptor), messageBitIndex_(messageBitIndex), + builderBitIndex_(builderBitIndex), context_(context), + name_resolver_(context->GetNameResolver()) { + SetMessageVariables(descriptor, messageBitIndex, builderBitIndex, + context->GetFieldGeneratorInfo(descriptor), + name_resolver_, &variables_); +} + +ImmutableMapFieldLiteGenerator:: +~ImmutableMapFieldLiteGenerator() {} + +int ImmutableMapFieldLiteGenerator::GetNumBitsForMessage() const { + return 0; +} + +int ImmutableMapFieldLiteGenerator::GetNumBitsForBuilder() const { + return 0; +} + +void ImmutableMapFieldLiteGenerator:: +GenerateInterfaceMembers(io::Printer* printer) const { + if (GetJavaType(ValueField(descriptor_)) == JAVATYPE_ENUM) { + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "$deprecation$java.util.Map<$boxed_key_type$, $value_enum_type$>\n" + "get$capitalized_name$();\n"); + if (SupportUnknownEnumValue(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "$deprecation$java.util.Map<$type_parameters$>\n" + "get$capitalized_name$Value();\n"); + } + } else { + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "$deprecation$java.util.Map<$type_parameters$>\n" + "get$capitalized_name$();\n"); + } +} + +void ImmutableMapFieldLiteGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print( + variables_, + "private static final class $capitalized_name$DefaultEntryHolder {\n" + " static final com.google.protobuf.MapEntry$lite$<\n" + " $type_parameters$> defaultEntry =\n" + " com.google.protobuf.MapEntry$lite$\n" + " .<$type_parameters$>newDefaultInstance(\n" + " $descriptor$\n" + " $key_wire_type$,\n" + " $key_default_value$,\n" + " $value_wire_type$,\n" + " $value_default_value$);\n" + "}\n"); + printer->Print( + variables_, + "private com.google.protobuf.MapField$lite$<\n" + " $type_parameters$> $name$_ =\n" + " com.google.protobuf.MapField$lite$.emptyMapField();\n" + "private com.google.protobuf.MapField$lite$<$type_parameters$>\n" + "internalGet$capitalized_name$() {\n" + " return $name$_;\n" + "}\n" + "private com.google.protobuf.MapField$lite$<$type_parameters$>\n" + "internalGetMutable$capitalized_name$() {\n" + " if (!$name$_.isMutable()) {\n" + " $name$_ = $name$_.copy();\n" + " }\n" + " return $name$_;\n" + "}\n"); + if (GetJavaType(ValueField(descriptor_)) == JAVATYPE_ENUM) { + printer->Print( + variables_, + "private static final\n" + "com.google.protobuf.Internal.MapAdapter.Converter<\n" + " java.lang.Integer, $value_enum_type$> $name$ValueConverter =\n" + " com.google.protobuf.Internal.MapAdapter.newEnumConverter(\n" + " $value_enum_type$.internalGetValueMap(),\n" + " $unrecognized_value$);\n"); + if (SupportUnknownEnumValue(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "$deprecation$\n" + "public java.util.Map<$boxed_key_type$, $boxed_value_type$>\n" + "get$capitalized_name$Value() {\n" + " return internalGet$capitalized_name$().getMap();\n" + "}\n"); + } + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "$deprecation$\n" + "public java.util.Map<$boxed_key_type$, $value_enum_type$>\n" + "get$capitalized_name$() {\n" + " return new com.google.protobuf.Internal.MapAdapter<\n" + " $boxed_key_type$, $value_enum_type$, java.lang.Integer>(\n" + " internalGet$capitalized_name$().getMap(),\n" + " $name$ValueConverter);\n" + "}\n"); + } else { + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "$deprecation$\n" + "public java.util.Map<$type_parameters$> get$capitalized_name$() {\n" + " return internalGet$capitalized_name$().getMap();\n" + "}\n"); + } + + // Generate private setters for the builder to proxy into. + if (GetJavaType(ValueField(descriptor_)) == JAVATYPE_ENUM) { + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "private java.util.Map<$boxed_key_type$, $value_enum_type$>\n" + "getMutable$capitalized_name$() {\n" + " return new com.google.protobuf.Internal.MapAdapter<\n" + " $boxed_key_type$, $value_enum_type$, java.lang.Integer>(\n" + " internalGetMutable$capitalized_name$().getMutableMap(),\n" + " $name$ValueConverter);\n" + "}\n"); + if (SupportUnknownEnumValue(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "private java.util.Map<$boxed_key_type$, $boxed_value_type$>\n" + "getMutable$capitalized_name$Value() {\n" + " return internalGetMutable$capitalized_name$().getMutableMap();\n" + "}\n"); + } + } else { + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "private java.util.Map<$type_parameters$>\n" + "getMutable$capitalized_name$() {\n" + " return internalGetMutable$capitalized_name$().getMutableMap();\n" + "}\n"); + } +} + +void ImmutableMapFieldLiteGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + if (GetJavaType(ValueField(descriptor_)) == JAVATYPE_ENUM) { + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "$deprecation$\n" + "public java.util.Map<$boxed_key_type$, $value_enum_type$>\n" + "get$capitalized_name$() {\n" + " return instance.get$capitalized_name$();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "$deprecation$\n" + "public java.util.Map<$boxed_key_type$, $value_enum_type$>\n" + "getMutable$capitalized_name$() {\n" + " copyOnWrite();\n" + " return instance.getMutable$capitalized_name$();\n" + "}\n"); + if (SupportUnknownEnumValue(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "$deprecation$\n" + "public java.util.Map<$boxed_key_type$, $boxed_value_type$>\n" + "get$capitalized_name$Value() {\n" + " return instance.get$capitalized_name$Value();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "$deprecation$\n" + "public java.util.Map<$boxed_key_type$, $boxed_value_type$>\n" + "getMutable$capitalized_name$Value() {\n" + " copyOnWrite();\n" + " return instance.getMutable$capitalized_name$Value();\n" + "}\n"); + } + } else { + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "public java.util.Map<$type_parameters$> get$capitalized_name$() {\n" + " return instance.get$capitalized_name$();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "public java.util.Map<$type_parameters$>\n" + "getMutable$capitalized_name$() {\n" + " copyOnWrite();\n" + " return instance.getMutable$capitalized_name$();\n" + "}\n"); + } +} + +void ImmutableMapFieldLiteGenerator:: +GenerateFieldBuilderInitializationCode(io::Printer* printer) const { + // Nothing to initialize. +} + +void ImmutableMapFieldLiteGenerator:: +GenerateInitializationCode(io::Printer* printer) const { + // Nothing to initialize. +} + +void ImmutableMapFieldLiteGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print( + variables_, + "internalGetMutable$capitalized_name$().mergeFrom(\n" + " other.internalGet$capitalized_name$());\n"); +} + +void ImmutableMapFieldLiteGenerator:: +GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const { + printer->Print(variables_, + "$name$_.makeImmutable();\n"); +} + +void ImmutableMapFieldLiteGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print( + variables_, + "if (!$name$_.isMutable()) {\n" + " $name$_ = $name$_.copy();\n" + "}\n"); + if (!SupportUnknownEnumValue(descriptor_->file()) && + GetJavaType(ValueField(descriptor_)) == JAVATYPE_ENUM) { + printer->Print( + variables_, + "com.google.protobuf.ByteString bytes = input.readBytes();\n" + "com.google.protobuf.MapEntry$lite$<$type_parameters$>\n" + "$name$ = $default_entry$.getParserForType().parseFrom(bytes);\n"); + printer->Print( + variables_, + "if ($value_enum_type$.valueOf($name$.getValue()) == null) {\n" + " unknownFields.mergeLengthDelimitedField($number$, bytes);\n" + "} else {\n" + " $name$_.getMutableMap().put($name$.getKey(), $name$.getValue());\n" + "}\n"); + } else { + printer->Print( + variables_, + "com.google.protobuf.MapEntry$lite$<$type_parameters$>\n" + "$name$ = input.readMessage(\n" + " $default_entry$.getParserForType(), extensionRegistry);\n" + "$name$_.getMutableMap().put($name$.getKey(), $name$.getValue());\n"); + } +} + +void ImmutableMapFieldLiteGenerator:: +GenerateParsingDoneCode(io::Printer* printer) const { + // Nothing to do here. +} + +void ImmutableMapFieldLiteGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print( + variables_, + "for (java.util.Map.Entry<$type_parameters$> entry\n" + " : internalGet$capitalized_name$().getMap().entrySet()) {\n" + " com.google.protobuf.MapEntry$lite$<$type_parameters$>\n" + " $name$ = $default_entry$.newBuilderForType()\n" + " .setKey(entry.getKey())\n" + " .setValue(entry.getValue())\n" + " .build();\n" + " output.writeMessage($number$, $name$);\n" + "}\n"); +} + +void ImmutableMapFieldLiteGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print( + variables_, + "for (java.util.Map.Entry<$type_parameters$> entry\n" + " : internalGet$capitalized_name$().getMap().entrySet()) {\n" + " com.google.protobuf.MapEntry$lite$<$type_parameters$>\n" + " $name$ = $default_entry$.newBuilderForType()\n" + " .setKey(entry.getKey())\n" + " .setValue(entry.getValue())\n" + " .build();\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .computeMessageSize($number$, $name$);\n" + "}\n"); +} + +void ImmutableMapFieldLiteGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + printer->Print( + variables_, + "result = result && internalGet$capitalized_name$().equals(\n" + " other.internalGet$capitalized_name$());\n"); +} + +void ImmutableMapFieldLiteGenerator:: +GenerateHashCode(io::Printer* printer) const { + printer->Print( + variables_, + "if (!internalGet$capitalized_name$().getMap().isEmpty()) {\n" + " hash = (37 * hash) + $constant_name$;\n" + " hash = (53 * hash) + internalGet$capitalized_name$().hashCode();\n" + "}\n"); +} + +string ImmutableMapFieldLiteGenerator::GetBoxedType() const { + return name_resolver_->GetImmutableClassName(descriptor_->message_type()); +} + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_map_field_lite.h b/src/google/protobuf/compiler/java/java_map_field_lite.h new file mode 100644 index 00000000..82472602 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_map_field_lite.h @@ -0,0 +1,81 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_MAP_FIELD_LITE_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_MAP_FIELD_LITE_H__ + +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +class ImmutableMapFieldLiteGenerator : public ImmutableFieldLiteGenerator { + public: + explicit ImmutableMapFieldLiteGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~ImmutableMapFieldLiteGenerator(); + + // implements ImmutableFieldLiteGenerator ------------------------------------ + int GetNumBitsForMessage() const; + int GetNumBitsForBuilder() const; + void GenerateInterfaceMembers(io::Printer* printer) const; + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateInitializationCode(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateParsingDoneCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCode(io::Printer* printer) const; + + string GetBoxedType() const; + + private: + const FieldDescriptor* descriptor_; + map variables_; + const int messageBitIndex_; + const int builderBitIndex_; + Context* context_; + ClassNameResolver* name_resolver_; +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_MAP_FIELD_LITE_H__ diff --git a/src/google/protobuf/compiler/java/java_message.cc b/src/google/protobuf/compiler/java/java_message.cc index 63df10b4..09b0fd94 100644 --- a/src/google/protobuf/compiler/java/java_message.cc +++ b/src/google/protobuf/compiler/java/java_message.cc @@ -49,6 +49,8 @@ #include #include #include +#include +#include #include #include #include @@ -93,43 +95,46 @@ ImmutableMessageGenerator::ImmutableMessageGenerator( : MessageGenerator(descriptor), context_(context), name_resolver_(context->GetNameResolver()), field_generators_(descriptor, context_) { + GOOGLE_CHECK_NE( + FileOptions::LITE_RUNTIME, descriptor->file()->options().optimize_for()); } ImmutableMessageGenerator::~ImmutableMessageGenerator() {} void ImmutableMessageGenerator::GenerateStaticVariables(io::Printer* printer) { - if (HasDescriptorMethods(descriptor_)) { - // Because descriptor.proto (com.google.protobuf.DescriptorProtos) is - // used in the construction of descriptors, we have a tricky bootstrapping - // problem. To help control static initialization order, we make sure all - // descriptors and other static data that depends on them are members of - // the outermost class in the file. This way, they will be initialized in - // a deterministic order. - - map vars; - vars["identifier"] = UniqueFileScopeIdentifier(descriptor_); - vars["index"] = SimpleItoa(descriptor_->index()); - vars["classname"] = name_resolver_->GetImmutableClassName(descriptor_); - if (descriptor_->containing_type() != NULL) { - vars["parent"] = UniqueFileScopeIdentifier( - descriptor_->containing_type()); - } - if (MultipleJavaFiles(descriptor_->file(), /* immutable = */ true)) { - // We can only make these package-private since the classes that use them - // are in separate files. - vars["private"] = ""; - } else { - vars["private"] = "private "; - } - - // The descriptor for this type. - printer->Print(vars, - "$private$static com.google.protobuf.Descriptors.Descriptor\n" - " internal_$identifier$_descriptor;\n"); + // Because descriptor.proto (com.google.protobuf.DescriptorProtos) is + // used in the construction of descriptors, we have a tricky bootstrapping + // problem. To help control static initialization order, we make sure all + // descriptors and other static data that depends on them are members of + // the outermost class in the file. This way, they will be initialized in + // a deterministic order. - // And the FieldAccessorTable. - GenerateFieldAccessorTable(printer); + map vars; + vars["identifier"] = UniqueFileScopeIdentifier(descriptor_); + vars["index"] = SimpleItoa(descriptor_->index()); + vars["classname"] = name_resolver_->GetImmutableClassName(descriptor_); + if (descriptor_->containing_type() != NULL) { + vars["parent"] = UniqueFileScopeIdentifier( + descriptor_->containing_type()); } + if (MultipleJavaFiles(descriptor_->file(), /* immutable = */ true)) { + // We can only make these package-private since the classes that use them + // are in separate files. + vars["private"] = ""; + } else { + vars["private"] = "private "; + } + + // The descriptor for this type. + printer->Print(vars, + // TODO(teboring): final needs to be added back. The way to fix it is to + // generate methods that can construct the types, and then still declare the + // types, and then init them in clinit with the new method calls. + "$private$static com.google.protobuf.Descriptors.Descriptor\n" + " internal_$identifier$_descriptor;\n"); + + // And the FieldAccessorTable. + GenerateFieldAccessorTable(printer); // Generate static members for all nested types. for (int i = 0; i < descriptor_->nested_type_count(); i++) { @@ -142,38 +147,37 @@ void ImmutableMessageGenerator::GenerateStaticVariables(io::Printer* printer) { int ImmutableMessageGenerator::GenerateStaticVariableInitializers( io::Printer* printer) { int bytecode_estimate = 0; - if (HasDescriptorMethods(descriptor_)) { - map vars; - vars["identifier"] = UniqueFileScopeIdentifier(descriptor_); - vars["index"] = SimpleItoa(descriptor_->index()); - vars["classname"] = name_resolver_->GetImmutableClassName(descriptor_); - if (descriptor_->containing_type() != NULL) { - vars["parent"] = UniqueFileScopeIdentifier( - descriptor_->containing_type()); - } - - // The descriptor for this type. - if (descriptor_->containing_type() == NULL) { - printer->Print(vars, - "internal_$identifier$_descriptor =\n" - " getDescriptor().getMessageTypes().get($index$);\n"); - bytecode_estimate += 30; - } else { - printer->Print(vars, - "internal_$identifier$_descriptor =\n" - " internal_$parent$_descriptor.getNestedTypes().get($index$);\n"); - bytecode_estimate += 30; - } + map vars; + vars["identifier"] = UniqueFileScopeIdentifier(descriptor_); + vars["index"] = SimpleItoa(descriptor_->index()); + vars["classname"] = name_resolver_->GetImmutableClassName(descriptor_); + if (descriptor_->containing_type() != NULL) { + vars["parent"] = UniqueFileScopeIdentifier( + descriptor_->containing_type()); + } - // And the FieldAccessorTable. - bytecode_estimate += GenerateFieldAccessorTableInitializer(printer); + // The descriptor for this type. + if (descriptor_->containing_type() == NULL) { + printer->Print(vars, + "internal_$identifier$_descriptor =\n" + " getDescriptor().getMessageTypes().get($index$);\n"); + bytecode_estimate += 30; + } else { + printer->Print(vars, + "internal_$identifier$_descriptor =\n" + " internal_$parent$_descriptor.getNestedTypes().get($index$);\n"); + bytecode_estimate += 30; } + // And the FieldAccessorTable. + bytecode_estimate += GenerateFieldAccessorTableInitializer(printer); + // Generate static member initializers for all nested types. for (int i = 0; i < descriptor_->nested_type_count(); i++) { // TODO(kenton): Reuse MessageGenerator objects? - bytecode_estimate += ImmutableMessageGenerator(descriptor_->nested_type(i), context_) - .GenerateStaticVariableInitializers(printer); + bytecode_estimate += + ImmutableMessageGenerator(descriptor_->nested_type(i), context_) + .GenerateStaticVariableInitializers(printer); } return bytecode_estimate; } @@ -229,40 +233,20 @@ GenerateFieldAccessorTableInitializer(io::Printer* printer) { void ImmutableMessageGenerator::GenerateInterface(io::Printer* printer) { if (descriptor_->extension_range_count() > 0) { - if (HasDescriptorMethods(descriptor_)) { - printer->Print( - "public interface $classname$OrBuilder extends\n" - " $extra_interfaces$\n" - " com.google.protobuf.GeneratedMessage.\n" - " ExtendableMessageOrBuilder<$classname$> {\n", - "extra_interfaces", ExtraMessageOrBuilderInterfaces(descriptor_), - "classname", descriptor_->name()); - } else { - printer->Print( - "public interface $classname$OrBuilder extends \n" - " $extra_interfaces$\n" - " com.google.protobuf.GeneratedMessageLite.\n" - " ExtendableMessageOrBuilder<\n" - " $classname$, $classname$.Builder> {\n", - "extra_interfaces", ExtraMessageOrBuilderInterfaces(descriptor_), - "classname", descriptor_->name()); - } + printer->Print( + "public interface $classname$OrBuilder extends\n" + " $extra_interfaces$\n" + " com.google.protobuf.GeneratedMessage.\n" + " ExtendableMessageOrBuilder<$classname$> {\n", + "extra_interfaces", ExtraMessageOrBuilderInterfaces(descriptor_), + "classname", descriptor_->name()); } else { - if (HasDescriptorMethods(descriptor_)) { - printer->Print( - "public interface $classname$OrBuilder extends\n" - " $extra_interfaces$\n" - " com.google.protobuf.MessageOrBuilder {\n", - "extra_interfaces", ExtraMessageOrBuilderInterfaces(descriptor_), - "classname", descriptor_->name()); - } else { - printer->Print( - "public interface $classname$OrBuilder extends\n" - " $extra_interfaces$\n" - " com.google.protobuf.MessageLiteOrBuilder {\n", - "extra_interfaces", ExtraMessageOrBuilderInterfaces(descriptor_), - "classname", descriptor_->name()); - } + printer->Print( + "public interface $classname$OrBuilder extends\n" + " $extra_interfaces$\n" + " com.google.protobuf.MessageOrBuilder {\n", + "extra_interfaces", ExtraMessageOrBuilderInterfaces(descriptor_), + "classname", descriptor_->name()); } printer->Indent(); @@ -287,98 +271,71 @@ void ImmutableMessageGenerator::Generate(io::Printer* printer) { variables["static"] = is_own_file ? " " : " static "; variables["classname"] = descriptor_->name(); variables["extra_interfaces"] = ExtraMessageInterfaces(descriptor_); - variables["lite"] = HasDescriptorMethods(descriptor_) ? "" : "Lite"; WriteMessageDocComment(printer, descriptor_); // The builder_type stores the super type name of the nested Builder class. string builder_type; if (descriptor_->extension_range_count() > 0) { - if (HasDescriptorMethods(descriptor_)) { - printer->Print(variables, - "public $static$final class $classname$ extends\n" - " com.google.protobuf.GeneratedMessage.ExtendableMessage<\n" - " $classname$> implements\n" - " $extra_interfaces$\n" - " $classname$OrBuilder {\n"); - } else { - printer->Print(variables, - "public $static$final class $classname$ extends\n" - " com.google.protobuf.GeneratedMessageLite.ExtendableMessage<\n" - " $classname$, $classname$.Builder> implements\n" - " $extra_interfaces$\n" - " $classname$OrBuilder {\n"); - } + printer->Print(variables, + "public $static$final class $classname$ extends\n" + " com.google.protobuf.GeneratedMessage.ExtendableMessage<\n" + " $classname$> implements\n" + " $extra_interfaces$\n" + " $classname$OrBuilder {\n"); builder_type = strings::Substitute( - "com.google.protobuf.GeneratedMessage$1.ExtendableBuilder<$0, ?>", - name_resolver_->GetImmutableClassName(descriptor_), - variables["lite"]); + "com.google.protobuf.GeneratedMessage.ExtendableBuilder<$0, ?>", + name_resolver_->GetImmutableClassName(descriptor_)); } else { - if (HasDescriptorMethods(descriptor_)) { - printer->Print(variables, - "public $static$final class $classname$ extends\n" - " com.google.protobuf.GeneratedMessage implements\n" - " $extra_interfaces$\n" - " $classname$OrBuilder {\n"); - } else { - printer->Print(variables, - "public $static$final class $classname$ extends\n" - " com.google.protobuf.GeneratedMessageLite<\n" - " $classname$, $classname$.Builder> implements\n" - " $extra_interfaces$\n" - " $classname$OrBuilder {\n"); - } + printer->Print(variables, + "public $static$final class $classname$ extends\n" + " com.google.protobuf.GeneratedMessage implements\n" + " $extra_interfaces$\n" + " $classname$OrBuilder {\n"); - builder_type = strings::Substitute( - "com.google.protobuf.GeneratedMessage$0.Builder", - variables["lite"]); + builder_type = "com.google.protobuf.GeneratedMessage.Builder"; } printer->Indent(); - if (HasDescriptorMethods(descriptor_)) { - // Using builder_type, instead of Builder, prevents the Builder class from - // being loaded into PermGen space when the default instance is created. - // This optimizes the PermGen space usage for clients that do not modify - // messages. - printer->Print( - "// Use $classname$.newBuilder() to construct.\n" - "private $classname$($buildertype$ builder) {\n" - " super(builder);\n" - "}\n", - "classname", descriptor_->name(), - "buildertype", builder_type); - printer->Print( - "private $classname$() {\n", - "classname", descriptor_->name()); - printer->Indent(); - GenerateInitializers(printer); - printer->Outdent(); - printer->Print( - "}\n" - "\n"); - } + // Using builder_type, instead of Builder, prevents the Builder class from + // being loaded into PermGen space when the default instance is created. + // This optimizes the PermGen space usage for clients that do not modify + // messages. + printer->Print( + "// Use $classname$.newBuilder() to construct.\n" + "private $classname$($buildertype$ builder) {\n" + " super(builder);\n" + "}\n", + "classname", descriptor_->name(), + "buildertype", builder_type); + printer->Print( + "private $classname$() {\n", + "classname", descriptor_->name()); + printer->Indent(); + GenerateInitializers(printer); + printer->Outdent(); + printer->Print( + "}\n" + "\n"); - if (HasDescriptorMethods(descriptor_)) { + printer->Print( + "@java.lang.Override\n" + "public final com.google.protobuf.UnknownFieldSet\n" + "getUnknownFields() {\n"); + if (PreserveUnknownFields(descriptor_)) { printer->Print( - "@java.lang.Override\n" - "public final com.google.protobuf.UnknownFieldSet\n" - "getUnknownFields() {\n"); - if (PreserveUnknownFields(descriptor_)) { - printer->Print( - " return this.unknownFields;\n"); - } else { - printer->Print( - " return com.google.protobuf.UnknownFieldSet.getDefaultInstance();\n"); - } + " return this.unknownFields;\n"); + } else { printer->Print( - "}\n"); + " return com.google.protobuf.UnknownFieldSet.getDefaultInstance();\n"); } + printer->Print( + "}\n"); if (HasGeneratedMethods(descriptor_)) { GenerateParsingConstructor(printer); } - GenerateDescriptorMethods(printer, false); - GenerateParser(printer); + GenerateDescriptorMethods(printer); // Nested types for (int i = 0; i < descriptor_->enum_type_count(); i++) { @@ -488,7 +445,7 @@ void ImmutableMessageGenerator::Generate(io::Printer* printer) { } if (HasGeneratedMethods(descriptor_)) { - GenerateIsInitialized(printer, MEMOIZE); + GenerateIsInitialized(printer); GenerateMessageSerializationMethods(printer); } @@ -509,78 +466,39 @@ void ImmutableMessageGenerator::Generate(io::Printer* printer) { // Carefully initialize the default instance in such a way that it doesn't // conflict with other initialization. printer->Print( - "private static final $classname$ defaultInstance;\n", + "private static final $classname$ DEFAULT_INSTANCE;\n", "classname", name_resolver_->GetImmutableClassName(descriptor_)); - if (HasDescriptorMethods(descriptor_)) { - printer->Print( - "static {\n" - " defaultInstance = new $classname$();\n" - "}\n" - "\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_)); - } else { - // LITE_RUNTIME only has one constructor. - printer->Print( - "static {\n" - " defaultInstance = new $classname$(\n" - " com.google.protobuf.Internal\n" - " .EMPTY_CODED_INPUT_STREAM,\n" - " com.google.protobuf.ExtensionRegistryLite\n" - " .getEmptyRegistry());\n" - "}\n" - "\n", - "classname", descriptor_->name()); - } + printer->Print( + "static {\n" + " DEFAULT_INSTANCE = new $classname$();\n" + "}\n" + "\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); + printer->Print( "public static $classname$ getDefaultInstance() {\n" - " return defaultInstance;\n" + " return DEFAULT_INSTANCE;\n" "}\n" "\n", "classname", name_resolver_->GetImmutableClassName(descriptor_)); - if (HasDescriptorMethods(descriptor_)) { - // LITE_RUNTIME implements this at the GeneratedMessageLite level. - printer->Print( - "public $classname$ getDefaultInstanceForType() {\n" - " return defaultInstance;\n" - "}\n" - "\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_)); - } else { - // LITE_RUNTIME uses this to implement the *ForType methods at the - // GeneratedMessageLite level. - printer->Print( - "static {" - " com.google.protobuf.GeneratedMessageLite.onLoad(\n" - " $classname$.class, new com.google.protobuf.GeneratedMessageLite\n" - " .PrototypeHolder<$classname$, Builder>(\n" - " defaultInstance, PARSER));" - "}\n" - "\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_)); - } + GenerateParser(printer); - // Extensions must be declared after the defaultInstance is initialized - // because the defaultInstance is used by the extension to lazily retrieve + printer->Print( + "public $classname$ getDefaultInstanceForType() {\n" + " return DEFAULT_INSTANCE;\n" + "}\n" + "\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); + + // Extensions must be declared after the DEFAULT_INSTANCE is initialized + // because the DEFAULT_INSTANCE is used by the extension to lazily retrieve // the outer class's FileDescriptor. for (int i = 0; i < descriptor_->extension_count(); i++) { ImmutableExtensionGenerator(descriptor_->extension(i), context_) .Generate(printer); } - // Some fields also have static members that must be initialized after we - // have the default instance available. - printer->Print( - "static {\n"); - printer->Indent(); - for (int i = 0; i < descriptor_->field_count(); i++) { - field_generators_.get(descriptor_->field(i)) - .GenerateStaticInitializationCode(printer); - } - printer->Outdent(); - printer->Print( - "}\n"); - printer->Outdent(); printer->Print("}\n\n"); } @@ -617,35 +535,17 @@ GenerateMessageSerializationMethods(io::Printer* printer) { if (descriptor_->extension_range_count() > 0) { if (descriptor_->options().message_set_wire_format()) { - if (HasDescriptorMethods(descriptor_)) { - printer->Print( - "com.google.protobuf.GeneratedMessage\n" - " .ExtendableMessage<$classname$>.ExtensionWriter\n" - " extensionWriter = newMessageSetExtensionWriter();\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_)); - } else { - printer->Print( - "com.google.protobuf.GeneratedMessageLite\n" - " .ExtendableMessage<$classname$, $classname$.Builder>\n" - " .ExtensionWriter extensionWriter =\n" - " newMessageSetExtensionWriter();\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_)); - } + printer->Print( + "com.google.protobuf.GeneratedMessage\n" + " .ExtendableMessage<$classname$>.ExtensionWriter\n" + " extensionWriter = newMessageSetExtensionWriter();\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); } else { - if (HasDescriptorMethods(descriptor_)) { - printer->Print( - "com.google.protobuf.GeneratedMessage\n" - " .ExtendableMessage<$classname$>.ExtensionWriter\n" - " extensionWriter = newExtensionWriter();\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_)); - } else { - printer->Print( - "com.google.protobuf.GeneratedMessageLite\n" - " .ExtendableMessage<$classname$, $classname$.Builder>\n" - " .ExtensionWriter extensionWriter =\n" - " newExtensionWriter();\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_)); - } + printer->Print( + "com.google.protobuf.GeneratedMessage\n" + " .ExtendableMessage<$classname$>.ExtensionWriter\n" + " extensionWriter = newExtensionWriter();\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); } } @@ -665,8 +565,7 @@ GenerateMessageSerializationMethods(io::Printer* printer) { } if (PreserveUnknownFields(descriptor_)) { - if (descriptor_->options().message_set_wire_format() - && HasDescriptorMethods(descriptor_)) { + if (descriptor_->options().message_set_wire_format()) { printer->Print( "unknownFields.writeAsMessageSetTo(output);\n"); } else { @@ -702,8 +601,7 @@ GenerateMessageSerializationMethods(io::Printer* printer) { } if (PreserveUnknownFields(descriptor_)) { - if (descriptor_->options().message_set_wire_format() - && HasDescriptorMethods(descriptor_)) { + if (descriptor_->options().message_set_wire_format()) { printer->Print( "size += unknownFields.getSerializedSizeAsMessageSet();\n"); } else { @@ -800,609 +698,115 @@ void ImmutableMessageGenerator::GenerateSerializeOneExtensionRange( // =================================================================== void ImmutableMessageGenerator::GenerateBuilder(io::Printer* printer) { - if (HasDescriptorMethods(descriptor_)) { - // LITE_RUNTIME implements this at the GeneratedMessageLite level. - printer->Print( - "public Builder newBuilderForType() { return newBuilder(); }\n"); - } + // LITE_RUNTIME implements this at the GeneratedMessageLite level. + printer->Print( + "public Builder newBuilderForType() { return newBuilder(); }\n"); printer->Print( "public static Builder newBuilder() {\n" - " return defaultInstance.toBuilder();\n" + " return DEFAULT_INSTANCE.toBuilder();\n" "}\n" "public static Builder newBuilder($classname$ prototype) {\n" - " return defaultInstance.toBuilder().mergeFrom(prototype);\n" + " return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);\n" "}\n" "public Builder toBuilder() {\n" - " return this == defaultInstance\n" + " return this == DEFAULT_INSTANCE\n" " ? new Builder() : new Builder().mergeFrom(this);\n" "}\n" "\n", "classname", name_resolver_->GetImmutableClassName(descriptor_)); - if (HasNestedBuilders(descriptor_)) { - printer->Print( - "@java.lang.Override\n" - "protected Builder newBuilderForType(\n" - " com.google.protobuf.GeneratedMessage.BuilderParent parent) {\n" - " Builder builder = new Builder(parent);\n" - " return builder;\n" - "}\n"); - } - - WriteMessageDocComment(printer, descriptor_); - - if (descriptor_->extension_range_count() > 0) { - if (HasDescriptorMethods(descriptor_)) { - printer->Print( - "public static final class Builder extends\n" - " com.google.protobuf.GeneratedMessage.ExtendableBuilder<\n" - " $classname$, Builder> implements\n" - " $extra_interfaces$\n" - " $classname$OrBuilder {\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_), - "extra_interfaces", ExtraBuilderInterfaces(descriptor_)); - } else { - printer->Print( - "public static final class Builder extends\n" - " com.google.protobuf.GeneratedMessageLite.ExtendableBuilder<\n" - " $classname$, Builder> implements\n" - " $extra_interfaces$\n" - " $classname$OrBuilder {\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_), - "extra_interfaces", ExtraBuilderInterfaces(descriptor_)); - } - } else { - if (HasDescriptorMethods(descriptor_)) { - printer->Print( - "public static final class Builder extends\n" - " com.google.protobuf.GeneratedMessage.Builder implements\n" - " $extra_interfaces$\n" - " $classname$OrBuilder {\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_), - "extra_interfaces", ExtraBuilderInterfaces(descriptor_)); - } else { - printer->Print( - "public static final class Builder extends\n" - " com.google.protobuf.GeneratedMessageLite.Builder<\n" - " $classname$, Builder>\n" - " implements\n" - " $extra_interfaces$\n" - " $classname$OrBuilder {\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_), - "extra_interfaces", ExtraBuilderInterfaces(descriptor_)); - } - } - printer->Indent(); - - GenerateDescriptorMethods(printer, true); - GenerateCommonBuilderMethods(printer); - - if (HasGeneratedMethods(descriptor_)) { - GenerateIsInitialized(printer, DONT_MEMOIZE); - GenerateBuilderParsingMethods(printer); - } - - // oneof - map vars; - for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { - vars["oneof_name"] = context_->GetOneofGeneratorInfo( - descriptor_->oneof_decl(i))->name; - vars["oneof_capitalized_name"] = context_->GetOneofGeneratorInfo( - descriptor_->oneof_decl(i))->capitalized_name; - vars["oneof_index"] = SimpleItoa(descriptor_->oneof_decl(i)->index()); - // oneofCase_ and oneof_ - printer->Print(vars, - "private int $oneof_name$Case_ = 0;\n" - "private java.lang.Object $oneof_name$_;\n"); - // oneofCase() and clearOneof() - printer->Print(vars, - "public $oneof_capitalized_name$Case\n" - " get$oneof_capitalized_name$Case() {\n" - " return $oneof_capitalized_name$Case.valueOf(\n" - " $oneof_name$Case_);\n" - "}\n" - "\n" - "public Builder clear$oneof_capitalized_name$() {\n" - " $oneof_name$Case_ = 0;\n" - " $oneof_name$_ = null;\n"); - if (HasDescriptorMethods(descriptor_)) { - printer->Print(" onChanged();\n"); - } - printer->Print( - " return this;\n" - "}\n" - "\n"); - } - - if (GenerateHasBits(descriptor_)) { - // Integers for bit fields. - int totalBits = 0; - for (int i = 0; i < descriptor_->field_count(); i++) { - totalBits += field_generators_.get(descriptor_->field(i)) - .GetNumBitsForBuilder(); - } - int totalInts = (totalBits + 31) / 32; - for (int i = 0; i < totalInts; i++) { - printer->Print("private int $bit_field_name$;\n", - "bit_field_name", GetBitFieldName(i)); - } - } - - for (int i = 0; i < descriptor_->field_count(); i++) { - printer->Print("\n"); - field_generators_.get(descriptor_->field(i)) - .GenerateBuilderMembers(printer); - } - - if (!PreserveUnknownFields(descriptor_)) { - printer->Print( - "public final Builder setUnknownFields(\n" - " final com.google.protobuf.UnknownFieldSet unknownFields) {\n" - " return this;\n" - "}\n" - "\n" - "public final Builder mergeUnknownFields(\n" - " final com.google.protobuf.UnknownFieldSet unknownFields) {\n" - " return this;\n" - "}\n" - "\n"); - } - printer->Print( - "\n" - "// @@protoc_insertion_point(builder_scope:$full_name$)\n", - "full_name", descriptor_->full_name()); - - printer->Outdent(); - printer->Print("}\n"); + "@java.lang.Override\n" + "protected Builder newBuilderForType(\n" + " com.google.protobuf.GeneratedMessage.BuilderParent parent) {\n" + " Builder builder = new Builder(parent);\n" + " return builder;\n" + "}\n"); + + MessageBuilderGenerator builderGenerator(descriptor_, context_); + builderGenerator.Generate(printer); } void ImmutableMessageGenerator:: -GenerateDescriptorMethods(io::Printer* printer, bool is_builder) { - if (HasDescriptorMethods(descriptor_)) { - if (!descriptor_->options().no_standard_descriptor_accessor()) { - printer->Print( - "public static final com.google.protobuf.Descriptors.Descriptor\n" - " getDescriptor() {\n" - " return $fileclass$.internal_$identifier$_descriptor;\n" - "}\n" - "\n", - "fileclass", name_resolver_->GetImmutableClassName(descriptor_->file()), - "identifier", UniqueFileScopeIdentifier(descriptor_)); - } - vector map_fields; - for (int i = 0; i < descriptor_->field_count(); i++) { - const FieldDescriptor* field = descriptor_->field(i); - if (GetJavaType(field) == JAVATYPE_MESSAGE && - IsMapEntry(field->message_type())) { - map_fields.push_back(field); - } - } - if (!map_fields.empty()) { - printer->Print( - "@SuppressWarnings({\"rawtypes\"})\n" - "protected com.google.protobuf.MapField internalGetMapField(\n" - " int number) {\n" - " switch (number) {\n"); - printer->Indent(); - printer->Indent(); - for (int i = 0; i < map_fields.size(); ++i) { - const FieldDescriptor* field = map_fields[i]; - const FieldGeneratorInfo* info = context_->GetFieldGeneratorInfo(field); - printer->Print( - "case $number$:\n" - " return internalGet$capitalized_name$();\n", - "number", SimpleItoa(field->number()), - "capitalized_name", info->capitalized_name); - } - printer->Print( - "default:\n" - " throw new RuntimeException(\n" - " \"Invalid map field number: \" + number);\n"); - printer->Outdent(); - printer->Outdent(); - printer->Print( - " }\n" - "}\n"); - if (is_builder) { - printer->Print( - "@SuppressWarnings({\"rawtypes\"})\n" - "protected com.google.protobuf.MapField internalGetMutableMapField(\n" - " int number) {\n" - " switch (number) {\n"); - printer->Indent(); - printer->Indent(); - for (int i = 0; i < map_fields.size(); ++i) { - const FieldDescriptor* field = map_fields[i]; - const FieldGeneratorInfo* info = - context_->GetFieldGeneratorInfo(field); - printer->Print( - "case $number$:\n" - " return internalGetMutable$capitalized_name$();\n", - "number", SimpleItoa(field->number()), - "capitalized_name", info->capitalized_name); - } - printer->Print( - "default:\n" - " throw new RuntimeException(\n" - " \"Invalid map field number: \" + number);\n"); - printer->Outdent(); - printer->Outdent(); - printer->Print( - " }\n" - "}\n"); - } - } +GenerateDescriptorMethods(io::Printer* printer) { + if (!descriptor_->options().no_standard_descriptor_accessor()) { printer->Print( - "protected com.google.protobuf.GeneratedMessage.FieldAccessorTable\n" - " internalGetFieldAccessorTable() {\n" - " return $fileclass$.internal_$identifier$_fieldAccessorTable\n" - " .ensureFieldAccessorsInitialized(\n" - " $classname$.class, $classname$.Builder.class);\n" + "public static final com.google.protobuf.Descriptors.Descriptor\n" + " getDescriptor() {\n" + " return $fileclass$.internal_$identifier$_descriptor;\n" "}\n" "\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_), "fileclass", name_resolver_->GetImmutableClassName(descriptor_->file()), "identifier", UniqueFileScopeIdentifier(descriptor_)); } -} - -// =================================================================== - -void ImmutableMessageGenerator:: -GenerateCommonBuilderMethods(io::Printer* printer) { - if (HasDescriptorMethods(descriptor_)) { - printer->Print( - "// Construct using $classname$.newBuilder()\n" - "private Builder() {\n" - " maybeForceBuilderInitialization();\n" - "}\n" - "\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_)); - - printer->Print( - "private Builder(\n" - " com.google.protobuf.GeneratedMessage.BuilderParent parent) {\n" - " super(parent);\n" - " maybeForceBuilderInitialization();\n" - "}\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_)); - } else { - // LITE runtime passes along the default instance to implement - // getDefaultInstanceForType() at the GeneratedMessageLite level. - printer->Print( - "// Construct using $classname$.newBuilder()\n" - "private Builder() {\n" - " super(defaultInstance);\n" - " maybeForceBuilderInitialization();\n" - "}\n" - "\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_)); + vector map_fields; + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + if (GetJavaType(field) == JAVATYPE_MESSAGE && + IsMapEntry(field->message_type())) { + map_fields.push_back(field); + } } - - - if (HasNestedBuilders(descriptor_)) { + if (!map_fields.empty()) { printer->Print( - "private void maybeForceBuilderInitialization() {\n" - " if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {\n"); - + "@SuppressWarnings({\"rawtypes\"})\n" + "protected com.google.protobuf.MapField internalGetMapField(\n" + " int number) {\n" + " switch (number) {\n"); printer->Indent(); printer->Indent(); - for (int i = 0; i < descriptor_->field_count(); i++) { - if (!descriptor_->field(i)->containing_oneof()) { - field_generators_.get(descriptor_->field(i)) - .GenerateFieldBuilderInitializationCode(printer); - } + for (int i = 0; i < map_fields.size(); ++i) { + const FieldDescriptor* field = map_fields[i]; + const FieldGeneratorInfo* info = context_->GetFieldGeneratorInfo(field); + printer->Print( + "case $number$:\n" + " return internalGet$capitalized_name$();\n", + "number", SimpleItoa(field->number()), + "capitalized_name", info->capitalized_name); } + printer->Print( + "default:\n" + " throw new RuntimeException(\n" + " \"Invalid map field number: \" + number);\n"); printer->Outdent(); printer->Outdent(); - - printer->Print( - " }\n" - "}\n"); - } else { - printer->Print( - "private void maybeForceBuilderInitialization() {\n" - "}\n"); - } - - printer->Print( - "public Builder clear() {\n" - " super.clear();\n"); - - printer->Indent(); - - for (int i = 0; i < descriptor_->field_count(); i++) { - if (!descriptor_->field(i)->containing_oneof()) { - field_generators_.get(descriptor_->field(i)) - .GenerateBuilderClearCode(printer); - } - } - - for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { - printer->Print( - "$oneof_name$Case_ = 0;\n" - "$oneof_name$_ = null;\n", - "oneof_name", context_->GetOneofGeneratorInfo( - descriptor_->oneof_decl(i))->name); - } - - printer->Outdent(); - - printer->Print( - " return this;\n" - "}\n" - "\n"); - - if (HasDescriptorMethods(descriptor_)) { - printer->Print( - "public com.google.protobuf.Descriptors.Descriptor\n" - " getDescriptorForType() {\n" - " return $fileclass$.internal_$identifier$_descriptor;\n" - "}\n" - "\n", - "fileclass", name_resolver_->GetImmutableClassName(descriptor_->file()), - "identifier", UniqueFileScopeIdentifier(descriptor_)); - - // LITE runtime implements this in GeneratedMessageLite. - printer->Print( - "public $classname$ getDefaultInstanceForType() {\n" - " return $classname$.getDefaultInstance();\n" - "}\n" - "\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_)); - } - - // ----------------------------------------------------------------- - - if (HasDescriptorMethods(descriptor_)) { - // LITE implements build in GeneratedMessageLite to save methods. printer->Print( - "public $classname$ build() {\n" - " $classname$ result = buildPartial();\n" - " if (!result.isInitialized()) {\n" - " throw newUninitializedMessageException(result);\n" - " }\n" - " return result;\n" - "}\n" - "\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_)); - } - - if (HasDescriptorMethods(descriptor_)) { - printer->Print( - "public $classname$ buildPartial() {\n" - " $classname$ result = new $classname$(this);\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_)); - } else { - // LITE_RUNTIME only provides a single message constructor. - printer->Print( - "public $classname$ buildPartial() {\n" - " $classname$ result = new $classname$(\n" - " com.google.protobuf.Internal\n" - " .EMPTY_CODED_INPUT_STREAM,\n" - " com.google.protobuf.ExtensionRegistryLite\n" - " .getEmptyRegistry());\n" - " result.unknownFields = this.unknownFields;\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_)); - - if (descriptor_->extension_range_count() > 0) { - printer->Print( - " result.extensions = this.buildExtensions();\n"); - } - } - - printer->Indent(); - - int totalBuilderBits = 0; - int totalMessageBits = 0; - for (int i = 0; i < descriptor_->field_count(); i++) { - const ImmutableFieldGenerator& field = - field_generators_.get(descriptor_->field(i)); - totalBuilderBits += field.GetNumBitsForBuilder(); - totalMessageBits += field.GetNumBitsForMessage(); - } - int totalBuilderInts = (totalBuilderBits + 31) / 32; - int totalMessageInts = (totalMessageBits + 31) / 32; - - if (GenerateHasBits(descriptor_)) { - // Local vars for from and to bit fields to avoid accessing the builder and - // message over and over for these fields. Seems to provide a slight - // perforamance improvement in micro benchmark and this is also what proto1 - // code does. - for (int i = 0; i < totalBuilderInts; i++) { - printer->Print("int from_$bit_field_name$ = $bit_field_name$;\n", - "bit_field_name", GetBitFieldName(i)); - } - for (int i = 0; i < totalMessageInts; i++) { - printer->Print("int to_$bit_field_name$ = 0;\n", - "bit_field_name", GetBitFieldName(i)); - } - } - - // Output generation code for each field. - for (int i = 0; i < descriptor_->field_count(); i++) { - field_generators_.get(descriptor_->field(i)).GenerateBuildingCode(printer); - } - - if (GenerateHasBits(descriptor_)) { - // Copy the bit field results to the generated message - for (int i = 0; i < totalMessageInts; i++) { - printer->Print("result.$bit_field_name$ = to_$bit_field_name$;\n", - "bit_field_name", GetBitFieldName(i)); - } - } - - for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { - printer->Print("result.$oneof_name$Case_ = $oneof_name$Case_;\n", - "oneof_name", context_->GetOneofGeneratorInfo( - descriptor_->oneof_decl(i))->name); - } - - printer->Outdent(); - - if (HasDescriptorMethods(descriptor_)) { - printer->Print( - " onBuilt();\n"); + " }\n" + "}\n"); } - printer->Print( - " return result;\n" + "protected com.google.protobuf.GeneratedMessage.FieldAccessorTable\n" + " internalGetFieldAccessorTable() {\n" + " return $fileclass$.internal_$identifier$_fieldAccessorTable\n" + " .ensureFieldAccessorsInitialized(\n" + " $classname$.class, $classname$.Builder.class);\n" "}\n" "\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_)); - - // ----------------------------------------------------------------- - - if (HasGeneratedMethods(descriptor_)) { - // MergeFrom(Message other) requires the ability to distinguish the other - // messages type by its descriptor. - if (HasDescriptorMethods(descriptor_)) { - printer->Print( - "public Builder mergeFrom(com.google.protobuf.Message other) {\n" - " if (other instanceof $classname$) {\n" - " return mergeFrom(($classname$)other);\n" - " } else {\n" - " super.mergeFrom(other);\n" - " return this;\n" - " }\n" - "}\n" - "\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_)); - } - - printer->Print( - "public Builder mergeFrom($classname$ other) {\n" - // Optimization: If other is the default instance, we know none of its - // fields are set so we can skip the merge. - " if (other == $classname$.getDefaultInstance()) return this;\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_)); - printer->Indent(); - - for (int i = 0; i < descriptor_->field_count(); i++) { - if (!descriptor_->field(i)->containing_oneof()) { - field_generators_.get( - descriptor_->field(i)).GenerateMergingCode(printer); - } - } - - // Merge oneof fields. - for (int i = 0; i < descriptor_->oneof_decl_count(); ++i) { - printer->Print( - "switch (other.get$oneof_capitalized_name$Case()) {\n", - "oneof_capitalized_name", - context_->GetOneofGeneratorInfo( - descriptor_->oneof_decl(i))->capitalized_name); - printer->Indent(); - for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) { - const FieldDescriptor* field = descriptor_->oneof_decl(i)->field(j); - printer->Print( - "case $field_name$: {\n", - "field_name", - ToUpper(field->name())); - printer->Indent(); - field_generators_.get(field).GenerateMergingCode(printer); - printer->Print( - "break;\n"); - printer->Outdent(); - printer->Print( - "}\n"); - } - printer->Print( - "case $cap_oneof_name$_NOT_SET: {\n" - " break;\n" - "}\n", - "cap_oneof_name", - ToUpper(context_->GetOneofGeneratorInfo( - descriptor_->oneof_decl(i))->name)); - printer->Outdent(); - printer->Print( - "}\n"); - } - - printer->Outdent(); - - // if message type has extensions - if (descriptor_->extension_range_count() > 0) { - printer->Print( - " this.mergeExtensionFields(other);\n"); - } - - if (PreserveUnknownFields(descriptor_)) { - printer->Print( - " this.mergeUnknownFields(other.unknownFields);\n"); - } - - if (HasDescriptorMethods(descriptor_)) { - printer->Print(" onChanged();\n"); - } - - printer->Print( - " return this;\n" - "}\n" - "\n"); - } -} - -// =================================================================== - -void ImmutableMessageGenerator:: -GenerateBuilderParsingMethods(io::Printer* printer) { - if (HasDescriptorMethods(descriptor_)) { - // LITE_RUNTIME implements this at the GeneratedMessageLite level. - printer->Print( - "public Builder mergeFrom(\n" - " com.google.protobuf.CodedInputStream input,\n" - " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" - " throws java.io.IOException {\n" - " $classname$ parsedMessage = null;\n" - " try {\n" - " parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);\n" - " } catch (com.google.protobuf.InvalidProtocolBufferException e) {\n" - " parsedMessage = ($classname$) e.getUnfinishedMessage();\n" - " throw e;\n" - " } finally {\n" - " if (parsedMessage != null) {\n" - " mergeFrom(parsedMessage);\n" - " }\n" - " }\n" - " return this;\n" - "}\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_)); - } + "classname", name_resolver_->GetImmutableClassName(descriptor_), + "fileclass", name_resolver_->GetImmutableClassName(descriptor_->file()), + "identifier", UniqueFileScopeIdentifier(descriptor_)); } // =================================================================== void ImmutableMessageGenerator::GenerateIsInitialized( - io::Printer* printer, UseMemoization useMemoization) { - // LITE_RUNTIME avoids generating isInitialized if it's not needed. - if (!HasDescriptorMethods(descriptor_) - && !HasRequiredFields(descriptor_)) { - return; - } - - bool memoization = useMemoization == MEMOIZE; - if (memoization) { - // Memoizes whether the protocol buffer is fully initialized (has all - // required fields). -1 means not yet computed. 0 means false and 1 means - // true. - printer->Print( - "private byte memoizedIsInitialized = -1;\n"); - } + io::Printer* printer) { + // Memoizes whether the protocol buffer is fully initialized (has all + // required fields). -1 means not yet computed. 0 means false and 1 means + // true. + printer->Print( + "private byte memoizedIsInitialized = -1;\n"); printer->Print( "public final boolean isInitialized() {\n"); printer->Indent(); - if (memoization) { - // Don't directly compare to -1 to avoid an Android x86 JIT bug. - printer->Print( - "byte isInitialized = memoizedIsInitialized;\n" - "if (isInitialized == 1) return true;\n" - "if (isInitialized == 0) return false;\n" - "\n"); - } + // Don't directly compare to -1 to avoid an Android x86 JIT bug. + printer->Print( + "byte isInitialized = memoizedIsInitialized;\n" + "if (isInitialized == 1) return true;\n" + "if (isInitialized == 0) return false;\n" + "\n"); // Check that all required fields in this message are set. // TODO(kenton): We can optimize this when we switch to putting all the @@ -1414,11 +818,10 @@ void ImmutableMessageGenerator::GenerateIsInitialized( if (field->is_required()) { printer->Print( "if (!has$name$()) {\n" - " $memoize$\n" + " memoizedIsInitialized = 0;\n" " return false;\n" "}\n", - "name", info->capitalized_name, - "memoize", memoization ? "memoizedIsInitialized = 0;" : ""); + "name", info->capitalized_name); } } @@ -1432,13 +835,12 @@ void ImmutableMessageGenerator::GenerateIsInitialized( case FieldDescriptor::LABEL_REQUIRED: printer->Print( "if (!get$name$().isInitialized()) {\n" - " $memoize$\n" + " memoizedIsInitialized = 0;\n" " return false;\n" "}\n", "type", name_resolver_->GetImmutableClassName( field->message_type()), - "name", info->capitalized_name, - "memoize", memoization ? "memoizedIsInitialized = 0;" : ""); + "name", info->capitalized_name); break; case FieldDescriptor::LABEL_OPTIONAL: if (!SupportFieldPresence(descriptor_->file()) && @@ -1457,38 +859,35 @@ void ImmutableMessageGenerator::GenerateIsInitialized( } printer->Print( " if (!get$name$().isInitialized()) {\n" - " $memoize$\n" + " memoizedIsInitialized = 0;\n" " return false;\n" " }\n" "}\n", - "name", info->capitalized_name, - "memoize", memoization ? "memoizedIsInitialized = 0;" : ""); + "name", info->capitalized_name); break; case FieldDescriptor::LABEL_REPEATED: if (IsMapEntry(field->message_type())) { printer->Print( "for ($type$ item : get$name$().values()) {\n" " if (!item.isInitialized()) {\n" - " $memoize$\n" + " memoizedIsInitialized = 0;\n" " return false;\n" " }\n" "}\n", "type", MapValueImmutableClassdName(field->message_type(), name_resolver_), - "name", info->capitalized_name, - "memoize", memoization ? "memoizedIsInitialized = 0;" : ""); + "name", info->capitalized_name); } else { printer->Print( "for (int i = 0; i < get$name$Count(); i++) {\n" " if (!get$name$(i).isInitialized()) {\n" - " $memoize$\n" + " memoizedIsInitialized = 0;\n" " return false;\n" " }\n" "}\n", "type", name_resolver_->GetImmutableClassName( field->message_type()), - "name", info->capitalized_name, - "memoize", memoization ? "memoizedIsInitialized = 0;" : ""); + "name", info->capitalized_name); } break; } @@ -1498,18 +897,15 @@ void ImmutableMessageGenerator::GenerateIsInitialized( if (descriptor_->extension_range_count() > 0) { printer->Print( "if (!extensionsAreInitialized()) {\n" - " $memoize$\n" + " memoizedIsInitialized = 0;\n" " return false;\n" - "}\n", - "memoize", memoization ? "memoizedIsInitialized = 0;" : ""); + "}\n"); } printer->Outdent(); - if (memoization) { - printer->Print( - " memoizedIsInitialized = 1;\n"); - } + printer->Print( + " memoizedIsInitialized = 1;\n"); printer->Print( " return true;\n" @@ -1575,12 +971,10 @@ GenerateEqualsAndHashCode(io::Printer* printer) { printer->Print( "result = result && unknownFields.equals(other.unknownFields);\n"); } - if (HasDescriptorMethods(descriptor_)) { - if (descriptor_->extension_range_count() > 0) { - printer->Print( - "result = result &&\n" - " getExtensionFields().equals(other.getExtensionFields());\n"); - } + if (descriptor_->extension_range_count() > 0) { + printer->Print( + "result = result &&\n" + " getExtensionFields().equals(other.getExtensionFields());\n"); } printer->Print( "return result;\n"); @@ -1603,14 +997,7 @@ GenerateEqualsAndHashCode(io::Printer* printer) { "}\n" "int hash = 41;\n"); - if (HasDescriptorMethods(descriptor_)) { - printer->Print("hash = (19 * hash) + getDescriptorForType().hashCode();\n"); - } else { - // Include the hash of the class so that two objects with different types - // but the same field values will probably have different hashes. - printer->Print("hash = (19 * hash) + $classname$.class.hashCode();\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_)); - } + printer->Print("hash = (19 * hash) + getDescriptorForType().hashCode();\n"); for (int i = 0; i < descriptor_->field_count(); i++) { const FieldDescriptor* field = descriptor_->field(i); @@ -1628,11 +1015,9 @@ GenerateEqualsAndHashCode(io::Printer* printer) { printer->Print("}\n"); } } - if (HasDescriptorMethods(descriptor_)) { - if (descriptor_->extension_range_count() > 0) { - printer->Print( - "hash = hashFields(hash, getExtensionFields());\n"); - } + if (descriptor_->extension_range_count() > 0) { + printer->Print( + "hash = hashFields(hash, getExtensionFields());\n"); } printer->Print( @@ -1675,13 +1060,8 @@ GenerateParsingConstructor(io::Printer* printer) { printer->Indent(); // Initialize all fields to default. - if (HasDescriptorMethods(descriptor_)) { - printer->Print( - "this();\n"); - } else { - // LITE_RUNTIME only has one constructor. - GenerateInitializers(printer); - } + printer->Print( + "this();\n"); // Use builder bits to track mutable repeated fields. int totalBuilderBits = 0; @@ -1697,15 +1077,9 @@ GenerateParsingConstructor(io::Printer* printer) { } if (PreserveUnknownFields(descriptor_)) { - if (HasDescriptorMethods(descriptor_)) { - printer->Print( - "com.google.protobuf.UnknownFieldSet.Builder unknownFields =\n" - " com.google.protobuf.UnknownFieldSet.newBuilder();\n"); - } else { - printer->Print( - "com.google.protobuf.UnknownFieldSetLite.Builder unknownFields =\n" - " com.google.protobuf.UnknownFieldSetLite.newBuilder();\n"); - } + printer->Print( + "com.google.protobuf.UnknownFieldSet.Builder unknownFields =\n" + " com.google.protobuf.UnknownFieldSet.newBuilder();\n"); } printer->Print( @@ -1728,29 +1102,14 @@ GenerateParsingConstructor(io::Printer* printer) { " break;\n"); if (PreserveUnknownFields(descriptor_)) { - if (!HasDescriptorMethods(descriptor_) - && descriptor_->extension_range_count() > 0) { - // Lite runtime directly invokes parseUnknownField to reduce method - // counts. - printer->Print( - "default: {\n" - " if (!parseUnknownField(extensions, getDefaultInstanceForType(),\n" - " input, unknownFields,\n" - " extensionRegistry, tag)) {\n" - " done = true;\n" // it's an endgroup tag - " }\n" - " break;\n" - "}\n"); - } else { - printer->Print( - "default: {\n" - " if (!parseUnknownField(input, unknownFields,\n" - " extensionRegistry, tag)) {\n" - " done = true;\n" // it's an endgroup tag - " }\n" - " break;\n" - "}\n"); - } + printer->Print( + "default: {\n" + " if (!parseUnknownField(input, unknownFields,\n" + " extensionRegistry, tag)) {\n" + " done = true;\n" // it's an endgroup tag + " }\n" + " break;\n" + "}\n"); } else { printer->Print( "default: {\n" @@ -1825,18 +1184,9 @@ GenerateParsingConstructor(io::Printer* printer) { printer->Print("this.unknownFields = unknownFields.build();\n"); } - if (!HasDescriptorMethods(descriptor_)) { - // LITE runtime uses a static method to reduce method count. - if (descriptor_->extension_range_count() > 0) { - // Make extensions immutable. - printer->Print( - "makeExtensionsImmutable(extensions);\n"); - } - } else { - // Make extensions immutable. - printer->Print( - "makeExtensionsImmutable();\n"); - } + // Make extensions immutable. + printer->Print( + "makeExtensionsImmutable();\n"); printer->Outdent(); printer->Outdent(); @@ -1874,9 +1224,9 @@ void ImmutableMessageGenerator::GenerateParser(io::Printer* printer) { " }\n", "classname", descriptor_->name()); } else { - // When parsing constructor isn't generated, use builder to parse messages. - // Note, will fallback to use reflection based mergeFieldFrom() in - // AbstractMessage.Builder. + // When parsing constructor isn't generated, use builder to parse + // messages. Note, will fallback to use reflection based mergeFieldFrom() + // in AbstractMessage.Builder. printer->Indent(); printer->Print( "Builder builder = newBuilder();\n" @@ -1886,7 +1236,8 @@ void ImmutableMessageGenerator::GenerateParser(io::Printer* printer) { " throw e.setUnfinishedMessage(builder.buildPartial());\n" "} catch (java.io.IOException e) {\n" " throw new com.google.protobuf.InvalidProtocolBufferException(\n" - " e.getMessage()).setUnfinishedMessage(builder.buildPartial());\n" + " e.getMessage()).setUnfinishedMessage(\n" + " builder.buildPartial());\n" "}\n" "return builder.buildPartial();\n"); printer->Outdent(); @@ -1898,16 +1249,13 @@ void ImmutableMessageGenerator::GenerateParser(io::Printer* printer) { "};\n" "\n"); - if (HasDescriptorMethods(descriptor_)) { - // LITE_RUNTIME implements this at the GeneratedMessageLite level. - printer->Print( - "@java.lang.Override\n" - "public com.google.protobuf.Parser<$classname$> getParserForType() {\n" - " return PARSER;\n" - "}\n" - "\n", - "classname", descriptor_->name()); - } + printer->Print( + "@java.lang.Override\n" + "public com.google.protobuf.Parser<$classname$> getParserForType() {\n" + " return PARSER;\n" + "}\n" + "\n", + "classname", descriptor_->name()); } // =================================================================== diff --git a/src/google/protobuf/compiler/java/java_message.h b/src/google/protobuf/compiler/java/java_message.h index 58dd5f99..c3c37765 100644 --- a/src/google/protobuf/compiler/java/java_message.h +++ b/src/google/protobuf/compiler/java/java_message.h @@ -102,10 +102,6 @@ class ImmutableMessageGenerator : public MessageGenerator { virtual int GenerateStaticVariableInitializers(io::Printer* printer); private: - enum UseMemoization { - MEMOIZE, - DONT_MEMOIZE - }; void GenerateFieldAccessorTable(io::Printer* printer); @@ -120,11 +116,8 @@ class ImmutableMessageGenerator : public MessageGenerator { io::Printer* printer, const Descriptor::ExtensionRange* range); void GenerateBuilder(io::Printer* printer); - void GenerateCommonBuilderMethods(io::Printer* printer); - void GenerateDescriptorMethods(io::Printer* printer, bool is_builder); - void GenerateBuilderParsingMethods(io::Printer* printer); - void GenerateIsInitialized(io::Printer* printer, - UseMemoization useMemoization); + void GenerateIsInitialized(io::Printer* printer); + void GenerateDescriptorMethods(io::Printer* printer); void GenerateInitializers(io::Printer* printer); void GenerateEqualsAndHashCode(io::Printer* printer); void GenerateParser(io::Printer* printer); diff --git a/src/google/protobuf/compiler/java/java_message_builder.cc b/src/google/protobuf/compiler/java/java_message_builder.cc new file mode 100644 index 00000000..72694119 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_message_builder.cc @@ -0,0 +1,661 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: dweis@google.com (Daniel Weis) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include + +#include +#include +#include +#include +#ifndef _SHARED_PTR_H +#include +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +namespace { +bool GenerateHasBits(const Descriptor* descriptor) { + return SupportFieldPresence(descriptor->file()) || + HasRepeatedFields(descriptor); +} + +string MapValueImmutableClassdName(const Descriptor* descriptor, + ClassNameResolver* name_resolver) { + const FieldDescriptor* value_field = descriptor->FindFieldByName("value"); + GOOGLE_CHECK_EQ(FieldDescriptor::TYPE_MESSAGE, value_field->type()); + return name_resolver->GetImmutableClassName(value_field->message_type()); +} +} // namespace + +MessageBuilderGenerator::MessageBuilderGenerator( + const Descriptor* descriptor, Context* context) + : descriptor_(descriptor), context_(context), + name_resolver_(context->GetNameResolver()), + field_generators_(descriptor, context_) { + GOOGLE_CHECK_NE( + FileOptions::LITE_RUNTIME, descriptor->file()->options().optimize_for()); +} + +MessageBuilderGenerator::~MessageBuilderGenerator() {} + +void MessageBuilderGenerator:: +Generate(io::Printer* printer) { + WriteMessageDocComment(printer, descriptor_); + if (descriptor_->extension_range_count() > 0) { + printer->Print( + "public static final class Builder extends\n" + " com.google.protobuf.GeneratedMessage.ExtendableBuilder<\n" + " $classname$, Builder> implements\n" + " $extra_interfaces$\n" + " $classname$OrBuilder {\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_), + "extra_interfaces", ExtraBuilderInterfaces(descriptor_)); + } else { + printer->Print( + "public static final class Builder extends\n" + " com.google.protobuf.GeneratedMessage.Builder implements\n" + " $extra_interfaces$\n" + " $classname$OrBuilder {\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_), + "extra_interfaces", ExtraBuilderInterfaces(descriptor_)); + } + printer->Indent(); + + GenerateDescriptorMethods(printer); + GenerateCommonBuilderMethods(printer); + + if (HasGeneratedMethods(descriptor_)) { + GenerateIsInitialized(printer); + GenerateBuilderParsingMethods(printer); + } + + // oneof + map vars; + for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { + vars["oneof_name"] = context_->GetOneofGeneratorInfo( + descriptor_->oneof_decl(i))->name; + vars["oneof_capitalized_name"] = context_->GetOneofGeneratorInfo( + descriptor_->oneof_decl(i))->capitalized_name; + vars["oneof_index"] = SimpleItoa(descriptor_->oneof_decl(i)->index()); + // oneofCase_ and oneof_ + printer->Print(vars, + "private int $oneof_name$Case_ = 0;\n" + "private java.lang.Object $oneof_name$_;\n"); + // oneofCase() and clearOneof() + printer->Print(vars, + "public $oneof_capitalized_name$Case\n" + " get$oneof_capitalized_name$Case() {\n" + " return $oneof_capitalized_name$Case.valueOf(\n" + " $oneof_name$Case_);\n" + "}\n" + "\n" + "public Builder clear$oneof_capitalized_name$() {\n" + " $oneof_name$Case_ = 0;\n" + " $oneof_name$_ = null;\n"); + printer->Print(" onChanged();\n"); + printer->Print( + " return this;\n" + "}\n" + "\n"); + } + + if (GenerateHasBits(descriptor_)) { + // Integers for bit fields. + int totalBits = 0; + for (int i = 0; i < descriptor_->field_count(); i++) { + totalBits += field_generators_.get(descriptor_->field(i)) + .GetNumBitsForBuilder(); + } + int totalInts = (totalBits + 31) / 32; + for (int i = 0; i < totalInts; i++) { + printer->Print("private int $bit_field_name$;\n", + "bit_field_name", GetBitFieldName(i)); + } + } + + for (int i = 0; i < descriptor_->field_count(); i++) { + printer->Print("\n"); + field_generators_.get(descriptor_->field(i)) + .GenerateBuilderMembers(printer); + } + + if (!PreserveUnknownFields(descriptor_)) { + printer->Print( + "public final Builder setUnknownFields(\n" + " final com.google.protobuf.UnknownFieldSet unknownFields) {\n" + " return this;\n" + "}\n" + "\n" + "public final Builder mergeUnknownFields(\n" + " final com.google.protobuf.UnknownFieldSet unknownFields) {\n" + " return this;\n" + "}\n" + "\n"); + } + + printer->Print( + "\n" + "// @@protoc_insertion_point(builder_scope:$full_name$)\n", + "full_name", descriptor_->full_name()); + + printer->Outdent(); + printer->Print("}\n"); +} + +// =================================================================== + +void MessageBuilderGenerator:: +GenerateDescriptorMethods(io::Printer* printer) { + if (!descriptor_->options().no_standard_descriptor_accessor()) { + printer->Print( + "public static final com.google.protobuf.Descriptors.Descriptor\n" + " getDescriptor() {\n" + " return $fileclass$.internal_$identifier$_descriptor;\n" + "}\n" + "\n", + "fileclass", name_resolver_->GetImmutableClassName(descriptor_->file()), + "identifier", UniqueFileScopeIdentifier(descriptor_)); + } + vector map_fields; + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + if (GetJavaType(field) == JAVATYPE_MESSAGE && + IsMapEntry(field->message_type())) { + map_fields.push_back(field); + } + } + if (!map_fields.empty()) { + printer->Print( + "@SuppressWarnings({\"rawtypes\"})\n" + "protected com.google.protobuf.MapField internalGetMapField(\n" + " int number) {\n" + " switch (number) {\n"); + printer->Indent(); + printer->Indent(); + for (int i = 0; i < map_fields.size(); ++i) { + const FieldDescriptor* field = map_fields[i]; + const FieldGeneratorInfo* info = context_->GetFieldGeneratorInfo(field); + printer->Print( + "case $number$:\n" + " return internalGet$capitalized_name$();\n", + "number", SimpleItoa(field->number()), + "capitalized_name", info->capitalized_name); + } + printer->Print( + "default:\n" + " throw new RuntimeException(\n" + " \"Invalid map field number: \" + number);\n"); + printer->Outdent(); + printer->Outdent(); + printer->Print( + " }\n" + "}\n"); + printer->Print( + "@SuppressWarnings({\"rawtypes\"})\n" + "protected com.google.protobuf.MapField internalGetMutableMapField(\n" + " int number) {\n" + " switch (number) {\n"); + printer->Indent(); + printer->Indent(); + for (int i = 0; i < map_fields.size(); ++i) { + const FieldDescriptor* field = map_fields[i]; + const FieldGeneratorInfo* info = + context_->GetFieldGeneratorInfo(field); + printer->Print( + "case $number$:\n" + " return internalGetMutable$capitalized_name$();\n", + "number", SimpleItoa(field->number()), + "capitalized_name", info->capitalized_name); + } + printer->Print( + "default:\n" + " throw new RuntimeException(\n" + " \"Invalid map field number: \" + number);\n"); + printer->Outdent(); + printer->Outdent(); + printer->Print( + " }\n" + "}\n"); + } + printer->Print( + "protected com.google.protobuf.GeneratedMessage.FieldAccessorTable\n" + " internalGetFieldAccessorTable() {\n" + " return $fileclass$.internal_$identifier$_fieldAccessorTable\n" + " .ensureFieldAccessorsInitialized(\n" + " $classname$.class, $classname$.Builder.class);\n" + "}\n" + "\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_), + "fileclass", name_resolver_->GetImmutableClassName(descriptor_->file()), + "identifier", UniqueFileScopeIdentifier(descriptor_)); +} + +// =================================================================== + +void MessageBuilderGenerator:: +GenerateCommonBuilderMethods(io::Printer* printer) { + printer->Print( + "// Construct using $classname$.newBuilder()\n" + "private Builder() {\n" + " maybeForceBuilderInitialization();\n" + "}\n" + "\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); + + printer->Print( + "private Builder(\n" + " com.google.protobuf.GeneratedMessage.BuilderParent parent) {\n" + " super(parent);\n" + " maybeForceBuilderInitialization();\n" + "}\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); + + printer->Print( + "private void maybeForceBuilderInitialization() {\n" + " if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {\n"); + + printer->Indent(); + printer->Indent(); + for (int i = 0; i < descriptor_->field_count(); i++) { + if (!descriptor_->field(i)->containing_oneof()) { + field_generators_.get(descriptor_->field(i)) + .GenerateFieldBuilderInitializationCode(printer); + } + } + printer->Outdent(); + printer->Outdent(); + + printer->Print( + " }\n" + "}\n"); + + printer->Print( + "public Builder clear() {\n" + " super.clear();\n"); + + printer->Indent(); + + for (int i = 0; i < descriptor_->field_count(); i++) { + if (!descriptor_->field(i)->containing_oneof()) { + field_generators_.get(descriptor_->field(i)) + .GenerateBuilderClearCode(printer); + } + } + + for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { + printer->Print( + "$oneof_name$Case_ = 0;\n" + "$oneof_name$_ = null;\n", + "oneof_name", context_->GetOneofGeneratorInfo( + descriptor_->oneof_decl(i))->name); + } + + printer->Outdent(); + + printer->Print( + " return this;\n" + "}\n" + "\n"); + + printer->Print( + "public com.google.protobuf.Descriptors.Descriptor\n" + " getDescriptorForType() {\n" + " return $fileclass$.internal_$identifier$_descriptor;\n" + "}\n" + "\n", + "fileclass", name_resolver_->GetImmutableClassName(descriptor_->file()), + "identifier", UniqueFileScopeIdentifier(descriptor_)); + + // LITE runtime implements this in GeneratedMessageLite. + printer->Print( + "public $classname$ getDefaultInstanceForType() {\n" + " return $classname$.getDefaultInstance();\n" + "}\n" + "\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); + + printer->Print( + "public $classname$ build() {\n" + " $classname$ result = buildPartial();\n" + " if (!result.isInitialized()) {\n" + " throw newUninitializedMessageException(result);\n" + " }\n" + " return result;\n" + "}\n" + "\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); + + printer->Print( + "public $classname$ buildPartial() {\n" + " $classname$ result = new $classname$(this);\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); + + printer->Indent(); + + int totalBuilderBits = 0; + int totalMessageBits = 0; + for (int i = 0; i < descriptor_->field_count(); i++) { + const ImmutableFieldGenerator& field = + field_generators_.get(descriptor_->field(i)); + totalBuilderBits += field.GetNumBitsForBuilder(); + totalMessageBits += field.GetNumBitsForMessage(); + } + int totalBuilderInts = (totalBuilderBits + 31) / 32; + int totalMessageInts = (totalMessageBits + 31) / 32; + + if (GenerateHasBits(descriptor_)) { + // Local vars for from and to bit fields to avoid accessing the builder and + // message over and over for these fields. Seems to provide a slight + // perforamance improvement in micro benchmark and this is also what proto1 + // code does. + for (int i = 0; i < totalBuilderInts; i++) { + printer->Print("int from_$bit_field_name$ = $bit_field_name$;\n", + "bit_field_name", GetBitFieldName(i)); + } + for (int i = 0; i < totalMessageInts; i++) { + printer->Print("int to_$bit_field_name$ = 0;\n", + "bit_field_name", GetBitFieldName(i)); + } + } + + // Output generation code for each field. + for (int i = 0; i < descriptor_->field_count(); i++) { + field_generators_.get(descriptor_->field(i)).GenerateBuildingCode(printer); + } + + if (GenerateHasBits(descriptor_)) { + // Copy the bit field results to the generated message + for (int i = 0; i < totalMessageInts; i++) { + printer->Print("result.$bit_field_name$ = to_$bit_field_name$;\n", + "bit_field_name", GetBitFieldName(i)); + } + } + + for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { + printer->Print("result.$oneof_name$Case_ = $oneof_name$Case_;\n", + "oneof_name", context_->GetOneofGeneratorInfo( + descriptor_->oneof_decl(i))->name); + } + + printer->Outdent(); + + printer->Print( + " onBuilt();\n"); + + printer->Print( + " return result;\n" + "}\n" + "\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); + + // ----------------------------------------------------------------- + + if (HasGeneratedMethods(descriptor_)) { + printer->Print( + "public Builder mergeFrom(com.google.protobuf.Message other) {\n" + " if (other instanceof $classname$) {\n" + " return mergeFrom(($classname$)other);\n" + " } else {\n" + " super.mergeFrom(other);\n" + " return this;\n" + " }\n" + "}\n" + "\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); + + printer->Print( + "public Builder mergeFrom($classname$ other) {\n" + // Optimization: If other is the default instance, we know none of its + // fields are set so we can skip the merge. + " if (other == $classname$.getDefaultInstance()) return this;\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); + printer->Indent(); + + for (int i = 0; i < descriptor_->field_count(); i++) { + if (!descriptor_->field(i)->containing_oneof()) { + field_generators_.get( + descriptor_->field(i)).GenerateMergingCode(printer); + } + } + + // Merge oneof fields. + for (int i = 0; i < descriptor_->oneof_decl_count(); ++i) { + printer->Print( + "switch (other.get$oneof_capitalized_name$Case()) {\n", + "oneof_capitalized_name", + context_->GetOneofGeneratorInfo( + descriptor_->oneof_decl(i))->capitalized_name); + printer->Indent(); + for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) { + const FieldDescriptor* field = descriptor_->oneof_decl(i)->field(j); + printer->Print( + "case $field_name$: {\n", + "field_name", + ToUpper(field->name())); + printer->Indent(); + field_generators_.get(field).GenerateMergingCode(printer); + printer->Print( + "break;\n"); + printer->Outdent(); + printer->Print( + "}\n"); + } + printer->Print( + "case $cap_oneof_name$_NOT_SET: {\n" + " break;\n" + "}\n", + "cap_oneof_name", + ToUpper(context_->GetOneofGeneratorInfo( + descriptor_->oneof_decl(i))->name)); + printer->Outdent(); + printer->Print( + "}\n"); + } + + printer->Outdent(); + + // if message type has extensions + if (descriptor_->extension_range_count() > 0) { + printer->Print( + " this.mergeExtensionFields(other);\n"); + } + + if (PreserveUnknownFields(descriptor_)) { + printer->Print( + " this.mergeUnknownFields(other.unknownFields);\n"); + } + + printer->Print( + " onChanged();\n"); + + printer->Print( + " return this;\n" + "}\n" + "\n"); + } +} + +// =================================================================== + +void MessageBuilderGenerator:: +GenerateBuilderParsingMethods(io::Printer* printer) { + printer->Print( + "public Builder mergeFrom(\n" + " com.google.protobuf.CodedInputStream input,\n" + " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" + " throws java.io.IOException {\n" + " $classname$ parsedMessage = null;\n" + " try {\n" + " parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);\n" + " } catch (com.google.protobuf.InvalidProtocolBufferException e) {\n" + " parsedMessage = ($classname$) e.getUnfinishedMessage();\n" + " throw e;\n" + " } finally {\n" + " if (parsedMessage != null) {\n" + " mergeFrom(parsedMessage);\n" + " }\n" + " }\n" + " return this;\n" + "}\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); +} + +// =================================================================== + +void MessageBuilderGenerator::GenerateIsInitialized( + io::Printer* printer) { + printer->Print( + "public final boolean isInitialized() {\n"); + printer->Indent(); + + // Check that all required fields in this message are set. + // TODO(kenton): We can optimize this when we switch to putting all the + // "has" fields into a single bitfield. + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + const FieldGeneratorInfo* info = context_->GetFieldGeneratorInfo(field); + + if (field->is_required()) { + printer->Print( + "if (!has$name$()) {\n" + " return false;\n" + "}\n", + "name", info->capitalized_name); + } + } + + // Now check that all embedded messages are initialized. + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + const FieldGeneratorInfo* info = context_->GetFieldGeneratorInfo(field); + if (GetJavaType(field) == JAVATYPE_MESSAGE && + HasRequiredFields(field->message_type())) { + switch (field->label()) { + case FieldDescriptor::LABEL_REQUIRED: + printer->Print( + "if (!get$name$().isInitialized()) {\n" + " return false;\n" + "}\n", + "type", name_resolver_->GetImmutableClassName( + field->message_type()), + "name", info->capitalized_name); + break; + case FieldDescriptor::LABEL_OPTIONAL: + if (!SupportFieldPresence(descriptor_->file()) && + field->containing_oneof() != NULL) { + const OneofDescriptor* oneof = field->containing_oneof(); + const OneofGeneratorInfo* oneof_info = + context_->GetOneofGeneratorInfo(oneof); + printer->Print( + "if ($oneof_name$Case_ == $field_number$) {\n", + "oneof_name", oneof_info->name, + "field_number", SimpleItoa(field->number())); + } else { + printer->Print( + "if (has$name$()) {\n", + "name", info->capitalized_name); + } + printer->Print( + " if (!get$name$().isInitialized()) {\n" + " return false;\n" + " }\n" + "}\n", + "name", info->capitalized_name); + break; + case FieldDescriptor::LABEL_REPEATED: + if (IsMapEntry(field->message_type())) { + printer->Print( + "for ($type$ item : get$name$().values()) {\n" + " if (!item.isInitialized()) {\n" + " return false;\n" + " }\n" + "}\n", + "type", MapValueImmutableClassdName(field->message_type(), + name_resolver_), + "name", info->capitalized_name); + } else { + printer->Print( + "for (int i = 0; i < get$name$Count(); i++) {\n" + " if (!get$name$(i).isInitialized()) {\n" + " return false;\n" + " }\n" + "}\n", + "type", name_resolver_->GetImmutableClassName( + field->message_type()), + "name", info->capitalized_name); + } + break; + } + } + } + + if (descriptor_->extension_range_count() > 0) { + printer->Print( + "if (!extensionsAreInitialized()) {\n" + " return false;\n" + "}\n"); + } + + printer->Outdent(); + + printer->Print( + " return true;\n" + "}\n" + "\n"); +} + +// =================================================================== + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_message_builder.h b/src/google/protobuf/compiler/java/java_message_builder.h new file mode 100644 index 00000000..015ea062 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_message_builder.h @@ -0,0 +1,86 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: dweis@google.com (Daniel Weis) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_BUILDER_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_BUILDER_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { + namespace compiler { + namespace java { + class Context; // context.h + class ClassNameResolver; // name_resolver.h + } + } + namespace io { + class Printer; // printer.h + } +} + +namespace protobuf { +namespace compiler { +namespace java { + +class MessageBuilderGenerator { + public: + explicit MessageBuilderGenerator(const Descriptor* descriptor, + Context* context); + virtual ~MessageBuilderGenerator(); + + virtual void Generate(io::Printer* printer); + + private: + void GenerateCommonBuilderMethods(io::Printer* printer); + void GenerateDescriptorMethods(io::Printer* printer); + void GenerateBuilderParsingMethods(io::Printer* printer); + void GenerateIsInitialized(io::Printer* printer); + + const Descriptor* descriptor_; + Context* context_; + ClassNameResolver* name_resolver_; + FieldGeneratorMap field_generators_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageBuilderGenerator); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_BUILDER_H__ diff --git a/src/google/protobuf/compiler/java/java_message_builder_lite.cc b/src/google/protobuf/compiler/java/java_message_builder_lite.cc new file mode 100644 index 00000000..8719d00d --- /dev/null +++ b/src/google/protobuf/compiler/java/java_message_builder_lite.cc @@ -0,0 +1,192 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: dweis@google.com (Daniel Weis) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include + +#include +#include +#include +#include +#ifndef _SHARED_PTR_H +#include +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +namespace { +bool GenerateHasBits(const Descriptor* descriptor) { + return SupportFieldPresence(descriptor->file()) || + HasRepeatedFields(descriptor); +} + +string MapValueImmutableClassdName(const Descriptor* descriptor, + ClassNameResolver* name_resolver) { + const FieldDescriptor* value_field = descriptor->FindFieldByName("value"); + GOOGLE_CHECK_EQ(FieldDescriptor::TYPE_MESSAGE, value_field->type()); + return name_resolver->GetImmutableClassName(value_field->message_type()); +} +} // namespace + +MessageBuilderLiteGenerator::MessageBuilderLiteGenerator( + const Descriptor* descriptor, Context* context) + : descriptor_(descriptor), context_(context), + name_resolver_(context->GetNameResolver()), + field_generators_(descriptor, context_) { + GOOGLE_CHECK_EQ( + FileOptions::LITE_RUNTIME, descriptor->file()->options().optimize_for()); +} + +MessageBuilderLiteGenerator::~MessageBuilderLiteGenerator() {} + +void MessageBuilderLiteGenerator:: +Generate(io::Printer* printer) { + WriteMessageDocComment(printer, descriptor_); + printer->Print( + "public static final class Builder extends\n" + " com.google.protobuf.GeneratedMessageLite.$extendible$Builder<\n" + " $classname$, Builder> implements\n" + " $extra_interfaces$\n" + " $classname$OrBuilder {\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_), + "extra_interfaces", ExtraBuilderInterfaces(descriptor_), + "extendible", + descriptor_->extension_range_count() > 0 ? "Extendable" : ""); + printer->Indent(); + + GenerateCommonBuilderMethods(printer); + + // oneof + map vars; + for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { + vars["oneof_name"] = context_->GetOneofGeneratorInfo( + descriptor_->oneof_decl(i))->name; + vars["oneof_capitalized_name"] = context_->GetOneofGeneratorInfo( + descriptor_->oneof_decl(i))->capitalized_name; + vars["oneof_index"] = SimpleItoa(descriptor_->oneof_decl(i)->index()); + + // oneofCase() and clearOneof() + printer->Print(vars, + "public $oneof_capitalized_name$Case\n" + " get$oneof_capitalized_name$Case() {\n" + " return instance.get$oneof_capitalized_name$Case();\n" + "}\n" + "\n" + "public Builder clear$oneof_capitalized_name$() {\n" + " copyOnWrite();\n" + " instance.clear$oneof_capitalized_name$();\n" + " return this;\n" + "}\n" + "\n"); + } + + if (GenerateHasBits(descriptor_)) { + // Integers for bit fields. + int totalBits = 0; + for (int i = 0; i < descriptor_->field_count(); i++) { + totalBits += field_generators_.get(descriptor_->field(i)) + .GetNumBitsForBuilder(); + } + int totalInts = (totalBits + 31) / 32; + for (int i = 0; i < totalInts; i++) { + printer->Print("private int $bit_field_name$;\n", + "bit_field_name", GetBitFieldName(i)); + } + } + + for (int i = 0; i < descriptor_->field_count(); i++) { + printer->Print("\n"); + field_generators_.get(descriptor_->field(i)) + .GenerateBuilderMembers(printer); + } + + if (!PreserveUnknownFields(descriptor_)) { + printer->Print( + "public final Builder setUnknownFields(\n" + " final com.google.protobuf.UnknownFieldSet unknownFields) {\n" + " return this;\n" + "}\n" + "\n" + "public final Builder mergeUnknownFields(\n" + " final com.google.protobuf.UnknownFieldSet unknownFields) {\n" + " return this;\n" + "}\n" + "\n"); + } + + printer->Print( + "\n" + "// @@protoc_insertion_point(builder_scope:$full_name$)\n", + "full_name", descriptor_->full_name()); + + printer->Outdent(); + printer->Print("}\n"); +} + +// =================================================================== + +void MessageBuilderLiteGenerator:: +GenerateCommonBuilderMethods(io::Printer* printer) { + printer->Print( + "// Construct using $classname$.newBuilder()\n" + "private Builder() {\n" + " super(DEFAULT_INSTANCE);\n" + "}\n" + "\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); +} + +// =================================================================== + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_message_builder_lite.h b/src/google/protobuf/compiler/java/java_message_builder_lite.h new file mode 100644 index 00000000..8597b2e6 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_message_builder_lite.h @@ -0,0 +1,83 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: dweis@google.com (Daniel Weis) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_BUILDER_LITE_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_BUILDER_LITE_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { + namespace compiler { + namespace java { + class Context; // context.h + class ClassNameResolver; // name_resolver.h + } + } + namespace io { + class Printer; // printer.h + } +} + +namespace protobuf { +namespace compiler { +namespace java { + +class MessageBuilderLiteGenerator { + public: + explicit MessageBuilderLiteGenerator(const Descriptor* descriptor, + Context* context); + virtual ~MessageBuilderLiteGenerator(); + + virtual void Generate(io::Printer* printer); + + private: + void GenerateCommonBuilderMethods(io::Printer* printer); + + const Descriptor* descriptor_; + Context* context_; + ClassNameResolver* name_resolver_; + FieldGeneratorMap field_generators_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageBuilderLiteGenerator); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_BUILDER_LITE_H__ diff --git a/src/google/protobuf/compiler/java/java_message_field.cc b/src/google/protobuf/compiler/java/java_message_field.cc index a2d12a38..b180b4a7 100644 --- a/src/google/protobuf/compiler/java/java_message_field.cc +++ b/src/google/protobuf/compiler/java/java_message_field.cc @@ -157,11 +157,9 @@ GenerateInterfaceMembers(io::Printer* printer) const { printer->Print(variables_, "$deprecation$$type$ get$capitalized_name$();\n"); - if (HasNestedBuilders(descriptor_->containing_type())) { - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$$type$OrBuilder get$capitalized_name$OrBuilder();\n"); - } + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$$type$OrBuilder get$capitalized_name$OrBuilder();\n"); } void ImmutableMessageFieldGenerator:: @@ -182,14 +180,12 @@ GenerateMembers(io::Printer* printer) const { " return $name$_ == null ? $type$.getDefaultInstance() : $name$_;\n" "}\n"); - if (HasNestedBuilders(descriptor_->containing_type())) { - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public $type$OrBuilder " - "get$capitalized_name$OrBuilder() {\n" - " return $name$_ == null ? $type$.getDefaultInstance() : $name$_;\n" - "}\n"); - } + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$OrBuilder " + "get$capitalized_name$OrBuilder() {\n" + " return $name$_ == null ? $type$.getDefaultInstance() : $name$_;\n" + "}\n"); } else { WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, @@ -202,14 +198,12 @@ GenerateMembers(io::Printer* printer) const { " return $name$_ == null ? $type$.getDefaultInstance() : $name$_;\n" "}\n"); - if (HasNestedBuilders(descriptor_->containing_type())) { - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public $type$OrBuilder " - "get$capitalized_name$OrBuilder() {\n" - " return get$capitalized_name$();\n" - "}\n"); - } + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$OrBuilder " + "get$capitalized_name$OrBuilder() {\n" + " return get$capitalized_name$();\n" + "}\n"); } } @@ -217,19 +211,15 @@ void ImmutableMessageFieldGenerator::PrintNestedBuilderCondition( io::Printer* printer, const char* regular_case, const char* nested_builder_case) const { - if (HasNestedBuilders(descriptor_->containing_type())) { - printer->Print(variables_, "if ($name$Builder_ == null) {\n"); - printer->Indent(); - printer->Print(variables_, regular_case); - printer->Outdent(); - printer->Print("} else {\n"); - printer->Indent(); - printer->Print(variables_, nested_builder_case); - printer->Outdent(); - printer->Print("}\n"); - } else { - printer->Print(variables_, regular_case); - } + printer->Print(variables_, "if ($name$Builder_ == null) {\n"); + printer->Indent(); + printer->Print(variables_, regular_case); + printer->Outdent(); + printer->Print("} else {\n"); + printer->Indent(); + printer->Print(variables_, nested_builder_case); + printer->Outdent(); + printer->Print("}\n"); } void ImmutableMessageFieldGenerator::PrintNestedBuilderFunction( @@ -260,14 +250,12 @@ GenerateBuilderMembers(io::Printer* printer) const { printer->Print(variables_, "private $type$ $name$_ = null;\n"); - if (HasNestedBuilders(descriptor_->containing_type())) { - printer->Print(variables_, + printer->Print(variables_, // If this builder is non-null, it is used and the other fields are // ignored. "private com.google.protobuf.SingleFieldBuilder<\n" " $type$, $type$.Builder, $type$OrBuilder> $name$Builder_;" "\n"); - } // The comments above the methods below are based on a hypothetical // field of type "Field" called "Field". @@ -368,40 +356,38 @@ GenerateBuilderMembers(io::Printer* printer) const { "$clear_has_field_bit_builder$\n" "return this;\n"); - if (HasNestedBuilders(descriptor_->containing_type())) { - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public $type$.Builder get$capitalized_name$Builder() {\n" - " $set_has_field_bit_builder$\n" - " $on_changed$\n" - " return get$capitalized_name$FieldBuilder().getBuilder();\n" - "}\n"); - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder() {\n" - " if ($name$Builder_ != null) {\n" - " return $name$Builder_.getMessageOrBuilder();\n" - " } else {\n" - " return $name$_ == null ?\n" - " $type$.getDefaultInstance() : $name$_;\n" - " }\n" - "}\n"); - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "private com.google.protobuf.SingleFieldBuilder<\n" - " $type$, $type$.Builder, $type$OrBuilder> \n" - " get$capitalized_name$FieldBuilder() {\n" - " if ($name$Builder_ == null) {\n" - " $name$Builder_ = new com.google.protobuf.SingleFieldBuilder<\n" - " $type$, $type$.Builder, $type$OrBuilder>(\n" - " get$capitalized_name$(),\n" - " getParentForChildren(),\n" - " isClean());\n" - " $name$_ = null;\n" - " }\n" - " return $name$Builder_;\n" - "}\n"); - } + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$.Builder get$capitalized_name$Builder() {\n" + " $set_has_field_bit_builder$\n" + " $on_changed$\n" + " return get$capitalized_name$FieldBuilder().getBuilder();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder() {\n" + " if ($name$Builder_ != null) {\n" + " return $name$Builder_.getMessageOrBuilder();\n" + " } else {\n" + " return $name$_ == null ?\n" + " $type$.getDefaultInstance() : $name$_;\n" + " }\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private com.google.protobuf.SingleFieldBuilder<\n" + " $type$, $type$.Builder, $type$OrBuilder> \n" + " get$capitalized_name$FieldBuilder() {\n" + " if ($name$Builder_ == null) {\n" + " $name$Builder_ = new com.google.protobuf.SingleFieldBuilder<\n" + " $type$, $type$.Builder, $type$OrBuilder>(\n" + " get$capitalized_name$(),\n" + " getParentForChildren(),\n" + " isClean());\n" + " $name$_ = null;\n" + " }\n" + " return $name$Builder_;\n" + "}\n"); } void ImmutableMessageFieldGenerator:: @@ -557,16 +543,14 @@ GenerateMembers(io::Printer* printer) const { " return $type$.getDefaultInstance();\n" "}\n"); - if (HasNestedBuilders(descriptor_->containing_type())) { - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder() {\n" - " if ($has_oneof_case_message$) {\n" - " return ($type$) $oneof_name$_;\n" - " }\n" - " return $type$.getDefaultInstance();\n" - "}\n"); - } + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder() {\n" + " if ($has_oneof_case_message$) {\n" + " return ($type$) $oneof_name$_;\n" + " }\n" + " return $type$.getDefaultInstance();\n" + "}\n"); } void ImmutableMessageOneofFieldGenerator:: @@ -574,14 +558,12 @@ GenerateBuilderMembers(io::Printer* printer) const { // When using nested-builders, the code initially works just like the // non-nested builder case. It only creates a nested builder lazily on // demand and then forever delegates to it after creation. - if (HasNestedBuilders(descriptor_->containing_type())) { - printer->Print(variables_, - // If this builder is non-null, it is used and the other fields are - // ignored. - "private com.google.protobuf.SingleFieldBuilder<\n" - " $type$, $type$.Builder, $type$OrBuilder> $name$Builder_;" - "\n"); - } + printer->Print(variables_, + // If this builder is non-null, it is used and the other fields are + // ignored. + "private com.google.protobuf.SingleFieldBuilder<\n" + " $type$, $type$.Builder, $type$OrBuilder> $name$Builder_;" + "\n"); // The comments above the methods below are based on a hypothetical // field of type "Field" called "Field". @@ -683,45 +665,43 @@ GenerateBuilderMembers(io::Printer* printer) const { "return this;\n"); - if (HasNestedBuilders(descriptor_->containing_type())) { - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public $type$.Builder get$capitalized_name$Builder() {\n" - " return get$capitalized_name$FieldBuilder().getBuilder();\n" - "}\n"); - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder() {\n" - " if (($has_oneof_case_message$) && ($name$Builder_ != null)) {\n" - " return $name$Builder_.getMessageOrBuilder();\n" - " } else {\n" - " if ($has_oneof_case_message$) {\n" - " return ($type$) $oneof_name$_;\n" - " }\n" - " return $type$.getDefaultInstance();\n" - " }\n" - "}\n"); - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "private com.google.protobuf.SingleFieldBuilder<\n" - " $type$, $type$.Builder, $type$OrBuilder> \n" - " get$capitalized_name$FieldBuilder() {\n" - " if ($name$Builder_ == null) {\n" - " if (!($has_oneof_case_message$)) {\n" - " $oneof_name$_ = $type$.getDefaultInstance();\n" - " }\n" - " $name$Builder_ = new com.google.protobuf.SingleFieldBuilder<\n" - " $type$, $type$.Builder, $type$OrBuilder>(\n" - " ($type$) $oneof_name$_,\n" - " getParentForChildren(),\n" - " isClean());\n" - " $oneof_name$_ = null;\n" - " }\n" - " $set_oneof_case_message$;\n" - " $on_changed$;\n" - " return $name$Builder_;\n" - "}\n"); - } + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$.Builder get$capitalized_name$Builder() {\n" + " return get$capitalized_name$FieldBuilder().getBuilder();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder() {\n" + " if (($has_oneof_case_message$) && ($name$Builder_ != null)) {\n" + " return $name$Builder_.getMessageOrBuilder();\n" + " } else {\n" + " if ($has_oneof_case_message$) {\n" + " return ($type$) $oneof_name$_;\n" + " }\n" + " return $type$.getDefaultInstance();\n" + " }\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private com.google.protobuf.SingleFieldBuilder<\n" + " $type$, $type$.Builder, $type$OrBuilder> \n" + " get$capitalized_name$FieldBuilder() {\n" + " if ($name$Builder_ == null) {\n" + " if (!($has_oneof_case_message$)) {\n" + " $oneof_name$_ = $type$.getDefaultInstance();\n" + " }\n" + " $name$Builder_ = new com.google.protobuf.SingleFieldBuilder<\n" + " $type$, $type$.Builder, $type$OrBuilder>(\n" + " ($type$) $oneof_name$_,\n" + " getParentForChildren(),\n" + " isClean());\n" + " $oneof_name$_ = null;\n" + " }\n" + " $set_oneof_case_message$;\n" + " $on_changed$;\n" + " return $name$Builder_;\n" + "}\n"); } void ImmutableMessageOneofFieldGenerator:: @@ -831,16 +811,15 @@ GenerateInterfaceMembers(io::Printer* printer) const { WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, "$deprecation$int get$capitalized_name$Count();\n"); - if (HasNestedBuilders(descriptor_->containing_type())) { - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$java.util.List \n" - " get$capitalized_name$OrBuilderList();\n"); - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$$type$OrBuilder get$capitalized_name$OrBuilder(\n" - " int index);\n"); - } + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$java.util.List \n" + " get$capitalized_name$OrBuilderList();\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$$type$OrBuilder get$capitalized_name$OrBuilder(\n" + " int index);\n"); } void RepeatedImmutableMessageFieldGenerator:: @@ -882,19 +861,15 @@ void RepeatedImmutableMessageFieldGenerator::PrintNestedBuilderCondition( io::Printer* printer, const char* regular_case, const char* nested_builder_case) const { - if (HasNestedBuilders(descriptor_->containing_type())) { - printer->Print(variables_, "if ($name$Builder_ == null) {\n"); - printer->Indent(); - printer->Print(variables_, regular_case); - printer->Outdent(); - printer->Print("} else {\n"); - printer->Indent(); - printer->Print(variables_, nested_builder_case); - printer->Outdent(); - printer->Print("}\n"); - } else { - printer->Print(variables_, regular_case); - } + printer->Print(variables_, "if ($name$Builder_ == null) {\n"); + printer->Indent(); + printer->Print(variables_, regular_case); + printer->Outdent(); + printer->Print("} else {\n"); + printer->Indent(); + printer->Print(variables_, nested_builder_case); + printer->Outdent(); + printer->Print("}\n"); } void RepeatedImmutableMessageFieldGenerator::PrintNestedBuilderFunction( @@ -942,14 +917,12 @@ GenerateBuilderMembers(io::Printer* printer) const { "}\n" "\n"); - if (HasNestedBuilders(descriptor_->containing_type())) { - printer->Print(variables_, - // If this builder is non-null, it is used and the other fields are - // ignored. - "private com.google.protobuf.RepeatedFieldBuilder<\n" - " $type$, $type$.Builder, $type$OrBuilder> $name$Builder_;\n" - "\n"); - } + printer->Print(variables_, + // If this builder is non-null, it is used and the other fields are + // ignored. + "private com.google.protobuf.RepeatedFieldBuilder<\n" + " $type$, $type$.Builder, $type$OrBuilder> $name$Builder_;\n" + "\n"); // The comments above the methods below are based on a hypothetical // repeated field of type "Field" called "RepeatedField". @@ -1116,70 +1089,68 @@ GenerateBuilderMembers(io::Printer* printer) const { "return this;\n"); - if (HasNestedBuilders(descriptor_->containing_type())) { - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public $type$.Builder get$capitalized_name$Builder(\n" - " int index) {\n" - " return get$capitalized_name$FieldBuilder().getBuilder(index);\n" - "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$.Builder get$capitalized_name$Builder(\n" + " int index) {\n" + " return get$capitalized_name$FieldBuilder().getBuilder(index);\n" + "}\n"); - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder(\n" - " int index) {\n" - " if ($name$Builder_ == null) {\n" - " return $name$_.get(index);" - " } else {\n" - " return $name$Builder_.getMessageOrBuilder(index);\n" - " }\n" - "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder(\n" + " int index) {\n" + " if ($name$Builder_ == null) {\n" + " return $name$_.get(index);" + " } else {\n" + " return $name$Builder_.getMessageOrBuilder(index);\n" + " }\n" + "}\n"); - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public java.util.List \n" - " get$capitalized_name$OrBuilderList() {\n" - " if ($name$Builder_ != null) {\n" - " return $name$Builder_.getMessageOrBuilderList();\n" - " } else {\n" - " return java.util.Collections.unmodifiableList($name$_);\n" - " }\n" - "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.util.List \n" + " get$capitalized_name$OrBuilderList() {\n" + " if ($name$Builder_ != null) {\n" + " return $name$Builder_.getMessageOrBuilderList();\n" + " } else {\n" + " return java.util.Collections.unmodifiableList($name$_);\n" + " }\n" + "}\n"); - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public $type$.Builder add$capitalized_name$Builder() {\n" - " return get$capitalized_name$FieldBuilder().addBuilder(\n" - " $type$.getDefaultInstance());\n" - "}\n"); - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public $type$.Builder add$capitalized_name$Builder(\n" - " int index) {\n" - " return get$capitalized_name$FieldBuilder().addBuilder(\n" - " index, $type$.getDefaultInstance());\n" - "}\n"); - WriteFieldDocComment(printer, descriptor_); - printer->Print(variables_, - "$deprecation$public java.util.List<$type$.Builder> \n" - " get$capitalized_name$BuilderList() {\n" - " return get$capitalized_name$FieldBuilder().getBuilderList();\n" - "}\n" - "private com.google.protobuf.RepeatedFieldBuilder<\n" - " $type$, $type$.Builder, $type$OrBuilder> \n" - " get$capitalized_name$FieldBuilder() {\n" - " if ($name$Builder_ == null) {\n" - " $name$Builder_ = new com.google.protobuf.RepeatedFieldBuilder<\n" - " $type$, $type$.Builder, $type$OrBuilder>(\n" - " $name$_,\n" - " $get_mutable_bit_builder$,\n" - " getParentForChildren(),\n" - " isClean());\n" - " $name$_ = null;\n" - " }\n" - " return $name$Builder_;\n" - "}\n"); - } + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$.Builder add$capitalized_name$Builder() {\n" + " return get$capitalized_name$FieldBuilder().addBuilder(\n" + " $type$.getDefaultInstance());\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$.Builder add$capitalized_name$Builder(\n" + " int index) {\n" + " return get$capitalized_name$FieldBuilder().addBuilder(\n" + " index, $type$.getDefaultInstance());\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.util.List<$type$.Builder> \n" + " get$capitalized_name$BuilderList() {\n" + " return get$capitalized_name$FieldBuilder().getBuilderList();\n" + "}\n" + "private com.google.protobuf.RepeatedFieldBuilder<\n" + " $type$, $type$.Builder, $type$OrBuilder> \n" + " get$capitalized_name$FieldBuilder() {\n" + " if ($name$Builder_ == null) {\n" + " $name$Builder_ = new com.google.protobuf.RepeatedFieldBuilder<\n" + " $type$, $type$.Builder, $type$OrBuilder>(\n" + " $name$_,\n" + " $get_mutable_bit_builder$,\n" + " getParentForChildren(),\n" + " isClean());\n" + " $name$_ = null;\n" + " }\n" + " return $name$Builder_;\n" + "}\n"); } void RepeatedImmutableMessageFieldGenerator:: diff --git a/src/google/protobuf/compiler/java/java_message_field_lite.cc b/src/google/protobuf/compiler/java/java_message_field_lite.cc new file mode 100644 index 00000000..8332202c --- /dev/null +++ b/src/google/protobuf/compiler/java/java_message_field_lite.cc @@ -0,0 +1,944 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +namespace { + +void SetMessageVariables(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + const FieldGeneratorInfo* info, + ClassNameResolver* name_resolver, + map* variables) { + SetCommonFieldVariables(descriptor, info, variables); + + (*variables)["type"] = + name_resolver->GetImmutableClassName(descriptor->message_type()); + (*variables)["mutable_type"] = + name_resolver->GetMutableClassName(descriptor->message_type()); + (*variables)["group_or_message"] = + (GetType(descriptor) == FieldDescriptor::TYPE_GROUP) ? + "Group" : "Message"; + // TODO(birdo): Add @deprecated javadoc when generating javadoc is supported + // by the proto compiler + (*variables)["deprecation"] = descriptor->options().deprecated() + ? "@java.lang.Deprecated " : ""; + (*variables)["on_changed"] = + HasDescriptorMethods(descriptor->containing_type()) ? "onChanged();" : ""; + + if (SupportFieldPresence(descriptor->file())) { + // For singular messages and builders, one bit is used for the hasField bit. + (*variables)["get_has_field_bit_message"] = GenerateGetBit(messageBitIndex); + + // Note that these have a trailing ";". + (*variables)["set_has_field_bit_message"] = + GenerateSetBit(messageBitIndex) + ";"; + (*variables)["clear_has_field_bit_message"] = + GenerateClearBit(messageBitIndex) + ";"; + + (*variables)["is_field_present_message"] = GenerateGetBit(messageBitIndex); + } else { + (*variables)["set_has_field_bit_message"] = ""; + (*variables)["clear_has_field_bit_message"] = ""; + + (*variables)["is_field_present_message"] = + (*variables)["name"] + "_ != null"; + } + + // For repeated builders, the underlying list tracks mutability state. + (*variables)["is_mutable"] = (*variables)["name"] + "_.isModifiable()"; + + (*variables)["get_has_field_bit_from_local"] = + GenerateGetBitFromLocal(builderBitIndex); + (*variables)["set_has_field_bit_to_local"] = + GenerateSetBitToLocal(messageBitIndex); +} + +} // namespace + +// =================================================================== + +ImmutableMessageFieldLiteGenerator:: +ImmutableMessageFieldLiteGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) + : descriptor_(descriptor), messageBitIndex_(messageBitIndex), + builderBitIndex_(builderBitIndex), context_(context), + name_resolver_(context->GetNameResolver()) { + SetMessageVariables(descriptor, messageBitIndex, builderBitIndex, + context->GetFieldGeneratorInfo(descriptor), + name_resolver_, &variables_); +} + +ImmutableMessageFieldLiteGenerator::~ImmutableMessageFieldLiteGenerator() {} + +int ImmutableMessageFieldLiteGenerator::GetNumBitsForMessage() const { + return 1; +} + +int ImmutableMessageFieldLiteGenerator::GetNumBitsForBuilder() const { + return 0; +} + +void ImmutableMessageFieldLiteGenerator:: +GenerateInterfaceMembers(io::Printer* printer) const { + // TODO(jonp): In the future, consider having a method specific to the + // interface so that builders can choose dynamically to either return a + // message or a nested builder, so that asking for the interface doesn't + // cause a message to ever be built. + if (SupportFieldPresence(descriptor_->file()) || + descriptor_->containing_oneof() == NULL) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$boolean has$capitalized_name$();\n"); + } + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$$type$ get$capitalized_name$();\n"); +} + +void ImmutableMessageFieldLiteGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print(variables_, + "private $type$ $name$_;\n"); + PrintExtraFieldInfo(variables_, printer); + + if (SupportFieldPresence(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return $get_has_field_bit_message$;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$() {\n" + " return $name$_ == null ? $type$.getDefaultInstance() : $name$_;\n" + "}\n"); + } else { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return $name$_ != null;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$() {\n" + " return $name$_ == null ? $type$.getDefaultInstance() : $name$_;\n" + "}\n"); + } + + // Field.Builder setField(Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$($type$ value) {\n" + " if (value == null) {\n" + " throw new NullPointerException();\n" + " }\n" + " $name$_ = value;\n" + " $set_has_field_bit_message$\n" + " }\n"); + + // Field.Builder setField(Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$(\n" + " $type$.Builder builderForValue) {\n" + " $name$_ = builderForValue.build();\n" + " $set_has_field_bit_message$\n" + "}\n"); + + // Field.Builder mergeField(Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void merge$capitalized_name$($type$ value) {\n" + " if ($name$_ != null &&\n" + " $name$_ != $type$.getDefaultInstance()) {\n" + " $name$_ =\n" + " $type$.newBuilder($name$_).mergeFrom(value).buildPartial();\n" + " } else {\n" + " $name$_ = value;\n" + " }\n" + " $set_has_field_bit_message$\n" + "}\n"); + + // Field.Builder clearField() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void clear$capitalized_name$() {" + " $name$_ = null;\n" + " $clear_has_field_bit_message$\n" + "}\n"); +} + +void ImmutableMessageFieldLiteGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + // The comments above the methods below are based on a hypothetical + // field of type "Field" called "Field". + + // boolean hasField() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return instance.has$capitalized_name$();\n" + "}\n"); + + // Field getField() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$() {\n" + " return instance.get$capitalized_name$();\n" + "}\n"); + + // Field.Builder setField(Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$($type$ value) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(value);\n" + " return this;\n" + " }\n"); + + // Field.Builder setField(Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$(\n" + " $type$.Builder builderForValue) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(builderForValue);\n" + " return this;\n" + "}\n"); + + // Field.Builder mergeField(Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder merge$capitalized_name$($type$ value) {\n" + " copyOnWrite();\n" + " instance.merge$capitalized_name$(value);\n" + " return this;\n" + "}\n"); + + // Field.Builder clearField() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder clear$capitalized_name$() {" + " copyOnWrite();\n" + " instance.clear$capitalized_name$();\n" + " return this;\n" + "}\n"); +} + +void ImmutableMessageFieldLiteGenerator:: +GenerateFieldBuilderInitializationCode(io::Printer* printer) const { + if (SupportFieldPresence(descriptor_->file())) { + printer->Print(variables_, + "get$capitalized_name$FieldBuilder();\n"); + } +} + + +void ImmutableMessageFieldLiteGenerator:: +GenerateInitializationCode(io::Printer* printer) const {} + +void ImmutableMessageFieldLiteGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (other.has$capitalized_name$()) {\n" + " merge$capitalized_name$(other.get$capitalized_name$());\n" + "}\n"); +} + +void ImmutableMessageFieldLiteGenerator:: +GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const { + // noop for scalars +} + +void ImmutableMessageFieldLiteGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "$type$.Builder subBuilder = null;\n" + "if ($is_field_present_message$) {\n" + " subBuilder = $name$_.toBuilder();\n" + "}\n"); + + if (GetType(descriptor_) == FieldDescriptor::TYPE_GROUP) { + printer->Print(variables_, + "$name$_ = input.readGroup($number$, $type$.PARSER,\n" + " extensionRegistry);\n"); + } else { + printer->Print(variables_, + "$name$_ = input.readMessage($type$.PARSER, extensionRegistry);\n"); + } + + printer->Print(variables_, + "if (subBuilder != null) {\n" + " subBuilder.mergeFrom($name$_);\n" + " $name$_ = subBuilder.buildPartial();\n" + "}\n" + "$set_has_field_bit_message$\n"); +} + +void ImmutableMessageFieldLiteGenerator:: +GenerateParsingDoneCode(io::Printer* printer) const { + // noop for messages. +} + +void ImmutableMessageFieldLiteGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($is_field_present_message$) {\n" + " output.write$group_or_message$($number$, get$capitalized_name$());\n" + "}\n"); +} + +void ImmutableMessageFieldLiteGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($is_field_present_message$) {\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .compute$group_or_message$Size($number$, get$capitalized_name$());\n" + "}\n"); +} + +void ImmutableMessageFieldLiteGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + printer->Print(variables_, + "result = result && get$capitalized_name$()\n" + " .equals(other.get$capitalized_name$());\n"); +} + +void ImmutableMessageFieldLiteGenerator:: +GenerateHashCode(io::Printer* printer) const { + printer->Print(variables_, + "hash = (37 * hash) + $constant_name$;\n" + "hash = (53 * hash) + get$capitalized_name$().hashCode();\n"); +} + +string ImmutableMessageFieldLiteGenerator::GetBoxedType() const { + return name_resolver_->GetImmutableClassName(descriptor_->message_type()); +} + +// =================================================================== + +ImmutableMessageOneofFieldLiteGenerator:: +ImmutableMessageOneofFieldLiteGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) + : ImmutableMessageFieldLiteGenerator( + descriptor, messageBitIndex, builderBitIndex, context) { + const OneofGeneratorInfo* info = + context->GetOneofGeneratorInfo(descriptor->containing_oneof()); + SetCommonOneofVariables(descriptor, info, &variables_); +} + +ImmutableMessageOneofFieldLiteGenerator:: +~ImmutableMessageOneofFieldLiteGenerator() {} + +void ImmutableMessageOneofFieldLiteGenerator:: +GenerateMembers(io::Printer* printer) const { + PrintExtraFieldInfo(variables_, printer); + if (SupportFieldPresence(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return $has_oneof_case_message$;\n" + "}\n"); + } + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$() {\n" + " if ($has_oneof_case_message$) {\n" + " return ($type$) $oneof_name$_;\n" + " }\n" + " return $type$.getDefaultInstance();\n" + "}\n"); + + // Field.Builder setField(Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$($type$ value) {\n" + " if (value == null) {\n" + " throw new NullPointerException();\n" + " }\n" + " $oneof_name$_ = value;\n" + " $set_oneof_case_message$;\n" + "}\n"); + + // Field.Builder setField(Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$(\n" + " $type$.Builder builderForValue) {\n" + " $oneof_name$_ = builderForValue.build();\n" + " $set_oneof_case_message$;\n" + "}\n"); + + // Field.Builder mergeField(Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void merge$capitalized_name$($type$ value) {\n" + " if ($has_oneof_case_message$ &&\n" + " $oneof_name$_ != $type$.getDefaultInstance()) {\n" + " $oneof_name$_ = $type$.newBuilder(($type$) $oneof_name$_)\n" + " .mergeFrom(value).buildPartial();\n" + " } else {\n" + " $oneof_name$_ = value;\n" + " }\n" + " $set_oneof_case_message$;\n" + "}\n"); + + // Field.Builder clearField() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void clear$capitalized_name$() {\n" + " if ($has_oneof_case_message$) {\n" + " $clear_oneof_case_message$;\n" + " $oneof_name$_ = null;\n" + " }\n" + "}\n"); +} + +void ImmutableMessageOneofFieldLiteGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + // The comments above the methods below are based on a hypothetical + // field of type "Field" called "Field". + + if (SupportFieldPresence(descriptor_->file())) { + // boolean hasField() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return instance.has$capitalized_name$();\n" + "}\n"); + } + + // Field getField() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$() {\n" + " return instance.get$capitalized_name$();\n" + "}\n"); + + // Field.Builder setField(Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$($type$ value) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(value);\n" + " return this;\n" + "}\n"); + + // Field.Builder setField(Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$(\n" + " $type$.Builder builderForValue) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(builderForValue);\n" + " return this;\n" + "}\n"); + + // Field.Builder mergeField(Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder merge$capitalized_name$($type$ value) {\n" + " copyOnWrite();\n" + " instance.merge$capitalized_name$(value);\n" + " return this;\n" + "}\n"); + + // Field.Builder clearField() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder clear$capitalized_name$() {\n" + " copyOnWrite();\n" + " instance.clear$capitalized_name$();\n" + " return this;\n" + "}\n"); +} + +void ImmutableMessageOneofFieldLiteGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, + "merge$capitalized_name$(other.get$capitalized_name$());\n"); +} + +void ImmutableMessageOneofFieldLiteGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "$type$.Builder subBuilder = null;\n" + "if ($has_oneof_case_message$) {\n" + " subBuilder = (($type$) $oneof_name$_).toBuilder();\n" + "}\n"); + + if (GetType(descriptor_) == FieldDescriptor::TYPE_GROUP) { + printer->Print(variables_, + "$oneof_name$_ = input.readGroup($number$, $type$.PARSER,\n" + " extensionRegistry);\n"); + } else { + printer->Print(variables_, + "$oneof_name$_ = input.readMessage($type$.PARSER, extensionRegistry);\n"); + } + + printer->Print(variables_, + "if (subBuilder != null) {\n" + " subBuilder.mergeFrom(($type$) $oneof_name$_);\n" + " $oneof_name$_ = subBuilder.buildPartial();\n" + "}\n"); + printer->Print(variables_, + "$set_oneof_case_message$;\n"); +} + +void ImmutableMessageOneofFieldLiteGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($has_oneof_case_message$) {\n" + " output.write$group_or_message$($number$, ($type$) $oneof_name$_);\n" + "}\n"); +} + +void ImmutableMessageOneofFieldLiteGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($has_oneof_case_message$) {\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .compute$group_or_message$Size($number$, ($type$) $oneof_name$_);\n" + "}\n"); +} + +// =================================================================== + +RepeatedImmutableMessageFieldLiteGenerator:: +RepeatedImmutableMessageFieldLiteGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) + : descriptor_(descriptor), messageBitIndex_(messageBitIndex), + builderBitIndex_(builderBitIndex), context_(context), + name_resolver_(context->GetNameResolver()) { + SetMessageVariables(descriptor, messageBitIndex, builderBitIndex, + context->GetFieldGeneratorInfo(descriptor), + name_resolver_, &variables_); +} + +RepeatedImmutableMessageFieldLiteGenerator:: +~RepeatedImmutableMessageFieldLiteGenerator() {} + +int RepeatedImmutableMessageFieldLiteGenerator::GetNumBitsForMessage() const { + return 0; +} + +int RepeatedImmutableMessageFieldLiteGenerator::GetNumBitsForBuilder() const { + return 0; +} + +void RepeatedImmutableMessageFieldLiteGenerator:: +GenerateInterfaceMembers(io::Printer* printer) const { + // TODO(jonp): In the future, consider having methods specific to the + // interface so that builders can choose dynamically to either return a + // message or a nested builder, so that asking for the interface doesn't + // cause a message to ever be built. + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$java.util.List<$type$> \n" + " get$capitalized_name$List();\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$$type$ get$capitalized_name$(int index);\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$int get$capitalized_name$Count();\n"); +} + +void RepeatedImmutableMessageFieldLiteGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print(variables_, + "private com.google.protobuf.Internal.ProtobufList<$type$> $name$_;\n"); + PrintExtraFieldInfo(variables_, printer); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.util.List<$type$> get$capitalized_name$List() {\n" + " return $name$_;\n" // note: unmodifiable list + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.util.List \n" + " get$capitalized_name$OrBuilderList() {\n" + " return $name$_;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public int get$capitalized_name$Count() {\n" + " return $name$_.size();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$(int index) {\n" + " return $name$_.get(index);\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$OrBuilder get$capitalized_name$OrBuilder(\n" + " int index) {\n" + " return $name$_.get(index);\n" + "}\n"); + + printer->Print(variables_, + "private void ensure$capitalized_name$IsMutable() {\n" + " if (!$is_mutable$) {\n" + " $name$_ = newProtobufList($name$_);\n" + " }\n" + "}\n" + "\n"); + + // Builder setRepeatedField(int index, Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$(\n" + " int index, $type$ value) {\n" + " if (value == null) {\n" + " throw new NullPointerException();\n" + " }\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.set(index, value);\n" + "}\n"); + + // Builder setRepeatedField(int index, Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$(\n" + " int index, $type$.Builder builderForValue) {\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.set(index, builderForValue.build());\n" + "}\n"); + + // Builder addRepeatedField(Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void add$capitalized_name$($type$ value) {\n" + " if (value == null) {\n" + " throw new NullPointerException();\n" + " }\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.add(value);\n" + "}\n"); + + // Builder addRepeatedField(int index, Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void add$capitalized_name$(\n" + " int index, $type$ value) {\n" + " if (value == null) {\n" + " throw new NullPointerException();\n" + " }\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.add(index, value);\n" + "}\n"); + // Builder addRepeatedField(Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void add$capitalized_name$(\n" + " $type$.Builder builderForValue) {\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.add(builderForValue.build());\n" + "}\n"); + + // Builder addRepeatedField(int index, Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void add$capitalized_name$(\n" + " int index, $type$.Builder builderForValue) {\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.add(index, builderForValue.build());\n" + "}\n"); + + // Builder addAllRepeatedField(Iterable values) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void addAll$capitalized_name$(\n" + " java.lang.Iterable values) {\n" + " ensure$capitalized_name$IsMutable();\n" + " com.google.protobuf.AbstractMessageLite.addAll(\n" + " values, $name$_);\n" + "}\n"); + + // Builder clearAllRepeatedField() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void clear$capitalized_name$() {\n" + " $name$_ = emptyProtobufList();\n" + "}\n"); + + // Builder removeRepeatedField(int index) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void remove$capitalized_name$(int index) {\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.remove(index);\n" + "}\n"); +} + +void RepeatedImmutableMessageFieldLiteGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + // The comments above the methods below are based on a hypothetical + // repeated field of type "Field" called "RepeatedField". + + // List getRepeatedFieldList() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.util.List<$type$> get$capitalized_name$List() {\n" + " return java.util.Collections.unmodifiableList(\n" + " instance.get$capitalized_name$List());\n" + "}\n"); + + // int getRepeatedFieldCount() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public int get$capitalized_name$Count() {\n" + " return instance.get$capitalized_name$Count();\n" + "}"); + + // Field getRepeatedField(int index) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$(int index) {\n" + " return instance.get$capitalized_name$(index);\n" + "}\n"); + + // Builder setRepeatedField(int index, Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$(\n" + " int index, $type$ value) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(index, value);\n" + " return this;\n" + "}\n"); + + // Builder setRepeatedField(int index, Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$(\n" + " int index, $type$.Builder builderForValue) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(index, builderForValue);\n" + " return this;\n" + "}\n"); + + // Builder addRepeatedField(Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder add$capitalized_name$($type$ value) {\n" + " copyOnWrite();\n" + " instance.add$capitalized_name$(value);\n" + " return this;\n" + "}\n"); + + // Builder addRepeatedField(int index, Field value) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder add$capitalized_name$(\n" + " int index, $type$ value) {\n" + " copyOnWrite();\n" + " instance.add$capitalized_name$(index, value);\n" + " return this;\n" + "}\n"); + // Builder addRepeatedField(Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder add$capitalized_name$(\n" + " $type$.Builder builderForValue) {\n" + " copyOnWrite();\n" + " instance.add$capitalized_name$(builderForValue);\n" + " return this;\n" + "}\n"); + + // Builder addRepeatedField(int index, Field.Builder builderForValue) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder add$capitalized_name$(\n" + " int index, $type$.Builder builderForValue) {\n" + " copyOnWrite();\n" + " instance.add$capitalized_name$(index, builderForValue);\n" + " return this;\n" + "}\n"); + + // Builder addAllRepeatedField(Iterable values) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder addAll$capitalized_name$(\n" + " java.lang.Iterable values) {\n" + " copyOnWrite();\n" + " instance.addAll$capitalized_name$(values);\n" + " return this;\n" + "}\n"); + + // Builder clearAllRepeatedField() + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder clear$capitalized_name$() {\n" + " copyOnWrite();\n" + " instance.clear$capitalized_name$();\n" + " return this;\n" + "}\n"); + + // Builder removeRepeatedField(int index) + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder remove$capitalized_name$(int index) {\n" + " copyOnWrite();\n" + " instance.remove$capitalized_name$(index);\n" + " return this;\n" + "}\n"); +} + +void RepeatedImmutableMessageFieldLiteGenerator:: +GenerateFieldBuilderInitializationCode(io::Printer* printer) const { + printer->Print(variables_, + "get$capitalized_name$FieldBuilder();\n"); +} + +void RepeatedImmutableMessageFieldLiteGenerator:: +GenerateInitializationCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_ = emptyProtobufList();\n"); +} + +void RepeatedImmutableMessageFieldLiteGenerator:: +GenerateMergingCode(io::Printer* printer) const { + // The code below does two optimizations (non-nested builder case): + // 1. If the other list is empty, there's nothing to do. This ensures we + // don't allocate a new array if we already have an immutable one. + // 2. If the other list is non-empty and our current list is empty, we can + // reuse the other list which is guaranteed to be immutable. + printer->Print(variables_, + "if (!other.$name$_.isEmpty()) {\n" + " if ($name$_.isEmpty()) {\n" + " $name$_ = other.$name$_;\n" + " } else {\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.addAll(other.$name$_);\n" + " }\n" + " $on_changed$\n" + "}\n"); +} + +void RepeatedImmutableMessageFieldLiteGenerator:: +GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const { + printer->Print(variables_, + "$name$_.makeImmutable();\n"); +} + +void RepeatedImmutableMessageFieldLiteGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (!$is_mutable$) {\n" + " $name$_ = newProtobufList();\n" + "}\n"); + + if (GetType(descriptor_) == FieldDescriptor::TYPE_GROUP) { + printer->Print(variables_, + "$name$_.add(input.readGroup($number$, $type$.PARSER,\n" + " extensionRegistry));\n"); + } else { + printer->Print(variables_, + "$name$_.add(input.readMessage($type$.PARSER, extensionRegistry));\n"); + } +} + +void RepeatedImmutableMessageFieldLiteGenerator:: +GenerateParsingDoneCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($is_mutable$) {\n" + " $name$_.makeImmutable();\n" + "}\n"); +} + +void RepeatedImmutableMessageFieldLiteGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "for (int i = 0; i < $name$_.size(); i++) {\n" + " output.write$group_or_message$($number$, $name$_.get(i));\n" + "}\n"); +} + +void RepeatedImmutableMessageFieldLiteGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "for (int i = 0; i < $name$_.size(); i++) {\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .compute$group_or_message$Size($number$, $name$_.get(i));\n" + "}\n"); +} + +void RepeatedImmutableMessageFieldLiteGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + printer->Print(variables_, + "result = result && get$capitalized_name$List()\n" + " .equals(other.get$capitalized_name$List());\n"); +} + +void RepeatedImmutableMessageFieldLiteGenerator:: +GenerateHashCode(io::Printer* printer) const { + printer->Print(variables_, + "if (get$capitalized_name$Count() > 0) {\n" + " hash = (37 * hash) + $constant_name$;\n" + " hash = (53 * hash) + get$capitalized_name$List().hashCode();\n" + "}\n"); +} + +string RepeatedImmutableMessageFieldLiteGenerator::GetBoxedType() const { + return name_resolver_->GetImmutableClassName(descriptor_->message_type()); +} + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_message_field_lite.h b/src/google/protobuf/compiler/java/java_message_field_lite.h new file mode 100644 index 00000000..ae26c06a --- /dev/null +++ b/src/google/protobuf/compiler/java/java_message_field_lite.h @@ -0,0 +1,157 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_FIELD_LITE_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_FIELD_LITE_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { + namespace compiler { + namespace java { + class Context; // context.h + class ClassNameResolver; // name_resolver.h + } + } +} + +namespace protobuf { +namespace compiler { +namespace java { + +class ImmutableMessageFieldLiteGenerator : public ImmutableFieldLiteGenerator { + public: + explicit ImmutableMessageFieldLiteGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~ImmutableMessageFieldLiteGenerator(); + + // implements ImmutableFieldLiteGenerator ------------------------------------ + int GetNumBitsForMessage() const; + int GetNumBitsForBuilder() const; + void GenerateInterfaceMembers(io::Printer* printer) const; + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateInitializationCode(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateParsingDoneCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCode(io::Printer* printer) const; + + string GetBoxedType() const; + + protected: + const FieldDescriptor* descriptor_; + map variables_; + const int messageBitIndex_; + const int builderBitIndex_; + Context* context_; + ClassNameResolver* name_resolver_; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableMessageFieldLiteGenerator); +}; + +class ImmutableMessageOneofFieldLiteGenerator + : public ImmutableMessageFieldLiteGenerator { + public: + ImmutableMessageOneofFieldLiteGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~ImmutableMessageOneofFieldLiteGenerator(); + + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableMessageOneofFieldLiteGenerator); +}; + +class RepeatedImmutableMessageFieldLiteGenerator + : public ImmutableFieldLiteGenerator { + public: + explicit RepeatedImmutableMessageFieldLiteGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~RepeatedImmutableMessageFieldLiteGenerator(); + + // implements ImmutableFieldLiteGenerator ------------------------------------ + int GetNumBitsForMessage() const; + int GetNumBitsForBuilder() const; + void GenerateInterfaceMembers(io::Printer* printer) const; + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateInitializationCode(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateParsingDoneCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCode(io::Printer* printer) const; + + string GetBoxedType() const; + + protected: + const FieldDescriptor* descriptor_; + map variables_; + const int messageBitIndex_; + const int builderBitIndex_; + Context* context_; + ClassNameResolver* name_resolver_; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedImmutableMessageFieldLiteGenerator); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_FIELD_LITE_H__ diff --git a/src/google/protobuf/compiler/java/java_message_lite.cc b/src/google/protobuf/compiler/java/java_message_lite.cc new file mode 100644 index 00000000..3accee92 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_message_lite.cc @@ -0,0 +1,1174 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: dweis@google.com (Daniel Weis) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include + +#include +#include +#include +#include +#ifndef _SHARED_PTR_H +#include +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +using internal::WireFormat; +using internal::WireFormatLite; + +namespace { +bool GenerateHasBits(const Descriptor* descriptor) { + return SupportFieldPresence(descriptor->file()) || + HasRepeatedFields(descriptor); +} + +string MapValueImmutableClassdName(const Descriptor* descriptor, + ClassNameResolver* name_resolver) { + const FieldDescriptor* value_field = descriptor->FindFieldByName("value"); + GOOGLE_CHECK_EQ(FieldDescriptor::TYPE_MESSAGE, value_field->type()); + return name_resolver->GetImmutableClassName(value_field->message_type()); +} +} // namespace + +// =================================================================== +ImmutableMessageLiteGenerator::ImmutableMessageLiteGenerator( + const Descriptor* descriptor, Context* context) + : MessageGenerator(descriptor), context_(context), + name_resolver_(context->GetNameResolver()), + field_generators_(descriptor, context_) { + GOOGLE_CHECK_EQ( + FileOptions::LITE_RUNTIME, descriptor->file()->options().optimize_for()); +} + +ImmutableMessageLiteGenerator::~ImmutableMessageLiteGenerator() {} + +void ImmutableMessageLiteGenerator::GenerateStaticVariables( + io::Printer* printer) { + // Generate static members for all nested types. + for (int i = 0; i < descriptor_->nested_type_count(); i++) { + // TODO(kenton): Reuse MessageGenerator objects? + ImmutableMessageLiteGenerator(descriptor_->nested_type(i), context_) + .GenerateStaticVariables(printer); + } +} + +int ImmutableMessageLiteGenerator::GenerateStaticVariableInitializers( + io::Printer* printer) { + int bytecode_estimate = 0; + // Generate static member initializers for all nested types. + for (int i = 0; i < descriptor_->nested_type_count(); i++) { + // TODO(kenton): Reuse MessageGenerator objects? + bytecode_estimate += + ImmutableMessageLiteGenerator(descriptor_->nested_type(i), context_) + .GenerateStaticVariableInitializers(printer); + } + return bytecode_estimate; +} + +// =================================================================== + +void ImmutableMessageLiteGenerator::GenerateInterface(io::Printer* printer) { + if (descriptor_->extension_range_count() > 0) { + printer->Print( + "public interface $classname$OrBuilder extends \n" + " $extra_interfaces$\n" + " com.google.protobuf.GeneratedMessageLite.\n" + " ExtendableMessageOrBuilder<\n" + " $classname$, $classname$.Builder> {\n", + "extra_interfaces", ExtraMessageOrBuilderInterfaces(descriptor_), + "classname", descriptor_->name()); + } else { + printer->Print( + "public interface $classname$OrBuilder extends\n" + " $extra_interfaces$\n" + " com.google.protobuf.MessageLiteOrBuilder {\n", + "extra_interfaces", ExtraMessageOrBuilderInterfaces(descriptor_), + "classname", descriptor_->name()); + } + + printer->Indent(); + for (int i = 0; i < descriptor_->field_count(); i++) { + printer->Print("\n"); + field_generators_.get(descriptor_->field(i)) + .GenerateInterfaceMembers(printer); + } + printer->Outdent(); + + printer->Print("}\n"); +} + +// =================================================================== + +void ImmutableMessageLiteGenerator::Generate(io::Printer* printer) { + bool is_own_file = + descriptor_->containing_type() == NULL && + MultipleJavaFiles(descriptor_->file(), /* immutable = */ true); + + map variables; + variables["static"] = is_own_file ? " " : " static "; + variables["classname"] = descriptor_->name(); + variables["extra_interfaces"] = ExtraMessageInterfaces(descriptor_); + + WriteMessageDocComment(printer, descriptor_); + + // The builder_type stores the super type name of the nested Builder class. + string builder_type; + if (descriptor_->extension_range_count() > 0) { + printer->Print(variables, + "public $static$final class $classname$ extends\n" + " com.google.protobuf.GeneratedMessageLite.ExtendableMessage<\n" + " $classname$, $classname$.Builder> implements\n" + " $extra_interfaces$\n" + " $classname$OrBuilder {\n"); + builder_type = strings::Substitute( + "com.google.protobuf.GeneratedMessageLite.ExtendableBuilder<$0, ?>", + name_resolver_->GetImmutableClassName(descriptor_)); + } else { + printer->Print(variables, + "public $static$final class $classname$ extends\n" + " com.google.protobuf.GeneratedMessageLite<\n" + " $classname$, $classname$.Builder> implements\n" + " $extra_interfaces$\n" + " $classname$OrBuilder {\n"); + + builder_type = "com.google.protobuf.GeneratedMessageLite.Builder"; + } + printer->Indent(); + + GenerateParsingConstructor(printer); + + // Nested types + for (int i = 0; i < descriptor_->enum_type_count(); i++) { + EnumGenerator(descriptor_->enum_type(i), true, context_) + .Generate(printer); + } + + for (int i = 0; i < descriptor_->nested_type_count(); i++) { + // Don't generate Java classes for map entry messages. + if (IsMapEntry(descriptor_->nested_type(i))) continue; + ImmutableMessageLiteGenerator messageGenerator( + descriptor_->nested_type(i), context_); + messageGenerator.GenerateInterface(printer); + messageGenerator.Generate(printer); + } + + if (GenerateHasBits(descriptor_)) { + // Integers for bit fields. + int totalBits = 0; + for (int i = 0; i < descriptor_->field_count(); i++) { + totalBits += field_generators_.get(descriptor_->field(i)) + .GetNumBitsForMessage(); + } + int totalInts = (totalBits + 31) / 32; + for (int i = 0; i < totalInts; i++) { + printer->Print("private int $bit_field_name$;\n", + "bit_field_name", GetBitFieldName(i)); + } + } + + // oneof + map vars; + for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { + vars["oneof_name"] = context_->GetOneofGeneratorInfo( + descriptor_->oneof_decl(i))->name; + vars["oneof_capitalized_name"] = context_->GetOneofGeneratorInfo( + descriptor_->oneof_decl(i))->capitalized_name; + vars["oneof_index"] = SimpleItoa(descriptor_->oneof_decl(i)->index()); + // oneofCase_ and oneof_ + printer->Print(vars, + "private int $oneof_name$Case_ = 0;\n" + "private java.lang.Object $oneof_name$_;\n"); + // OneofCase enum + printer->Print(vars, + "public enum $oneof_capitalized_name$Case\n" + " implements com.google.protobuf.Internal.EnumLite {\n"); + printer->Indent(); + for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) { + const FieldDescriptor* field = descriptor_->oneof_decl(i)->field(j); + printer->Print( + "$field_name$($field_number$),\n", + "field_name", + ToUpper(field->name()), + "field_number", + SimpleItoa(field->number())); + } + printer->Print( + "$cap_oneof_name$_NOT_SET(0);\n", + "cap_oneof_name", + ToUpper(vars["oneof_name"])); + printer->Print(vars, + "private int value = 0;\n" + "private $oneof_capitalized_name$Case(int value) {\n" + " this.value = value;\n" + "}\n"); + printer->Print(vars, + "public static $oneof_capitalized_name$Case valueOf(int value) {\n" + " switch (value) {\n"); + for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) { + const FieldDescriptor* field = descriptor_->oneof_decl(i)->field(j); + printer->Print( + " case $field_number$: return $field_name$;\n", + "field_number", + SimpleItoa(field->number()), + "field_name", + ToUpper(field->name())); + } + printer->Print( + " case 0: return $cap_oneof_name$_NOT_SET;\n" + " default: throw new java.lang.IllegalArgumentException(\n" + " \"Value is undefined for this oneof enum.\");\n" + " }\n" + "}\n" + "public int getNumber() {\n" + " return this.value;\n" + "}\n", + "cap_oneof_name", ToUpper(vars["oneof_name"])); + printer->Outdent(); + printer->Print("};\n\n"); + // oneofCase() + printer->Print(vars, + "public $oneof_capitalized_name$Case\n" + "get$oneof_capitalized_name$Case() {\n" + " return $oneof_capitalized_name$Case.valueOf(\n" + " $oneof_name$Case_);\n" + "}\n" + "\n" + "private void clear$oneof_capitalized_name$() {\n" + " $oneof_name$Case_ = 0;\n" + " $oneof_name$_ = null;\n" + "}\n" + "\n"); + } + + // Fields + for (int i = 0; i < descriptor_->field_count(); i++) { + printer->Print("public static final int $constant_name$ = $number$;\n", + "constant_name", FieldConstantName(descriptor_->field(i)), + "number", SimpleItoa(descriptor_->field(i)->number())); + field_generators_.get(descriptor_->field(i)).GenerateMembers(printer); + printer->Print("\n"); + } + + GenerateMessageSerializationMethods(printer); + + if (HasEqualsAndHashCode(descriptor_)) { + GenerateEqualsAndHashCode(printer); + } + + + GenerateParseFromMethods(printer); + GenerateBuilder(printer); + + if (HasRequiredFields(descriptor_)) { + // Memoizes whether the protocol buffer is fully initialized (has all + // required fields). -1 means not yet computed. 0 means false and 1 means + // true. + printer->Print( + "private byte memoizedIsInitialized = -1;\n"); + } + + printer->Print( + "protected final Object dynamicMethod(\n" + " com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,\n" + " Object... args) {\n" + " switch (method) {\n" + " case PARSE_PARTIAL_FROM: {\n" + " return new $classname$(" + " (com.google.protobuf.CodedInputStream) args[0],\n" + " (com.google.protobuf.ExtensionRegistryLite) args[1]);\n" + " }\n" + " case NEW_INSTANCE: {\n" + " return new $classname$(\n" + " com.google.protobuf.Internal.EMPTY_CODED_INPUT_STREAM,\n" + " com.google.protobuf.ExtensionRegistryLite\n" + " .getEmptyRegistry());\n" + " }\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); + + printer->Indent(); + printer->Indent(); + + printer->Print( + "case IS_INITIALIZED: {\n"); + printer->Indent(); + GenerateDynamicMethodIsInitialized(printer); + printer->Outdent(); + + printer->Print( + "}\n" + "case MAKE_IMMUTABLE: {\n"); + + printer->Indent(); + GenerateDynamicMethodMakeImmutable(printer); + printer->Outdent(); + + printer->Print( + "}\n" + "case NEW_BUILDER: {\n"); + + printer->Indent(); + GenerateDynamicMethodNewBuilder(printer); + printer->Outdent(); + + printer->Print( + "}\n" + "case MERGE_FROM: {\n"); + + printer->Indent(); + GenerateDynamicMethodMergeFrom(printer); + printer->Outdent(); + + printer->Print( + "}\n"); + + printer->Outdent(); + printer->Outdent(); + + printer->Print( + " }\n" + " throw new UnsupportedOperationException();\n" + "}\n" + "\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); + + printer->Print( + "\n" + "// @@protoc_insertion_point(class_scope:$full_name$)\n", + "full_name", descriptor_->full_name()); + + + // Carefully initialize the default instance in such a way that it doesn't + // conflict with other initialization. + printer->Print( + "private static final $classname$ DEFAULT_INSTANCE;\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); + + printer->Print( + "static {\n" + " DEFAULT_INSTANCE = new $classname$(\n" + " com.google.protobuf.Internal\n" + " .EMPTY_CODED_INPUT_STREAM,\n" + " com.google.protobuf.ExtensionRegistryLite\n" + " .getEmptyRegistry());\n" + "}\n" + "\n", + "classname", descriptor_->name()); + printer->Print( + "public static $classname$ getDefaultInstance() {\n" + " return DEFAULT_INSTANCE;\n" + "}\n" + "\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); + + GenerateParser(printer); + + // LITE_RUNTIME uses this to implement the *ForType methods at the + // GeneratedMessageLite level. + printer->Print( + "static {\n" + " com.google.protobuf.GeneratedMessageLite.onLoad(\n" + " $classname$.class, new com.google.protobuf.GeneratedMessageLite\n" + " .PrototypeHolder<$classname$, Builder>(\n" + " DEFAULT_INSTANCE, PARSER));" + "}\n" + "\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); + + // Extensions must be declared after the DEFAULT_INSTANCE is initialized + // because the DEFAULT_INSTANCE is used by the extension to lazily retrieve + // the outer class's FileDescriptor. + for (int i = 0; i < descriptor_->extension_count(); i++) { + ImmutableExtensionGenerator(descriptor_->extension(i), context_) + .Generate(printer); + } + + printer->Outdent(); + printer->Print("}\n\n"); +} + +// =================================================================== + +void ImmutableMessageLiteGenerator:: +GenerateMessageSerializationMethods(io::Printer* printer) { + google::protobuf::scoped_array sorted_fields( + SortFieldsByNumber(descriptor_)); + + vector sorted_extensions; + for (int i = 0; i < descriptor_->extension_range_count(); ++i) { + sorted_extensions.push_back(descriptor_->extension_range(i)); + } + std::sort(sorted_extensions.begin(), sorted_extensions.end(), + ExtensionRangeOrdering()); + + printer->Print( + "public void writeTo(com.google.protobuf.CodedOutputStream output)\n" + " throws java.io.IOException {\n"); + printer->Indent(); + if (HasPackedFields(descriptor_)) { + // writeTo(CodedOutputStream output) might be invoked without + // getSerializedSize() ever being called, but we need the memoized + // sizes in case this message has packed fields. Rather than emit checks for + // each packed field, just call getSerializedSize() up front. + // In most cases, getSerializedSize() will have already been called anyway + // by one of the wrapper writeTo() methods, making this call cheap. + printer->Print( + "getSerializedSize();\n"); + } + + if (descriptor_->extension_range_count() > 0) { + if (descriptor_->options().message_set_wire_format()) { + printer->Print( + "com.google.protobuf.GeneratedMessageLite\n" + " .ExtendableMessage<$classname$, $classname$.Builder>\n" + " .ExtensionWriter extensionWriter =\n" + " newMessageSetExtensionWriter();\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); + } else { + printer->Print( + "com.google.protobuf.GeneratedMessageLite\n" + " .ExtendableMessage<$classname$, $classname$.Builder>\n" + " .ExtensionWriter extensionWriter =\n" + " newExtensionWriter();\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); + } + } + + // Merge the fields and the extension ranges, both sorted by field number. + for (int i = 0, j = 0; + i < descriptor_->field_count() || j < sorted_extensions.size(); + ) { + if (i == descriptor_->field_count()) { + GenerateSerializeOneExtensionRange(printer, sorted_extensions[j++]); + } else if (j == sorted_extensions.size()) { + GenerateSerializeOneField(printer, sorted_fields[i++]); + } else if (sorted_fields[i]->number() < sorted_extensions[j]->start) { + GenerateSerializeOneField(printer, sorted_fields[i++]); + } else { + GenerateSerializeOneExtensionRange(printer, sorted_extensions[j++]); + } + } + + if (PreserveUnknownFields(descriptor_)) { + printer->Print( + "unknownFields.writeTo(output);\n"); + } + + printer->Outdent(); + printer->Print( + "}\n" + "\n" + "public int getSerializedSize() {\n" + " int size = memoizedSerializedSize;\n" + " if (size != -1) return size;\n" + "\n" + " size = 0;\n"); + printer->Indent(); + + for (int i = 0; i < descriptor_->field_count(); i++) { + field_generators_.get(sorted_fields[i]).GenerateSerializedSizeCode(printer); + } + + if (descriptor_->extension_range_count() > 0) { + if (descriptor_->options().message_set_wire_format()) { + printer->Print( + "size += extensionsSerializedSizeAsMessageSet();\n"); + } else { + printer->Print( + "size += extensionsSerializedSize();\n"); + } + } + + if (PreserveUnknownFields(descriptor_)) { + printer->Print( + "size += unknownFields.getSerializedSize();\n"); + } + + printer->Outdent(); + printer->Print( + " memoizedSerializedSize = size;\n" + " return size;\n" + "}\n" + "\n"); + + printer->Print( + "private static final long serialVersionUID = 0L;\n"); +} + +void ImmutableMessageLiteGenerator:: +GenerateParseFromMethods(io::Printer* printer) { + // Note: These are separate from GenerateMessageSerializationMethods() + // because they need to be generated even for messages that are optimized + // for code size. + printer->Print( + "public static $classname$ parseFrom(\n" + " com.google.protobuf.ByteString data)\n" + " throws com.google.protobuf.InvalidProtocolBufferException {\n" + " return PARSER.parseFrom(data);\n" + "}\n" + "public static $classname$ parseFrom(\n" + " com.google.protobuf.ByteString data,\n" + " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" + " throws com.google.protobuf.InvalidProtocolBufferException {\n" + " return PARSER.parseFrom(data, extensionRegistry);\n" + "}\n" + "public static $classname$ parseFrom(byte[] data)\n" + " throws com.google.protobuf.InvalidProtocolBufferException {\n" + " return PARSER.parseFrom(data);\n" + "}\n" + "public static $classname$ parseFrom(\n" + " byte[] data,\n" + " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" + " throws com.google.protobuf.InvalidProtocolBufferException {\n" + " return PARSER.parseFrom(data, extensionRegistry);\n" + "}\n" + "public static $classname$ parseFrom(java.io.InputStream input)\n" + " throws java.io.IOException {\n" + " return PARSER.parseFrom(input);\n" + "}\n" + "public static $classname$ parseFrom(\n" + " java.io.InputStream input,\n" + " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" + " throws java.io.IOException {\n" + " return PARSER.parseFrom(input, extensionRegistry);\n" + "}\n" + "public static $classname$ parseDelimitedFrom(java.io.InputStream input)\n" + " throws java.io.IOException {\n" + " return PARSER.parseDelimitedFrom(input);\n" + "}\n" + "public static $classname$ parseDelimitedFrom(\n" + " java.io.InputStream input,\n" + " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" + " throws java.io.IOException {\n" + " return PARSER.parseDelimitedFrom(input, extensionRegistry);\n" + "}\n" + "public static $classname$ parseFrom(\n" + " com.google.protobuf.CodedInputStream input)\n" + " throws java.io.IOException {\n" + " return PARSER.parseFrom(input);\n" + "}\n" + "public static $classname$ parseFrom(\n" + " com.google.protobuf.CodedInputStream input,\n" + " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" + " throws java.io.IOException {\n" + " return PARSER.parseFrom(input, extensionRegistry);\n" + "}\n" + "\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); +} + +void ImmutableMessageLiteGenerator::GenerateSerializeOneField( + io::Printer* printer, const FieldDescriptor* field) { + field_generators_.get(field).GenerateSerializationCode(printer); +} + +void ImmutableMessageLiteGenerator::GenerateSerializeOneExtensionRange( + io::Printer* printer, const Descriptor::ExtensionRange* range) { + printer->Print( + "extensionWriter.writeUntil($end$, output);\n", + "end", SimpleItoa(range->end)); +} + +// =================================================================== + +void ImmutableMessageLiteGenerator::GenerateBuilder(io::Printer* printer) { + printer->Print( + "public static Builder newBuilder() {\n" + " return DEFAULT_INSTANCE.toBuilder();\n" + "}\n" + "public static Builder newBuilder($classname$ prototype) {\n" + " return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);\n" + "}\n" + "\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); + + MessageBuilderLiteGenerator builderGenerator(descriptor_, context_); + builderGenerator.Generate(printer); +} + +// =================================================================== + +void ImmutableMessageLiteGenerator::GenerateDynamicMethodIsInitialized( + io::Printer* printer) { + // Returns null for false, DEFAULT_INSTANCE for true. + if (!HasRequiredFields(descriptor_)) { + printer->Print("return DEFAULT_INSTANCE;\n"); + return; + } + + // Don't directly compare to -1 to avoid an Android x86 JIT bug. + printer->Print( + "byte isInitialized = memoizedIsInitialized;\n" + "if (isInitialized == 1) return DEFAULT_INSTANCE;\n" + "if (isInitialized == 0) return null;\n" + "\n" + "boolean shouldMemoize = ((Boolean) args[0]).booleanValue();\n"); + + // Check that all required fields in this message are set. + // TODO(kenton): We can optimize this when we switch to putting all the + // "has" fields into a single bitfield. + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + const FieldGeneratorInfo* info = context_->GetFieldGeneratorInfo(field); + + if (field->is_required()) { + printer->Print( + "if (!has$name$()) {\n" + " if (shouldMemoize) {\n" + " memoizedIsInitialized = 0;\n" + " }\n" + " return null;\n" + "}\n", + "name", info->capitalized_name); + } + } + + // Now check that all embedded messages are initialized. + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + const FieldGeneratorInfo* info = context_->GetFieldGeneratorInfo(field); + if (GetJavaType(field) == JAVATYPE_MESSAGE && + HasRequiredFields(field->message_type())) { + switch (field->label()) { + case FieldDescriptor::LABEL_REQUIRED: + printer->Print( + "if (!get$name$().isInitialized()) {\n" + " if (shouldMemoize) {\n" + " memoizedIsInitialized = 0;\n" + " }\n" + " return null;\n" + "}\n", + "type", name_resolver_->GetImmutableClassName( + field->message_type()), + "name", info->capitalized_name); + break; + case FieldDescriptor::LABEL_OPTIONAL: + if (!SupportFieldPresence(descriptor_->file()) && + field->containing_oneof() != NULL) { + const OneofDescriptor* oneof = field->containing_oneof(); + const OneofGeneratorInfo* oneof_info = + context_->GetOneofGeneratorInfo(oneof); + printer->Print( + "if ($oneof_name$Case_ == $field_number$) {\n", + "oneof_name", oneof_info->name, + "field_number", SimpleItoa(field->number())); + } else { + printer->Print( + "if (has$name$()) {\n", + "name", info->capitalized_name); + } + printer->Print( + " if (!get$name$().isInitialized()) {\n" + " if (shouldMemoize) {\n" + " memoizedIsInitialized = 0;\n" + " }\n" + " return null;\n" + " }\n" + "}\n", + "name", info->capitalized_name); + break; + case FieldDescriptor::LABEL_REPEATED: + if (IsMapEntry(field->message_type())) { + printer->Print( + "for ($type$ item : get$name$().values()) {\n" + " if (!item.isInitialized()) {\n" + " if (shouldMemoize) {\n" + " memoizedIsInitialized = 0;\n" + " }\n" + " return null;\n" + " }\n" + "}\n", + "type", MapValueImmutableClassdName(field->message_type(), + name_resolver_), + "name", info->capitalized_name); + } else { + printer->Print( + "for (int i = 0; i < get$name$Count(); i++) {\n" + " if (!get$name$(i).isInitialized()) {\n" + " if (shouldMemoize) {\n" + " memoizedIsInitialized = 0;\n" + " }\n" + " return null;\n" + " }\n" + "}\n", + "type", name_resolver_->GetImmutableClassName( + field->message_type()), + "name", info->capitalized_name); + } + break; + } + } + } + + if (descriptor_->extension_range_count() > 0) { + printer->Print( + "if (!extensionsAreInitialized()) {\n" + " if (shouldMemoize) {\n" + " memoizedIsInitialized = 0;\n" + " }\n" + " return null;\n" + "}\n"); + } + + printer->Print( + "if (shouldMemoize) memoizedIsInitialized = 1;\n"); + + printer->Print( + "return DEFAULT_INSTANCE;\n" + "\n"); +} + +// =================================================================== + +void ImmutableMessageLiteGenerator::GenerateDynamicMethodMakeImmutable( + io::Printer* printer) { + // Output generation code for each field. + for (int i = 0; i < descriptor_->field_count(); i++) { + field_generators_.get(descriptor_->field(i)) + .GenerateDynamicMethodMakeImmutableCode(printer); + } + printer->Print( + "return null;"); +} + +// =================================================================== + +void ImmutableMessageLiteGenerator::GenerateDynamicMethodNewBuilder( + io::Printer* printer) { + printer->Print( + "return new Builder();"); +} + +// =================================================================== + +void ImmutableMessageLiteGenerator::GenerateDynamicMethodMergeFrom( + io::Printer* printer) { + printer->Print( + // Optimization: If other is the default instance, we know none of its + // fields are set so we can skip the merge. + "Object arg = args[0];\n" + "if (arg == $classname$.getDefaultInstance()) return this;\n" + "$classname$ other = ($classname$) arg;\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); + + for (int i = 0; i < descriptor_->field_count(); i++) { + if (!descriptor_->field(i)->containing_oneof()) { + field_generators_.get( + descriptor_->field(i)).GenerateMergingCode(printer); + } + } + + // Merge oneof fields. + for (int i = 0; i < descriptor_->oneof_decl_count(); ++i) { + printer->Print( + "switch (other.get$oneof_capitalized_name$Case()) {\n", + "oneof_capitalized_name", + context_->GetOneofGeneratorInfo( + descriptor_->oneof_decl(i))->capitalized_name); + printer->Indent(); + for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) { + const FieldDescriptor* field = descriptor_->oneof_decl(i)->field(j); + printer->Print( + "case $field_name$: {\n", + "field_name", + ToUpper(field->name())); + printer->Indent(); + field_generators_.get(field).GenerateMergingCode(printer); + printer->Print( + "break;\n"); + printer->Outdent(); + printer->Print( + "}\n"); + } + printer->Print( + "case $cap_oneof_name$_NOT_SET: {\n" + " break;\n" + "}\n", + "cap_oneof_name", + ToUpper(context_->GetOneofGeneratorInfo( + descriptor_->oneof_decl(i))->name)); + printer->Outdent(); + printer->Print( + "}\n"); + } + + // if message type has extensions + if (descriptor_->extension_range_count() > 0) { + printer->Print( + "this.mergeExtensionFields(other);\n"); + } + + if (PreserveUnknownFields(descriptor_)) { + printer->Print( + "this.mergeUnknownFields(other.unknownFields);\n"); + } + + printer->Print( + "return this;\n"); +} + +// =================================================================== + +namespace { +bool CheckHasBitsForEqualsAndHashCode(const FieldDescriptor* field) { + if (field->is_repeated()) { + return false; + } + if (SupportFieldPresence(field->file())) { + return true; + } + return GetJavaType(field) == JAVATYPE_MESSAGE && + field->containing_oneof() == NULL; +} +} // namespace + +void ImmutableMessageLiteGenerator:: +GenerateEqualsAndHashCode(io::Printer* printer) { + printer->Print( + "@java.lang.Override\n" + "public boolean equals(final java.lang.Object obj) {\n"); + printer->Indent(); + printer->Print( + "if (obj == this) {\n" + " return true;\n" + "}\n" + "if (!(obj instanceof $classname$)) {\n" + " return super.equals(obj);\n" + "}\n" + "$classname$ other = ($classname$) obj;\n" + "\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); + + printer->Print("boolean result = true;\n"); + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + const FieldGeneratorInfo* info = context_->GetFieldGeneratorInfo(field); + bool check_has_bits = CheckHasBitsForEqualsAndHashCode(field); + if (check_has_bits) { + printer->Print( + "result = result && (has$name$() == other.has$name$());\n" + "if (has$name$()) {\n", + "name", info->capitalized_name); + printer->Indent(); + } + field_generators_.get(field).GenerateEqualsCode(printer); + if (check_has_bits) { + printer->Outdent(); + printer->Print( + "}\n"); + } + } + if (PreserveUnknownFields(descriptor_)) { + // Always consider unknown fields for equality. This will sometimes return + // false for non-canonical ordering when running in LITE_RUNTIME but it's + // the best we can do. + printer->Print( + "result = result && unknownFields.equals(other.unknownFields);\n"); + } + printer->Print( + "return result;\n"); + printer->Outdent(); + printer->Print( + "}\n" + "\n"); + + printer->Print( + "@java.lang.Override\n" + "public int hashCode() {\n"); + printer->Indent(); + printer->Print( + "if (memoizedHashCode != 0) {\n"); + printer->Indent(); + printer->Print( + "return memoizedHashCode;\n"); + printer->Outdent(); + printer->Print( + "}\n" + "int hash = 41;\n"); + + // Include the hash of the class so that two objects with different types + // but the same field values will probably have different hashes. + printer->Print("hash = (19 * hash) + $classname$.class.hashCode();\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); + + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + const FieldGeneratorInfo* info = context_->GetFieldGeneratorInfo(field); + bool check_has_bits = CheckHasBitsForEqualsAndHashCode(field); + if (check_has_bits) { + printer->Print( + "if (has$name$()) {\n", + "name", info->capitalized_name); + printer->Indent(); + } + field_generators_.get(field).GenerateHashCode(printer); + if (check_has_bits) { + printer->Outdent(); + printer->Print("}\n"); + } + } + + printer->Print( + "hash = (29 * hash) + unknownFields.hashCode();\n"); + printer->Print( + "memoizedHashCode = hash;\n" + "return hash;\n"); + printer->Outdent(); + printer->Print( + "}\n" + "\n"); +} + +// =================================================================== + +void ImmutableMessageLiteGenerator:: +GenerateExtensionRegistrationCode(io::Printer* printer) { + for (int i = 0; i < descriptor_->extension_count(); i++) { + ImmutableExtensionGenerator(descriptor_->extension(i), context_) + .GenerateRegistrationCode(printer); + } + + for (int i = 0; i < descriptor_->nested_type_count(); i++) { + ImmutableMessageLiteGenerator(descriptor_->nested_type(i), context_) + .GenerateExtensionRegistrationCode(printer); + } +} + +// =================================================================== +void ImmutableMessageLiteGenerator:: +GenerateParsingConstructor(io::Printer* printer) { + google::protobuf::scoped_array sorted_fields( + SortFieldsByNumber(descriptor_)); + + printer->Print( + "private $classname$(\n" + " com.google.protobuf.CodedInputStream input,\n" + " com.google.protobuf.ExtensionRegistryLite extensionRegistry) {\n", + "classname", descriptor_->name()); + printer->Indent(); + + // Initialize all fields to default. + GenerateInitializers(printer); + + // Use builder bits to track mutable repeated fields. + int totalBuilderBits = 0; + for (int i = 0; i < descriptor_->field_count(); i++) { + const ImmutableFieldLiteGenerator& field = + field_generators_.get(descriptor_->field(i)); + totalBuilderBits += field.GetNumBitsForBuilder(); + } + int totalBuilderInts = (totalBuilderBits + 31) / 32; + for (int i = 0; i < totalBuilderInts; i++) { + printer->Print("int mutable_$bit_field_name$ = 0;\n", + "bit_field_name", GetBitFieldName(i)); + } + + if (PreserveUnknownFields(descriptor_)) { + printer->Print( + "com.google.protobuf.UnknownFieldSetLite.Builder unknownFields =\n" + " com.google.protobuf.UnknownFieldSetLite.newBuilder();\n"); + } + + printer->Print( + "try {\n"); + printer->Indent(); + + printer->Print( + "boolean done = false;\n" + "while (!done) {\n"); + printer->Indent(); + + printer->Print( + "int tag = input.readTag();\n" + "switch (tag) {\n"); + printer->Indent(); + + printer->Print( + "case 0:\n" // zero signals EOF / limit reached + " done = true;\n" + " break;\n"); + + if (PreserveUnknownFields(descriptor_)) { + if (descriptor_->extension_range_count() > 0) { + // Lite runtime directly invokes parseUnknownField to reduce method + // counts. + printer->Print( + "default: {\n" + " if (!parseUnknownField(extensions, getDefaultInstanceForType(),\n" + " input, unknownFields,\n" + " extensionRegistry, tag)) {\n" + " done = true;\n" // it's an endgroup tag + " }\n" + " break;\n" + "}\n"); + } else { + printer->Print( + "default: {\n" + " if (!parseUnknownField(input, unknownFields,\n" + " extensionRegistry, tag)) {\n" + " done = true;\n" // it's an endgroup tag + " }\n" + " break;\n" + "}\n"); + } + } else { + printer->Print( + "default: {\n" + " if (!input.skipField(tag)) {\n" + " done = true;\n" // it's an endgroup tag + " }\n" + " break;\n" + "}\n"); + } + + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = sorted_fields[i]; + uint32 tag = WireFormatLite::MakeTag(field->number(), + WireFormat::WireTypeForFieldType(field->type())); + + printer->Print( + "case $tag$: {\n", + "tag", SimpleItoa(tag)); + printer->Indent(); + + field_generators_.get(field).GenerateParsingCode(printer); + + printer->Outdent(); + printer->Print( + " break;\n" + "}\n"); + + if (field->is_packable()) { + // To make packed = true wire compatible, we generate parsing code from a + // packed version of this field regardless of field->options().packed(). + uint32 packed_tag = WireFormatLite::MakeTag(field->number(), + WireFormatLite::WIRETYPE_LENGTH_DELIMITED); + printer->Print( + "case $tag$: {\n", + "tag", SimpleItoa(packed_tag)); + printer->Indent(); + + field_generators_.get(field).GenerateParsingCodeFromPacked(printer); + + printer->Outdent(); + printer->Print( + " break;\n" + "}\n"); + } + } + + printer->Outdent(); + printer->Outdent(); + printer->Print( + " }\n" // switch (tag) + "}\n"); // while (!done) + + printer->Outdent(); + printer->Print( + "} catch (com.google.protobuf.InvalidProtocolBufferException e) {\n" + " throw new RuntimeException(e.setUnfinishedMessage(this));\n" + "} catch (java.io.IOException e) {\n" + " throw new RuntimeException(\n" + " new com.google.protobuf.InvalidProtocolBufferException(\n" + " e.getMessage()).setUnfinishedMessage(this));\n" + "} finally {\n"); + printer->Indent(); + + // Make repeated field list immutable. + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = sorted_fields[i]; + field_generators_.get(field).GenerateParsingDoneCode(printer); + } + + if (PreserveUnknownFields(descriptor_)) { + // Make unknown fields immutable. + printer->Print("this.unknownFields = unknownFields.build();\n"); + } + + if (descriptor_->extension_range_count() > 0) { + // Make extensions immutable. + printer->Print( + "makeExtensionsImmutable(extensions);\n"); + } + + printer->Outdent(); + printer->Outdent(); + printer->Print( + " }\n" // finally + "}\n"); +} + +// =================================================================== +void ImmutableMessageLiteGenerator::GenerateParser(io::Printer* printer) { + printer->Print( + "public static final com.google.protobuf.Parser<$classname$> PARSER =\n" + " new DefaultInstanceBasedParser(DEFAULT_INSTANCE);\n" + "\n", + "classname", descriptor_->name()); +} + +// =================================================================== +void ImmutableMessageLiteGenerator::GenerateInitializers(io::Printer* printer) { + for (int i = 0; i < descriptor_->field_count(); i++) { + if (!descriptor_->field(i)->containing_oneof()) { + field_generators_.get(descriptor_->field(i)) + .GenerateInitializationCode(printer); + } + } +} + + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_message_lite.h b/src/google/protobuf/compiler/java/java_message_lite.h new file mode 100644 index 00000000..2bd3cdd4 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_message_lite.h @@ -0,0 +1,91 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: dweis@google.com (Daniel Weis) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_LITE_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_LITE_H__ + +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +class ImmutableMessageLiteGenerator : public MessageGenerator { + public: + explicit ImmutableMessageLiteGenerator(const Descriptor* descriptor, + Context* context); + virtual ~ImmutableMessageLiteGenerator(); + + virtual void Generate(io::Printer* printer); + virtual void GenerateInterface(io::Printer* printer); + virtual void GenerateExtensionRegistrationCode(io::Printer* printer); + virtual void GenerateStaticVariables(io::Printer* printer); + virtual int GenerateStaticVariableInitializers(io::Printer* printer); + + private: + + void GenerateMessageSerializationMethods(io::Printer* printer); + void GenerateParseFromMethods(io::Printer* printer); + void GenerateSerializeOneField(io::Printer* printer, + const FieldDescriptor* field); + void GenerateSerializeOneExtensionRange( + io::Printer* printer, const Descriptor::ExtensionRange* range); + + void GenerateBuilder(io::Printer* printer); + void GenerateDynamicMethodIsInitialized(io::Printer* printer); + void GenerateDynamicMethodMakeImmutable(io::Printer* printer); + void GenerateDynamicMethodMergeFrom(io::Printer* printer); + void GenerateDynamicMethodNewBuilder(io::Printer* printer); + void GenerateInitializers(io::Printer* printer); + void GenerateEqualsAndHashCode(io::Printer* printer); + void GenerateParser(io::Printer* printer); + void GenerateParsingConstructor(io::Printer* printer); + + Context* context_; + ClassNameResolver* name_resolver_; + FieldGeneratorMap field_generators_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableMessageLiteGenerator); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_LITE_H__ diff --git a/src/google/protobuf/compiler/java/java_primitive_field.cc b/src/google/protobuf/compiler/java/java_primitive_field.cc index 48757c60..7bebe12a 100644 --- a/src/google/protobuf/compiler/java/java_primitive_field.cc +++ b/src/google/protobuf/compiler/java/java_primitive_field.cc @@ -74,7 +74,12 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, "" : ("= " + ImmutableDefaultValue(descriptor, name_resolver)); (*variables)["capitalized_type"] = GetCapitalizedType(descriptor, /* immutable = */ true); - (*variables)["tag"] = SimpleItoa(WireFormat::MakeTag(descriptor)); + if (descriptor->is_packed()) { + (*variables)["tag"] = SimpleItoa(WireFormatLite::MakeTag( + descriptor->number(), WireFormatLite::WIRETYPE_LENGTH_DELIMITED)); + } else { + (*variables)["tag"] = SimpleItoa(WireFormat::MakeTag(descriptor)); + } (*variables)["tag_size"] = SimpleItoa( WireFormat::TagSize(descriptor->number(), GetType(descriptor))); if (IsReferenceType(GetJavaType(descriptor))) { @@ -599,7 +604,7 @@ GenerateMembers(io::Printer* printer) const { " return $name$_.get(index);\n" "}\n"); - if (descriptor_->options().packed() && + if (descriptor_->is_packed() && HasGeneratedMethods(descriptor_->containing_type())) { printer->Print(variables_, "private int $name$MemoizedSerializedSize = -1;\n"); @@ -771,7 +776,7 @@ GenerateParsingDoneCode(io::Printer* printer) const { void RepeatedImmutablePrimitiveFieldGenerator:: GenerateSerializationCode(io::Printer* printer) const { - if (descriptor_->options().packed()) { + if (descriptor_->is_packed()) { // We invoke getSerializedSize in writeTo for messages that have packed // fields in ImmutableMessageGenerator::GenerateMessageSerializationMethods. // That makes it safe to rely on the memoized size here. @@ -812,7 +817,7 @@ GenerateSerializedSizeCode(io::Printer* printer) const { printer->Print( "size += dataSize;\n"); - if (descriptor_->options().packed()) { + if (descriptor_->is_packed()) { printer->Print(variables_, "if (!get$capitalized_name$List().isEmpty()) {\n" " size += $tag_size$;\n" @@ -825,7 +830,7 @@ GenerateSerializedSizeCode(io::Printer* printer) const { } // cache the data size for packed fields. - if (descriptor_->options().packed()) { + if (descriptor_->is_packed()) { printer->Print(variables_, "$name$MemoizedSerializedSize = dataSize;\n"); } diff --git a/src/google/protobuf/compiler/java/java_primitive_field_lite.cc b/src/google/protobuf/compiler/java/java_primitive_field_lite.cc new file mode 100644 index 00000000..217ff9b6 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_primitive_field_lite.cc @@ -0,0 +1,892 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +using internal::WireFormat; +using internal::WireFormatLite; + +namespace { + +void SetPrimitiveVariables(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + const FieldGeneratorInfo* info, + ClassNameResolver* name_resolver, + map* variables) { + SetCommonFieldVariables(descriptor, info, variables); + + (*variables)["type"] = PrimitiveTypeName(GetJavaType(descriptor)); + (*variables)["boxed_type"] = BoxedPrimitiveTypeName(GetJavaType(descriptor)); + (*variables)["field_type"] = (*variables)["type"]; + (*variables)["default"] = ImmutableDefaultValue(descriptor, name_resolver); + (*variables)["default_init"] = IsDefaultValueJavaDefault(descriptor) ? + "" : ("= " + ImmutableDefaultValue(descriptor, name_resolver)); + (*variables)["capitalized_type"] = + GetCapitalizedType(descriptor, /* immutable = */ true); + (*variables)["tag"] = SimpleItoa(WireFormat::MakeTag(descriptor)); + (*variables)["tag_size"] = SimpleItoa( + WireFormat::TagSize(descriptor->number(), GetType(descriptor))); + + string capitalized_type = UnderscoresToCamelCase(PrimitiveTypeName( + GetJavaType(descriptor)), true /* cap_next_letter */); + switch (GetJavaType(descriptor)) { + case JAVATYPE_INT: + case JAVATYPE_LONG: + case JAVATYPE_FLOAT: + case JAVATYPE_DOUBLE: + case JAVATYPE_BOOLEAN: + (*variables)["field_list_type"] = + "com.google.protobuf.Internal." + capitalized_type + "List"; + (*variables)["new_list"] = "new" + capitalized_type + "List"; + (*variables)["empty_list"] = "empty" + capitalized_type + "List()"; + (*variables)["make_name_unmodifiable"] = + (*variables)["name"] + "_.makeImmutable()"; + (*variables)["repeated_index_get"] = + (*variables)["name"] + "_.get" + capitalized_type + "(index)"; + (*variables)["repeated_add"] = + (*variables)["name"] + "_.add" + capitalized_type; + (*variables)["repeated_set"] = + (*variables)["name"] + "_.set" + capitalized_type; + break; + default: + (*variables)["field_list_type"] = + "com.google.protobuf.Internal.ProtobufList<" + + (*variables)["boxed_type"] + ">"; + (*variables)["new_list"] = "newProtobufList"; + (*variables)["empty_list"] = "emptyProtobufList()"; + (*variables)["make_name_unmodifiable"] = + (*variables)["name"] + "_.makeImmutable()"; + (*variables)["repeated_index_get"] = + (*variables)["name"] + "_.get(index)"; + (*variables)["repeated_add"] = (*variables)["name"] + "_.add"; + (*variables)["repeated_set"] = (*variables)["name"] + "_.set"; + } + + if (IsReferenceType(GetJavaType(descriptor))) { + (*variables)["null_check"] = + " if (value == null) {\n" + " throw new NullPointerException();\n" + " }\n"; + } else { + (*variables)["null_check"] = ""; + } + // TODO(birdo): Add @deprecated javadoc when generating javadoc is supported + // by the proto compiler + (*variables)["deprecation"] = descriptor->options().deprecated() + ? "@java.lang.Deprecated " : ""; + int fixed_size = FixedSize(GetType(descriptor)); + if (fixed_size != -1) { + (*variables)["fixed_size"] = SimpleItoa(fixed_size); + } + (*variables)["on_changed"] = + HasDescriptorMethods(descriptor->containing_type()) ? "onChanged();" : ""; + + if (SupportFieldPresence(descriptor->file())) { + // For singular messages and builders, one bit is used for the hasField bit. + (*variables)["get_has_field_bit_message"] = GenerateGetBit(messageBitIndex); + + // Note that these have a trailing ";". + (*variables)["set_has_field_bit_message"] = + GenerateSetBit(messageBitIndex) + ";"; + (*variables)["clear_has_field_bit_message"] = + GenerateClearBit(messageBitIndex) + ";"; + + (*variables)["is_field_present_message"] = GenerateGetBit(messageBitIndex); + } else { + (*variables)["set_has_field_bit_message"] = ""; + (*variables)["set_has_field_bit_message"] = ""; + (*variables)["clear_has_field_bit_message"] = ""; + + if (descriptor->type() == FieldDescriptor::TYPE_BYTES) { + (*variables)["is_field_present_message"] = + "!" + (*variables)["name"] + "_.isEmpty()"; + } else { + (*variables)["is_field_present_message"] = + (*variables)["name"] + "_ != " + (*variables)["default"]; + } + } + + // For repeated builders, the underlying list tracks mutability state. + (*variables)["is_mutable"] = (*variables)["name"] + "_.isModifiable()"; + + (*variables)["get_has_field_bit_from_local"] = + GenerateGetBitFromLocal(builderBitIndex); + (*variables)["set_has_field_bit_to_local"] = + GenerateSetBitToLocal(messageBitIndex); +} + +} // namespace + +// =================================================================== + +ImmutablePrimitiveFieldLiteGenerator:: +ImmutablePrimitiveFieldLiteGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) + : descriptor_(descriptor), messageBitIndex_(messageBitIndex), + builderBitIndex_(builderBitIndex), context_(context), + name_resolver_(context->GetNameResolver()) { + SetPrimitiveVariables(descriptor, messageBitIndex, builderBitIndex, + context->GetFieldGeneratorInfo(descriptor), + name_resolver_, &variables_); +} + +ImmutablePrimitiveFieldLiteGenerator::~ImmutablePrimitiveFieldLiteGenerator() {} + +int ImmutablePrimitiveFieldLiteGenerator::GetNumBitsForMessage() const { + return 1; +} + +int ImmutablePrimitiveFieldLiteGenerator::GetNumBitsForBuilder() const { + return 0; +} + +void ImmutablePrimitiveFieldLiteGenerator:: +GenerateInterfaceMembers(io::Printer* printer) const { + if (SupportFieldPresence(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$boolean has$capitalized_name$();\n"); + } + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$$type$ get$capitalized_name$();\n"); +} + +void ImmutablePrimitiveFieldLiteGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print(variables_, + "private $field_type$ $name$_;\n"); + PrintExtraFieldInfo(variables_, printer); + if (SupportFieldPresence(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return $get_has_field_bit_message$;\n" + "}\n"); + } + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$() {\n" + " return $name$_;\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$($type$ value) {\n" + "$null_check$" + " $set_has_field_bit_message$\n" + " $name$_ = value;\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void clear$capitalized_name$() {\n" + " $clear_has_field_bit_message$\n"); + JavaType type = GetJavaType(descriptor_); + if (type == JAVATYPE_STRING || type == JAVATYPE_BYTES) { + // The default value is not a simple literal so we want to avoid executing + // it multiple times. Instead, get the default out of the default instance. + printer->Print(variables_, + " $name$_ = getDefaultInstance().get$capitalized_name$();\n"); + } else { + printer->Print(variables_, + " $name$_ = $default$;\n"); + } + printer->Print(variables_, + "}\n"); +} + +void ImmutablePrimitiveFieldLiteGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + if (SupportFieldPresence(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return instance.has$capitalized_name$();\n" + "}\n"); + } + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$() {\n" + " return instance.get$capitalized_name$();\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$($type$ value) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(value);\n" + " return this;\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder clear$capitalized_name$() {\n" + " copyOnWrite();\n" + " instance.clear$capitalized_name$();\n" + " return this;\n" + "}\n"); +} + +void ImmutablePrimitiveFieldLiteGenerator:: +GenerateFieldBuilderInitializationCode(io::Printer* printer) const { + // noop for primitives +} + +void ImmutablePrimitiveFieldLiteGenerator:: +GenerateInitializationCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_ = $default$;\n"); +} + +void ImmutablePrimitiveFieldLiteGenerator:: +GenerateBuilderClearCode(io::Printer* printer) const { + // noop for primitives +} + +void ImmutablePrimitiveFieldLiteGenerator:: +GenerateMergingCode(io::Printer* printer) const { + if (SupportFieldPresence(descriptor_->file())) { + printer->Print(variables_, + "if (other.has$capitalized_name$()) {\n" + " set$capitalized_name$(other.get$capitalized_name$());\n" + "}\n"); + } else { + printer->Print(variables_, + "if (other.get$capitalized_name$() != $default$) {\n" + " set$capitalized_name$(other.get$capitalized_name$());\n" + "}\n"); + } +} + +void ImmutablePrimitiveFieldLiteGenerator:: +GenerateBuildingCode(io::Printer* printer) const { + // noop for primitives +} + +void ImmutablePrimitiveFieldLiteGenerator:: +GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const { + // noop for scalars +} + +void ImmutablePrimitiveFieldLiteGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "$set_has_field_bit_message$\n" + "$name$_ = input.read$capitalized_type$();\n"); +} + +void ImmutablePrimitiveFieldLiteGenerator:: +GenerateParsingDoneCode(io::Printer* printer) const { + // noop for primitives. +} + +void ImmutablePrimitiveFieldLiteGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($is_field_present_message$) {\n" + " output.write$capitalized_type$($number$, $name$_);\n" + "}\n"); +} + +void ImmutablePrimitiveFieldLiteGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($is_field_present_message$) {\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .compute$capitalized_type$Size($number$, $name$_);\n" + "}\n"); +} + +void ImmutablePrimitiveFieldLiteGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + switch (GetJavaType(descriptor_)) { + case JAVATYPE_INT: + case JAVATYPE_LONG: + case JAVATYPE_BOOLEAN: + printer->Print(variables_, + "result = result && (get$capitalized_name$()\n" + " == other.get$capitalized_name$());\n"); + break; + + case JAVATYPE_FLOAT: + printer->Print(variables_, + "result = result && (\n" + " java.lang.Float.floatToIntBits(get$capitalized_name$())\n" + " == java.lang.Float.floatToIntBits(\n" + " other.get$capitalized_name$()));\n"); + break; + + case JAVATYPE_DOUBLE: + printer->Print(variables_, + "result = result && (\n" + " java.lang.Double.doubleToLongBits(get$capitalized_name$())\n" + " == java.lang.Double.doubleToLongBits(\n" + " other.get$capitalized_name$()));\n"); + break; + + case JAVATYPE_STRING: + case JAVATYPE_BYTES: + printer->Print(variables_, + "result = result && get$capitalized_name$()\n" + " .equals(other.get$capitalized_name$());\n"); + break; + + case JAVATYPE_ENUM: + case JAVATYPE_MESSAGE: + default: + GOOGLE_LOG(FATAL) << "Can't get here."; + break; + } +} + +void ImmutablePrimitiveFieldLiteGenerator:: +GenerateHashCode(io::Printer* printer) const { + printer->Print(variables_, + "hash = (37 * hash) + $constant_name$;\n"); + switch (GetJavaType(descriptor_)) { + case JAVATYPE_INT: + printer->Print(variables_, + "hash = (53 * hash) + get$capitalized_name$();\n"); + break; + + case JAVATYPE_LONG: + printer->Print(variables_, + "hash = (53 * hash) + com.google.protobuf.Internal.hashLong(\n" + " get$capitalized_name$());\n"); + break; + + case JAVATYPE_BOOLEAN: + printer->Print(variables_, + "hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean(\n" + " get$capitalized_name$());\n"); + break; + + case JAVATYPE_FLOAT: + printer->Print(variables_, + "hash = (53 * hash) + java.lang.Float.floatToIntBits(\n" + " get$capitalized_name$());\n"); + break; + + case JAVATYPE_DOUBLE: + printer->Print(variables_, + "hash = (53 * hash) + com.google.protobuf.Internal.hashLong(\n" + " java.lang.Double.doubleToLongBits(get$capitalized_name$()));\n"); + break; + + case JAVATYPE_STRING: + case JAVATYPE_BYTES: + printer->Print(variables_, + "hash = (53 * hash) + get$capitalized_name$().hashCode();\n"); + break; + + case JAVATYPE_ENUM: + case JAVATYPE_MESSAGE: + default: + GOOGLE_LOG(FATAL) << "Can't get here."; + break; + } +} + +string ImmutablePrimitiveFieldLiteGenerator::GetBoxedType() const { + return BoxedPrimitiveTypeName(GetJavaType(descriptor_)); +} + +// =================================================================== + +ImmutablePrimitiveOneofFieldLiteGenerator:: +ImmutablePrimitiveOneofFieldLiteGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) + : ImmutablePrimitiveFieldLiteGenerator( + descriptor, messageBitIndex, builderBitIndex, context) { + const OneofGeneratorInfo* info = + context->GetOneofGeneratorInfo(descriptor->containing_oneof()); + SetCommonOneofVariables(descriptor, info, &variables_); +} + +ImmutablePrimitiveOneofFieldLiteGenerator:: +~ImmutablePrimitiveOneofFieldLiteGenerator() {} + +void ImmutablePrimitiveOneofFieldLiteGenerator:: +GenerateMembers(io::Printer* printer) const { + PrintExtraFieldInfo(variables_, printer); + if (SupportFieldPresence(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return $has_oneof_case_message$;\n" + "}\n"); + } + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$() {\n" + " if ($has_oneof_case_message$) {\n" + " return ($boxed_type$) $oneof_name$_;\n" + " }\n" + " return $default$;\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$($type$ value) {\n" + "$null_check$" + " $set_oneof_case_message$;\n" + " $oneof_name$_ = value;\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void clear$capitalized_name$() {\n" + " if ($has_oneof_case_message$) {\n" + " $clear_oneof_case_message$;\n" + " $oneof_name$_ = null;\n" + " }\n" + "}\n"); +} + + +void ImmutablePrimitiveOneofFieldLiteGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + if (SupportFieldPresence(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return instance.has$capitalized_name$();\n" + "}\n"); + } + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$() {\n" + " return instance.get$capitalized_name$();\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$($type$ value) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(value);\n" + " return this;\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder clear$capitalized_name$() {\n" + " copyOnWrite();\n" + " instance.clear$capitalized_name$();\n" + " return this;\n" + "}\n"); +} + +void ImmutablePrimitiveOneofFieldLiteGenerator:: +GenerateBuildingCode(io::Printer* printer) const { + // noop for primitives +} + +void ImmutablePrimitiveOneofFieldLiteGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, + "set$capitalized_name$(other.get$capitalized_name$());\n"); +} + +void ImmutablePrimitiveOneofFieldLiteGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "$set_oneof_case_message$;\n" + "$oneof_name$_ = input.read$capitalized_type$();\n"); +} + +void ImmutablePrimitiveOneofFieldLiteGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($has_oneof_case_message$) {\n" + " output.write$capitalized_type$(\n" + " $number$, ($type$)(($boxed_type$) $oneof_name$_));\n" + "}\n"); +} + +void ImmutablePrimitiveOneofFieldLiteGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($has_oneof_case_message$) {\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .compute$capitalized_type$Size(\n" + " $number$, ($type$)(($boxed_type$) $oneof_name$_));\n" + "}\n"); +} + +// =================================================================== + +RepeatedImmutablePrimitiveFieldLiteGenerator:: +RepeatedImmutablePrimitiveFieldLiteGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) + : descriptor_(descriptor), messageBitIndex_(messageBitIndex), + builderBitIndex_(builderBitIndex), context_(context), + name_resolver_(context->GetNameResolver()) { + SetPrimitiveVariables(descriptor, messageBitIndex, builderBitIndex, + context->GetFieldGeneratorInfo(descriptor), + name_resolver_, &variables_); +} + +RepeatedImmutablePrimitiveFieldLiteGenerator:: +~RepeatedImmutablePrimitiveFieldLiteGenerator() {} + +int RepeatedImmutablePrimitiveFieldLiteGenerator::GetNumBitsForMessage() const { + return 0; +} + +int RepeatedImmutablePrimitiveFieldLiteGenerator::GetNumBitsForBuilder() const { + return 0; +} + +void RepeatedImmutablePrimitiveFieldLiteGenerator:: +GenerateInterfaceMembers(io::Printer* printer) const { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$java.util.List<$boxed_type$> get$capitalized_name$List();\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$int get$capitalized_name$Count();\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$$type$ get$capitalized_name$(int index);\n"); +} + + +void RepeatedImmutablePrimitiveFieldLiteGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print(variables_, + "private $field_list_type$ $name$_;\n"); + PrintExtraFieldInfo(variables_, printer); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.util.List<$boxed_type$>\n" + " get$capitalized_name$List() {\n" + " return $name$_;\n" // note: unmodifiable list + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public int get$capitalized_name$Count() {\n" + " return $name$_.size();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$(int index) {\n" + " return $repeated_index_get$;\n" + "}\n"); + + if (descriptor_->options().packed() && + HasGeneratedMethods(descriptor_->containing_type())) { + printer->Print(variables_, + "private int $name$MemoizedSerializedSize = -1;\n"); + } + + printer->Print(variables_, + "private void ensure$capitalized_name$IsMutable() {\n" + " if (!$is_mutable$) {\n" + " $name$_ = $new_list$($name$_);\n" + " }\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$(\n" + " int index, $type$ value) {\n" + "$null_check$" + " ensure$capitalized_name$IsMutable();\n" + " $repeated_set$(index, value);\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void add$capitalized_name$($type$ value) {\n" + "$null_check$" + " ensure$capitalized_name$IsMutable();\n" + " $repeated_add$(value);\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void addAll$capitalized_name$(\n" + " java.lang.Iterable values) {\n" + " ensure$capitalized_name$IsMutable();\n" + " com.google.protobuf.AbstractMessageLite.addAll(\n" + " values, $name$_);\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void clear$capitalized_name$() {\n" + " $name$_ = $empty_list$;\n" + "}\n"); +} + +void RepeatedImmutablePrimitiveFieldLiteGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.util.List<$boxed_type$>\n" + " get$capitalized_name$List() {\n" + " return java.util.Collections.unmodifiableList(\n" + " instance.get$capitalized_name$List());\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public int get$capitalized_name$Count() {\n" + " return instance.get$capitalized_name$Count();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public $type$ get$capitalized_name$(int index) {\n" + " return instance.get$capitalized_name$(index);\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$(\n" + " int index, $type$ value) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(index, value);\n" + " return this;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder add$capitalized_name$($type$ value) {\n" + " copyOnWrite();\n" + " instance.add$capitalized_name$(value);\n" + " return this;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder addAll$capitalized_name$(\n" + " java.lang.Iterable values) {\n" + " copyOnWrite();\n" + " instance.addAll$capitalized_name$(values);\n" + " return this;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder clear$capitalized_name$() {\n" + " copyOnWrite();\n" + " instance.clear$capitalized_name$();\n" + " return this;\n" + "}\n"); +} + +void RepeatedImmutablePrimitiveFieldLiteGenerator:: +GenerateFieldBuilderInitializationCode(io::Printer* printer) const { + // noop for primitives +} + +void RepeatedImmutablePrimitiveFieldLiteGenerator:: +GenerateInitializationCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_ = $empty_list$;\n"); +} + +void RepeatedImmutablePrimitiveFieldLiteGenerator:: +GenerateBuilderClearCode(io::Printer* printer) const { + // noop for primitives +} + +void RepeatedImmutablePrimitiveFieldLiteGenerator:: +GenerateMergingCode(io::Printer* printer) const { + // The code below does two optimizations: + // 1. If the other list is empty, there's nothing to do. This ensures we + // don't allocate a new array if we already have an immutable one. + // 2. If the other list is non-empty and our current list is empty, we can + // reuse the other list which is guaranteed to be immutable. + printer->Print(variables_, + "if (!other.$name$_.isEmpty()) {\n" + " if ($name$_.isEmpty()) {\n" + " $name$_ = other.$name$_;\n" + " } else {\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.addAll(other.$name$_);\n" + " }\n" + " $on_changed$\n" + "}\n"); +} + +void RepeatedImmutablePrimitiveFieldLiteGenerator:: +GenerateBuildingCode(io::Printer* printer) const { + // noop for primitives +} + +void RepeatedImmutablePrimitiveFieldLiteGenerator:: +GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const { + printer->Print(variables_, + "$name$_.makeImmutable();\n"); +} + +void RepeatedImmutablePrimitiveFieldLiteGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (!$is_mutable$) {\n" + " $name$_ = $new_list$();\n" + "}\n" + "$repeated_add$(input.read$capitalized_type$());\n"); +} + +void RepeatedImmutablePrimitiveFieldLiteGenerator:: +GenerateParsingCodeFromPacked(io::Printer* printer) const { + printer->Print(variables_, + "int length = input.readRawVarint32();\n" + "int limit = input.pushLimit(length);\n" + "if (!$is_mutable$ && input.getBytesUntilLimit() > 0) {\n" + " $name$_ = $new_list$();\n" + "}\n" + "while (input.getBytesUntilLimit() > 0) {\n" + " $repeated_add$(input.read$capitalized_type$());\n" + "}\n" + "input.popLimit(limit);\n"); +} + +void RepeatedImmutablePrimitiveFieldLiteGenerator:: +GenerateParsingDoneCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($is_mutable$) {\n" + " $make_name_unmodifiable$;\n" + "}\n"); +} + +void RepeatedImmutablePrimitiveFieldLiteGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + if (descriptor_->options().packed()) { + // We invoke getSerializedSize in writeTo for messages that have packed + // fields in ImmutableMessageGenerator::GenerateMessageSerializationMethods. + // That makes it safe to rely on the memoized size here. + printer->Print(variables_, + "if (get$capitalized_name$List().size() > 0) {\n" + " output.writeRawVarint32($tag$);\n" + " output.writeRawVarint32($name$MemoizedSerializedSize);\n" + "}\n" + "for (int i = 0; i < $name$_.size(); i++) {\n" + " output.write$capitalized_type$NoTag($name$_.get(i));\n" + "}\n"); + } else { + printer->Print(variables_, + "for (int i = 0; i < $name$_.size(); i++) {\n" + " output.write$capitalized_type$($number$, $name$_.get(i));\n" + "}\n"); + } +} + +void RepeatedImmutablePrimitiveFieldLiteGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "{\n" + " int dataSize = 0;\n"); + printer->Indent(); + + if (FixedSize(GetType(descriptor_)) == -1) { + printer->Print(variables_, + "for (int i = 0; i < $name$_.size(); i++) {\n" + " dataSize += com.google.protobuf.CodedOutputStream\n" + " .compute$capitalized_type$SizeNoTag($name$_.get(i));\n" + "}\n"); + } else { + printer->Print(variables_, + "dataSize = $fixed_size$ * get$capitalized_name$List().size();\n"); + } + + printer->Print( + "size += dataSize;\n"); + + if (descriptor_->options().packed()) { + printer->Print(variables_, + "if (!get$capitalized_name$List().isEmpty()) {\n" + " size += $tag_size$;\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .computeInt32SizeNoTag(dataSize);\n" + "}\n"); + } else { + printer->Print(variables_, + "size += $tag_size$ * get$capitalized_name$List().size();\n"); + } + + // cache the data size for packed fields. + if (descriptor_->options().packed()) { + printer->Print(variables_, + "$name$MemoizedSerializedSize = dataSize;\n"); + } + + printer->Outdent(); + printer->Print("}\n"); +} + +void RepeatedImmutablePrimitiveFieldLiteGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + printer->Print(variables_, + "result = result && get$capitalized_name$List()\n" + " .equals(other.get$capitalized_name$List());\n"); +} + +void RepeatedImmutablePrimitiveFieldLiteGenerator:: +GenerateHashCode(io::Printer* printer) const { + printer->Print(variables_, + "if (get$capitalized_name$Count() > 0) {\n" + " hash = (37 * hash) + $constant_name$;\n" + " hash = (53 * hash) + get$capitalized_name$List().hashCode();\n" + "}\n"); +} + +string RepeatedImmutablePrimitiveFieldLiteGenerator::GetBoxedType() const { + return BoxedPrimitiveTypeName(GetJavaType(descriptor_)); +} + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_primitive_field_lite.h b/src/google/protobuf/compiler/java/java_primitive_field_lite.h new file mode 100644 index 00000000..ad603c2a --- /dev/null +++ b/src/google/protobuf/compiler/java/java_primitive_field_lite.h @@ -0,0 +1,163 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_PRIMITIVE_FIELD_LITE_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_PRIMITIVE_FIELD_LITE_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { + namespace compiler { + namespace java { + class Context; // context.h + class ClassNameResolver; // name_resolver.h + } + } +} + +namespace protobuf { +namespace compiler { +namespace java { + +class ImmutablePrimitiveFieldLiteGenerator + : public ImmutableFieldLiteGenerator { + public: + explicit ImmutablePrimitiveFieldLiteGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~ImmutablePrimitiveFieldLiteGenerator(); + + // implements ImmutableFieldLiteGenerator ------------------------------------ + int GetNumBitsForMessage() const; + int GetNumBitsForBuilder() const; + void GenerateInterfaceMembers(io::Printer* printer) const; + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateInitializationCode(io::Printer* printer) const; + void GenerateBuilderClearCode(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateBuildingCode(io::Printer* printer) const; + void GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateParsingDoneCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCode(io::Printer* printer) const; + + string GetBoxedType() const; + + protected: + const FieldDescriptor* descriptor_; + map variables_; + const int messageBitIndex_; + const int builderBitIndex_; + Context* context_; + ClassNameResolver* name_resolver_; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutablePrimitiveFieldLiteGenerator); +}; + +class ImmutablePrimitiveOneofFieldLiteGenerator + : public ImmutablePrimitiveFieldLiteGenerator { + public: + ImmutablePrimitiveOneofFieldLiteGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~ImmutablePrimitiveOneofFieldLiteGenerator(); + + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateBuildingCode(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutablePrimitiveOneofFieldLiteGenerator); +}; + +class RepeatedImmutablePrimitiveFieldLiteGenerator + : public ImmutableFieldLiteGenerator { + public: + explicit RepeatedImmutablePrimitiveFieldLiteGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + virtual ~RepeatedImmutablePrimitiveFieldLiteGenerator(); + + // implements ImmutableFieldLiteGenerator ------------------------------------ + int GetNumBitsForMessage() const; + int GetNumBitsForBuilder() const; + void GenerateInterfaceMembers(io::Printer* printer) const; + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateInitializationCode(io::Printer* printer) const; + void GenerateBuilderClearCode(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateBuildingCode(io::Printer* printer) const; + void GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateParsingCodeFromPacked(io::Printer* printer) const; + void GenerateParsingDoneCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCode(io::Printer* printer) const; + + string GetBoxedType() const; + + private: + const FieldDescriptor* descriptor_; + map variables_; + const int messageBitIndex_; + const int builderBitIndex_; + Context* context_; + ClassNameResolver* name_resolver_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedImmutablePrimitiveFieldLiteGenerator); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_PRIMITIVE_FIELD_LITE_H__ diff --git a/src/google/protobuf/compiler/java/java_string_field.cc b/src/google/protobuf/compiler/java/java_string_field.cc index 8aa5699c..68e863cc 100644 --- a/src/google/protobuf/compiler/java/java_string_field.cc +++ b/src/google/protobuf/compiler/java/java_string_field.cc @@ -208,7 +208,7 @@ GenerateInterfaceMembers(io::Printer* printer) const { void ImmutableStringFieldGenerator:: GenerateMembers(io::Printer* printer) const { printer->Print(variables_, - "private java.lang.Object $name$_;\n"); + "private volatile java.lang.Object $name$_;\n"); PrintExtraFieldInfo(variables_, printer); if (SupportFieldPresence(descriptor_->file())) { diff --git a/src/google/protobuf/compiler/java/java_string_field_lite.cc b/src/google/protobuf/compiler/java/java_string_field_lite.cc new file mode 100644 index 00000000..51bb245c --- /dev/null +++ b/src/google/protobuf/compiler/java/java_string_field_lite.cc @@ -0,0 +1,1013 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: kenton@google.com (Kenton Varda) +// Author: jonp@google.com (Jon Perlow) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +using internal::WireFormat; +using internal::WireFormatLite; + +namespace { + +void SetPrimitiveVariables(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + const FieldGeneratorInfo* info, + ClassNameResolver* name_resolver, + map* variables) { + SetCommonFieldVariables(descriptor, info, variables); + + (*variables)["empty_list"] = "emptyLazyStringArrayList()"; + + (*variables)["default"] = ImmutableDefaultValue(descriptor, name_resolver); + (*variables)["default_init"] = + "= " + ImmutableDefaultValue(descriptor, name_resolver); + (*variables)["capitalized_type"] = "String"; + (*variables)["tag"] = SimpleItoa(WireFormat::MakeTag(descriptor)); + (*variables)["tag_size"] = SimpleItoa( + WireFormat::TagSize(descriptor->number(), GetType(descriptor))); + (*variables)["null_check"] = + " if (value == null) {\n" + " throw new NullPointerException();\n" + " }\n"; + + // TODO(birdo): Add @deprecated javadoc when generating javadoc is supported + // by the proto compiler + (*variables)["deprecation"] = descriptor->options().deprecated() + ? "@java.lang.Deprecated " : ""; + (*variables)["on_changed"] = + HasDescriptorMethods(descriptor->containing_type()) ? "onChanged();" : ""; + + if (SupportFieldPresence(descriptor->file())) { + // For singular messages and builders, one bit is used for the hasField bit. + (*variables)["get_has_field_bit_message"] = GenerateGetBit(messageBitIndex); + + // Note that these have a trailing ";". + (*variables)["set_has_field_bit_message"] = + GenerateSetBit(messageBitIndex) + ";"; + (*variables)["clear_has_field_bit_message"] = + GenerateClearBit(messageBitIndex) + ";"; + + (*variables)["is_field_present_message"] = GenerateGetBit(messageBitIndex); + } else { + (*variables)["set_has_field_bit_message"] = ""; + (*variables)["clear_has_field_bit_message"] = ""; + + (*variables)["is_field_present_message"] = + "!get" + (*variables)["capitalized_name"] + "Bytes().isEmpty()"; + } + + // For repeated builders, the underlying list tracks mutability state. + (*variables)["is_mutable"] = (*variables)["name"] + "_.isModifiable()"; + + (*variables)["get_has_field_bit_from_local"] = + GenerateGetBitFromLocal(builderBitIndex); + (*variables)["set_has_field_bit_to_local"] = + GenerateSetBitToLocal(messageBitIndex); +} + +bool CheckUtf8(const FieldDescriptor* descriptor) { + return descriptor->file()->options().java_string_check_utf8(); +} + +} // namespace + +// =================================================================== + +ImmutableStringFieldLiteGenerator:: +ImmutableStringFieldLiteGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) + : descriptor_(descriptor), messageBitIndex_(messageBitIndex), + builderBitIndex_(builderBitIndex), context_(context), + name_resolver_(context->GetNameResolver()) { + SetPrimitiveVariables(descriptor, messageBitIndex, builderBitIndex, + context->GetFieldGeneratorInfo(descriptor), + name_resolver_, &variables_); +} + +ImmutableStringFieldLiteGenerator::~ImmutableStringFieldLiteGenerator() {} + +int ImmutableStringFieldLiteGenerator::GetNumBitsForMessage() const { + return 1; +} + +int ImmutableStringFieldLiteGenerator::GetNumBitsForBuilder() const { + return 0; +} + +// A note about how strings are handled. This code used to just store a String +// in the Message. This had two issues: +// +// 1. It wouldn't roundtrip byte arrays that were not vaid UTF-8 encoded +// strings, but rather fields that were raw bytes incorrectly marked +// as strings in the proto file. This is common because in the proto1 +// syntax, string was the way to indicate bytes and C++ engineers can +// easily make this mistake without affecting the C++ API. By converting to +// strings immediately, some java code might corrupt these byte arrays as +// it passes through a java server even if the field was never accessed by +// application code. +// +// 2. There's a performance hit to converting between bytes and strings and +// it many cases, the field is never even read by the application code. This +// avoids unnecessary conversions in the common use cases. +// +// So now, the field for String is maintained as an Object reference which can +// either store a String or a ByteString. The code uses an instanceof check +// to see which one it has and converts to the other one if needed. It remembers +// the last value requested (in a thread safe manner) as this is most likely +// the one needed next. The thread safety is such that if two threads both +// convert the field because the changes made by each thread were not visible to +// the other, they may cause a conversion to happen more times than would +// otherwise be necessary. This was deemed better than adding synchronization +// overhead. It will not cause any corruption issues or affect the behavior of +// the API. The instanceof check is also highly optimized in the JVM and we +// decided it was better to reduce the memory overhead by not having two +// separate fields but rather use dynamic type checking. +// +// For single fields, the logic for this is done inside the generated code. For +// repeated fields, the logic is done in LazyStringArrayList and +// UnmodifiableLazyStringList. +void ImmutableStringFieldLiteGenerator:: +GenerateInterfaceMembers(io::Printer* printer) const { + if (SupportFieldPresence(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$boolean has$capitalized_name$();\n"); + } + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$java.lang.String get$capitalized_name$();\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$com.google.protobuf.ByteString\n" + " get$capitalized_name$Bytes();\n"); +} + +void ImmutableStringFieldLiteGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print(variables_, + "private java.lang.Object $name$_;\n"); + PrintExtraFieldInfo(variables_, printer); + + if (SupportFieldPresence(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return $get_has_field_bit_message$;\n" + "}\n"); + } + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.lang.String get$capitalized_name$() {\n" + " java.lang.Object ref = $name$_;\n" + " if (ref instanceof java.lang.String) {\n" + " return (java.lang.String) ref;\n" + " } else {\n" + " com.google.protobuf.ByteString bs = \n" + " (com.google.protobuf.ByteString) ref;\n" + " java.lang.String s = bs.toStringUtf8();\n"); + if (CheckUtf8(descriptor_)) { + printer->Print(variables_, + " $name$_ = s;\n"); + } else { + printer->Print(variables_, + " if (bs.isValidUtf8()) {\n" + " $name$_ = s;\n" + " }\n"); + } + printer->Print(variables_, + " return s;\n" + " }\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public com.google.protobuf.ByteString\n" + " get$capitalized_name$Bytes() {\n" + " java.lang.Object ref = $name$_;\n" + " if (ref instanceof java.lang.String) {\n" + " com.google.protobuf.ByteString b = \n" + " com.google.protobuf.ByteString.copyFromUtf8(\n" + " (java.lang.String) ref);\n" + " $name$_ = b;\n" + " return b;\n" + " } else {\n" + " return (com.google.protobuf.ByteString) ref;\n" + " }\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$(\n" + " java.lang.String value) {\n" + "$null_check$" + " $set_has_field_bit_message$\n" + " $name$_ = value;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void clear$capitalized_name$() {\n" + " $clear_has_field_bit_message$\n" + // The default value is not a simple literal so we want to avoid executing + // it multiple times. Instead, get the default out of the default instance. + " $name$_ = getDefaultInstance().get$capitalized_name$();\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$Bytes(\n" + " com.google.protobuf.ByteString value) {\n" + "$null_check$"); + if (CheckUtf8(descriptor_)) { + printer->Print(variables_, + " checkByteStringIsUtf8(value);\n"); + } + printer->Print(variables_, + " $set_has_field_bit_message$\n" + " $name$_ = value;\n" + "}\n"); +} + +void ImmutableStringFieldLiteGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + if (SupportFieldPresence(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return instance.has$capitalized_name$();\n" + "}\n"); + } + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.lang.String get$capitalized_name$() {\n" + " return instance.get$capitalized_name$();\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public com.google.protobuf.ByteString\n" + " get$capitalized_name$Bytes() {\n" + " return instance.get$capitalized_name$Bytes();\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$(\n" + " java.lang.String value) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(value);\n" + " return this;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder clear$capitalized_name$() {\n" + " copyOnWrite();\n" + " instance.clear$capitalized_name$();\n" + " return this;\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$Bytes(\n" + " com.google.protobuf.ByteString value) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$Bytes(value);\n" + " return this;\n" + "}\n"); +} + +void ImmutableStringFieldLiteGenerator:: +GenerateFieldBuilderInitializationCode(io::Printer* printer) const { + // noop for strings +} + +void ImmutableStringFieldLiteGenerator:: +GenerateInitializationCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_ = $default$;\n"); +} + +void ImmutableStringFieldLiteGenerator:: +GenerateMergingCode(io::Printer* printer) const { + if (SupportFieldPresence(descriptor_->file())) { + // Allow a slight breach of abstraction here in order to avoid forcing + // all string fields to Strings when copying fields from a Message. + printer->Print(variables_, + "if (other.has$capitalized_name$()) {\n" + " $set_has_field_bit_message$\n" + " $name$_ = other.$name$_;\n" + " $on_changed$\n" + "}\n"); + } else { + printer->Print(variables_, + "if (!other.get$capitalized_name$().isEmpty()) {\n" + " $name$_ = other.$name$_;\n" + " $on_changed$\n" + "}\n"); + } +} + +void ImmutableStringFieldLiteGenerator:: +GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const { + // noop for scalars +} + +void ImmutableStringFieldLiteGenerator:: +GenerateParsingCode(io::Printer* printer) const { + if (CheckUtf8(descriptor_)) { + printer->Print(variables_, + "String s = input.readStringRequireUtf8();\n" + "$set_has_field_bit_message$\n" + "$name$_ = s;\n"); + } else if (!HasDescriptorMethods(descriptor_->file())) { + // Lite runtime should attempt to reduce allocations by attempting to + // construct the string directly from the input stream buffer. This avoids + // spurious intermediary ByteString allocations, cutting overall allocations + // in half. + printer->Print(variables_, + "String s = input.readString();\n" + "$set_has_field_bit_message$\n" + "$name$_ = s;\n"); + } else { + printer->Print(variables_, + "com.google.protobuf.ByteString bs = input.readBytes();\n" + "$set_has_field_bit_message$\n" + "$name$_ = bs;\n"); + } +} + +void ImmutableStringFieldLiteGenerator:: +GenerateParsingDoneCode(io::Printer* printer) const { + // noop for strings +} + +void ImmutableStringFieldLiteGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($is_field_present_message$) {\n" + " output.writeBytes($number$, get$capitalized_name$Bytes());\n" + "}\n"); +} + +void ImmutableStringFieldLiteGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($is_field_present_message$) {\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .computeBytesSize($number$, get$capitalized_name$Bytes());\n" + "}\n"); +} + +void ImmutableStringFieldLiteGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + printer->Print(variables_, + "result = result && get$capitalized_name$()\n" + " .equals(other.get$capitalized_name$());\n"); +} + +void ImmutableStringFieldLiteGenerator:: +GenerateHashCode(io::Printer* printer) const { + printer->Print(variables_, + "hash = (37 * hash) + $constant_name$;\n"); + printer->Print(variables_, + "hash = (53 * hash) + get$capitalized_name$().hashCode();\n"); +} + +string ImmutableStringFieldLiteGenerator::GetBoxedType() const { + return "java.lang.String"; +} + +// =================================================================== + +ImmutableStringOneofFieldLiteGenerator:: +ImmutableStringOneofFieldLiteGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) + : ImmutableStringFieldLiteGenerator( + descriptor, messageBitIndex, builderBitIndex, context) { + const OneofGeneratorInfo* info = + context->GetOneofGeneratorInfo(descriptor->containing_oneof()); + SetCommonOneofVariables(descriptor, info, &variables_); +} + +ImmutableStringOneofFieldLiteGenerator:: +~ImmutableStringOneofFieldLiteGenerator() {} + +void ImmutableStringOneofFieldLiteGenerator:: +GenerateMembers(io::Printer* printer) const { + PrintExtraFieldInfo(variables_, printer); + + if (SupportFieldPresence(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return $has_oneof_case_message$;\n" + "}\n"); + } + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.lang.String get$capitalized_name$() {\n" + " java.lang.Object ref $default_init$;\n" + " if ($has_oneof_case_message$) {\n" + " ref = $oneof_name$_;\n" + " }\n" + " if (ref instanceof java.lang.String) {\n" + " return (java.lang.String) ref;\n" + " } else {\n" + " com.google.protobuf.ByteString bs = \n" + " (com.google.protobuf.ByteString) ref;\n" + " java.lang.String s = bs.toStringUtf8();\n"); + if (CheckUtf8(descriptor_)) { + printer->Print(variables_, + " if ($has_oneof_case_message$) {\n" + " $oneof_name$_ = s;\n" + " }\n"); + } else { + printer->Print(variables_, + " if (bs.isValidUtf8() && ($has_oneof_case_message$)) {\n" + " $oneof_name$_ = s;\n" + " }\n"); + } + printer->Print(variables_, + " return s;\n" + " }\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + + printer->Print(variables_, + "$deprecation$public com.google.protobuf.ByteString\n" + " get$capitalized_name$Bytes() {\n" + " java.lang.Object ref $default_init$;\n" + " if ($has_oneof_case_message$) {\n" + " ref = $oneof_name$_;\n" + " }\n" + " if (ref instanceof java.lang.String) {\n" + " com.google.protobuf.ByteString b = \n" + " com.google.protobuf.ByteString.copyFromUtf8(\n" + " (java.lang.String) ref);\n" + " if ($has_oneof_case_message$) {\n" + " $oneof_name$_ = b;\n" + " }\n" + " return b;\n" + " } else {\n" + " return (com.google.protobuf.ByteString) ref;\n" + " }\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$(\n" + " java.lang.String value) {\n" + "$null_check$" + " $set_oneof_case_message$;\n" + " $oneof_name$_ = value;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void clear$capitalized_name$() {\n" + " if ($has_oneof_case_message$) {\n" + " $clear_oneof_case_message$;\n" + " $oneof_name$_ = null;\n" + " }\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$Bytes(\n" + " com.google.protobuf.ByteString value) {\n" + "$null_check$"); + if (CheckUtf8(descriptor_)) { + printer->Print(variables_, + " checkByteStringIsUtf8(value);\n"); + } + printer->Print(variables_, + " $set_oneof_case_message$;\n" + " $oneof_name$_ = value;\n" + "}\n"); +} + +void ImmutableStringOneofFieldLiteGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + if (SupportFieldPresence(descriptor_->file())) { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public boolean has$capitalized_name$() {\n" + " return instance.has$capitalized_name$();\n" + "}\n"); + } + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.lang.String get$capitalized_name$() {\n" + " return instance.get$capitalized_name$();\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public com.google.protobuf.ByteString\n" + " get$capitalized_name$Bytes() {\n" + " return instance.get$capitalized_name$Bytes();\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$(\n" + " java.lang.String value) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(value);\n" + " return this;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder clear$capitalized_name$() {\n" + " copyOnWrite();\n" + " instance.clear$capitalized_name$();\n" + " return this;\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$Bytes(\n" + " com.google.protobuf.ByteString value) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$Bytes(value);\n" + " return this;\n" + "}\n"); +} + +void ImmutableStringOneofFieldLiteGenerator:: +GenerateMergingCode(io::Printer* printer) const { + // Allow a slight breach of abstraction here in order to avoid forcing + // all string fields to Strings when copying fields from a Message. + printer->Print(variables_, + "$set_oneof_case_message$;\n" + "$oneof_name$_ = other.$oneof_name$_;\n" + "$on_changed$\n"); +} + +void ImmutableStringOneofFieldLiteGenerator:: +GenerateParsingCode(io::Printer* printer) const { + if (CheckUtf8(descriptor_)) { + printer->Print(variables_, + "String s = input.readStringRequireUtf8();\n" + "$set_oneof_case_message$;\n" + "$oneof_name$_ = s;\n"); + } else if (!HasDescriptorMethods(descriptor_->file())) { + // Lite runtime should attempt to reduce allocations by attempting to + // construct the string directly from the input stream buffer. This avoids + // spurious intermediary ByteString allocations, cutting overall allocations + // in half. + printer->Print(variables_, + "String s = input.readString();\n" + "$set_oneof_case_message$;\n" + "$oneof_name$_ = s;\n"); + } else { + printer->Print(variables_, + "com.google.protobuf.ByteString bs = input.readBytes();\n" + "$set_oneof_case_message$;\n" + "$oneof_name$_ = bs;\n"); + } +} + +void ImmutableStringOneofFieldLiteGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($has_oneof_case_message$) {\n" + " output.writeBytes($number$, get$capitalized_name$Bytes());\n" + "}\n"); +} + +void ImmutableStringOneofFieldLiteGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($has_oneof_case_message$) {\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .computeBytesSize($number$, get$capitalized_name$Bytes());\n" + "}\n"); +} + +// =================================================================== + +RepeatedImmutableStringFieldLiteGenerator:: +RepeatedImmutableStringFieldLiteGenerator(const FieldDescriptor* descriptor, + int messageBitIndex, + int builderBitIndex, + Context* context) + : descriptor_(descriptor), messageBitIndex_(messageBitIndex), + builderBitIndex_(builderBitIndex), context_(context), + name_resolver_(context->GetNameResolver()) { + SetPrimitiveVariables(descriptor, messageBitIndex, builderBitIndex, + context->GetFieldGeneratorInfo(descriptor), + name_resolver_, &variables_); +} + +RepeatedImmutableStringFieldLiteGenerator:: +~RepeatedImmutableStringFieldLiteGenerator() {} + +int RepeatedImmutableStringFieldLiteGenerator::GetNumBitsForMessage() const { + return 0; +} + +int RepeatedImmutableStringFieldLiteGenerator::GetNumBitsForBuilder() const { + return 0; +} + +void RepeatedImmutableStringFieldLiteGenerator:: +GenerateInterfaceMembers(io::Printer* printer) const { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$com.google.protobuf.ProtocolStringList\n" + " get$capitalized_name$List();\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$int get$capitalized_name$Count();\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$java.lang.String get$capitalized_name$(int index);\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$com.google.protobuf.ByteString\n" + " get$capitalized_name$Bytes(int index);\n"); +} + + +void RepeatedImmutableStringFieldLiteGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print(variables_, + "private com.google.protobuf.LazyStringArrayList $name$_;\n"); + PrintExtraFieldInfo(variables_, printer); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public com.google.protobuf.ProtocolStringList\n" + " get$capitalized_name$List() {\n" + " return $name$_;\n" // note: unmodifiable list + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public int get$capitalized_name$Count() {\n" + " return $name$_.size();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.lang.String get$capitalized_name$(int index) {\n" + " return $name$_.get(index);\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public com.google.protobuf.ByteString\n" + " get$capitalized_name$Bytes(int index) {\n" + " return $name$_.getByteString(index);\n" + "}\n"); + + if (descriptor_->options().packed() && + HasGeneratedMethods(descriptor_->containing_type())) { + printer->Print(variables_, + "private int $name$MemoizedSerializedSize = -1;\n"); + } + + printer->Print(variables_, + "private void ensure$capitalized_name$IsMutable() {\n" + " if (!$is_mutable$) {\n" + " $name$_ = new com.google.protobuf.LazyStringArrayList($name$_);\n" + " }\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void set$capitalized_name$(\n" + " int index, java.lang.String value) {\n" + "$null_check$" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.set(index, value);\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void add$capitalized_name$(\n" + " java.lang.String value) {\n" + "$null_check$" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.add(value);\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void addAll$capitalized_name$(\n" + " java.lang.Iterable values) {\n" + " ensure$capitalized_name$IsMutable();\n" + " com.google.protobuf.AbstractMessageLite.addAll(\n" + " values, $name$_);\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void clear$capitalized_name$() {\n" + " $name$_ = $empty_list$;\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "private void add$capitalized_name$Bytes(\n" + " com.google.protobuf.ByteString value) {\n" + "$null_check$"); + if (CheckUtf8(descriptor_)) { + printer->Print(variables_, + " checkByteStringIsUtf8(value);\n"); + } + printer->Print(variables_, + " ensure$capitalized_name$IsMutable();\n" + " $name$_.add(value);\n" + "}\n"); +} + +void RepeatedImmutableStringFieldLiteGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public com.google.protobuf.ProtocolStringList\n" + " get$capitalized_name$List() {\n" + " return ((com.google.protobuf.LazyStringList)\n" + " instance.get$capitalized_name$List()).getUnmodifiableView();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public int get$capitalized_name$Count() {\n" + " return instance.get$capitalized_name$Count();\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public java.lang.String get$capitalized_name$(int index) {\n" + " return instance.get$capitalized_name$(index);\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public com.google.protobuf.ByteString\n" + " get$capitalized_name$Bytes(int index) {\n" + " return instance.get$capitalized_name$Bytes(index);\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder set$capitalized_name$(\n" + " int index, java.lang.String value) {\n" + " copyOnWrite();\n" + " instance.set$capitalized_name$(index, value);\n" + " return this;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder add$capitalized_name$(\n" + " java.lang.String value) {\n" + " copyOnWrite();\n" + " instance.add$capitalized_name$(value);\n" + " return this;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder addAll$capitalized_name$(\n" + " java.lang.Iterable values) {\n" + " copyOnWrite();\n" + " instance.addAll$capitalized_name$(values);\n" + " return this;\n" + "}\n"); + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder clear$capitalized_name$() {\n" + " copyOnWrite();\n" + " instance.clear$capitalized_name$();\n" + " return this;\n" + "}\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$deprecation$public Builder add$capitalized_name$Bytes(\n" + " com.google.protobuf.ByteString value) {\n" + " copyOnWrite();\n" + " instance.add$capitalized_name$Bytes(value);\n" + " return this;\n" + "}\n"); +} + +void RepeatedImmutableStringFieldLiteGenerator:: +GenerateFieldBuilderInitializationCode(io::Printer* printer) const { + // noop for strings +} + +void RepeatedImmutableStringFieldLiteGenerator:: +GenerateInitializationCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_ = $empty_list$;\n"); +} + +void RepeatedImmutableStringFieldLiteGenerator:: +GenerateMergingCode(io::Printer* printer) const { + // The code below does two optimizations: + // 1. If the other list is empty, there's nothing to do. This ensures we + // don't allocate a new array if we already have an immutable one. + // 2. If the other list is non-empty and our current list is empty, we can + // reuse the other list which is guaranteed to be immutable. + printer->Print(variables_, + "if (!other.$name$_.isEmpty()) {\n" + " if ($name$_.isEmpty()) {\n" + " $name$_ = other.$name$_;\n" + " } else {\n" + " ensure$capitalized_name$IsMutable();\n" + " $name$_.addAll(other.$name$_);\n" + " }\n" + " $on_changed$\n" + "}\n"); +} + +void RepeatedImmutableStringFieldLiteGenerator:: +GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const { + printer->Print(variables_, + "$name$_.makeImmutable();\n"); +} + +void RepeatedImmutableStringFieldLiteGenerator:: +GenerateParsingCode(io::Printer* printer) const { + if (CheckUtf8(descriptor_)) { + printer->Print(variables_, + "String s = input.readStringRequireUtf8();\n"); + } else if (!HasDescriptorMethods(descriptor_->file())) { + // Lite runtime should attempt to reduce allocations by attempting to + // construct the string directly from the input stream buffer. This avoids + // spurious intermediary ByteString allocations, cutting overall allocations + // in half. + printer->Print(variables_, + "String s = input.readString();\n"); + } else { + printer->Print(variables_, + "com.google.protobuf.ByteString bs = input.readBytes();\n"); + } + printer->Print(variables_, + "if (!$is_mutable$) {\n" + " $name$_ = new com.google.protobuf.LazyStringArrayList();\n" + "}\n"); + if (CheckUtf8(descriptor_) || !HasDescriptorMethods(descriptor_->file())) { + printer->Print(variables_, + "$name$_.add(s);\n"); + } else { + printer->Print(variables_, + "$name$_.add(bs);\n"); + } +} + +void RepeatedImmutableStringFieldLiteGenerator:: +GenerateParsingCodeFromPacked(io::Printer* printer) const { + printer->Print(variables_, + "int length = input.readRawVarint32();\n" + "int limit = input.pushLimit(length);\n" + "if (!$is_mutable$ && input.getBytesUntilLimit() > 0) {\n" + " $name$_ = new com.google.protobuf.LazyStringArrayList();\n" + "}\n" + "while (input.getBytesUntilLimit() > 0) {\n"); + if (CheckUtf8(descriptor_)) { + printer->Print(variables_, + " String s = input.readStringRequireUtf8();\n"); + } else { + printer->Print(variables_, + " String s = input.readString();\n"); + } + printer->Print(variables_, + " $name$.add(s);\n"); + printer->Print(variables_, + "}\n" + "input.popLimit(limit);\n"); +} + +void RepeatedImmutableStringFieldLiteGenerator:: +GenerateParsingDoneCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($is_mutable$) {\n" + " $name$_.makeImmutable();\n" + "}\n"); +} + +void RepeatedImmutableStringFieldLiteGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + if (descriptor_->options().packed()) { + printer->Print(variables_, + "if (get$capitalized_name$List().size() > 0) {\n" + " output.writeRawVarint32($tag$);\n" + " output.writeRawVarint32($name$MemoizedSerializedSize);\n" + "}\n" + "for (int i = 0; i < $name$_.size(); i++) {\n" + " output.write$capitalized_type$NoTag($name$_.get(i));\n" + "}\n"); + } else { + printer->Print(variables_, + "for (int i = 0; i < $name$_.size(); i++) {\n" + " output.writeBytes($number$, $name$_.getByteString(i));\n" + "}\n"); + } +} + +void RepeatedImmutableStringFieldLiteGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "{\n" + " int dataSize = 0;\n"); + printer->Indent(); + + printer->Print(variables_, + "for (int i = 0; i < $name$_.size(); i++) {\n" + " dataSize += com.google.protobuf.CodedOutputStream\n" + " .computeBytesSizeNoTag($name$_.getByteString(i));\n" + "}\n"); + + printer->Print( + "size += dataSize;\n"); + + if (descriptor_->options().packed()) { + printer->Print(variables_, + "if (!get$capitalized_name$List().isEmpty()) {\n" + " size += $tag_size$;\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .computeInt32SizeNoTag(dataSize);\n" + "}\n"); + } else { + printer->Print(variables_, + "size += $tag_size$ * get$capitalized_name$List().size();\n"); + } + + // cache the data size for packed fields. + if (descriptor_->options().packed()) { + printer->Print(variables_, + "$name$MemoizedSerializedSize = dataSize;\n"); + } + + printer->Outdent(); + printer->Print("}\n"); +} + +void RepeatedImmutableStringFieldLiteGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + printer->Print(variables_, + "result = result && get$capitalized_name$List()\n" + " .equals(other.get$capitalized_name$List());\n"); +} + +void RepeatedImmutableStringFieldLiteGenerator:: +GenerateHashCode(io::Printer* printer) const { + printer->Print(variables_, + "if (get$capitalized_name$Count() > 0) {\n" + " hash = (37 * hash) + $constant_name$;\n" + " hash = (53 * hash) + get$capitalized_name$List().hashCode();\n" + "}\n"); +} + +string RepeatedImmutableStringFieldLiteGenerator::GetBoxedType() const { + return "String"; +} + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_string_field_lite.h b/src/google/protobuf/compiler/java/java_string_field_lite.h new file mode 100644 index 00000000..9d93b307 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_string_field_lite.h @@ -0,0 +1,158 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: kenton@google.com (Kenton Varda) +// Author: jonp@google.com (Jon Perlow) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_STRING_FIELD_LITE_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_STRING_FIELD_LITE_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { + namespace compiler { + namespace java { + class Context; // context.h + class ClassNameResolver; // name_resolver.h + } + } +} + +namespace protobuf { +namespace compiler { +namespace java { + +class ImmutableStringFieldLiteGenerator : public ImmutableFieldLiteGenerator { + public: + explicit ImmutableStringFieldLiteGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~ImmutableStringFieldLiteGenerator(); + + // implements ImmutableFieldLiteGenerator ------------------------------------ + int GetNumBitsForMessage() const; + int GetNumBitsForBuilder() const; + void GenerateInterfaceMembers(io::Printer* printer) const; + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateInitializationCode(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateParsingDoneCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCode(io::Printer* printer) const; + + string GetBoxedType() const; + + protected: + const FieldDescriptor* descriptor_; + map variables_; + const int messageBitIndex_; + const int builderBitIndex_; + Context* context_; + ClassNameResolver* name_resolver_; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableStringFieldLiteGenerator); +}; + +class ImmutableStringOneofFieldLiteGenerator + : public ImmutableStringFieldLiteGenerator { + public: + ImmutableStringOneofFieldLiteGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~ImmutableStringOneofFieldLiteGenerator(); + + private: + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableStringOneofFieldLiteGenerator); +}; + +class RepeatedImmutableStringFieldLiteGenerator + : public ImmutableFieldLiteGenerator { + public: + explicit RepeatedImmutableStringFieldLiteGenerator( + const FieldDescriptor* descriptor, int messageBitIndex, + int builderBitIndex, Context* context); + ~RepeatedImmutableStringFieldLiteGenerator(); + + // implements ImmutableFieldLiteGenerator ------------------------------------ + int GetNumBitsForMessage() const; + int GetNumBitsForBuilder() const; + void GenerateInterfaceMembers(io::Printer* printer) const; + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateInitializationCode(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateDynamicMethodMakeImmutableCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateParsingCodeFromPacked(io::Printer* printer) const; + void GenerateParsingDoneCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCode(io::Printer* printer) const; + + string GetBoxedType() const; + + private: + const FieldDescriptor* descriptor_; + map variables_; + const int messageBitIndex_; + const int builderBitIndex_; + Context* context_; + ClassNameResolver* name_resolver_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedImmutableStringFieldLiteGenerator); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_STRING_FIELD_LITE_H__ diff --git a/src/google/protobuf/compiler/parser.cc b/src/google/protobuf/compiler/parser.cc index c50cdf54..a2eeee2d 100644 --- a/src/google/protobuf/compiler/parser.cc +++ b/src/google/protobuf/compiler/parser.cc @@ -686,6 +686,8 @@ bool Parser::ParseMessageStatement(DescriptorProto* message, LocationRecorder location(message_location, DescriptorProto::kExtensionRangeFieldNumber); return ParseExtensions(message, location, containing_file); + } else if (LookingAt("reserved")) { + return ParseReserved(message, message_location); } else if (LookingAt("extend")) { LocationRecorder location(message_location, DescriptorProto::kExtensionFieldNumber); @@ -733,6 +735,13 @@ bool Parser::ParseMessageField(FieldDescriptorProto* field, FieldDescriptorProto::Label label; if (ParseLabel(&label, containing_file)) { field->set_label(label); + if (label == FieldDescriptorProto::LABEL_OPTIONAL && + syntax_identifier_ == "proto3") { + AddError( + "Explicit 'optional' labels are disallowed in the Proto3 syntax. " + "To define 'optional' fields in Proto3, simply remove the " + "'optional' label, as fields are 'optional' by default."); + } } } @@ -1353,6 +1362,77 @@ bool Parser::ParseExtensions(DescriptorProto* message, return true; } +// This is similar to extension range parsing, except that "max" is not +// supported, and accepts field name literals. +bool Parser::ParseReserved(DescriptorProto* message, + const LocationRecorder& message_location) { + // Parse the declaration. + DO(Consume("reserved")); + if (LookingAtType(io::Tokenizer::TYPE_STRING)) { + LocationRecorder location(message_location, + DescriptorProto::kReservedNameFieldNumber); + return ParseReservedNames(message, location); + } else { + LocationRecorder location(message_location, + DescriptorProto::kReservedRangeFieldNumber); + return ParseReservedNumbers(message, location); + } +} + + +bool Parser::ParseReservedNames(DescriptorProto* message, + const LocationRecorder& parent_location) { + do { + LocationRecorder location(parent_location, message->reserved_name_size()); + DO(ConsumeString(message->add_reserved_name(), "Expected field name.")); + } while (TryConsume(",")); + DO(ConsumeEndOfDeclaration(";", &parent_location)); + return true; +} + +bool Parser::ParseReservedNumbers(DescriptorProto* message, + const LocationRecorder& parent_location) { + bool first = true; + do { + LocationRecorder location(parent_location, message->reserved_range_size()); + + DescriptorProto::ReservedRange* range = message->add_reserved_range(); + int start, end; + io::Tokenizer::Token start_token; + { + LocationRecorder start_location( + location, DescriptorProto::ReservedRange::kStartFieldNumber); + start_token = input_->current(); + DO(ConsumeInteger(&start, (first ? + "Expected field name or number range." : + "Expected field number range."))); + } + + if (TryConsume("to")) { + LocationRecorder end_location( + location, DescriptorProto::ReservedRange::kEndFieldNumber); + DO(ConsumeInteger(&end, "Expected integer.")); + } else { + LocationRecorder end_location( + location, DescriptorProto::ReservedRange::kEndFieldNumber); + end_location.StartAt(start_token); + end_location.EndAt(start_token); + end = start; + } + + // Users like to specify inclusive ranges, but in code we like the end + // number to be exclusive. + ++end; + + range->set_start(start); + range->set_end(end); + first = false; + } while (TryConsume(",")); + + DO(ConsumeEndOfDeclaration(";", &parent_location)); + return true; +} + bool Parser::ParseExtend(RepeatedPtrField* extensions, RepeatedPtrField* messages, const LocationRecorder& parent_location, diff --git a/src/google/protobuf/compiler/parser.h b/src/google/protobuf/compiler/parser.h index 7cb1678a..16012e96 100644 --- a/src/google/protobuf/compiler/parser.h +++ b/src/google/protobuf/compiler/parser.h @@ -364,6 +364,14 @@ class LIBPROTOBUF_EXPORT Parser { const LocationRecorder& extensions_location, const FileDescriptorProto* containing_file); + // Parse an "reserved" declaration. + bool ParseReserved(DescriptorProto* message, + const LocationRecorder& message_location); + bool ParseReservedNames(DescriptorProto* message, + const LocationRecorder& parent_location); + bool ParseReservedNumbers(DescriptorProto* message, + const LocationRecorder& parent_location); + // Parse an "extend" declaration. (See also comments for // ParseMessageField().) bool ParseExtend(RepeatedPtrField* extensions, diff --git a/src/google/protobuf/compiler/parser_unittest.cc b/src/google/protobuf/compiler/parser_unittest.cc index 1684bebe..ddf34bfa 100644 --- a/src/google/protobuf/compiler/parser_unittest.cc +++ b/src/google/protobuf/compiler/parser_unittest.cc @@ -620,6 +620,36 @@ TEST_F(ParseMessageTest, NestedEnum) { "}"); } +TEST_F(ParseMessageTest, ReservedRange) { + ExpectParsesTo( + "message TestMessage {\n" + " required int32 foo = 1;\n" + " reserved 2, 15, 9 to 11, 3;\n" + "}\n", + + "message_type {" + " name: \"TestMessage\"" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_INT32 number:1 }" + " reserved_range { start:2 end:3 }" + " reserved_range { start:15 end:16 }" + " reserved_range { start:9 end:12 }" + " reserved_range { start:3 end:4 }" + "}"); +} + +TEST_F(ParseMessageTest, ReservedNames) { + ExpectParsesTo( + "message TestMessage {\n" + " reserved \"foo\", \"bar\";\n" + "}\n", + + "message_type {" + " name: \"TestMessage\"" + " reserved_name: \"foo\"" + " reserved_name: \"bar\"" + "}"); +} + TEST_F(ParseMessageTest, ExtensionRange) { ExpectParsesTo( "message TestMessage {\n" @@ -715,19 +745,17 @@ TEST_F(ParseMessageTest, MultipleExtensionsOneExtendee) { " type_name:\"TestMessage\" extendee: \"Extendee1\" }"); } -TEST_F(ParseMessageTest, OptionalOptionalLabelProto3) { +TEST_F(ParseMessageTest, OptionalLabelProto3) { ExpectParsesTo( "syntax = \"proto3\";\n" "message TestMessage {\n" " int32 foo = 1;\n" - " optional int32 bar = 2;\n" "}\n", "syntax: \"proto3\" " "message_type {" " name: \"TestMessage\"" - " field { name:\"foo\" label:LABEL_OPTIONAL type:TYPE_INT32 number:1 }" - " field { name:\"bar\" label:LABEL_OPTIONAL type:TYPE_INT32 number:2 } }"); + " field { name:\"foo\" label:LABEL_OPTIONAL type:TYPE_INT32 number:1 } }"); } // =================================================================== @@ -1230,6 +1258,18 @@ TEST_F(ParseErrorTest, EofInAggregateValue) { "1:0: Unexpected end of stream while parsing aggregate value.\n"); } +TEST_F(ParseErrorTest, ExplicitOptionalLabelProto3) { + ExpectHasErrors( + "syntax = 'proto3';\n" + "message TestMessage {\n" + " optional int32 foo = 1;\n" + "}\n", + "2:11: Explicit 'optional' labels are disallowed in the Proto3 syntax. " + "To define 'optional' fields in Proto3, simply remove the 'optional' " + "label, as fields are 'optional' by default.\n"); +} + + // ------------------------------------------------------------------- // Enum errors @@ -1247,6 +1287,33 @@ TEST_F(ParseErrorTest, EnumValueMissingNumber) { "1:5: Missing numeric value for enum constant.\n"); } +// ------------------------------------------------------------------- +// Reserved field number errors + +TEST_F(ParseErrorTest, ReservedMaxNotAllowed) { + ExpectHasErrors( + "message Foo {\n" + " reserved 10 to max;\n" + "}\n", + "1:17: Expected integer.\n"); +} + +TEST_F(ParseErrorTest, ReservedMixNameAndNumber) { + ExpectHasErrors( + "message Foo {\n" + " reserved 10, \"foo\";\n" + "}\n", + "1:15: Expected field number range.\n"); +} + +TEST_F(ParseErrorTest, ReservedMissingQuotes) { + ExpectHasErrors( + "message Foo {\n" + " reserved foo;\n" + "}\n", + "1:11: Expected field name or number range.\n"); +} + // ------------------------------------------------------------------- // Service errors diff --git a/src/google/protobuf/compiler/plugin.cc b/src/google/protobuf/compiler/plugin.cc index ad501acf..cdcaffde 100644 --- a/src/google/protobuf/compiler/plugin.cc +++ b/src/google/protobuf/compiler/plugin.cc @@ -134,20 +134,34 @@ int PluginMain(int argc, char* argv[], const CodeGenerator* generator) { CodeGeneratorResponse response; GeneratorResponseContext context(&response, parsed_files); - for (int i = 0; i < parsed_files.size(); i++) { - const FileDescriptor* file = parsed_files[i]; - + if (generator->HasGenerateAll()) { string error; - bool succeeded = generator->Generate( - file, request.parameter(), &context, &error); + bool succeeded = generator->GenerateAll( + parsed_files, request.parameter(), &context, &error); if (!succeeded && error.empty()) { error = "Code generator returned false but provided no error " "description."; } if (!error.empty()) { - response.set_error(file->name() + ": " + error); - break; + response.set_error(error); + } + } else { + for (int i = 0; i < parsed_files.size(); i++) { + const FileDescriptor* file = parsed_files[i]; + + string error; + bool succeeded = generator->Generate( + file, request.parameter(), &context, &error); + + if (!succeeded && error.empty()) { + error = "Code generator returned false but provided no error " + "description."; + } + if (!error.empty()) { + response.set_error(file->name() + ": " + error); + break; + } } } diff --git a/src/google/protobuf/compiler/plugin.pb.cc b/src/google/protobuf/compiler/plugin.pb.cc index d78b715e..344ed450 100644 --- a/src/google/protobuf/compiler/plugin.pb.cc +++ b/src/google/protobuf/compiler/plugin.pb.cc @@ -179,7 +179,7 @@ const int CodeGeneratorRequest::kProtoFileFieldNumber; #endif // !_MSC_VER CodeGeneratorRequest::CodeGeneratorRequest() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.compiler.CodeGeneratorRequest) } @@ -300,12 +300,15 @@ bool CodeGeneratorRequest::MergePartialFromCodedStream( case 15: { if (tag == 122) { parse_proto_file: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_proto_file: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_proto_file())); } else { goto handle_unusual; } - if (input->ExpectTag(122)) goto parse_proto_file; + if (input->ExpectTag(122)) goto parse_loop_proto_file; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectAtEnd()) goto success; break; } @@ -445,9 +448,9 @@ int CodeGeneratorRequest::ByteSize() const { void CodeGeneratorRequest::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const CodeGeneratorRequest* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const CodeGeneratorRequest* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -513,10 +516,10 @@ void CodeGeneratorRequest::InternalSwap(CodeGeneratorRequest* other) { // CodeGeneratorRequest // repeated string file_to_generate = 1; - int CodeGeneratorRequest::file_to_generate_size() const { +int CodeGeneratorRequest::file_to_generate_size() const { return file_to_generate_.size(); } - void CodeGeneratorRequest::clear_file_to_generate() { +void CodeGeneratorRequest::clear_file_to_generate() { file_to_generate_.Clear(); } const ::std::string& CodeGeneratorRequest::file_to_generate(int index) const { @@ -567,16 +570,16 @@ CodeGeneratorRequest::mutable_file_to_generate() { } // optional string parameter = 2; - bool CodeGeneratorRequest::has_parameter() const { +bool CodeGeneratorRequest::has_parameter() const { return (_has_bits_[0] & 0x00000002u) != 0; } - void CodeGeneratorRequest::set_has_parameter() { +void CodeGeneratorRequest::set_has_parameter() { _has_bits_[0] |= 0x00000002u; } - void CodeGeneratorRequest::clear_has_parameter() { +void CodeGeneratorRequest::clear_has_parameter() { _has_bits_[0] &= ~0x00000002u; } - void CodeGeneratorRequest::clear_parameter() { +void CodeGeneratorRequest::clear_parameter() { parameter_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_parameter(); } @@ -620,10 +623,10 @@ CodeGeneratorRequest::mutable_file_to_generate() { } // repeated .google.protobuf.FileDescriptorProto proto_file = 15; - int CodeGeneratorRequest::proto_file_size() const { +int CodeGeneratorRequest::proto_file_size() const { return proto_file_.size(); } - void CodeGeneratorRequest::clear_proto_file() { +void CodeGeneratorRequest::clear_proto_file() { proto_file_.Clear(); } const ::google::protobuf::FileDescriptorProto& CodeGeneratorRequest::proto_file(int index) const { @@ -660,7 +663,7 @@ const int CodeGeneratorResponse_File::kContentFieldNumber; #endif // !_MSC_VER CodeGeneratorResponse_File::CodeGeneratorResponse_File() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.compiler.CodeGeneratorResponse.File) } @@ -946,9 +949,9 @@ int CodeGeneratorResponse_File::ByteSize() const { void CodeGeneratorResponse_File::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const CodeGeneratorResponse_File* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const CodeGeneratorResponse_File* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -1024,7 +1027,7 @@ const int CodeGeneratorResponse::kFileFieldNumber; #endif // !_MSC_VER CodeGeneratorResponse::CodeGeneratorResponse() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.compiler.CodeGeneratorResponse) } @@ -1124,12 +1127,15 @@ bool CodeGeneratorResponse::MergePartialFromCodedStream( case 15: { if (tag == 122) { parse_file: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_file: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_file())); } else { goto handle_unusual; } - if (input->ExpectTag(122)) goto parse_file; + if (input->ExpectTag(122)) goto parse_loop_file; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectAtEnd()) goto success; break; } @@ -1242,9 +1248,9 @@ int CodeGeneratorResponse::ByteSize() const { void CodeGeneratorResponse::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const CodeGeneratorResponse* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const CodeGeneratorResponse* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -1307,16 +1313,16 @@ void CodeGeneratorResponse::InternalSwap(CodeGeneratorResponse* other) { // CodeGeneratorResponse_File // optional string name = 1; - bool CodeGeneratorResponse_File::has_name() const { +bool CodeGeneratorResponse_File::has_name() const { return (_has_bits_[0] & 0x00000001u) != 0; } - void CodeGeneratorResponse_File::set_has_name() { +void CodeGeneratorResponse_File::set_has_name() { _has_bits_[0] |= 0x00000001u; } - void CodeGeneratorResponse_File::clear_has_name() { +void CodeGeneratorResponse_File::clear_has_name() { _has_bits_[0] &= ~0x00000001u; } - void CodeGeneratorResponse_File::clear_name() { +void CodeGeneratorResponse_File::clear_name() { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_name(); } @@ -1360,16 +1366,16 @@ void CodeGeneratorResponse::InternalSwap(CodeGeneratorResponse* other) { } // optional string insertion_point = 2; - bool CodeGeneratorResponse_File::has_insertion_point() const { +bool CodeGeneratorResponse_File::has_insertion_point() const { return (_has_bits_[0] & 0x00000002u) != 0; } - void CodeGeneratorResponse_File::set_has_insertion_point() { +void CodeGeneratorResponse_File::set_has_insertion_point() { _has_bits_[0] |= 0x00000002u; } - void CodeGeneratorResponse_File::clear_has_insertion_point() { +void CodeGeneratorResponse_File::clear_has_insertion_point() { _has_bits_[0] &= ~0x00000002u; } - void CodeGeneratorResponse_File::clear_insertion_point() { +void CodeGeneratorResponse_File::clear_insertion_point() { insertion_point_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_insertion_point(); } @@ -1413,16 +1419,16 @@ void CodeGeneratorResponse::InternalSwap(CodeGeneratorResponse* other) { } // optional string content = 15; - bool CodeGeneratorResponse_File::has_content() const { +bool CodeGeneratorResponse_File::has_content() const { return (_has_bits_[0] & 0x00000004u) != 0; } - void CodeGeneratorResponse_File::set_has_content() { +void CodeGeneratorResponse_File::set_has_content() { _has_bits_[0] |= 0x00000004u; } - void CodeGeneratorResponse_File::clear_has_content() { +void CodeGeneratorResponse_File::clear_has_content() { _has_bits_[0] &= ~0x00000004u; } - void CodeGeneratorResponse_File::clear_content() { +void CodeGeneratorResponse_File::clear_content() { content_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_content(); } @@ -1470,16 +1476,16 @@ void CodeGeneratorResponse::InternalSwap(CodeGeneratorResponse* other) { // CodeGeneratorResponse // optional string error = 1; - bool CodeGeneratorResponse::has_error() const { +bool CodeGeneratorResponse::has_error() const { return (_has_bits_[0] & 0x00000001u) != 0; } - void CodeGeneratorResponse::set_has_error() { +void CodeGeneratorResponse::set_has_error() { _has_bits_[0] |= 0x00000001u; } - void CodeGeneratorResponse::clear_has_error() { +void CodeGeneratorResponse::clear_has_error() { _has_bits_[0] &= ~0x00000001u; } - void CodeGeneratorResponse::clear_error() { +void CodeGeneratorResponse::clear_error() { error_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_error(); } @@ -1523,10 +1529,10 @@ void CodeGeneratorResponse::InternalSwap(CodeGeneratorResponse* other) { } // repeated .google.protobuf.compiler.CodeGeneratorResponse.File file = 15; - int CodeGeneratorResponse::file_size() const { +int CodeGeneratorResponse::file_size() const { return file_.size(); } - void CodeGeneratorResponse::clear_file() { +void CodeGeneratorResponse::clear_file() { file_.Clear(); } const ::google::protobuf::compiler::CodeGeneratorResponse_File& CodeGeneratorResponse::file(int index) const { diff --git a/src/google/protobuf/compiler/plugin.pb.h b/src/google/protobuf/compiler/plugin.pb.h index ea48b8b5..6fcaea2e 100644 --- a/src/google/protobuf/compiler/plugin.pb.h +++ b/src/google/protobuf/compiler/plugin.pb.h @@ -796,6 +796,10 @@ CodeGeneratorResponse::mutable_file() { } #endif // !PROTOBUF_INLINE_NOT_IN_HEADERS +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + // @@protoc_insertion_point(namespace_scope) diff --git a/src/google/protobuf/descriptor.cc b/src/google/protobuf/descriptor.cc index bfdacd95..2855c377 100644 --- a/src/google/protobuf/descriptor.cc +++ b/src/google/protobuf/descriptor.cc @@ -1518,6 +1518,18 @@ Descriptor::FindExtensionRangeContainingNumber(int number) const { return NULL; } +const Descriptor::ReservedRange* +Descriptor::FindReservedRangeContainingNumber(int number) const { + // TODO(chrisn): Consider a non-linear search. + for (int i = 0; i < reserved_range_count(); i++) { + if (number >= reserved_range(i)->start && + number < reserved_range(i)->end) { + return reserved_range(i); + } + } + return NULL; +} + // ------------------------------------------------------------------- bool DescriptorPool::TryFindFileInFallbackDatabase(const string& name) const { @@ -1741,6 +1753,14 @@ void Descriptor::CopyTo(DescriptorProto* proto) const { for (int i = 0; i < extension_count(); i++) { extension(i)->CopyTo(proto->add_extension()); } + for (int i = 0; i < reserved_range_count(); i++) { + DescriptorProto::ReservedRange* range = proto->add_reserved_range(); + range->set_start(reserved_range(i)->start); + range->set_end(reserved_range(i)->end); + } + for (int i = 0; i < reserved_name_count(); i++) { + proto->add_reserved_name(reserved_name(i)); + } if (&options() != &MessageOptions::default_instance()) { proto->mutable_options()->CopyFrom(options()); @@ -2186,6 +2206,29 @@ void Descriptor::DebugString(int depth, string *contents, if (extension_count() > 0) strings::SubstituteAndAppend(contents, "$0 }\n", prefix); + if (reserved_range_count() > 0) { + strings::SubstituteAndAppend(contents, "$0 reserved ", prefix); + for (int i = 0; i < reserved_range_count(); i++) { + const Descriptor::ReservedRange* range = reserved_range(i); + if (range->end == range->start + 1) { + strings::SubstituteAndAppend(contents, "$0, ", range->start); + } else { + strings::SubstituteAndAppend(contents, "$0 to $1, ", + range->start, range->end - 1); + } + } + contents->replace(contents->size() - 2, 2, ";\n"); + } + + if (reserved_name_count() > 0) { + strings::SubstituteAndAppend(contents, "$0 reserved ", prefix); + for (int i = 0; i < reserved_name_count(); i++) { + strings::SubstituteAndAppend(contents, "\"$0\", ", + CEscape(reserved_name(i))); + } + contents->replace(contents->size() - 2, 2, ";\n"); + } + strings::SubstituteAndAppend(contents, "$0}\n", prefix); comment_printer.AddPostComment(contents); } @@ -2278,8 +2321,12 @@ void FieldDescriptor::DebugString(int depth, } if (type() == TYPE_GROUP) { - message_type()->DebugString(depth, contents, debug_string_options, - /* include_opening_clause */ false); + if (debug_string_options.elide_group_body) { + contents->append(" { ... };\n"); + } else { + message_type()->DebugString(depth, contents, debug_string_options, + /* include_opening_clause */ false); + } } else { contents->append(";\n"); } @@ -2308,12 +2355,16 @@ void OneofDescriptor::DebugString(int depth, string* contents, comment_printer(this, prefix, debug_string_options); comment_printer.AddPreComment(contents); strings::SubstituteAndAppend( - contents, "$0 oneof $1 {\n", prefix, name()); - for (int i = 0; i < field_count(); i++) { - field(i)->DebugString(depth, FieldDescriptor::OMIT_LABEL, contents, - debug_string_options); + contents, "$0 oneof $1 {", prefix, name()); + if (debug_string_options.elide_oneof_body) { + contents->append(" ... }\n"); + } else { + for (int i = 0; i < field_count(); i++) { + field(i)->DebugString(depth, FieldDescriptor::OMIT_LABEL, contents, + debug_string_options); + } + strings::SubstituteAndAppend(contents, "$0}\n", prefix); } - strings::SubstituteAndAppend(contents, "$0}\n", prefix); comment_printer.AddPostComment(contents); } @@ -2491,7 +2542,12 @@ bool FileDescriptor::GetSourceLocation(SourceLocation* out_location) const { } bool FieldDescriptor::is_packed() const { - return is_packable() && (options_ != NULL) && options_->packed(); + if (!is_packable()) return false; + if (file_->syntax() == FileDescriptor::SYNTAX_PROTO2) { + return (options_ != NULL) && options_->packed(); + } else { + return options_ == NULL || !options_->has_packed() || options_->packed(); + } } bool Descriptor::GetSourceLocation(SourceLocation* out_location) const { @@ -2604,7 +2660,7 @@ void MethodDescriptor::GetLocationPath(vector* output) const { namespace { // Represents an options message to interpret. Extension names in the option -// name are respolved relative to name_scope. element_name and orig_opt are +// name are resolved relative to name_scope. element_name and orig_opt are // used only for error reporting (since the parser records locations against // pointers in the original options, not the mutable copy). The Message must be // one of the Options messages in descriptor.proto. @@ -2830,6 +2886,9 @@ class DescriptorBuilder { void BuildExtensionRange(const DescriptorProto::ExtensionRange& proto, const Descriptor* parent, Descriptor::ExtensionRange* result); + void BuildReservedRange(const DescriptorProto::ReservedRange& proto, + const Descriptor* parent, + Descriptor::ReservedRange* result); void BuildOneof(const OneofDescriptorProto& proto, Descriptor* parent, OneofDescriptor* result); @@ -3920,6 +3979,17 @@ void DescriptorBuilder::BuildMessage(const DescriptorProto& proto, BUILD_ARRAY(proto, result, enum_type , BuildEnum , result); BUILD_ARRAY(proto, result, extension_range, BuildExtensionRange, result); BUILD_ARRAY(proto, result, extension , BuildExtension , result); + BUILD_ARRAY(proto, result, reserved_range , BuildReservedRange , result); + + // Copy reserved names. + int reserved_name_count = proto.reserved_name_size(); + result->reserved_name_count_ = reserved_name_count; + result->reserved_names_ = + tables_->AllocateArray(reserved_name_count); + for (int i = 0; i < reserved_name_count; ++i) { + result->reserved_names_[i] = + tables_->AllocateString(proto.reserved_name(i)); + } // Copy options. if (!proto.has_options()) { @@ -3931,7 +4001,34 @@ void DescriptorBuilder::BuildMessage(const DescriptorProto& proto, AddSymbol(result->full_name(), parent, result->name(), proto, Symbol(result)); - // Check that no fields have numbers in extension ranges. + for (int i = 0; i < proto.reserved_range_size(); i++) { + const DescriptorProto_ReservedRange& range1 = proto.reserved_range(i); + for (int j = i + 1; j < proto.reserved_range_size(); j++) { + const DescriptorProto_ReservedRange& range2 = proto.reserved_range(j); + if (range1.end() > range2.start() && range2.end() > range1.start()) { + AddError(result->full_name(), proto.reserved_range(i), + DescriptorPool::ErrorCollector::NUMBER, + strings::Substitute("Reserved range $0 to $1 overlaps with " + "already-defined range $2 to $3.", + range2.start(), range2.end() - 1, + range1.start(), range1.end() - 1)); + } + } + } + + hash_set reserved_name_set; + for (int i = 0; i < proto.reserved_name_size(); i++) { + const string& name = proto.reserved_name(i); + if (reserved_name_set.find(name) == reserved_name_set.end()) { + reserved_name_set.insert(name); + } else { + AddError(name, proto, DescriptorPool::ErrorCollector::NAME, + strings::Substitute( + "Field name \"$0\" is reserved multiple times.", + name)); + } + } + for (int i = 0; i < result->field_count(); i++) { const FieldDescriptor* field = result->field(i); for (int j = 0; j < result->extension_range_count(); j++) { @@ -3945,11 +4042,39 @@ void DescriptorBuilder::BuildMessage(const DescriptorProto& proto, field->name(), field->number())); } } + for (int j = 0; j < result->reserved_range_count(); j++) { + const Descriptor::ReservedRange* range = result->reserved_range(j); + if (range->start <= field->number() && field->number() < range->end) { + AddError(field->full_name(), proto.reserved_range(j), + DescriptorPool::ErrorCollector::NUMBER, + strings::Substitute( + "Field \"$0\" uses reserved number $1.", + field->name(), field->number())); + } + } + if (reserved_name_set.find(field->name()) != reserved_name_set.end()) { + AddError(field->full_name(), proto.field(i), + DescriptorPool::ErrorCollector::NAME, + strings::Substitute( + "Field name \"$0\" is reserved.", field->name())); + } } - // Check that extension ranges don't overlap. + // Check that extension ranges don't overlap and don't include + // reserved field numbers. for (int i = 0; i < result->extension_range_count(); i++) { const Descriptor::ExtensionRange* range1 = result->extension_range(i); + for (int j = 0; j < result->reserved_range_count(); j++) { + const Descriptor::ReservedRange* range2 = result->reserved_range(j); + if (range1->end > range2->start && range2->end > range1->start) { + AddError(result->full_name(), proto.extension_range(j), + DescriptorPool::ErrorCollector::NUMBER, + strings::Substitute("Extension range $0 to $1 overlaps with " + "reserved range $2 to $3.", + range1->start, range1->end - 1, + range2->start, range2->end - 1)); + } + } for (int j = i + 1; j < result->extension_range_count(); j++) { const Descriptor::ExtensionRange* range2 = result->extension_range(j); if (range1->end > range2->start && range2->end > range1->start) { @@ -4262,6 +4387,19 @@ void DescriptorBuilder::BuildExtensionRange( } } +void DescriptorBuilder::BuildReservedRange( + const DescriptorProto::ReservedRange& proto, + const Descriptor* parent, + Descriptor::ReservedRange* result) { + result->start = proto.start(); + result->end = proto.end(); + if (result->start <= 0) { + AddError(parent->full_name(), proto, + DescriptorPool::ErrorCollector::NUMBER, + "Reserved numbers must be positive integers."); + } +} + void DescriptorBuilder::BuildOneof(const OneofDescriptorProto& proto, Descriptor* parent, OneofDescriptor* result) { @@ -4503,6 +4641,23 @@ void DescriptorBuilder::CrossLinkMessage( for (int i = 0; i < message->field_count(); i++) { const OneofDescriptor* oneof_decl = message->field(i)->containing_oneof(); if (oneof_decl != NULL) { + // Make sure fields belonging to the same oneof are defined consecutively. + // This enables optimizations in codegens and reflection libraries to + // skip fields in the oneof group, as only one of the field can be set. + // Note that field_count() returns how many fields in this oneof we have + // seen so far. field_count() > 0 guarantees that i > 0, so field(i-1) is + // safe. + if (oneof_decl->field_count() > 0 && + message->field(i - 1)->containing_oneof() != oneof_decl) { + AddWarning( + message->full_name() + "." + message->field(i - 1)->name(), + proto.field(i - 1), DescriptorPool::ErrorCollector::OTHER, + strings::Substitute( + "Fields in the same oneof must be defined consecutively. " + "\"$0\" cannot be defined before the completion of the " + "\"$1\" oneof definition.", + message->field(i - 1)->name(), oneof_decl->name())); + } // Must go through oneof_decls_ array to get a non-const version of the // OneofDescriptor. ++message->oneof_decls_[oneof_decl->index()].field_count_; @@ -4933,6 +5088,12 @@ void DescriptorBuilder::ValidateProto3Field( field->containing_type()->full_name() + "\" which is a proto3 message type."); } + bool allow_groups = false; + if (field->type() == FieldDescriptor::TYPE_GROUP && !allow_groups) { + AddError(field->full_name(), proto, + DescriptorPool::ErrorCollector::TYPE, + "Groups are not supported in proto3 syntax."); + } } void DescriptorBuilder::ValidateProto3Enum( diff --git a/src/google/protobuf/descriptor.h b/src/google/protobuf/descriptor.h index c0a48cea..ca87d634 100644 --- a/src/google/protobuf/descriptor.h +++ b/src/google/protobuf/descriptor.h @@ -135,10 +135,14 @@ struct DebugStringOptions { // example, the C++ code generation for fields in the proto compiler) rely on // DebugString() output being unobstructed by user comments. bool include_comments; + // If true, elide the braced body in the debug string. + bool elide_group_body; + bool elide_oneof_body; DebugStringOptions() - : include_comments(false) - {} + : include_comments(false), + elide_group_body(false), + elide_oneof_body(false) {} }; // Describes a type of protocol message, or a particular group within a @@ -298,6 +302,36 @@ class LIBPROTOBUF_EXPORT Descriptor { // this message type's scope. const FieldDescriptor* FindExtensionByCamelcaseName(const string& name) const; + // Reserved fields ------------------------------------------------- + + // A range of reserved field numbers. + struct ReservedRange { + int start; // inclusive + int end; // exclusive + }; + + // The number of reserved ranges in this message type. + int reserved_range_count() const; + // Gets an reserved range by index, where 0 <= index < + // reserved_range_count(). These are returned in the order they were defined + // in the .proto file. + const ReservedRange* reserved_range(int index) const; + + // Returns true if the number is in one of the reserved ranges. + bool IsReservedNumber(int number) const; + + // Returns NULL if no reserved range contains the given number. + const ReservedRange* FindReservedRangeContainingNumber(int number) const; + + // The number of reserved field names in this message type. + int reserved_name_count() const; + + // Gets a reserved name by index, where 0 <= index < reserved_name_count(). + const string& reserved_name(int index) const; + + // Returns true if the field name is reserved. + bool IsReservedName(const string& name) const; + // Source Location --------------------------------------------------- // Updates |*out_location| to the source location of the complete @@ -343,6 +377,10 @@ class LIBPROTOBUF_EXPORT Descriptor { ExtensionRange* extension_ranges_; int extension_count_; FieldDescriptor* extensions_; + int reserved_range_count_; + ReservedRange* reserved_ranges_; + int reserved_name_count_; + const string** reserved_names_; // IMPORTANT: If you add a new field, make sure to search for all instances // of Allocate() and AllocateArray() in descriptor.cc // and update them to initialize the field. @@ -1567,6 +1605,12 @@ PROTOBUF_DEFINE_ARRAY_ACCESSOR(Descriptor, extension_range, const Descriptor::ExtensionRange*) PROTOBUF_DEFINE_ARRAY_ACCESSOR(Descriptor, extension, const FieldDescriptor*) + +PROTOBUF_DEFINE_ACCESSOR(Descriptor, reserved_range_count, int) +PROTOBUF_DEFINE_ARRAY_ACCESSOR(Descriptor, reserved_range, + const Descriptor::ReservedRange*) +PROTOBUF_DEFINE_ACCESSOR(Descriptor, reserved_name_count, int) + PROTOBUF_DEFINE_OPTIONS_ACCESSOR(Descriptor, MessageOptions); PROTOBUF_DEFINE_ACCESSOR(Descriptor, is_placeholder, bool) @@ -1667,6 +1711,25 @@ inline bool Descriptor::IsExtensionNumber(int number) const { return FindExtensionRangeContainingNumber(number) != NULL; } +inline bool Descriptor::IsReservedNumber(int number) const { + return FindReservedRangeContainingNumber(number) != NULL; +} + +inline bool Descriptor::IsReservedName(const string& name) const { + for (int i = 0; i < reserved_name_count(); i++) { + if (name == reserved_name(i)) { + return true; + } + } + return false; +} + +// Can't use PROTOBUF_DEFINE_ARRAY_ACCESSOR because reserved_names_ is actually +// an array of pointers rather than the usual array of objects. +inline const string& Descriptor::reserved_name(int index) const { + return *reserved_names_[index]; +} + inline bool FieldDescriptor::is_required() const { return label() == LABEL_REQUIRED; } diff --git a/src/google/protobuf/descriptor.pb.cc b/src/google/protobuf/descriptor.pb.cc index 8968f362..755c2617 100644 --- a/src/google/protobuf/descriptor.pb.cc +++ b/src/google/protobuf/descriptor.pb.cc @@ -33,6 +33,9 @@ const ::google::protobuf::internal::GeneratedMessageReflection* const ::google::protobuf::Descriptor* DescriptorProto_ExtensionRange_descriptor_ = NULL; const ::google::protobuf::internal::GeneratedMessageReflection* DescriptorProto_ExtensionRange_reflection_ = NULL; +const ::google::protobuf::Descriptor* DescriptorProto_ReservedRange_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + DescriptorProto_ReservedRange_reflection_ = NULL; const ::google::protobuf::Descriptor* FieldDescriptorProto_descriptor_ = NULL; const ::google::protobuf::internal::GeneratedMessageReflection* FieldDescriptorProto_reflection_ = NULL; @@ -64,6 +67,7 @@ const ::google::protobuf::Descriptor* FieldOptions_descriptor_ = NULL; const ::google::protobuf::internal::GeneratedMessageReflection* FieldOptions_reflection_ = NULL; const ::google::protobuf::EnumDescriptor* FieldOptions_CType_descriptor_ = NULL; +const ::google::protobuf::EnumDescriptor* FieldOptions_JSType_descriptor_ = NULL; const ::google::protobuf::Descriptor* EnumOptions_descriptor_ = NULL; const ::google::protobuf::internal::GeneratedMessageReflection* EnumOptions_reflection_ = NULL; @@ -140,7 +144,7 @@ void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto() { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileDescriptorProto, _internal_metadata_), -1); DescriptorProto_descriptor_ = file->message_type(2); - static const int DescriptorProto_offsets_[8] = { + static const int DescriptorProto_offsets_[10] = { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto, name_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto, field_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto, extension_), @@ -149,6 +153,8 @@ void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto() { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto, extension_range_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto, oneof_decl_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto, options_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto, reserved_range_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto, reserved_name_), }; DescriptorProto_reflection_ = ::google::protobuf::internal::GeneratedMessageReflection::NewGeneratedMessageReflection( @@ -177,6 +183,22 @@ void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto() { sizeof(DescriptorProto_ExtensionRange), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto_ExtensionRange, _internal_metadata_), -1); + DescriptorProto_ReservedRange_descriptor_ = DescriptorProto_descriptor_->nested_type(1); + static const int DescriptorProto_ReservedRange_offsets_[2] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto_ReservedRange, start_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto_ReservedRange, end_), + }; + DescriptorProto_ReservedRange_reflection_ = + ::google::protobuf::internal::GeneratedMessageReflection::NewGeneratedMessageReflection( + DescriptorProto_ReservedRange_descriptor_, + DescriptorProto_ReservedRange::default_instance_, + DescriptorProto_ReservedRange_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto_ReservedRange, _has_bits_[0]), + -1, + -1, + sizeof(DescriptorProto_ReservedRange), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto_ReservedRange, _internal_metadata_), + -1); FieldDescriptorProto_descriptor_ = file->message_type(3); static const int FieldDescriptorProto_offsets_[9] = { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldDescriptorProto, name_), @@ -338,9 +360,10 @@ void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto() { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(MessageOptions, _internal_metadata_), -1); FieldOptions_descriptor_ = file->message_type(11); - static const int FieldOptions_offsets_[6] = { + static const int FieldOptions_offsets_[7] = { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldOptions, ctype_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldOptions, packed_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldOptions, jstype_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldOptions, lazy_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldOptions, deprecated_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldOptions, weak_), @@ -358,6 +381,7 @@ void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto() { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldOptions, _internal_metadata_), -1); FieldOptions_CType_descriptor_ = FieldOptions_descriptor_->enum_type(0); + FieldOptions_JSType_descriptor_ = FieldOptions_descriptor_->enum_type(1); EnumOptions_descriptor_ = file->message_type(12); static const int EnumOptions_offsets_[3] = { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EnumOptions, allow_alias_), @@ -514,6 +538,8 @@ void protobuf_RegisterTypes(const ::std::string&) { DescriptorProto_descriptor_, &DescriptorProto::default_instance()); ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( DescriptorProto_ExtensionRange_descriptor_, &DescriptorProto_ExtensionRange::default_instance()); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + DescriptorProto_ReservedRange_descriptor_, &DescriptorProto_ReservedRange::default_instance()); ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( FieldDescriptorProto_descriptor_, &FieldDescriptorProto::default_instance()); ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( @@ -561,6 +587,8 @@ void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto() { delete DescriptorProto_reflection_; delete DescriptorProto_ExtensionRange::default_instance_; delete DescriptorProto_ExtensionRange_reflection_; + delete DescriptorProto_ReservedRange::default_instance_; + delete DescriptorProto_ReservedRange_reflection_; delete FieldDescriptorProto::default_instance_; delete FieldDescriptorProto_reflection_; delete OneofDescriptorProto::default_instance_; @@ -619,7 +647,7 @@ void protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto() { "\022-\n\007options\030\010 \001(\0132\034.google.protobuf.File" "Options\0229\n\020source_code_info\030\t \001(\0132\037.goog" "le.protobuf.SourceCodeInfo\022\016\n\006syntax\030\014 \001" - "(\t\"\344\003\n\017DescriptorProto\022\014\n\004name\030\001 \001(\t\0224\n\005" + "(\t\"\360\004\n\017DescriptorProto\022\014\n\004name\030\001 \001(\t\0224\n\005" "field\030\002 \003(\0132%.google.protobuf.FieldDescr" "iptorProto\0228\n\textension\030\006 \003(\0132%.google.p" "rotobuf.FieldDescriptorProto\0225\n\013nested_t" @@ -630,104 +658,111 @@ void protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto() { "ExtensionRange\0229\n\noneof_decl\030\010 \003(\0132%.goo" "gle.protobuf.OneofDescriptorProto\0220\n\007opt" "ions\030\007 \001(\0132\037.google.protobuf.MessageOpti" - "ons\032,\n\016ExtensionRange\022\r\n\005start\030\001 \001(\005\022\013\n\003" - "end\030\002 \001(\005\"\251\005\n\024FieldDescriptorProto\022\014\n\004na" - "me\030\001 \001(\t\022\016\n\006number\030\003 \001(\005\022:\n\005label\030\004 \001(\0162" - "+.google.protobuf.FieldDescriptorProto.L" - "abel\0228\n\004type\030\005 \001(\0162*.google.protobuf.Fie" - "ldDescriptorProto.Type\022\021\n\ttype_name\030\006 \001(" - "\t\022\020\n\010extendee\030\002 \001(\t\022\025\n\rdefault_value\030\007 \001" - "(\t\022\023\n\013oneof_index\030\t \001(\005\022.\n\007options\030\010 \001(\013" - "2\035.google.protobuf.FieldOptions\"\266\002\n\004Type" - "\022\017\n\013TYPE_DOUBLE\020\001\022\016\n\nTYPE_FLOAT\020\002\022\016\n\nTYP" - "E_INT64\020\003\022\017\n\013TYPE_UINT64\020\004\022\016\n\nTYPE_INT32" - "\020\005\022\020\n\014TYPE_FIXED64\020\006\022\020\n\014TYPE_FIXED32\020\007\022\r" - "\n\tTYPE_BOOL\020\010\022\017\n\013TYPE_STRING\020\t\022\016\n\nTYPE_G" - "ROUP\020\n\022\020\n\014TYPE_MESSAGE\020\013\022\016\n\nTYPE_BYTES\020\014" - "\022\017\n\013TYPE_UINT32\020\r\022\r\n\tTYPE_ENUM\020\016\022\021\n\rTYPE" - "_SFIXED32\020\017\022\021\n\rTYPE_SFIXED64\020\020\022\017\n\013TYPE_S" - "INT32\020\021\022\017\n\013TYPE_SINT64\020\022\"C\n\005Label\022\022\n\016LAB" - "EL_OPTIONAL\020\001\022\022\n\016LABEL_REQUIRED\020\002\022\022\n\016LAB" - "EL_REPEATED\020\003\"$\n\024OneofDescriptorProto\022\014\n" - "\004name\030\001 \001(\t\"\214\001\n\023EnumDescriptorProto\022\014\n\004n" - "ame\030\001 \001(\t\0228\n\005value\030\002 \003(\0132).google.protob" - "uf.EnumValueDescriptorProto\022-\n\007options\030\003" - " \001(\0132\034.google.protobuf.EnumOptions\"l\n\030En" - "umValueDescriptorProto\022\014\n\004name\030\001 \001(\t\022\016\n\006" - "number\030\002 \001(\005\0222\n\007options\030\003 \001(\0132!.google.p" - "rotobuf.EnumValueOptions\"\220\001\n\026ServiceDesc" - "riptorProto\022\014\n\004name\030\001 \001(\t\0226\n\006method\030\002 \003(" - "\0132&.google.protobuf.MethodDescriptorProt" - "o\0220\n\007options\030\003 \001(\0132\037.google.protobuf.Ser" - "viceOptions\"\301\001\n\025MethodDescriptorProto\022\014\n" - "\004name\030\001 \001(\t\022\022\n\ninput_type\030\002 \001(\t\022\023\n\013outpu" - "t_type\030\003 \001(\t\022/\n\007options\030\004 \001(\0132\036.google.p" - "rotobuf.MethodOptions\022\037\n\020client_streamin" - "g\030\005 \001(\010:\005false\022\037\n\020server_streaming\030\006 \001(\010" - ":\005false\"\201\005\n\013FileOptions\022\024\n\014java_package\030" - "\001 \001(\t\022\034\n\024java_outer_classname\030\010 \001(\t\022\"\n\023j" - "ava_multiple_files\030\n \001(\010:\005false\022,\n\035java_" - "generate_equals_and_hash\030\024 \001(\010:\005false\022%\n" - "\026java_string_check_utf8\030\033 \001(\010:\005false\022F\n\014" - "optimize_for\030\t \001(\0162).google.protobuf.Fil" - "eOptions.OptimizeMode:\005SPEED\022\022\n\ngo_packa" - "ge\030\013 \001(\t\022\"\n\023cc_generic_services\030\020 \001(\010:\005f" - "alse\022$\n\025java_generic_services\030\021 \001(\010:\005fal" - "se\022\"\n\023py_generic_services\030\022 \001(\010:\005false\022\031" - "\n\ndeprecated\030\027 \001(\010:\005false\022\037\n\020cc_enable_a" - "renas\030\037 \001(\010:\005false\022\031\n\021objc_class_prefix\030" - "$ \001(\t\022\030\n\020csharp_namespace\030% \001(\t\022C\n\024unint" - "erpreted_option\030\347\007 \003(\0132$.google.protobuf" - ".UninterpretedOption\":\n\014OptimizeMode\022\t\n\005" - "SPEED\020\001\022\r\n\tCODE_SIZE\020\002\022\020\n\014LITE_RUNTIME\020\003" - "*\t\010\350\007\020\200\200\200\200\002\"\346\001\n\016MessageOptions\022&\n\027messag" - "e_set_wire_format\030\001 \001(\010:\005false\022.\n\037no_sta" - "ndard_descriptor_accessor\030\002 \001(\010:\005false\022\031" - "\n\ndeprecated\030\003 \001(\010:\005false\022\021\n\tmap_entry\030\007" - " \001(\010\022C\n\024uninterpreted_option\030\347\007 \003(\0132$.go" - "ogle.protobuf.UninterpretedOption*\t\010\350\007\020\200" - "\200\200\200\002\"\240\002\n\014FieldOptions\022:\n\005ctype\030\001 \001(\0162#.g" - "oogle.protobuf.FieldOptions.CType:\006STRIN" - "G\022\016\n\006packed\030\002 \001(\010\022\023\n\004lazy\030\005 \001(\010:\005false\022\031" - "\n\ndeprecated\030\003 \001(\010:\005false\022\023\n\004weak\030\n \001(\010:" - "\005false\022C\n\024uninterpreted_option\030\347\007 \003(\0132$." - "google.protobuf.UninterpretedOption\"/\n\005C" - "Type\022\n\n\006STRING\020\000\022\010\n\004CORD\020\001\022\020\n\014STRING_PIE" - "CE\020\002*\t\010\350\007\020\200\200\200\200\002\"\215\001\n\013EnumOptions\022\023\n\013allow" - "_alias\030\002 \001(\010\022\031\n\ndeprecated\030\003 \001(\010:\005false\022" - "C\n\024uninterpreted_option\030\347\007 \003(\0132$.google." - "protobuf.UninterpretedOption*\t\010\350\007\020\200\200\200\200\002\"" - "}\n\020EnumValueOptions\022\031\n\ndeprecated\030\001 \001(\010:" - "\005false\022C\n\024uninterpreted_option\030\347\007 \003(\0132$." - "google.protobuf.UninterpretedOption*\t\010\350\007" - "\020\200\200\200\200\002\"{\n\016ServiceOptions\022\031\n\ndeprecated\030!" + "ons\022F\n\016reserved_range\030\t \003(\0132..google.pro" + "tobuf.DescriptorProto.ReservedRange\022\025\n\rr" + "eserved_name\030\n \003(\t\032,\n\016ExtensionRange\022\r\n\005" + "start\030\001 \001(\005\022\013\n\003end\030\002 \001(\005\032+\n\rReservedRang" + "e\022\r\n\005start\030\001 \001(\005\022\013\n\003end\030\002 \001(\005\"\251\005\n\024FieldD" + "escriptorProto\022\014\n\004name\030\001 \001(\t\022\016\n\006number\030\003" + " \001(\005\022:\n\005label\030\004 \001(\0162+.google.protobuf.Fi" + "eldDescriptorProto.Label\0228\n\004type\030\005 \001(\0162*" + ".google.protobuf.FieldDescriptorProto.Ty" + "pe\022\021\n\ttype_name\030\006 \001(\t\022\020\n\010extendee\030\002 \001(\t\022" + "\025\n\rdefault_value\030\007 \001(\t\022\023\n\013oneof_index\030\t " + "\001(\005\022.\n\007options\030\010 \001(\0132\035.google.protobuf.F" + "ieldOptions\"\266\002\n\004Type\022\017\n\013TYPE_DOUBLE\020\001\022\016\n" + "\nTYPE_FLOAT\020\002\022\016\n\nTYPE_INT64\020\003\022\017\n\013TYPE_UI" + "NT64\020\004\022\016\n\nTYPE_INT32\020\005\022\020\n\014TYPE_FIXED64\020\006" + "\022\020\n\014TYPE_FIXED32\020\007\022\r\n\tTYPE_BOOL\020\010\022\017\n\013TYP" + "E_STRING\020\t\022\016\n\nTYPE_GROUP\020\n\022\020\n\014TYPE_MESSA" + "GE\020\013\022\016\n\nTYPE_BYTES\020\014\022\017\n\013TYPE_UINT32\020\r\022\r\n" + "\tTYPE_ENUM\020\016\022\021\n\rTYPE_SFIXED32\020\017\022\021\n\rTYPE_" + "SFIXED64\020\020\022\017\n\013TYPE_SINT32\020\021\022\017\n\013TYPE_SINT" + "64\020\022\"C\n\005Label\022\022\n\016LABEL_OPTIONAL\020\001\022\022\n\016LAB" + "EL_REQUIRED\020\002\022\022\n\016LABEL_REPEATED\020\003\"$\n\024One" + "ofDescriptorProto\022\014\n\004name\030\001 \001(\t\"\214\001\n\023Enum" + "DescriptorProto\022\014\n\004name\030\001 \001(\t\0228\n\005value\030\002" + " \003(\0132).google.protobuf.EnumValueDescript" + "orProto\022-\n\007options\030\003 \001(\0132\034.google.protob" + "uf.EnumOptions\"l\n\030EnumValueDescriptorPro" + "to\022\014\n\004name\030\001 \001(\t\022\016\n\006number\030\002 \001(\005\0222\n\007opti" + "ons\030\003 \001(\0132!.google.protobuf.EnumValueOpt" + "ions\"\220\001\n\026ServiceDescriptorProto\022\014\n\004name\030" + "\001 \001(\t\0226\n\006method\030\002 \003(\0132&.google.protobuf." + "MethodDescriptorProto\0220\n\007options\030\003 \001(\0132\037" + ".google.protobuf.ServiceOptions\"\301\001\n\025Meth" + "odDescriptorProto\022\014\n\004name\030\001 \001(\t\022\022\n\ninput" + "_type\030\002 \001(\t\022\023\n\013output_type\030\003 \001(\t\022/\n\007opti" + "ons\030\004 \001(\0132\036.google.protobuf.MethodOption" + "s\022\037\n\020client_streaming\030\005 \001(\010:\005false\022\037\n\020se" + "rver_streaming\030\006 \001(\010:\005false\"\201\005\n\013FileOpti" + "ons\022\024\n\014java_package\030\001 \001(\t\022\034\n\024java_outer_" + "classname\030\010 \001(\t\022\"\n\023java_multiple_files\030\n" + " \001(\010:\005false\022,\n\035java_generate_equals_and_" + "hash\030\024 \001(\010:\005false\022%\n\026java_string_check_u" + "tf8\030\033 \001(\010:\005false\022F\n\014optimize_for\030\t \001(\0162)" + ".google.protobuf.FileOptions.OptimizeMod" + "e:\005SPEED\022\022\n\ngo_package\030\013 \001(\t\022\"\n\023cc_gener" + "ic_services\030\020 \001(\010:\005false\022$\n\025java_generic" + "_services\030\021 \001(\010:\005false\022\"\n\023py_generic_ser" + "vices\030\022 \001(\010:\005false\022\031\n\ndeprecated\030\027 \001(\010:\005" + "false\022\037\n\020cc_enable_arenas\030\037 \001(\010:\005false\022\031" + "\n\021objc_class_prefix\030$ \001(\t\022\030\n\020csharp_name" + "space\030% \001(\t\022C\n\024uninterpreted_option\030\347\007 \003" + "(\0132$.google.protobuf.UninterpretedOption" + "\":\n\014OptimizeMode\022\t\n\005SPEED\020\001\022\r\n\tCODE_SIZE" + "\020\002\022\020\n\014LITE_RUNTIME\020\003*\t\010\350\007\020\200\200\200\200\002\"\346\001\n\016Mess" + "ageOptions\022&\n\027message_set_wire_format\030\001 " + "\001(\010:\005false\022.\n\037no_standard_descriptor_acc" + "essor\030\002 \001(\010:\005false\022\031\n\ndeprecated\030\003 \001(\010:\005" + "false\022\021\n\tmap_entry\030\007 \001(\010\022C\n\024uninterprete" + "d_option\030\347\007 \003(\0132$.google.protobuf.Uninte" + "rpretedOption*\t\010\350\007\020\200\200\200\200\002\"\230\003\n\014FieldOption" + "s\022:\n\005ctype\030\001 \001(\0162#.google.protobuf.Field" + "Options.CType:\006STRING\022\016\n\006packed\030\002 \001(\010\022\?\n" + "\006jstype\030\006 \001(\0162$.google.protobuf.FieldOpt" + "ions.JSType:\tJS_NORMAL\022\023\n\004lazy\030\005 \001(\010:\005fa" + "lse\022\031\n\ndeprecated\030\003 \001(\010:\005false\022\023\n\004weak\030\n" " \001(\010:\005false\022C\n\024uninterpreted_option\030\347\007 \003" "(\0132$.google.protobuf.UninterpretedOption" - "*\t\010\350\007\020\200\200\200\200\002\"z\n\rMethodOptions\022\031\n\ndeprecat" - "ed\030! \001(\010:\005false\022C\n\024uninterpreted_option\030" - "\347\007 \003(\0132$.google.protobuf.UninterpretedOp" - "tion*\t\010\350\007\020\200\200\200\200\002\"\236\002\n\023UninterpretedOption\022" - ";\n\004name\030\002 \003(\0132-.google.protobuf.Uninterp" - "retedOption.NamePart\022\030\n\020identifier_value" - "\030\003 \001(\t\022\032\n\022positive_int_value\030\004 \001(\004\022\032\n\022ne" - "gative_int_value\030\005 \001(\003\022\024\n\014double_value\030\006" - " \001(\001\022\024\n\014string_value\030\007 \001(\014\022\027\n\017aggregate_" - "value\030\010 \001(\t\0323\n\010NamePart\022\021\n\tname_part\030\001 \002" - "(\t\022\024\n\014is_extension\030\002 \002(\010\"\325\001\n\016SourceCodeI" - "nfo\022:\n\010location\030\001 \003(\0132(.google.protobuf." - "SourceCodeInfo.Location\032\206\001\n\010Location\022\020\n\004" - "path\030\001 \003(\005B\002\020\001\022\020\n\004span\030\002 \003(\005B\002\020\001\022\030\n\020lead" - "ing_comments\030\003 \001(\t\022\031\n\021trailing_comments\030" - "\004 \001(\t\022!\n\031leading_detached_comments\030\006 \003(\t" - "BY\n\023com.google.protobufB\020DescriptorProto" - "sH\001\242\002\003GPB\252\002\'Google.ProtocolBuffers.Descr" - "iptorProtos", 4691); + "\"/\n\005CType\022\n\n\006STRING\020\000\022\010\n\004CORD\020\001\022\020\n\014STRIN" + "G_PIECE\020\002\"5\n\006JSType\022\r\n\tJS_NORMAL\020\000\022\r\n\tJS" + "_STRING\020\001\022\r\n\tJS_NUMBER\020\002*\t\010\350\007\020\200\200\200\200\002\"\215\001\n\013" + "EnumOptions\022\023\n\013allow_alias\030\002 \001(\010\022\031\n\ndepr" + "ecated\030\003 \001(\010:\005false\022C\n\024uninterpreted_opt" + "ion\030\347\007 \003(\0132$.google.protobuf.Uninterpret" + "edOption*\t\010\350\007\020\200\200\200\200\002\"}\n\020EnumValueOptions\022" + "\031\n\ndeprecated\030\001 \001(\010:\005false\022C\n\024uninterpre" + "ted_option\030\347\007 \003(\0132$.google.protobuf.Unin" + "terpretedOption*\t\010\350\007\020\200\200\200\200\002\"{\n\016ServiceOpt" + "ions\022\031\n\ndeprecated\030! \001(\010:\005false\022C\n\024unint" + "erpreted_option\030\347\007 \003(\0132$.google.protobuf" + ".UninterpretedOption*\t\010\350\007\020\200\200\200\200\002\"z\n\rMetho" + "dOptions\022\031\n\ndeprecated\030! \001(\010:\005false\022C\n\024u" + "ninterpreted_option\030\347\007 \003(\0132$.google.prot" + "obuf.UninterpretedOption*\t\010\350\007\020\200\200\200\200\002\"\236\002\n\023" + "UninterpretedOption\022;\n\004name\030\002 \003(\0132-.goog" + "le.protobuf.UninterpretedOption.NamePart" + "\022\030\n\020identifier_value\030\003 \001(\t\022\032\n\022positive_i" + "nt_value\030\004 \001(\004\022\032\n\022negative_int_value\030\005 \001" + "(\003\022\024\n\014double_value\030\006 \001(\001\022\024\n\014string_value" + "\030\007 \001(\014\022\027\n\017aggregate_value\030\010 \001(\t\0323\n\010NameP" + "art\022\021\n\tname_part\030\001 \002(\t\022\024\n\014is_extension\030\002" + " \002(\010\"\325\001\n\016SourceCodeInfo\022:\n\010location\030\001 \003(" + "\0132(.google.protobuf.SourceCodeInfo.Locat" + "ion\032\206\001\n\010Location\022\020\n\004path\030\001 \003(\005B\002\020\001\022\020\n\004sp" + "an\030\002 \003(\005B\002\020\001\022\030\n\020leading_comments\030\003 \001(\t\022\031" + "\n\021trailing_comments\030\004 \001(\t\022!\n\031leading_det" + "ached_comments\030\006 \003(\tBY\n\023com.google.proto" + "bufB\020DescriptorProtosH\001\242\002\003GPB\252\002\'Google.P" + "rotocolBuffers.DescriptorProtos", 4951); ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile( "google/protobuf/descriptor.proto", &protobuf_RegisterTypes); FileDescriptorSet::default_instance_ = new FileDescriptorSet(); FileDescriptorProto::default_instance_ = new FileDescriptorProto(); DescriptorProto::default_instance_ = new DescriptorProto(); DescriptorProto_ExtensionRange::default_instance_ = new DescriptorProto_ExtensionRange(); + DescriptorProto_ReservedRange::default_instance_ = new DescriptorProto_ReservedRange(); FieldDescriptorProto::default_instance_ = new FieldDescriptorProto(); OneofDescriptorProto::default_instance_ = new OneofDescriptorProto(); EnumDescriptorProto::default_instance_ = new EnumDescriptorProto(); @@ -749,6 +784,7 @@ void protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto() { FileDescriptorProto::default_instance_->InitAsDefaultInstance(); DescriptorProto::default_instance_->InitAsDefaultInstance(); DescriptorProto_ExtensionRange::default_instance_->InitAsDefaultInstance(); + DescriptorProto_ReservedRange::default_instance_->InitAsDefaultInstance(); FieldDescriptorProto::default_instance_->InitAsDefaultInstance(); OneofDescriptorProto::default_instance_->InitAsDefaultInstance(); EnumDescriptorProto::default_instance_->InitAsDefaultInstance(); @@ -793,7 +829,7 @@ const int FileDescriptorSet::kFileFieldNumber; #endif // !_MSC_VER FileDescriptorSet::FileDescriptorSet() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.FileDescriptorSet) } @@ -870,13 +906,15 @@ bool FileDescriptorSet::MergePartialFromCodedStream( // repeated .google.protobuf.FileDescriptorProto file = 1; case 1: { if (tag == 10) { - parse_file: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_file: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_file())); } else { goto handle_unusual; } - if (input->ExpectTag(10)) goto parse_file; + if (input->ExpectTag(10)) goto parse_loop_file; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectAtEnd()) goto success; break; } @@ -961,9 +999,9 @@ int FileDescriptorSet::ByteSize() const { void FileDescriptorSet::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const FileDescriptorSet* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const FileDescriptorSet* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -1020,10 +1058,10 @@ void FileDescriptorSet::InternalSwap(FileDescriptorSet* other) { // FileDescriptorSet // repeated .google.protobuf.FileDescriptorProto file = 1; - int FileDescriptorSet::file_size() const { +int FileDescriptorSet::file_size() const { return file_.size(); } - void FileDescriptorSet::clear_file() { +void FileDescriptorSet::clear_file() { file_.Clear(); } const ::google::protobuf::FileDescriptorProto& FileDescriptorSet::file(int index) const { @@ -1069,7 +1107,7 @@ const int FileDescriptorProto::kSyntaxFieldNumber; #endif // !_MSC_VER FileDescriptorProto::FileDescriptorProto() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.FileDescriptorProto) } @@ -1237,54 +1275,63 @@ bool FileDescriptorProto::MergePartialFromCodedStream( case 4: { if (tag == 34) { parse_message_type: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_message_type: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_message_type())); } else { goto handle_unusual; } - if (input->ExpectTag(34)) goto parse_message_type; - if (input->ExpectTag(42)) goto parse_enum_type; + if (input->ExpectTag(34)) goto parse_loop_message_type; + if (input->ExpectTag(42)) goto parse_loop_enum_type; + input->UnsafeDecrementRecursionDepth(); break; } // repeated .google.protobuf.EnumDescriptorProto enum_type = 5; case 5: { if (tag == 42) { - parse_enum_type: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_enum_type: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_enum_type())); } else { goto handle_unusual; } - if (input->ExpectTag(42)) goto parse_enum_type; - if (input->ExpectTag(50)) goto parse_service; + if (input->ExpectTag(42)) goto parse_loop_enum_type; + if (input->ExpectTag(50)) goto parse_loop_service; + input->UnsafeDecrementRecursionDepth(); break; } // repeated .google.protobuf.ServiceDescriptorProto service = 6; case 6: { if (tag == 50) { - parse_service: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_service: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_service())); } else { goto handle_unusual; } - if (input->ExpectTag(50)) goto parse_service; - if (input->ExpectTag(58)) goto parse_extension; + if (input->ExpectTag(50)) goto parse_loop_service; + if (input->ExpectTag(58)) goto parse_loop_extension; + input->UnsafeDecrementRecursionDepth(); break; } // repeated .google.protobuf.FieldDescriptorProto extension = 7; case 7: { if (tag == 58) { - parse_extension: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_extension: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_extension())); } else { goto handle_unusual; } - if (input->ExpectTag(58)) goto parse_extension; + if (input->ExpectTag(58)) goto parse_loop_extension; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectTag(66)) goto parse_options; break; } @@ -1712,9 +1759,9 @@ int FileDescriptorProto::ByteSize() const { void FileDescriptorProto::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const FileDescriptorProto* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const FileDescriptorProto* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -1816,16 +1863,16 @@ void FileDescriptorProto::InternalSwap(FileDescriptorProto* other) { // FileDescriptorProto // optional string name = 1; - bool FileDescriptorProto::has_name() const { +bool FileDescriptorProto::has_name() const { return (_has_bits_[0] & 0x00000001u) != 0; } - void FileDescriptorProto::set_has_name() { +void FileDescriptorProto::set_has_name() { _has_bits_[0] |= 0x00000001u; } - void FileDescriptorProto::clear_has_name() { +void FileDescriptorProto::clear_has_name() { _has_bits_[0] &= ~0x00000001u; } - void FileDescriptorProto::clear_name() { +void FileDescriptorProto::clear_name() { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_name(); } @@ -1869,16 +1916,16 @@ void FileDescriptorProto::InternalSwap(FileDescriptorProto* other) { } // optional string package = 2; - bool FileDescriptorProto::has_package() const { +bool FileDescriptorProto::has_package() const { return (_has_bits_[0] & 0x00000002u) != 0; } - void FileDescriptorProto::set_has_package() { +void FileDescriptorProto::set_has_package() { _has_bits_[0] |= 0x00000002u; } - void FileDescriptorProto::clear_has_package() { +void FileDescriptorProto::clear_has_package() { _has_bits_[0] &= ~0x00000002u; } - void FileDescriptorProto::clear_package() { +void FileDescriptorProto::clear_package() { package_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_package(); } @@ -1922,10 +1969,10 @@ void FileDescriptorProto::InternalSwap(FileDescriptorProto* other) { } // repeated string dependency = 3; - int FileDescriptorProto::dependency_size() const { +int FileDescriptorProto::dependency_size() const { return dependency_.size(); } - void FileDescriptorProto::clear_dependency() { +void FileDescriptorProto::clear_dependency() { dependency_.Clear(); } const ::std::string& FileDescriptorProto::dependency(int index) const { @@ -1976,10 +2023,10 @@ FileDescriptorProto::mutable_dependency() { } // repeated int32 public_dependency = 10; - int FileDescriptorProto::public_dependency_size() const { +int FileDescriptorProto::public_dependency_size() const { return public_dependency_.size(); } - void FileDescriptorProto::clear_public_dependency() { +void FileDescriptorProto::clear_public_dependency() { public_dependency_.Clear(); } ::google::protobuf::int32 FileDescriptorProto::public_dependency(int index) const { @@ -2006,10 +2053,10 @@ FileDescriptorProto::mutable_public_dependency() { } // repeated int32 weak_dependency = 11; - int FileDescriptorProto::weak_dependency_size() const { +int FileDescriptorProto::weak_dependency_size() const { return weak_dependency_.size(); } - void FileDescriptorProto::clear_weak_dependency() { +void FileDescriptorProto::clear_weak_dependency() { weak_dependency_.Clear(); } ::google::protobuf::int32 FileDescriptorProto::weak_dependency(int index) const { @@ -2036,10 +2083,10 @@ FileDescriptorProto::mutable_weak_dependency() { } // repeated .google.protobuf.DescriptorProto message_type = 4; - int FileDescriptorProto::message_type_size() const { +int FileDescriptorProto::message_type_size() const { return message_type_.size(); } - void FileDescriptorProto::clear_message_type() { +void FileDescriptorProto::clear_message_type() { message_type_.Clear(); } const ::google::protobuf::DescriptorProto& FileDescriptorProto::message_type(int index) const { @@ -2066,10 +2113,10 @@ FileDescriptorProto::mutable_message_type() { } // repeated .google.protobuf.EnumDescriptorProto enum_type = 5; - int FileDescriptorProto::enum_type_size() const { +int FileDescriptorProto::enum_type_size() const { return enum_type_.size(); } - void FileDescriptorProto::clear_enum_type() { +void FileDescriptorProto::clear_enum_type() { enum_type_.Clear(); } const ::google::protobuf::EnumDescriptorProto& FileDescriptorProto::enum_type(int index) const { @@ -2096,10 +2143,10 @@ FileDescriptorProto::mutable_enum_type() { } // repeated .google.protobuf.ServiceDescriptorProto service = 6; - int FileDescriptorProto::service_size() const { +int FileDescriptorProto::service_size() const { return service_.size(); } - void FileDescriptorProto::clear_service() { +void FileDescriptorProto::clear_service() { service_.Clear(); } const ::google::protobuf::ServiceDescriptorProto& FileDescriptorProto::service(int index) const { @@ -2126,10 +2173,10 @@ FileDescriptorProto::mutable_service() { } // repeated .google.protobuf.FieldDescriptorProto extension = 7; - int FileDescriptorProto::extension_size() const { +int FileDescriptorProto::extension_size() const { return extension_.size(); } - void FileDescriptorProto::clear_extension() { +void FileDescriptorProto::clear_extension() { extension_.Clear(); } const ::google::protobuf::FieldDescriptorProto& FileDescriptorProto::extension(int index) const { @@ -2156,16 +2203,16 @@ FileDescriptorProto::mutable_extension() { } // optional .google.protobuf.FileOptions options = 8; - bool FileDescriptorProto::has_options() const { +bool FileDescriptorProto::has_options() const { return (_has_bits_[0] & 0x00000200u) != 0; } - void FileDescriptorProto::set_has_options() { +void FileDescriptorProto::set_has_options() { _has_bits_[0] |= 0x00000200u; } - void FileDescriptorProto::clear_has_options() { +void FileDescriptorProto::clear_has_options() { _has_bits_[0] &= ~0x00000200u; } - void FileDescriptorProto::clear_options() { +void FileDescriptorProto::clear_options() { if (options_ != NULL) options_->::google::protobuf::FileOptions::Clear(); clear_has_options(); } @@ -2199,16 +2246,16 @@ FileDescriptorProto::mutable_extension() { } // optional .google.protobuf.SourceCodeInfo source_code_info = 9; - bool FileDescriptorProto::has_source_code_info() const { +bool FileDescriptorProto::has_source_code_info() const { return (_has_bits_[0] & 0x00000400u) != 0; } - void FileDescriptorProto::set_has_source_code_info() { +void FileDescriptorProto::set_has_source_code_info() { _has_bits_[0] |= 0x00000400u; } - void FileDescriptorProto::clear_has_source_code_info() { +void FileDescriptorProto::clear_has_source_code_info() { _has_bits_[0] &= ~0x00000400u; } - void FileDescriptorProto::clear_source_code_info() { +void FileDescriptorProto::clear_source_code_info() { if (source_code_info_ != NULL) source_code_info_->::google::protobuf::SourceCodeInfo::Clear(); clear_has_source_code_info(); } @@ -2242,16 +2289,16 @@ FileDescriptorProto::mutable_extension() { } // optional string syntax = 12; - bool FileDescriptorProto::has_syntax() const { +bool FileDescriptorProto::has_syntax() const { return (_has_bits_[0] & 0x00000800u) != 0; } - void FileDescriptorProto::set_has_syntax() { +void FileDescriptorProto::set_has_syntax() { _has_bits_[0] |= 0x00000800u; } - void FileDescriptorProto::clear_has_syntax() { +void FileDescriptorProto::clear_has_syntax() { _has_bits_[0] &= ~0x00000800u; } - void FileDescriptorProto::clear_syntax() { +void FileDescriptorProto::clear_syntax() { syntax_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_syntax(); } @@ -2304,7 +2351,7 @@ const int DescriptorProto_ExtensionRange::kEndFieldNumber; #endif // !_MSC_VER DescriptorProto_ExtensionRange::DescriptorProto_ExtensionRange() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.DescriptorProto.ExtensionRange) } @@ -2516,9 +2563,9 @@ int DescriptorProto_ExtensionRange::ByteSize() const { void DescriptorProto_ExtensionRange::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const DescriptorProto_ExtensionRange* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const DescriptorProto_ExtensionRange* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -2579,6 +2626,289 @@ void DescriptorProto_ExtensionRange::InternalSwap(DescriptorProto_ExtensionRange } +// ------------------------------------------------------------------- + +#ifndef _MSC_VER +const int DescriptorProto_ReservedRange::kStartFieldNumber; +const int DescriptorProto_ReservedRange::kEndFieldNumber; +#endif // !_MSC_VER + +DescriptorProto_ReservedRange::DescriptorProto_ReservedRange() + : ::google::protobuf::Message(), _internal_metadata_(NULL) { + SharedCtor(); + // @@protoc_insertion_point(constructor:google.protobuf.DescriptorProto.ReservedRange) +} + +void DescriptorProto_ReservedRange::InitAsDefaultInstance() { +} + +DescriptorProto_ReservedRange::DescriptorProto_ReservedRange(const DescriptorProto_ReservedRange& from) + : ::google::protobuf::Message(), + _internal_metadata_(NULL) { + SharedCtor(); + MergeFrom(from); + // @@protoc_insertion_point(copy_constructor:google.protobuf.DescriptorProto.ReservedRange) +} + +void DescriptorProto_ReservedRange::SharedCtor() { + _cached_size_ = 0; + start_ = 0; + end_ = 0; + ::memset(_has_bits_, 0, sizeof(_has_bits_)); +} + +DescriptorProto_ReservedRange::~DescriptorProto_ReservedRange() { + // @@protoc_insertion_point(destructor:google.protobuf.DescriptorProto.ReservedRange) + SharedDtor(); +} + +void DescriptorProto_ReservedRange::SharedDtor() { + if (this != default_instance_) { + } +} + +void DescriptorProto_ReservedRange::SetCachedSize(int size) const { + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); +} +const ::google::protobuf::Descriptor* DescriptorProto_ReservedRange::descriptor() { + protobuf_AssignDescriptorsOnce(); + return DescriptorProto_ReservedRange_descriptor_; +} + +const DescriptorProto_ReservedRange& DescriptorProto_ReservedRange::default_instance() { + if (default_instance_ == NULL) protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); + return *default_instance_; +} + +DescriptorProto_ReservedRange* DescriptorProto_ReservedRange::default_instance_ = NULL; + +DescriptorProto_ReservedRange* DescriptorProto_ReservedRange::New(::google::protobuf::Arena* arena) const { + DescriptorProto_ReservedRange* n = new DescriptorProto_ReservedRange; + if (arena != NULL) { + arena->Own(n); + } + return n; +} + +void DescriptorProto_ReservedRange::Clear() { +#define ZR_HELPER_(f) reinterpret_cast(\ + &reinterpret_cast(16)->f) + +#define ZR_(first, last) do {\ + ::memset(&first, 0,\ + ZR_HELPER_(last) - ZR_HELPER_(first) + sizeof(last));\ +} while (0) + + ZR_(start_, end_); + +#undef ZR_HELPER_ +#undef ZR_ + + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + if (_internal_metadata_.have_unknown_fields()) { + mutable_unknown_fields()->Clear(); + } +} + +bool DescriptorProto_ReservedRange::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) goto failure + ::google::protobuf::uint32 tag; + // @@protoc_insertion_point(parse_start:google.protobuf.DescriptorProto.ReservedRange) + for (;;) { + ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127); + tag = p.first; + if (!p.second) goto handle_unusual; + switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { + // optional int32 start = 1; + case 1: { + if (tag == 8) { + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>( + input, &start_))); + set_has_start(); + } else { + goto handle_unusual; + } + if (input->ExpectTag(16)) goto parse_end; + break; + } + + // optional int32 end = 2; + case 2: { + if (tag == 16) { + parse_end: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>( + input, &end_))); + set_has_end(); + } else { + goto handle_unusual; + } + if (input->ExpectAtEnd()) goto success; + break; + } + + default: { + handle_unusual: + if (tag == 0 || + ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { + goto success; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } +success: + // @@protoc_insertion_point(parse_success:google.protobuf.DescriptorProto.ReservedRange) + return true; +failure: + // @@protoc_insertion_point(parse_failure:google.protobuf.DescriptorProto.ReservedRange) + return false; +#undef DO_ +} + +void DescriptorProto_ReservedRange::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { + // @@protoc_insertion_point(serialize_start:google.protobuf.DescriptorProto.ReservedRange) + // optional int32 start = 1; + if (has_start()) { + ::google::protobuf::internal::WireFormatLite::WriteInt32(1, this->start(), output); + } + + // optional int32 end = 2; + if (has_end()) { + ::google::protobuf::internal::WireFormatLite::WriteInt32(2, this->end(), output); + } + + if (_internal_metadata_.have_unknown_fields()) { + ::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output); + } + // @@protoc_insertion_point(serialize_end:google.protobuf.DescriptorProto.ReservedRange) +} + +::google::protobuf::uint8* DescriptorProto_ReservedRange::SerializeWithCachedSizesToArray( + ::google::protobuf::uint8* target) const { + // @@protoc_insertion_point(serialize_to_array_start:google.protobuf.DescriptorProto.ReservedRange) + // optional int32 start = 1; + if (has_start()) { + target = ::google::protobuf::internal::WireFormatLite::WriteInt32ToArray(1, this->start(), target); + } + + // optional int32 end = 2; + if (has_end()) { + target = ::google::protobuf::internal::WireFormatLite::WriteInt32ToArray(2, this->end(), target); + } + + if (_internal_metadata_.have_unknown_fields()) { + target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( + unknown_fields(), target); + } + // @@protoc_insertion_point(serialize_to_array_end:google.protobuf.DescriptorProto.ReservedRange) + return target; +} + +int DescriptorProto_ReservedRange::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & 3) { + // optional int32 start = 1; + if (has_start()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::Int32Size( + this->start()); + } + + // optional int32 end = 2; + if (has_end()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::Int32Size( + this->end()); + } + + } + if (_internal_metadata_.have_unknown_fields()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = total_size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); + return total_size; +} + +void DescriptorProto_ReservedRange::MergeFrom(const ::google::protobuf::Message& from) { + if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); + const DescriptorProto_ReservedRange* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + MergeFrom(*source); + } +} + +void DescriptorProto_ReservedRange::MergeFrom(const DescriptorProto_ReservedRange& from) { + if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from.has_start()) { + set_start(from.start()); + } + if (from.has_end()) { + set_end(from.end()); + } + } + if (from._internal_metadata_.have_unknown_fields()) { + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); + } +} + +void DescriptorProto_ReservedRange::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void DescriptorProto_ReservedRange::CopyFrom(const DescriptorProto_ReservedRange& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool DescriptorProto_ReservedRange::IsInitialized() const { + + return true; +} + +void DescriptorProto_ReservedRange::Swap(DescriptorProto_ReservedRange* other) { + if (other == this) return; + InternalSwap(other); +} +void DescriptorProto_ReservedRange::InternalSwap(DescriptorProto_ReservedRange* other) { + std::swap(start_, other->start_); + std::swap(end_, other->end_); + std::swap(_has_bits_[0], other->_has_bits_[0]); + _internal_metadata_.Swap(&other->_internal_metadata_); + std::swap(_cached_size_, other->_cached_size_); +} + +::google::protobuf::Metadata DescriptorProto_ReservedRange::GetMetadata() const { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::Metadata metadata; + metadata.descriptor = DescriptorProto_ReservedRange_descriptor_; + metadata.reflection = DescriptorProto_ReservedRange_reflection_; + return metadata; +} + + // ------------------------------------------------------------------- #ifndef _MSC_VER @@ -2590,10 +2920,12 @@ const int DescriptorProto::kEnumTypeFieldNumber; const int DescriptorProto::kExtensionRangeFieldNumber; const int DescriptorProto::kOneofDeclFieldNumber; const int DescriptorProto::kOptionsFieldNumber; +const int DescriptorProto::kReservedRangeFieldNumber; +const int DescriptorProto::kReservedNameFieldNumber; #endif // !_MSC_VER DescriptorProto::DescriptorProto() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.DescriptorProto) } @@ -2670,6 +3002,8 @@ void DescriptorProto::Clear() { enum_type_.Clear(); extension_range_.Clear(); oneof_decl_.Clear(); + reserved_range_.Clear(); + reserved_name_.Clear(); ::memset(_has_bits_, 0, sizeof(_has_bits_)); if (_internal_metadata_.have_unknown_fields()) { mutable_unknown_fields()->Clear(); @@ -2706,68 +3040,79 @@ bool DescriptorProto::MergePartialFromCodedStream( case 2: { if (tag == 18) { parse_field: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_field: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_field())); } else { goto handle_unusual; } - if (input->ExpectTag(18)) goto parse_field; - if (input->ExpectTag(26)) goto parse_nested_type; + if (input->ExpectTag(18)) goto parse_loop_field; + if (input->ExpectTag(26)) goto parse_loop_nested_type; + input->UnsafeDecrementRecursionDepth(); break; } // repeated .google.protobuf.DescriptorProto nested_type = 3; case 3: { if (tag == 26) { - parse_nested_type: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_nested_type: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_nested_type())); } else { goto handle_unusual; } - if (input->ExpectTag(26)) goto parse_nested_type; - if (input->ExpectTag(34)) goto parse_enum_type; + if (input->ExpectTag(26)) goto parse_loop_nested_type; + if (input->ExpectTag(34)) goto parse_loop_enum_type; + input->UnsafeDecrementRecursionDepth(); break; } // repeated .google.protobuf.EnumDescriptorProto enum_type = 4; case 4: { if (tag == 34) { - parse_enum_type: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_enum_type: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_enum_type())); } else { goto handle_unusual; } - if (input->ExpectTag(34)) goto parse_enum_type; - if (input->ExpectTag(42)) goto parse_extension_range; + if (input->ExpectTag(34)) goto parse_loop_enum_type; + if (input->ExpectTag(42)) goto parse_loop_extension_range; + input->UnsafeDecrementRecursionDepth(); break; } // repeated .google.protobuf.DescriptorProto.ExtensionRange extension_range = 5; case 5: { if (tag == 42) { - parse_extension_range: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_extension_range: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_extension_range())); } else { goto handle_unusual; } - if (input->ExpectTag(42)) goto parse_extension_range; - if (input->ExpectTag(50)) goto parse_extension; + if (input->ExpectTag(42)) goto parse_loop_extension_range; + if (input->ExpectTag(50)) goto parse_loop_extension; + input->UnsafeDecrementRecursionDepth(); break; } // repeated .google.protobuf.FieldDescriptorProto extension = 6; case 6: { if (tag == 50) { - parse_extension: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_extension: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_extension())); } else { goto handle_unusual; } - if (input->ExpectTag(50)) goto parse_extension; + if (input->ExpectTag(50)) goto parse_loop_extension; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectTag(58)) goto parse_options; break; } @@ -2789,12 +3134,50 @@ bool DescriptorProto::MergePartialFromCodedStream( case 8: { if (tag == 66) { parse_oneof_decl: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_oneof_decl: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_oneof_decl())); } else { goto handle_unusual; } - if (input->ExpectTag(66)) goto parse_oneof_decl; + if (input->ExpectTag(66)) goto parse_loop_oneof_decl; + if (input->ExpectTag(74)) goto parse_loop_reserved_range; + input->UnsafeDecrementRecursionDepth(); + break; + } + + // repeated .google.protobuf.DescriptorProto.ReservedRange reserved_range = 9; + case 9: { + if (tag == 74) { + DO_(input->IncrementRecursionDepth()); + parse_loop_reserved_range: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( + input, add_reserved_range())); + } else { + goto handle_unusual; + } + if (input->ExpectTag(74)) goto parse_loop_reserved_range; + input->UnsafeDecrementRecursionDepth(); + if (input->ExpectTag(82)) goto parse_reserved_name; + break; + } + + // repeated string reserved_name = 10; + case 10: { + if (tag == 82) { + parse_reserved_name: + DO_(::google::protobuf::internal::WireFormatLite::ReadString( + input, this->add_reserved_name())); + ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField( + this->reserved_name(this->reserved_name_size() - 1).data(), + this->reserved_name(this->reserved_name_size() - 1).length(), + ::google::protobuf::internal::WireFormat::PARSE, + "google.protobuf.DescriptorProto.reserved_name"); + } else { + goto handle_unusual; + } + if (input->ExpectTag(82)) goto parse_reserved_name; if (input->ExpectAtEnd()) goto success; break; } @@ -2876,6 +3259,22 @@ void DescriptorProto::SerializeWithCachedSizes( 8, this->oneof_decl(i), output); } + // repeated .google.protobuf.DescriptorProto.ReservedRange reserved_range = 9; + for (unsigned int i = 0, n = this->reserved_range_size(); i < n; i++) { + ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( + 9, this->reserved_range(i), output); + } + + // repeated string reserved_name = 10; + for (int i = 0; i < this->reserved_name_size(); i++) { + ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField( + this->reserved_name(i).data(), this->reserved_name(i).length(), + ::google::protobuf::internal::WireFormat::SERIALIZE, + "google.protobuf.DescriptorProto.reserved_name"); + ::google::protobuf::internal::WireFormatLite::WriteString( + 10, this->reserved_name(i), output); + } + if (_internal_metadata_.have_unknown_fields()) { ::google::protobuf::internal::WireFormat::SerializeUnknownFields( unknown_fields(), output); @@ -2946,6 +3345,23 @@ void DescriptorProto::SerializeWithCachedSizes( 8, this->oneof_decl(i), target); } + // repeated .google.protobuf.DescriptorProto.ReservedRange reserved_range = 9; + for (unsigned int i = 0, n = this->reserved_range_size(); i < n; i++) { + target = ::google::protobuf::internal::WireFormatLite:: + WriteMessageNoVirtualToArray( + 9, this->reserved_range(i), target); + } + + // repeated string reserved_name = 10; + for (int i = 0; i < this->reserved_name_size(); i++) { + ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField( + this->reserved_name(i).data(), this->reserved_name(i).length(), + ::google::protobuf::internal::WireFormat::SERIALIZE, + "google.protobuf.DescriptorProto.reserved_name"); + target = ::google::protobuf::internal::WireFormatLite:: + WriteStringToArray(10, this->reserved_name(i), target); + } + if (_internal_metadata_.have_unknown_fields()) { target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( unknown_fields(), target); @@ -3021,6 +3437,21 @@ int DescriptorProto::ByteSize() const { this->oneof_decl(i)); } + // repeated .google.protobuf.DescriptorProto.ReservedRange reserved_range = 9; + total_size += 1 * this->reserved_range_size(); + for (int i = 0; i < this->reserved_range_size(); i++) { + total_size += + ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual( + this->reserved_range(i)); + } + + // repeated string reserved_name = 10; + total_size += 1 * this->reserved_name_size(); + for (int i = 0; i < this->reserved_name_size(); i++) { + total_size += ::google::protobuf::internal::WireFormatLite::StringSize( + this->reserved_name(i)); + } + if (_internal_metadata_.have_unknown_fields()) { total_size += ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( @@ -3034,9 +3465,9 @@ int DescriptorProto::ByteSize() const { void DescriptorProto::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const DescriptorProto* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const DescriptorProto* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -3052,6 +3483,8 @@ void DescriptorProto::MergeFrom(const DescriptorProto& from) { enum_type_.MergeFrom(from.enum_type_); extension_range_.MergeFrom(from.extension_range_); oneof_decl_.MergeFrom(from.oneof_decl_); + reserved_range_.MergeFrom(from.reserved_range_); + reserved_name_.MergeFrom(from.reserved_name_); if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { if (from.has_name()) { set_has_name(); @@ -3103,6 +3536,8 @@ void DescriptorProto::InternalSwap(DescriptorProto* other) { extension_range_.UnsafeArenaSwap(&other->extension_range_); oneof_decl_.UnsafeArenaSwap(&other->oneof_decl_); std::swap(options_, other->options_); + reserved_range_.UnsafeArenaSwap(&other->reserved_range_); + reserved_name_.UnsafeArenaSwap(&other->reserved_name_); std::swap(_has_bits_[0], other->_has_bits_[0]); _internal_metadata_.Swap(&other->_internal_metadata_); std::swap(_cached_size_, other->_cached_size_); @@ -3120,16 +3555,16 @@ void DescriptorProto::InternalSwap(DescriptorProto* other) { // DescriptorProto_ExtensionRange // optional int32 start = 1; - bool DescriptorProto_ExtensionRange::has_start() const { +bool DescriptorProto_ExtensionRange::has_start() const { return (_has_bits_[0] & 0x00000001u) != 0; } - void DescriptorProto_ExtensionRange::set_has_start() { +void DescriptorProto_ExtensionRange::set_has_start() { _has_bits_[0] |= 0x00000001u; } - void DescriptorProto_ExtensionRange::clear_has_start() { +void DescriptorProto_ExtensionRange::clear_has_start() { _has_bits_[0] &= ~0x00000001u; } - void DescriptorProto_ExtensionRange::clear_start() { +void DescriptorProto_ExtensionRange::clear_start() { start_ = 0; clear_has_start(); } @@ -3144,16 +3579,16 @@ void DescriptorProto::InternalSwap(DescriptorProto* other) { } // optional int32 end = 2; - bool DescriptorProto_ExtensionRange::has_end() const { +bool DescriptorProto_ExtensionRange::has_end() const { return (_has_bits_[0] & 0x00000002u) != 0; } - void DescriptorProto_ExtensionRange::set_has_end() { +void DescriptorProto_ExtensionRange::set_has_end() { _has_bits_[0] |= 0x00000002u; } - void DescriptorProto_ExtensionRange::clear_has_end() { +void DescriptorProto_ExtensionRange::clear_has_end() { _has_bits_[0] &= ~0x00000002u; } - void DescriptorProto_ExtensionRange::clear_end() { +void DescriptorProto_ExtensionRange::clear_end() { end_ = 0; clear_has_end(); } @@ -3169,19 +3604,71 @@ void DescriptorProto::InternalSwap(DescriptorProto* other) { // ------------------------------------------------------------------- +// DescriptorProto_ReservedRange + +// optional int32 start = 1; +bool DescriptorProto_ReservedRange::has_start() const { + return (_has_bits_[0] & 0x00000001u) != 0; +} +void DescriptorProto_ReservedRange::set_has_start() { + _has_bits_[0] |= 0x00000001u; +} +void DescriptorProto_ReservedRange::clear_has_start() { + _has_bits_[0] &= ~0x00000001u; +} +void DescriptorProto_ReservedRange::clear_start() { + start_ = 0; + clear_has_start(); +} + ::google::protobuf::int32 DescriptorProto_ReservedRange::start() const { + // @@protoc_insertion_point(field_get:google.protobuf.DescriptorProto.ReservedRange.start) + return start_; +} + void DescriptorProto_ReservedRange::set_start(::google::protobuf::int32 value) { + set_has_start(); + start_ = value; + // @@protoc_insertion_point(field_set:google.protobuf.DescriptorProto.ReservedRange.start) +} + +// optional int32 end = 2; +bool DescriptorProto_ReservedRange::has_end() const { + return (_has_bits_[0] & 0x00000002u) != 0; +} +void DescriptorProto_ReservedRange::set_has_end() { + _has_bits_[0] |= 0x00000002u; +} +void DescriptorProto_ReservedRange::clear_has_end() { + _has_bits_[0] &= ~0x00000002u; +} +void DescriptorProto_ReservedRange::clear_end() { + end_ = 0; + clear_has_end(); +} + ::google::protobuf::int32 DescriptorProto_ReservedRange::end() const { + // @@protoc_insertion_point(field_get:google.protobuf.DescriptorProto.ReservedRange.end) + return end_; +} + void DescriptorProto_ReservedRange::set_end(::google::protobuf::int32 value) { + set_has_end(); + end_ = value; + // @@protoc_insertion_point(field_set:google.protobuf.DescriptorProto.ReservedRange.end) +} + +// ------------------------------------------------------------------- + // DescriptorProto // optional string name = 1; - bool DescriptorProto::has_name() const { +bool DescriptorProto::has_name() const { return (_has_bits_[0] & 0x00000001u) != 0; } - void DescriptorProto::set_has_name() { +void DescriptorProto::set_has_name() { _has_bits_[0] |= 0x00000001u; } - void DescriptorProto::clear_has_name() { +void DescriptorProto::clear_has_name() { _has_bits_[0] &= ~0x00000001u; } - void DescriptorProto::clear_name() { +void DescriptorProto::clear_name() { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_name(); } @@ -3225,10 +3712,10 @@ void DescriptorProto::InternalSwap(DescriptorProto* other) { } // repeated .google.protobuf.FieldDescriptorProto field = 2; - int DescriptorProto::field_size() const { +int DescriptorProto::field_size() const { return field_.size(); } - void DescriptorProto::clear_field() { +void DescriptorProto::clear_field() { field_.Clear(); } const ::google::protobuf::FieldDescriptorProto& DescriptorProto::field(int index) const { @@ -3255,10 +3742,10 @@ DescriptorProto::mutable_field() { } // repeated .google.protobuf.FieldDescriptorProto extension = 6; - int DescriptorProto::extension_size() const { +int DescriptorProto::extension_size() const { return extension_.size(); } - void DescriptorProto::clear_extension() { +void DescriptorProto::clear_extension() { extension_.Clear(); } const ::google::protobuf::FieldDescriptorProto& DescriptorProto::extension(int index) const { @@ -3285,10 +3772,10 @@ DescriptorProto::mutable_extension() { } // repeated .google.protobuf.DescriptorProto nested_type = 3; - int DescriptorProto::nested_type_size() const { +int DescriptorProto::nested_type_size() const { return nested_type_.size(); } - void DescriptorProto::clear_nested_type() { +void DescriptorProto::clear_nested_type() { nested_type_.Clear(); } const ::google::protobuf::DescriptorProto& DescriptorProto::nested_type(int index) const { @@ -3315,10 +3802,10 @@ DescriptorProto::mutable_nested_type() { } // repeated .google.protobuf.EnumDescriptorProto enum_type = 4; - int DescriptorProto::enum_type_size() const { +int DescriptorProto::enum_type_size() const { return enum_type_.size(); } - void DescriptorProto::clear_enum_type() { +void DescriptorProto::clear_enum_type() { enum_type_.Clear(); } const ::google::protobuf::EnumDescriptorProto& DescriptorProto::enum_type(int index) const { @@ -3345,10 +3832,10 @@ DescriptorProto::mutable_enum_type() { } // repeated .google.protobuf.DescriptorProto.ExtensionRange extension_range = 5; - int DescriptorProto::extension_range_size() const { +int DescriptorProto::extension_range_size() const { return extension_range_.size(); } - void DescriptorProto::clear_extension_range() { +void DescriptorProto::clear_extension_range() { extension_range_.Clear(); } const ::google::protobuf::DescriptorProto_ExtensionRange& DescriptorProto::extension_range(int index) const { @@ -3375,10 +3862,10 @@ DescriptorProto::mutable_extension_range() { } // repeated .google.protobuf.OneofDescriptorProto oneof_decl = 8; - int DescriptorProto::oneof_decl_size() const { +int DescriptorProto::oneof_decl_size() const { return oneof_decl_.size(); } - void DescriptorProto::clear_oneof_decl() { +void DescriptorProto::clear_oneof_decl() { oneof_decl_.Clear(); } const ::google::protobuf::OneofDescriptorProto& DescriptorProto::oneof_decl(int index) const { @@ -3405,16 +3892,16 @@ DescriptorProto::mutable_oneof_decl() { } // optional .google.protobuf.MessageOptions options = 7; - bool DescriptorProto::has_options() const { +bool DescriptorProto::has_options() const { return (_has_bits_[0] & 0x00000080u) != 0; } - void DescriptorProto::set_has_options() { +void DescriptorProto::set_has_options() { _has_bits_[0] |= 0x00000080u; } - void DescriptorProto::clear_has_options() { +void DescriptorProto::clear_has_options() { _has_bits_[0] &= ~0x00000080u; } - void DescriptorProto::clear_options() { +void DescriptorProto::clear_options() { if (options_ != NULL) options_->::google::protobuf::MessageOptions::Clear(); clear_has_options(); } @@ -3447,6 +3934,90 @@ DescriptorProto::mutable_oneof_decl() { // @@protoc_insertion_point(field_set_allocated:google.protobuf.DescriptorProto.options) } +// repeated .google.protobuf.DescriptorProto.ReservedRange reserved_range = 9; +int DescriptorProto::reserved_range_size() const { + return reserved_range_.size(); +} +void DescriptorProto::clear_reserved_range() { + reserved_range_.Clear(); +} + const ::google::protobuf::DescriptorProto_ReservedRange& DescriptorProto::reserved_range(int index) const { + // @@protoc_insertion_point(field_get:google.protobuf.DescriptorProto.reserved_range) + return reserved_range_.Get(index); +} + ::google::protobuf::DescriptorProto_ReservedRange* DescriptorProto::mutable_reserved_range(int index) { + // @@protoc_insertion_point(field_mutable:google.protobuf.DescriptorProto.reserved_range) + return reserved_range_.Mutable(index); +} + ::google::protobuf::DescriptorProto_ReservedRange* DescriptorProto::add_reserved_range() { + // @@protoc_insertion_point(field_add:google.protobuf.DescriptorProto.reserved_range) + return reserved_range_.Add(); +} + const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ReservedRange >& +DescriptorProto::reserved_range() const { + // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.reserved_range) + return reserved_range_; +} + ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ReservedRange >* +DescriptorProto::mutable_reserved_range() { + // @@protoc_insertion_point(field_mutable_list:google.protobuf.DescriptorProto.reserved_range) + return &reserved_range_; +} + +// repeated string reserved_name = 10; +int DescriptorProto::reserved_name_size() const { + return reserved_name_.size(); +} +void DescriptorProto::clear_reserved_name() { + reserved_name_.Clear(); +} + const ::std::string& DescriptorProto::reserved_name(int index) const { + // @@protoc_insertion_point(field_get:google.protobuf.DescriptorProto.reserved_name) + return reserved_name_.Get(index); +} + ::std::string* DescriptorProto::mutable_reserved_name(int index) { + // @@protoc_insertion_point(field_mutable:google.protobuf.DescriptorProto.reserved_name) + return reserved_name_.Mutable(index); +} + void DescriptorProto::set_reserved_name(int index, const ::std::string& value) { + // @@protoc_insertion_point(field_set:google.protobuf.DescriptorProto.reserved_name) + reserved_name_.Mutable(index)->assign(value); +} + void DescriptorProto::set_reserved_name(int index, const char* value) { + reserved_name_.Mutable(index)->assign(value); + // @@protoc_insertion_point(field_set_char:google.protobuf.DescriptorProto.reserved_name) +} + void DescriptorProto::set_reserved_name(int index, const char* value, size_t size) { + reserved_name_.Mutable(index)->assign( + reinterpret_cast(value), size); + // @@protoc_insertion_point(field_set_pointer:google.protobuf.DescriptorProto.reserved_name) +} + ::std::string* DescriptorProto::add_reserved_name() { + return reserved_name_.Add(); +} + void DescriptorProto::add_reserved_name(const ::std::string& value) { + reserved_name_.Add()->assign(value); + // @@protoc_insertion_point(field_add:google.protobuf.DescriptorProto.reserved_name) +} + void DescriptorProto::add_reserved_name(const char* value) { + reserved_name_.Add()->assign(value); + // @@protoc_insertion_point(field_add_char:google.protobuf.DescriptorProto.reserved_name) +} + void DescriptorProto::add_reserved_name(const char* value, size_t size) { + reserved_name_.Add()->assign(reinterpret_cast(value), size); + // @@protoc_insertion_point(field_add_pointer:google.protobuf.DescriptorProto.reserved_name) +} + const ::google::protobuf::RepeatedPtrField< ::std::string>& +DescriptorProto::reserved_name() const { + // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.reserved_name) + return reserved_name_; +} + ::google::protobuf::RepeatedPtrField< ::std::string>* +DescriptorProto::mutable_reserved_name() { + // @@protoc_insertion_point(field_mutable_list:google.protobuf.DescriptorProto.reserved_name) + return &reserved_name_; +} + #endif // PROTOBUF_INLINE_NOT_IN_HEADERS // =================================================================== @@ -3540,7 +4111,7 @@ const int FieldDescriptorProto::kOptionsFieldNumber; #endif // !_MSC_VER FieldDescriptorProto::FieldDescriptorProto() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.FieldDescriptorProto) } @@ -4063,9 +4634,9 @@ int FieldDescriptorProto::ByteSize() const { void FieldDescriptorProto::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const FieldDescriptorProto* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const FieldDescriptorProto* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -4166,16 +4737,16 @@ void FieldDescriptorProto::InternalSwap(FieldDescriptorProto* other) { // FieldDescriptorProto // optional string name = 1; - bool FieldDescriptorProto::has_name() const { +bool FieldDescriptorProto::has_name() const { return (_has_bits_[0] & 0x00000001u) != 0; } - void FieldDescriptorProto::set_has_name() { +void FieldDescriptorProto::set_has_name() { _has_bits_[0] |= 0x00000001u; } - void FieldDescriptorProto::clear_has_name() { +void FieldDescriptorProto::clear_has_name() { _has_bits_[0] &= ~0x00000001u; } - void FieldDescriptorProto::clear_name() { +void FieldDescriptorProto::clear_name() { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_name(); } @@ -4219,16 +4790,16 @@ void FieldDescriptorProto::InternalSwap(FieldDescriptorProto* other) { } // optional int32 number = 3; - bool FieldDescriptorProto::has_number() const { +bool FieldDescriptorProto::has_number() const { return (_has_bits_[0] & 0x00000002u) != 0; } - void FieldDescriptorProto::set_has_number() { +void FieldDescriptorProto::set_has_number() { _has_bits_[0] |= 0x00000002u; } - void FieldDescriptorProto::clear_has_number() { +void FieldDescriptorProto::clear_has_number() { _has_bits_[0] &= ~0x00000002u; } - void FieldDescriptorProto::clear_number() { +void FieldDescriptorProto::clear_number() { number_ = 0; clear_has_number(); } @@ -4243,16 +4814,16 @@ void FieldDescriptorProto::InternalSwap(FieldDescriptorProto* other) { } // optional .google.protobuf.FieldDescriptorProto.Label label = 4; - bool FieldDescriptorProto::has_label() const { +bool FieldDescriptorProto::has_label() const { return (_has_bits_[0] & 0x00000004u) != 0; } - void FieldDescriptorProto::set_has_label() { +void FieldDescriptorProto::set_has_label() { _has_bits_[0] |= 0x00000004u; } - void FieldDescriptorProto::clear_has_label() { +void FieldDescriptorProto::clear_has_label() { _has_bits_[0] &= ~0x00000004u; } - void FieldDescriptorProto::clear_label() { +void FieldDescriptorProto::clear_label() { label_ = 1; clear_has_label(); } @@ -4268,16 +4839,16 @@ void FieldDescriptorProto::InternalSwap(FieldDescriptorProto* other) { } // optional .google.protobuf.FieldDescriptorProto.Type type = 5; - bool FieldDescriptorProto::has_type() const { +bool FieldDescriptorProto::has_type() const { return (_has_bits_[0] & 0x00000008u) != 0; } - void FieldDescriptorProto::set_has_type() { +void FieldDescriptorProto::set_has_type() { _has_bits_[0] |= 0x00000008u; } - void FieldDescriptorProto::clear_has_type() { +void FieldDescriptorProto::clear_has_type() { _has_bits_[0] &= ~0x00000008u; } - void FieldDescriptorProto::clear_type() { +void FieldDescriptorProto::clear_type() { type_ = 1; clear_has_type(); } @@ -4293,16 +4864,16 @@ void FieldDescriptorProto::InternalSwap(FieldDescriptorProto* other) { } // optional string type_name = 6; - bool FieldDescriptorProto::has_type_name() const { +bool FieldDescriptorProto::has_type_name() const { return (_has_bits_[0] & 0x00000010u) != 0; } - void FieldDescriptorProto::set_has_type_name() { +void FieldDescriptorProto::set_has_type_name() { _has_bits_[0] |= 0x00000010u; } - void FieldDescriptorProto::clear_has_type_name() { +void FieldDescriptorProto::clear_has_type_name() { _has_bits_[0] &= ~0x00000010u; } - void FieldDescriptorProto::clear_type_name() { +void FieldDescriptorProto::clear_type_name() { type_name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_type_name(); } @@ -4346,16 +4917,16 @@ void FieldDescriptorProto::InternalSwap(FieldDescriptorProto* other) { } // optional string extendee = 2; - bool FieldDescriptorProto::has_extendee() const { +bool FieldDescriptorProto::has_extendee() const { return (_has_bits_[0] & 0x00000020u) != 0; } - void FieldDescriptorProto::set_has_extendee() { +void FieldDescriptorProto::set_has_extendee() { _has_bits_[0] |= 0x00000020u; } - void FieldDescriptorProto::clear_has_extendee() { +void FieldDescriptorProto::clear_has_extendee() { _has_bits_[0] &= ~0x00000020u; } - void FieldDescriptorProto::clear_extendee() { +void FieldDescriptorProto::clear_extendee() { extendee_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_extendee(); } @@ -4399,16 +4970,16 @@ void FieldDescriptorProto::InternalSwap(FieldDescriptorProto* other) { } // optional string default_value = 7; - bool FieldDescriptorProto::has_default_value() const { +bool FieldDescriptorProto::has_default_value() const { return (_has_bits_[0] & 0x00000040u) != 0; } - void FieldDescriptorProto::set_has_default_value() { +void FieldDescriptorProto::set_has_default_value() { _has_bits_[0] |= 0x00000040u; } - void FieldDescriptorProto::clear_has_default_value() { +void FieldDescriptorProto::clear_has_default_value() { _has_bits_[0] &= ~0x00000040u; } - void FieldDescriptorProto::clear_default_value() { +void FieldDescriptorProto::clear_default_value() { default_value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_default_value(); } @@ -4452,16 +5023,16 @@ void FieldDescriptorProto::InternalSwap(FieldDescriptorProto* other) { } // optional int32 oneof_index = 9; - bool FieldDescriptorProto::has_oneof_index() const { +bool FieldDescriptorProto::has_oneof_index() const { return (_has_bits_[0] & 0x00000080u) != 0; } - void FieldDescriptorProto::set_has_oneof_index() { +void FieldDescriptorProto::set_has_oneof_index() { _has_bits_[0] |= 0x00000080u; } - void FieldDescriptorProto::clear_has_oneof_index() { +void FieldDescriptorProto::clear_has_oneof_index() { _has_bits_[0] &= ~0x00000080u; } - void FieldDescriptorProto::clear_oneof_index() { +void FieldDescriptorProto::clear_oneof_index() { oneof_index_ = 0; clear_has_oneof_index(); } @@ -4476,16 +5047,16 @@ void FieldDescriptorProto::InternalSwap(FieldDescriptorProto* other) { } // optional .google.protobuf.FieldOptions options = 8; - bool FieldDescriptorProto::has_options() const { +bool FieldDescriptorProto::has_options() const { return (_has_bits_[0] & 0x00000100u) != 0; } - void FieldDescriptorProto::set_has_options() { +void FieldDescriptorProto::set_has_options() { _has_bits_[0] |= 0x00000100u; } - void FieldDescriptorProto::clear_has_options() { +void FieldDescriptorProto::clear_has_options() { _has_bits_[0] &= ~0x00000100u; } - void FieldDescriptorProto::clear_options() { +void FieldDescriptorProto::clear_options() { if (options_ != NULL) options_->::google::protobuf::FieldOptions::Clear(); clear_has_options(); } @@ -4527,7 +5098,7 @@ const int OneofDescriptorProto::kNameFieldNumber; #endif // !_MSC_VER OneofDescriptorProto::OneofDescriptorProto() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.OneofDescriptorProto) } @@ -4709,9 +5280,9 @@ int OneofDescriptorProto::ByteSize() const { void OneofDescriptorProto::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const OneofDescriptorProto* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const OneofDescriptorProto* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -4772,16 +5343,16 @@ void OneofDescriptorProto::InternalSwap(OneofDescriptorProto* other) { // OneofDescriptorProto // optional string name = 1; - bool OneofDescriptorProto::has_name() const { +bool OneofDescriptorProto::has_name() const { return (_has_bits_[0] & 0x00000001u) != 0; } - void OneofDescriptorProto::set_has_name() { +void OneofDescriptorProto::set_has_name() { _has_bits_[0] |= 0x00000001u; } - void OneofDescriptorProto::clear_has_name() { +void OneofDescriptorProto::clear_has_name() { _has_bits_[0] &= ~0x00000001u; } - void OneofDescriptorProto::clear_name() { +void OneofDescriptorProto::clear_name() { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_name(); } @@ -4835,7 +5406,7 @@ const int EnumDescriptorProto::kOptionsFieldNumber; #endif // !_MSC_VER EnumDescriptorProto::EnumDescriptorProto() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.EnumDescriptorProto) } @@ -4943,12 +5514,15 @@ bool EnumDescriptorProto::MergePartialFromCodedStream( case 2: { if (tag == 18) { parse_value: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_value: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_value())); } else { goto handle_unusual; } - if (input->ExpectTag(18)) goto parse_value; + if (input->ExpectTag(18)) goto parse_loop_value; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectTag(26)) goto parse_options; break; } @@ -5096,9 +5670,9 @@ int EnumDescriptorProto::ByteSize() const { void EnumDescriptorProto::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const EnumDescriptorProto* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const EnumDescriptorProto* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -5169,16 +5743,16 @@ void EnumDescriptorProto::InternalSwap(EnumDescriptorProto* other) { // EnumDescriptorProto // optional string name = 1; - bool EnumDescriptorProto::has_name() const { +bool EnumDescriptorProto::has_name() const { return (_has_bits_[0] & 0x00000001u) != 0; } - void EnumDescriptorProto::set_has_name() { +void EnumDescriptorProto::set_has_name() { _has_bits_[0] |= 0x00000001u; } - void EnumDescriptorProto::clear_has_name() { +void EnumDescriptorProto::clear_has_name() { _has_bits_[0] &= ~0x00000001u; } - void EnumDescriptorProto::clear_name() { +void EnumDescriptorProto::clear_name() { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_name(); } @@ -5222,10 +5796,10 @@ void EnumDescriptorProto::InternalSwap(EnumDescriptorProto* other) { } // repeated .google.protobuf.EnumValueDescriptorProto value = 2; - int EnumDescriptorProto::value_size() const { +int EnumDescriptorProto::value_size() const { return value_.size(); } - void EnumDescriptorProto::clear_value() { +void EnumDescriptorProto::clear_value() { value_.Clear(); } const ::google::protobuf::EnumValueDescriptorProto& EnumDescriptorProto::value(int index) const { @@ -5252,16 +5826,16 @@ EnumDescriptorProto::mutable_value() { } // optional .google.protobuf.EnumOptions options = 3; - bool EnumDescriptorProto::has_options() const { +bool EnumDescriptorProto::has_options() const { return (_has_bits_[0] & 0x00000004u) != 0; } - void EnumDescriptorProto::set_has_options() { +void EnumDescriptorProto::set_has_options() { _has_bits_[0] |= 0x00000004u; } - void EnumDescriptorProto::clear_has_options() { +void EnumDescriptorProto::clear_has_options() { _has_bits_[0] &= ~0x00000004u; } - void EnumDescriptorProto::clear_options() { +void EnumDescriptorProto::clear_options() { if (options_ != NULL) options_->::google::protobuf::EnumOptions::Clear(); clear_has_options(); } @@ -5305,7 +5879,7 @@ const int EnumValueDescriptorProto::kOptionsFieldNumber; #endif // !_MSC_VER EnumValueDescriptorProto::EnumValueDescriptorProto() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.EnumValueDescriptorProto) } @@ -5564,9 +6138,9 @@ int EnumValueDescriptorProto::ByteSize() const { void EnumValueDescriptorProto::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const EnumValueDescriptorProto* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const EnumValueDescriptorProto* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -5638,16 +6212,16 @@ void EnumValueDescriptorProto::InternalSwap(EnumValueDescriptorProto* other) { // EnumValueDescriptorProto // optional string name = 1; - bool EnumValueDescriptorProto::has_name() const { +bool EnumValueDescriptorProto::has_name() const { return (_has_bits_[0] & 0x00000001u) != 0; } - void EnumValueDescriptorProto::set_has_name() { +void EnumValueDescriptorProto::set_has_name() { _has_bits_[0] |= 0x00000001u; } - void EnumValueDescriptorProto::clear_has_name() { +void EnumValueDescriptorProto::clear_has_name() { _has_bits_[0] &= ~0x00000001u; } - void EnumValueDescriptorProto::clear_name() { +void EnumValueDescriptorProto::clear_name() { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_name(); } @@ -5691,16 +6265,16 @@ void EnumValueDescriptorProto::InternalSwap(EnumValueDescriptorProto* other) { } // optional int32 number = 2; - bool EnumValueDescriptorProto::has_number() const { +bool EnumValueDescriptorProto::has_number() const { return (_has_bits_[0] & 0x00000002u) != 0; } - void EnumValueDescriptorProto::set_has_number() { +void EnumValueDescriptorProto::set_has_number() { _has_bits_[0] |= 0x00000002u; } - void EnumValueDescriptorProto::clear_has_number() { +void EnumValueDescriptorProto::clear_has_number() { _has_bits_[0] &= ~0x00000002u; } - void EnumValueDescriptorProto::clear_number() { +void EnumValueDescriptorProto::clear_number() { number_ = 0; clear_has_number(); } @@ -5715,16 +6289,16 @@ void EnumValueDescriptorProto::InternalSwap(EnumValueDescriptorProto* other) { } // optional .google.protobuf.EnumValueOptions options = 3; - bool EnumValueDescriptorProto::has_options() const { +bool EnumValueDescriptorProto::has_options() const { return (_has_bits_[0] & 0x00000004u) != 0; } - void EnumValueDescriptorProto::set_has_options() { +void EnumValueDescriptorProto::set_has_options() { _has_bits_[0] |= 0x00000004u; } - void EnumValueDescriptorProto::clear_has_options() { +void EnumValueDescriptorProto::clear_has_options() { _has_bits_[0] &= ~0x00000004u; } - void EnumValueDescriptorProto::clear_options() { +void EnumValueDescriptorProto::clear_options() { if (options_ != NULL) options_->::google::protobuf::EnumValueOptions::Clear(); clear_has_options(); } @@ -5768,7 +6342,7 @@ const int ServiceDescriptorProto::kOptionsFieldNumber; #endif // !_MSC_VER ServiceDescriptorProto::ServiceDescriptorProto() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.ServiceDescriptorProto) } @@ -5876,12 +6450,15 @@ bool ServiceDescriptorProto::MergePartialFromCodedStream( case 2: { if (tag == 18) { parse_method: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_method: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_method())); } else { goto handle_unusual; } - if (input->ExpectTag(18)) goto parse_method; + if (input->ExpectTag(18)) goto parse_loop_method; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectTag(26)) goto parse_options; break; } @@ -6029,9 +6606,9 @@ int ServiceDescriptorProto::ByteSize() const { void ServiceDescriptorProto::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const ServiceDescriptorProto* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const ServiceDescriptorProto* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -6102,16 +6679,16 @@ void ServiceDescriptorProto::InternalSwap(ServiceDescriptorProto* other) { // ServiceDescriptorProto // optional string name = 1; - bool ServiceDescriptorProto::has_name() const { +bool ServiceDescriptorProto::has_name() const { return (_has_bits_[0] & 0x00000001u) != 0; } - void ServiceDescriptorProto::set_has_name() { +void ServiceDescriptorProto::set_has_name() { _has_bits_[0] |= 0x00000001u; } - void ServiceDescriptorProto::clear_has_name() { +void ServiceDescriptorProto::clear_has_name() { _has_bits_[0] &= ~0x00000001u; } - void ServiceDescriptorProto::clear_name() { +void ServiceDescriptorProto::clear_name() { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_name(); } @@ -6155,10 +6732,10 @@ void ServiceDescriptorProto::InternalSwap(ServiceDescriptorProto* other) { } // repeated .google.protobuf.MethodDescriptorProto method = 2; - int ServiceDescriptorProto::method_size() const { +int ServiceDescriptorProto::method_size() const { return method_.size(); } - void ServiceDescriptorProto::clear_method() { +void ServiceDescriptorProto::clear_method() { method_.Clear(); } const ::google::protobuf::MethodDescriptorProto& ServiceDescriptorProto::method(int index) const { @@ -6185,16 +6762,16 @@ ServiceDescriptorProto::mutable_method() { } // optional .google.protobuf.ServiceOptions options = 3; - bool ServiceDescriptorProto::has_options() const { +bool ServiceDescriptorProto::has_options() const { return (_has_bits_[0] & 0x00000004u) != 0; } - void ServiceDescriptorProto::set_has_options() { +void ServiceDescriptorProto::set_has_options() { _has_bits_[0] |= 0x00000004u; } - void ServiceDescriptorProto::clear_has_options() { +void ServiceDescriptorProto::clear_has_options() { _has_bits_[0] &= ~0x00000004u; } - void ServiceDescriptorProto::clear_options() { +void ServiceDescriptorProto::clear_options() { if (options_ != NULL) options_->::google::protobuf::ServiceOptions::Clear(); clear_has_options(); } @@ -6241,7 +6818,7 @@ const int MethodDescriptorProto::kServerStreamingFieldNumber; #endif // !_MSC_VER MethodDescriptorProto::MethodDescriptorProto() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.MethodDescriptorProto) } @@ -6641,9 +7218,9 @@ int MethodDescriptorProto::ByteSize() const { void MethodDescriptorProto::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const MethodDescriptorProto* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const MethodDescriptorProto* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -6729,16 +7306,16 @@ void MethodDescriptorProto::InternalSwap(MethodDescriptorProto* other) { // MethodDescriptorProto // optional string name = 1; - bool MethodDescriptorProto::has_name() const { +bool MethodDescriptorProto::has_name() const { return (_has_bits_[0] & 0x00000001u) != 0; } - void MethodDescriptorProto::set_has_name() { +void MethodDescriptorProto::set_has_name() { _has_bits_[0] |= 0x00000001u; } - void MethodDescriptorProto::clear_has_name() { +void MethodDescriptorProto::clear_has_name() { _has_bits_[0] &= ~0x00000001u; } - void MethodDescriptorProto::clear_name() { +void MethodDescriptorProto::clear_name() { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_name(); } @@ -6782,16 +7359,16 @@ void MethodDescriptorProto::InternalSwap(MethodDescriptorProto* other) { } // optional string input_type = 2; - bool MethodDescriptorProto::has_input_type() const { +bool MethodDescriptorProto::has_input_type() const { return (_has_bits_[0] & 0x00000002u) != 0; } - void MethodDescriptorProto::set_has_input_type() { +void MethodDescriptorProto::set_has_input_type() { _has_bits_[0] |= 0x00000002u; } - void MethodDescriptorProto::clear_has_input_type() { +void MethodDescriptorProto::clear_has_input_type() { _has_bits_[0] &= ~0x00000002u; } - void MethodDescriptorProto::clear_input_type() { +void MethodDescriptorProto::clear_input_type() { input_type_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_input_type(); } @@ -6835,16 +7412,16 @@ void MethodDescriptorProto::InternalSwap(MethodDescriptorProto* other) { } // optional string output_type = 3; - bool MethodDescriptorProto::has_output_type() const { +bool MethodDescriptorProto::has_output_type() const { return (_has_bits_[0] & 0x00000004u) != 0; } - void MethodDescriptorProto::set_has_output_type() { +void MethodDescriptorProto::set_has_output_type() { _has_bits_[0] |= 0x00000004u; } - void MethodDescriptorProto::clear_has_output_type() { +void MethodDescriptorProto::clear_has_output_type() { _has_bits_[0] &= ~0x00000004u; } - void MethodDescriptorProto::clear_output_type() { +void MethodDescriptorProto::clear_output_type() { output_type_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_output_type(); } @@ -6888,16 +7465,16 @@ void MethodDescriptorProto::InternalSwap(MethodDescriptorProto* other) { } // optional .google.protobuf.MethodOptions options = 4; - bool MethodDescriptorProto::has_options() const { +bool MethodDescriptorProto::has_options() const { return (_has_bits_[0] & 0x00000008u) != 0; } - void MethodDescriptorProto::set_has_options() { +void MethodDescriptorProto::set_has_options() { _has_bits_[0] |= 0x00000008u; } - void MethodDescriptorProto::clear_has_options() { +void MethodDescriptorProto::clear_has_options() { _has_bits_[0] &= ~0x00000008u; } - void MethodDescriptorProto::clear_options() { +void MethodDescriptorProto::clear_options() { if (options_ != NULL) options_->::google::protobuf::MethodOptions::Clear(); clear_has_options(); } @@ -6931,16 +7508,16 @@ void MethodDescriptorProto::InternalSwap(MethodDescriptorProto* other) { } // optional bool client_streaming = 5 [default = false]; - bool MethodDescriptorProto::has_client_streaming() const { +bool MethodDescriptorProto::has_client_streaming() const { return (_has_bits_[0] & 0x00000010u) != 0; } - void MethodDescriptorProto::set_has_client_streaming() { +void MethodDescriptorProto::set_has_client_streaming() { _has_bits_[0] |= 0x00000010u; } - void MethodDescriptorProto::clear_has_client_streaming() { +void MethodDescriptorProto::clear_has_client_streaming() { _has_bits_[0] &= ~0x00000010u; } - void MethodDescriptorProto::clear_client_streaming() { +void MethodDescriptorProto::clear_client_streaming() { client_streaming_ = false; clear_has_client_streaming(); } @@ -6955,16 +7532,16 @@ void MethodDescriptorProto::InternalSwap(MethodDescriptorProto* other) { } // optional bool server_streaming = 6 [default = false]; - bool MethodDescriptorProto::has_server_streaming() const { +bool MethodDescriptorProto::has_server_streaming() const { return (_has_bits_[0] & 0x00000020u) != 0; } - void MethodDescriptorProto::set_has_server_streaming() { +void MethodDescriptorProto::set_has_server_streaming() { _has_bits_[0] |= 0x00000020u; } - void MethodDescriptorProto::clear_has_server_streaming() { +void MethodDescriptorProto::clear_has_server_streaming() { _has_bits_[0] &= ~0x00000020u; } - void MethodDescriptorProto::clear_server_streaming() { +void MethodDescriptorProto::clear_server_streaming() { server_streaming_ = false; clear_has_server_streaming(); } @@ -7024,7 +7601,7 @@ const int FileOptions::kUninterpretedOptionFieldNumber; #endif // !_MSC_VER FileOptions::FileOptions() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.FileOptions) } @@ -7381,12 +7958,15 @@ bool FileOptions::MergePartialFromCodedStream( case 999: { if (tag == 7994) { parse_uninterpreted_option: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_uninterpreted_option: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_uninterpreted_option())); } else { goto handle_unusual; } - if (input->ExpectTag(7994)) goto parse_uninterpreted_option; + if (input->ExpectTag(7994)) goto parse_loop_uninterpreted_option; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectAtEnd()) goto success; break; } @@ -7768,9 +8348,9 @@ int FileOptions::ByteSize() const { void FileOptions::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const FileOptions* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const FileOptions* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -7895,16 +8475,16 @@ void FileOptions::InternalSwap(FileOptions* other) { // FileOptions // optional string java_package = 1; - bool FileOptions::has_java_package() const { +bool FileOptions::has_java_package() const { return (_has_bits_[0] & 0x00000001u) != 0; } - void FileOptions::set_has_java_package() { +void FileOptions::set_has_java_package() { _has_bits_[0] |= 0x00000001u; } - void FileOptions::clear_has_java_package() { +void FileOptions::clear_has_java_package() { _has_bits_[0] &= ~0x00000001u; } - void FileOptions::clear_java_package() { +void FileOptions::clear_java_package() { java_package_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_java_package(); } @@ -7948,16 +8528,16 @@ void FileOptions::InternalSwap(FileOptions* other) { } // optional string java_outer_classname = 8; - bool FileOptions::has_java_outer_classname() const { +bool FileOptions::has_java_outer_classname() const { return (_has_bits_[0] & 0x00000002u) != 0; } - void FileOptions::set_has_java_outer_classname() { +void FileOptions::set_has_java_outer_classname() { _has_bits_[0] |= 0x00000002u; } - void FileOptions::clear_has_java_outer_classname() { +void FileOptions::clear_has_java_outer_classname() { _has_bits_[0] &= ~0x00000002u; } - void FileOptions::clear_java_outer_classname() { +void FileOptions::clear_java_outer_classname() { java_outer_classname_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_java_outer_classname(); } @@ -8001,16 +8581,16 @@ void FileOptions::InternalSwap(FileOptions* other) { } // optional bool java_multiple_files = 10 [default = false]; - bool FileOptions::has_java_multiple_files() const { +bool FileOptions::has_java_multiple_files() const { return (_has_bits_[0] & 0x00000004u) != 0; } - void FileOptions::set_has_java_multiple_files() { +void FileOptions::set_has_java_multiple_files() { _has_bits_[0] |= 0x00000004u; } - void FileOptions::clear_has_java_multiple_files() { +void FileOptions::clear_has_java_multiple_files() { _has_bits_[0] &= ~0x00000004u; } - void FileOptions::clear_java_multiple_files() { +void FileOptions::clear_java_multiple_files() { java_multiple_files_ = false; clear_has_java_multiple_files(); } @@ -8025,16 +8605,16 @@ void FileOptions::InternalSwap(FileOptions* other) { } // optional bool java_generate_equals_and_hash = 20 [default = false]; - bool FileOptions::has_java_generate_equals_and_hash() const { +bool FileOptions::has_java_generate_equals_and_hash() const { return (_has_bits_[0] & 0x00000008u) != 0; } - void FileOptions::set_has_java_generate_equals_and_hash() { +void FileOptions::set_has_java_generate_equals_and_hash() { _has_bits_[0] |= 0x00000008u; } - void FileOptions::clear_has_java_generate_equals_and_hash() { +void FileOptions::clear_has_java_generate_equals_and_hash() { _has_bits_[0] &= ~0x00000008u; } - void FileOptions::clear_java_generate_equals_and_hash() { +void FileOptions::clear_java_generate_equals_and_hash() { java_generate_equals_and_hash_ = false; clear_has_java_generate_equals_and_hash(); } @@ -8049,16 +8629,16 @@ void FileOptions::InternalSwap(FileOptions* other) { } // optional bool java_string_check_utf8 = 27 [default = false]; - bool FileOptions::has_java_string_check_utf8() const { +bool FileOptions::has_java_string_check_utf8() const { return (_has_bits_[0] & 0x00000010u) != 0; } - void FileOptions::set_has_java_string_check_utf8() { +void FileOptions::set_has_java_string_check_utf8() { _has_bits_[0] |= 0x00000010u; } - void FileOptions::clear_has_java_string_check_utf8() { +void FileOptions::clear_has_java_string_check_utf8() { _has_bits_[0] &= ~0x00000010u; } - void FileOptions::clear_java_string_check_utf8() { +void FileOptions::clear_java_string_check_utf8() { java_string_check_utf8_ = false; clear_has_java_string_check_utf8(); } @@ -8073,16 +8653,16 @@ void FileOptions::InternalSwap(FileOptions* other) { } // optional .google.protobuf.FileOptions.OptimizeMode optimize_for = 9 [default = SPEED]; - bool FileOptions::has_optimize_for() const { +bool FileOptions::has_optimize_for() const { return (_has_bits_[0] & 0x00000020u) != 0; } - void FileOptions::set_has_optimize_for() { +void FileOptions::set_has_optimize_for() { _has_bits_[0] |= 0x00000020u; } - void FileOptions::clear_has_optimize_for() { +void FileOptions::clear_has_optimize_for() { _has_bits_[0] &= ~0x00000020u; } - void FileOptions::clear_optimize_for() { +void FileOptions::clear_optimize_for() { optimize_for_ = 1; clear_has_optimize_for(); } @@ -8098,16 +8678,16 @@ void FileOptions::InternalSwap(FileOptions* other) { } // optional string go_package = 11; - bool FileOptions::has_go_package() const { +bool FileOptions::has_go_package() const { return (_has_bits_[0] & 0x00000040u) != 0; } - void FileOptions::set_has_go_package() { +void FileOptions::set_has_go_package() { _has_bits_[0] |= 0x00000040u; } - void FileOptions::clear_has_go_package() { +void FileOptions::clear_has_go_package() { _has_bits_[0] &= ~0x00000040u; } - void FileOptions::clear_go_package() { +void FileOptions::clear_go_package() { go_package_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_go_package(); } @@ -8151,16 +8731,16 @@ void FileOptions::InternalSwap(FileOptions* other) { } // optional bool cc_generic_services = 16 [default = false]; - bool FileOptions::has_cc_generic_services() const { +bool FileOptions::has_cc_generic_services() const { return (_has_bits_[0] & 0x00000080u) != 0; } - void FileOptions::set_has_cc_generic_services() { +void FileOptions::set_has_cc_generic_services() { _has_bits_[0] |= 0x00000080u; } - void FileOptions::clear_has_cc_generic_services() { +void FileOptions::clear_has_cc_generic_services() { _has_bits_[0] &= ~0x00000080u; } - void FileOptions::clear_cc_generic_services() { +void FileOptions::clear_cc_generic_services() { cc_generic_services_ = false; clear_has_cc_generic_services(); } @@ -8175,16 +8755,16 @@ void FileOptions::InternalSwap(FileOptions* other) { } // optional bool java_generic_services = 17 [default = false]; - bool FileOptions::has_java_generic_services() const { +bool FileOptions::has_java_generic_services() const { return (_has_bits_[0] & 0x00000100u) != 0; } - void FileOptions::set_has_java_generic_services() { +void FileOptions::set_has_java_generic_services() { _has_bits_[0] |= 0x00000100u; } - void FileOptions::clear_has_java_generic_services() { +void FileOptions::clear_has_java_generic_services() { _has_bits_[0] &= ~0x00000100u; } - void FileOptions::clear_java_generic_services() { +void FileOptions::clear_java_generic_services() { java_generic_services_ = false; clear_has_java_generic_services(); } @@ -8199,16 +8779,16 @@ void FileOptions::InternalSwap(FileOptions* other) { } // optional bool py_generic_services = 18 [default = false]; - bool FileOptions::has_py_generic_services() const { +bool FileOptions::has_py_generic_services() const { return (_has_bits_[0] & 0x00000200u) != 0; } - void FileOptions::set_has_py_generic_services() { +void FileOptions::set_has_py_generic_services() { _has_bits_[0] |= 0x00000200u; } - void FileOptions::clear_has_py_generic_services() { +void FileOptions::clear_has_py_generic_services() { _has_bits_[0] &= ~0x00000200u; } - void FileOptions::clear_py_generic_services() { +void FileOptions::clear_py_generic_services() { py_generic_services_ = false; clear_has_py_generic_services(); } @@ -8223,16 +8803,16 @@ void FileOptions::InternalSwap(FileOptions* other) { } // optional bool deprecated = 23 [default = false]; - bool FileOptions::has_deprecated() const { +bool FileOptions::has_deprecated() const { return (_has_bits_[0] & 0x00000400u) != 0; } - void FileOptions::set_has_deprecated() { +void FileOptions::set_has_deprecated() { _has_bits_[0] |= 0x00000400u; } - void FileOptions::clear_has_deprecated() { +void FileOptions::clear_has_deprecated() { _has_bits_[0] &= ~0x00000400u; } - void FileOptions::clear_deprecated() { +void FileOptions::clear_deprecated() { deprecated_ = false; clear_has_deprecated(); } @@ -8247,16 +8827,16 @@ void FileOptions::InternalSwap(FileOptions* other) { } // optional bool cc_enable_arenas = 31 [default = false]; - bool FileOptions::has_cc_enable_arenas() const { +bool FileOptions::has_cc_enable_arenas() const { return (_has_bits_[0] & 0x00000800u) != 0; } - void FileOptions::set_has_cc_enable_arenas() { +void FileOptions::set_has_cc_enable_arenas() { _has_bits_[0] |= 0x00000800u; } - void FileOptions::clear_has_cc_enable_arenas() { +void FileOptions::clear_has_cc_enable_arenas() { _has_bits_[0] &= ~0x00000800u; } - void FileOptions::clear_cc_enable_arenas() { +void FileOptions::clear_cc_enable_arenas() { cc_enable_arenas_ = false; clear_has_cc_enable_arenas(); } @@ -8271,16 +8851,16 @@ void FileOptions::InternalSwap(FileOptions* other) { } // optional string objc_class_prefix = 36; - bool FileOptions::has_objc_class_prefix() const { +bool FileOptions::has_objc_class_prefix() const { return (_has_bits_[0] & 0x00001000u) != 0; } - void FileOptions::set_has_objc_class_prefix() { +void FileOptions::set_has_objc_class_prefix() { _has_bits_[0] |= 0x00001000u; } - void FileOptions::clear_has_objc_class_prefix() { +void FileOptions::clear_has_objc_class_prefix() { _has_bits_[0] &= ~0x00001000u; } - void FileOptions::clear_objc_class_prefix() { +void FileOptions::clear_objc_class_prefix() { objc_class_prefix_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_objc_class_prefix(); } @@ -8324,16 +8904,16 @@ void FileOptions::InternalSwap(FileOptions* other) { } // optional string csharp_namespace = 37; - bool FileOptions::has_csharp_namespace() const { +bool FileOptions::has_csharp_namespace() const { return (_has_bits_[0] & 0x00002000u) != 0; } - void FileOptions::set_has_csharp_namespace() { +void FileOptions::set_has_csharp_namespace() { _has_bits_[0] |= 0x00002000u; } - void FileOptions::clear_has_csharp_namespace() { +void FileOptions::clear_has_csharp_namespace() { _has_bits_[0] &= ~0x00002000u; } - void FileOptions::clear_csharp_namespace() { +void FileOptions::clear_csharp_namespace() { csharp_namespace_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_csharp_namespace(); } @@ -8377,10 +8957,10 @@ void FileOptions::InternalSwap(FileOptions* other) { } // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999; - int FileOptions::uninterpreted_option_size() const { +int FileOptions::uninterpreted_option_size() const { return uninterpreted_option_.size(); } - void FileOptions::clear_uninterpreted_option() { +void FileOptions::clear_uninterpreted_option() { uninterpreted_option_.Clear(); } const ::google::protobuf::UninterpretedOption& FileOptions::uninterpreted_option(int index) const { @@ -8419,7 +8999,7 @@ const int MessageOptions::kUninterpretedOptionFieldNumber; #endif // !_MSC_VER MessageOptions::MessageOptions() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.MessageOptions) } @@ -8574,12 +9154,15 @@ bool MessageOptions::MergePartialFromCodedStream( case 999: { if (tag == 7994) { parse_uninterpreted_option: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_uninterpreted_option: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_uninterpreted_option())); } else { goto handle_unusual; } - if (input->ExpectTag(7994)) goto parse_uninterpreted_option; + if (input->ExpectTag(7994)) goto parse_loop_uninterpreted_option; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectAtEnd()) goto success; break; } @@ -8741,9 +9324,9 @@ int MessageOptions::ByteSize() const { void MessageOptions::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const MessageOptions* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const MessageOptions* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -8821,16 +9404,16 @@ void MessageOptions::InternalSwap(MessageOptions* other) { // MessageOptions // optional bool message_set_wire_format = 1 [default = false]; - bool MessageOptions::has_message_set_wire_format() const { +bool MessageOptions::has_message_set_wire_format() const { return (_has_bits_[0] & 0x00000001u) != 0; } - void MessageOptions::set_has_message_set_wire_format() { +void MessageOptions::set_has_message_set_wire_format() { _has_bits_[0] |= 0x00000001u; } - void MessageOptions::clear_has_message_set_wire_format() { +void MessageOptions::clear_has_message_set_wire_format() { _has_bits_[0] &= ~0x00000001u; } - void MessageOptions::clear_message_set_wire_format() { +void MessageOptions::clear_message_set_wire_format() { message_set_wire_format_ = false; clear_has_message_set_wire_format(); } @@ -8845,16 +9428,16 @@ void MessageOptions::InternalSwap(MessageOptions* other) { } // optional bool no_standard_descriptor_accessor = 2 [default = false]; - bool MessageOptions::has_no_standard_descriptor_accessor() const { +bool MessageOptions::has_no_standard_descriptor_accessor() const { return (_has_bits_[0] & 0x00000002u) != 0; } - void MessageOptions::set_has_no_standard_descriptor_accessor() { +void MessageOptions::set_has_no_standard_descriptor_accessor() { _has_bits_[0] |= 0x00000002u; } - void MessageOptions::clear_has_no_standard_descriptor_accessor() { +void MessageOptions::clear_has_no_standard_descriptor_accessor() { _has_bits_[0] &= ~0x00000002u; } - void MessageOptions::clear_no_standard_descriptor_accessor() { +void MessageOptions::clear_no_standard_descriptor_accessor() { no_standard_descriptor_accessor_ = false; clear_has_no_standard_descriptor_accessor(); } @@ -8869,16 +9452,16 @@ void MessageOptions::InternalSwap(MessageOptions* other) { } // optional bool deprecated = 3 [default = false]; - bool MessageOptions::has_deprecated() const { +bool MessageOptions::has_deprecated() const { return (_has_bits_[0] & 0x00000004u) != 0; } - void MessageOptions::set_has_deprecated() { +void MessageOptions::set_has_deprecated() { _has_bits_[0] |= 0x00000004u; } - void MessageOptions::clear_has_deprecated() { +void MessageOptions::clear_has_deprecated() { _has_bits_[0] &= ~0x00000004u; } - void MessageOptions::clear_deprecated() { +void MessageOptions::clear_deprecated() { deprecated_ = false; clear_has_deprecated(); } @@ -8893,16 +9476,16 @@ void MessageOptions::InternalSwap(MessageOptions* other) { } // optional bool map_entry = 7; - bool MessageOptions::has_map_entry() const { +bool MessageOptions::has_map_entry() const { return (_has_bits_[0] & 0x00000008u) != 0; } - void MessageOptions::set_has_map_entry() { +void MessageOptions::set_has_map_entry() { _has_bits_[0] |= 0x00000008u; } - void MessageOptions::clear_has_map_entry() { +void MessageOptions::clear_has_map_entry() { _has_bits_[0] &= ~0x00000008u; } - void MessageOptions::clear_map_entry() { +void MessageOptions::clear_map_entry() { map_entry_ = false; clear_has_map_entry(); } @@ -8917,10 +9500,10 @@ void MessageOptions::InternalSwap(MessageOptions* other) { } // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999; - int MessageOptions::uninterpreted_option_size() const { +int MessageOptions::uninterpreted_option_size() const { return uninterpreted_option_.size(); } - void MessageOptions::clear_uninterpreted_option() { +void MessageOptions::clear_uninterpreted_option() { uninterpreted_option_.Clear(); } const ::google::protobuf::UninterpretedOption& MessageOptions::uninterpreted_option(int index) const { @@ -8973,9 +9556,33 @@ const FieldOptions_CType FieldOptions::CType_MIN; const FieldOptions_CType FieldOptions::CType_MAX; const int FieldOptions::CType_ARRAYSIZE; #endif // _MSC_VER +const ::google::protobuf::EnumDescriptor* FieldOptions_JSType_descriptor() { + protobuf_AssignDescriptorsOnce(); + return FieldOptions_JSType_descriptor_; +} +bool FieldOptions_JSType_IsValid(int value) { + switch(value) { + case 0: + case 1: + case 2: + return true; + default: + return false; + } +} + +#ifndef _MSC_VER +const FieldOptions_JSType FieldOptions::JS_NORMAL; +const FieldOptions_JSType FieldOptions::JS_STRING; +const FieldOptions_JSType FieldOptions::JS_NUMBER; +const FieldOptions_JSType FieldOptions::JSType_MIN; +const FieldOptions_JSType FieldOptions::JSType_MAX; +const int FieldOptions::JSType_ARRAYSIZE; +#endif // _MSC_VER #ifndef _MSC_VER const int FieldOptions::kCtypeFieldNumber; const int FieldOptions::kPackedFieldNumber; +const int FieldOptions::kJstypeFieldNumber; const int FieldOptions::kLazyFieldNumber; const int FieldOptions::kDeprecatedFieldNumber; const int FieldOptions::kWeakFieldNumber; @@ -8983,7 +9590,7 @@ const int FieldOptions::kUninterpretedOptionFieldNumber; #endif // !_MSC_VER FieldOptions::FieldOptions() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.FieldOptions) } @@ -9003,6 +9610,7 @@ void FieldOptions::SharedCtor() { _cached_size_ = 0; ctype_ = 0; packed_ = false; + jstype_ = 0; lazy_ = false; deprecated_ = false; weak_ = false; @@ -9054,8 +9662,9 @@ void FieldOptions::Clear() { ZR_HELPER_(last) - ZR_HELPER_(first) + sizeof(last));\ } while (0) - if (_has_bits_[0 / 32] & 31u) { - ZR_(ctype_, weak_); + if (_has_bits_[0 / 32] & 63u) { + ZR_(ctype_, jstype_); + ZR_(packed_, weak_); } #undef ZR_HELPER_ @@ -9138,6 +9747,26 @@ bool FieldOptions::MergePartialFromCodedStream( } else { goto handle_unusual; } + if (input->ExpectTag(48)) goto parse_jstype; + break; + } + + // optional .google.protobuf.FieldOptions.JSType jstype = 6 [default = JS_NORMAL]; + case 6: { + if (tag == 48) { + parse_jstype: + int value; + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + int, ::google::protobuf::internal::WireFormatLite::TYPE_ENUM>( + input, &value))); + if (::google::protobuf::FieldOptions_JSType_IsValid(value)) { + set_jstype(static_cast< ::google::protobuf::FieldOptions_JSType >(value)); + } else { + mutable_unknown_fields()->AddVarint(6, value); + } + } else { + goto handle_unusual; + } if (input->ExpectTag(80)) goto parse_weak; break; } @@ -9161,12 +9790,15 @@ bool FieldOptions::MergePartialFromCodedStream( case 999: { if (tag == 7994) { parse_uninterpreted_option: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_uninterpreted_option: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_uninterpreted_option())); } else { goto handle_unusual; } - if (input->ExpectTag(7994)) goto parse_uninterpreted_option; + if (input->ExpectTag(7994)) goto parse_loop_uninterpreted_option; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectAtEnd()) goto success; break; } @@ -9222,6 +9854,12 @@ void FieldOptions::SerializeWithCachedSizes( ::google::protobuf::internal::WireFormatLite::WriteBool(5, this->lazy(), output); } + // optional .google.protobuf.FieldOptions.JSType jstype = 6 [default = JS_NORMAL]; + if (has_jstype()) { + ::google::protobuf::internal::WireFormatLite::WriteEnum( + 6, this->jstype(), output); + } + // optional bool weak = 10 [default = false]; if (has_weak()) { ::google::protobuf::internal::WireFormatLite::WriteBool(10, this->weak(), output); @@ -9268,6 +9906,12 @@ void FieldOptions::SerializeWithCachedSizes( target = ::google::protobuf::internal::WireFormatLite::WriteBoolToArray(5, this->lazy(), target); } + // optional .google.protobuf.FieldOptions.JSType jstype = 6 [default = JS_NORMAL]; + if (has_jstype()) { + target = ::google::protobuf::internal::WireFormatLite::WriteEnumToArray( + 6, this->jstype(), target); + } + // optional bool weak = 10 [default = false]; if (has_weak()) { target = ::google::protobuf::internal::WireFormatLite::WriteBoolToArray(10, this->weak(), target); @@ -9295,7 +9939,7 @@ void FieldOptions::SerializeWithCachedSizes( int FieldOptions::ByteSize() const { int total_size = 0; - if (_has_bits_[0 / 32] & 31) { + if (_has_bits_[0 / 32] & 63) { // optional .google.protobuf.FieldOptions.CType ctype = 1 [default = STRING]; if (has_ctype()) { total_size += 1 + @@ -9307,6 +9951,12 @@ int FieldOptions::ByteSize() const { total_size += 1 + 1; } + // optional .google.protobuf.FieldOptions.JSType jstype = 6 [default = JS_NORMAL]; + if (has_jstype()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::EnumSize(this->jstype()); + } + // optional bool lazy = 5 [default = false]; if (has_lazy()) { total_size += 1 + 1; @@ -9346,9 +9996,9 @@ int FieldOptions::ByteSize() const { void FieldOptions::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const FieldOptions* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const FieldOptions* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -9366,6 +10016,9 @@ void FieldOptions::MergeFrom(const FieldOptions& from) { if (from.has_packed()) { set_packed(from.packed()); } + if (from.has_jstype()) { + set_jstype(from.jstype()); + } if (from.has_lazy()) { set_lazy(from.lazy()); } @@ -9408,6 +10061,7 @@ void FieldOptions::Swap(FieldOptions* other) { void FieldOptions::InternalSwap(FieldOptions* other) { std::swap(ctype_, other->ctype_); std::swap(packed_, other->packed_); + std::swap(jstype_, other->jstype_); std::swap(lazy_, other->lazy_); std::swap(deprecated_, other->deprecated_); std::swap(weak_, other->weak_); @@ -9430,16 +10084,16 @@ void FieldOptions::InternalSwap(FieldOptions* other) { // FieldOptions // optional .google.protobuf.FieldOptions.CType ctype = 1 [default = STRING]; - bool FieldOptions::has_ctype() const { +bool FieldOptions::has_ctype() const { return (_has_bits_[0] & 0x00000001u) != 0; } - void FieldOptions::set_has_ctype() { +void FieldOptions::set_has_ctype() { _has_bits_[0] |= 0x00000001u; } - void FieldOptions::clear_has_ctype() { +void FieldOptions::clear_has_ctype() { _has_bits_[0] &= ~0x00000001u; } - void FieldOptions::clear_ctype() { +void FieldOptions::clear_ctype() { ctype_ = 0; clear_has_ctype(); } @@ -9455,16 +10109,16 @@ void FieldOptions::InternalSwap(FieldOptions* other) { } // optional bool packed = 2; - bool FieldOptions::has_packed() const { +bool FieldOptions::has_packed() const { return (_has_bits_[0] & 0x00000002u) != 0; } - void FieldOptions::set_has_packed() { +void FieldOptions::set_has_packed() { _has_bits_[0] |= 0x00000002u; } - void FieldOptions::clear_has_packed() { +void FieldOptions::clear_has_packed() { _has_bits_[0] &= ~0x00000002u; } - void FieldOptions::clear_packed() { +void FieldOptions::clear_packed() { packed_ = false; clear_has_packed(); } @@ -9478,17 +10132,42 @@ void FieldOptions::InternalSwap(FieldOptions* other) { // @@protoc_insertion_point(field_set:google.protobuf.FieldOptions.packed) } -// optional bool lazy = 5 [default = false]; - bool FieldOptions::has_lazy() const { +// optional .google.protobuf.FieldOptions.JSType jstype = 6 [default = JS_NORMAL]; +bool FieldOptions::has_jstype() const { return (_has_bits_[0] & 0x00000004u) != 0; } - void FieldOptions::set_has_lazy() { +void FieldOptions::set_has_jstype() { _has_bits_[0] |= 0x00000004u; } - void FieldOptions::clear_has_lazy() { +void FieldOptions::clear_has_jstype() { _has_bits_[0] &= ~0x00000004u; } - void FieldOptions::clear_lazy() { +void FieldOptions::clear_jstype() { + jstype_ = 0; + clear_has_jstype(); +} + ::google::protobuf::FieldOptions_JSType FieldOptions::jstype() const { + // @@protoc_insertion_point(field_get:google.protobuf.FieldOptions.jstype) + return static_cast< ::google::protobuf::FieldOptions_JSType >(jstype_); +} + void FieldOptions::set_jstype(::google::protobuf::FieldOptions_JSType value) { + assert(::google::protobuf::FieldOptions_JSType_IsValid(value)); + set_has_jstype(); + jstype_ = value; + // @@protoc_insertion_point(field_set:google.protobuf.FieldOptions.jstype) +} + +// optional bool lazy = 5 [default = false]; +bool FieldOptions::has_lazy() const { + return (_has_bits_[0] & 0x00000008u) != 0; +} +void FieldOptions::set_has_lazy() { + _has_bits_[0] |= 0x00000008u; +} +void FieldOptions::clear_has_lazy() { + _has_bits_[0] &= ~0x00000008u; +} +void FieldOptions::clear_lazy() { lazy_ = false; clear_has_lazy(); } @@ -9503,16 +10182,16 @@ void FieldOptions::InternalSwap(FieldOptions* other) { } // optional bool deprecated = 3 [default = false]; - bool FieldOptions::has_deprecated() const { - return (_has_bits_[0] & 0x00000008u) != 0; +bool FieldOptions::has_deprecated() const { + return (_has_bits_[0] & 0x00000010u) != 0; } - void FieldOptions::set_has_deprecated() { - _has_bits_[0] |= 0x00000008u; +void FieldOptions::set_has_deprecated() { + _has_bits_[0] |= 0x00000010u; } - void FieldOptions::clear_has_deprecated() { - _has_bits_[0] &= ~0x00000008u; +void FieldOptions::clear_has_deprecated() { + _has_bits_[0] &= ~0x00000010u; } - void FieldOptions::clear_deprecated() { +void FieldOptions::clear_deprecated() { deprecated_ = false; clear_has_deprecated(); } @@ -9527,16 +10206,16 @@ void FieldOptions::InternalSwap(FieldOptions* other) { } // optional bool weak = 10 [default = false]; - bool FieldOptions::has_weak() const { - return (_has_bits_[0] & 0x00000010u) != 0; +bool FieldOptions::has_weak() const { + return (_has_bits_[0] & 0x00000020u) != 0; } - void FieldOptions::set_has_weak() { - _has_bits_[0] |= 0x00000010u; +void FieldOptions::set_has_weak() { + _has_bits_[0] |= 0x00000020u; } - void FieldOptions::clear_has_weak() { - _has_bits_[0] &= ~0x00000010u; +void FieldOptions::clear_has_weak() { + _has_bits_[0] &= ~0x00000020u; } - void FieldOptions::clear_weak() { +void FieldOptions::clear_weak() { weak_ = false; clear_has_weak(); } @@ -9551,10 +10230,10 @@ void FieldOptions::InternalSwap(FieldOptions* other) { } // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999; - int FieldOptions::uninterpreted_option_size() const { +int FieldOptions::uninterpreted_option_size() const { return uninterpreted_option_.size(); } - void FieldOptions::clear_uninterpreted_option() { +void FieldOptions::clear_uninterpreted_option() { uninterpreted_option_.Clear(); } const ::google::protobuf::UninterpretedOption& FieldOptions::uninterpreted_option(int index) const { @@ -9591,7 +10270,7 @@ const int EnumOptions::kUninterpretedOptionFieldNumber; #endif // !_MSC_VER EnumOptions::EnumOptions() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.EnumOptions) } @@ -9714,12 +10393,15 @@ bool EnumOptions::MergePartialFromCodedStream( case 999: { if (tag == 7994) { parse_uninterpreted_option: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_uninterpreted_option: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_uninterpreted_option())); } else { goto handle_unusual; } - if (input->ExpectTag(7994)) goto parse_uninterpreted_option; + if (input->ExpectTag(7994)) goto parse_loop_uninterpreted_option; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectAtEnd()) goto success; break; } @@ -9851,9 +10533,9 @@ int EnumOptions::ByteSize() const { void EnumOptions::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const EnumOptions* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const EnumOptions* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -9923,16 +10605,16 @@ void EnumOptions::InternalSwap(EnumOptions* other) { // EnumOptions // optional bool allow_alias = 2; - bool EnumOptions::has_allow_alias() const { +bool EnumOptions::has_allow_alias() const { return (_has_bits_[0] & 0x00000001u) != 0; } - void EnumOptions::set_has_allow_alias() { +void EnumOptions::set_has_allow_alias() { _has_bits_[0] |= 0x00000001u; } - void EnumOptions::clear_has_allow_alias() { +void EnumOptions::clear_has_allow_alias() { _has_bits_[0] &= ~0x00000001u; } - void EnumOptions::clear_allow_alias() { +void EnumOptions::clear_allow_alias() { allow_alias_ = false; clear_has_allow_alias(); } @@ -9947,16 +10629,16 @@ void EnumOptions::InternalSwap(EnumOptions* other) { } // optional bool deprecated = 3 [default = false]; - bool EnumOptions::has_deprecated() const { +bool EnumOptions::has_deprecated() const { return (_has_bits_[0] & 0x00000002u) != 0; } - void EnumOptions::set_has_deprecated() { +void EnumOptions::set_has_deprecated() { _has_bits_[0] |= 0x00000002u; } - void EnumOptions::clear_has_deprecated() { +void EnumOptions::clear_has_deprecated() { _has_bits_[0] &= ~0x00000002u; } - void EnumOptions::clear_deprecated() { +void EnumOptions::clear_deprecated() { deprecated_ = false; clear_has_deprecated(); } @@ -9971,10 +10653,10 @@ void EnumOptions::InternalSwap(EnumOptions* other) { } // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999; - int EnumOptions::uninterpreted_option_size() const { +int EnumOptions::uninterpreted_option_size() const { return uninterpreted_option_.size(); } - void EnumOptions::clear_uninterpreted_option() { +void EnumOptions::clear_uninterpreted_option() { uninterpreted_option_.Clear(); } const ::google::protobuf::UninterpretedOption& EnumOptions::uninterpreted_option(int index) const { @@ -10010,7 +10692,7 @@ const int EnumValueOptions::kUninterpretedOptionFieldNumber; #endif // !_MSC_VER EnumValueOptions::EnumValueOptions() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.EnumValueOptions) } @@ -10105,12 +10787,15 @@ bool EnumValueOptions::MergePartialFromCodedStream( case 999: { if (tag == 7994) { parse_uninterpreted_option: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_uninterpreted_option: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_uninterpreted_option())); } else { goto handle_unusual; } - if (input->ExpectTag(7994)) goto parse_uninterpreted_option; + if (input->ExpectTag(7994)) goto parse_loop_uninterpreted_option; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectAtEnd()) goto success; break; } @@ -10225,9 +10910,9 @@ int EnumValueOptions::ByteSize() const { void EnumValueOptions::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const EnumValueOptions* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const EnumValueOptions* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -10293,16 +10978,16 @@ void EnumValueOptions::InternalSwap(EnumValueOptions* other) { // EnumValueOptions // optional bool deprecated = 1 [default = false]; - bool EnumValueOptions::has_deprecated() const { +bool EnumValueOptions::has_deprecated() const { return (_has_bits_[0] & 0x00000001u) != 0; } - void EnumValueOptions::set_has_deprecated() { +void EnumValueOptions::set_has_deprecated() { _has_bits_[0] |= 0x00000001u; } - void EnumValueOptions::clear_has_deprecated() { +void EnumValueOptions::clear_has_deprecated() { _has_bits_[0] &= ~0x00000001u; } - void EnumValueOptions::clear_deprecated() { +void EnumValueOptions::clear_deprecated() { deprecated_ = false; clear_has_deprecated(); } @@ -10317,10 +11002,10 @@ void EnumValueOptions::InternalSwap(EnumValueOptions* other) { } // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999; - int EnumValueOptions::uninterpreted_option_size() const { +int EnumValueOptions::uninterpreted_option_size() const { return uninterpreted_option_.size(); } - void EnumValueOptions::clear_uninterpreted_option() { +void EnumValueOptions::clear_uninterpreted_option() { uninterpreted_option_.Clear(); } const ::google::protobuf::UninterpretedOption& EnumValueOptions::uninterpreted_option(int index) const { @@ -10356,7 +11041,7 @@ const int ServiceOptions::kUninterpretedOptionFieldNumber; #endif // !_MSC_VER ServiceOptions::ServiceOptions() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.ServiceOptions) } @@ -10451,12 +11136,15 @@ bool ServiceOptions::MergePartialFromCodedStream( case 999: { if (tag == 7994) { parse_uninterpreted_option: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_uninterpreted_option: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_uninterpreted_option())); } else { goto handle_unusual; } - if (input->ExpectTag(7994)) goto parse_uninterpreted_option; + if (input->ExpectTag(7994)) goto parse_loop_uninterpreted_option; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectAtEnd()) goto success; break; } @@ -10571,9 +11259,9 @@ int ServiceOptions::ByteSize() const { void ServiceOptions::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const ServiceOptions* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const ServiceOptions* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -10639,16 +11327,16 @@ void ServiceOptions::InternalSwap(ServiceOptions* other) { // ServiceOptions // optional bool deprecated = 33 [default = false]; - bool ServiceOptions::has_deprecated() const { +bool ServiceOptions::has_deprecated() const { return (_has_bits_[0] & 0x00000001u) != 0; } - void ServiceOptions::set_has_deprecated() { +void ServiceOptions::set_has_deprecated() { _has_bits_[0] |= 0x00000001u; } - void ServiceOptions::clear_has_deprecated() { +void ServiceOptions::clear_has_deprecated() { _has_bits_[0] &= ~0x00000001u; } - void ServiceOptions::clear_deprecated() { +void ServiceOptions::clear_deprecated() { deprecated_ = false; clear_has_deprecated(); } @@ -10663,10 +11351,10 @@ void ServiceOptions::InternalSwap(ServiceOptions* other) { } // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999; - int ServiceOptions::uninterpreted_option_size() const { +int ServiceOptions::uninterpreted_option_size() const { return uninterpreted_option_.size(); } - void ServiceOptions::clear_uninterpreted_option() { +void ServiceOptions::clear_uninterpreted_option() { uninterpreted_option_.Clear(); } const ::google::protobuf::UninterpretedOption& ServiceOptions::uninterpreted_option(int index) const { @@ -10702,7 +11390,7 @@ const int MethodOptions::kUninterpretedOptionFieldNumber; #endif // !_MSC_VER MethodOptions::MethodOptions() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.MethodOptions) } @@ -10797,12 +11485,15 @@ bool MethodOptions::MergePartialFromCodedStream( case 999: { if (tag == 7994) { parse_uninterpreted_option: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_uninterpreted_option: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_uninterpreted_option())); } else { goto handle_unusual; } - if (input->ExpectTag(7994)) goto parse_uninterpreted_option; + if (input->ExpectTag(7994)) goto parse_loop_uninterpreted_option; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectAtEnd()) goto success; break; } @@ -10917,9 +11608,9 @@ int MethodOptions::ByteSize() const { void MethodOptions::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const MethodOptions* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const MethodOptions* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -10985,16 +11676,16 @@ void MethodOptions::InternalSwap(MethodOptions* other) { // MethodOptions // optional bool deprecated = 33 [default = false]; - bool MethodOptions::has_deprecated() const { +bool MethodOptions::has_deprecated() const { return (_has_bits_[0] & 0x00000001u) != 0; } - void MethodOptions::set_has_deprecated() { +void MethodOptions::set_has_deprecated() { _has_bits_[0] |= 0x00000001u; } - void MethodOptions::clear_has_deprecated() { +void MethodOptions::clear_has_deprecated() { _has_bits_[0] &= ~0x00000001u; } - void MethodOptions::clear_deprecated() { +void MethodOptions::clear_deprecated() { deprecated_ = false; clear_has_deprecated(); } @@ -11009,10 +11700,10 @@ void MethodOptions::InternalSwap(MethodOptions* other) { } // repeated .google.protobuf.UninterpretedOption uninterpreted_option = 999; - int MethodOptions::uninterpreted_option_size() const { +int MethodOptions::uninterpreted_option_size() const { return uninterpreted_option_.size(); } - void MethodOptions::clear_uninterpreted_option() { +void MethodOptions::clear_uninterpreted_option() { uninterpreted_option_.Clear(); } const ::google::protobuf::UninterpretedOption& MethodOptions::uninterpreted_option(int index) const { @@ -11048,7 +11739,7 @@ const int UninterpretedOption_NamePart::kIsExtensionFieldNumber; #endif // !_MSC_VER UninterpretedOption_NamePart::UninterpretedOption_NamePart() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.UninterpretedOption.NamePart) } @@ -11281,9 +11972,9 @@ int UninterpretedOption_NamePart::ByteSize() const { void UninterpretedOption_NamePart::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const UninterpretedOption_NamePart* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const UninterpretedOption_NamePart* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -11359,7 +12050,7 @@ const int UninterpretedOption::kAggregateValueFieldNumber; #endif // !_MSC_VER UninterpretedOption::UninterpretedOption() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.UninterpretedOption) } @@ -11470,13 +12161,15 @@ bool UninterpretedOption::MergePartialFromCodedStream( // repeated .google.protobuf.UninterpretedOption.NamePart name = 2; case 2: { if (tag == 18) { - parse_name: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_name: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_name())); } else { goto handle_unusual; } - if (input->ExpectTag(18)) goto parse_name; + if (input->ExpectTag(18)) goto parse_loop_name; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectTag(26)) goto parse_identifier_value; break; } @@ -11780,9 +12473,9 @@ int UninterpretedOption::ByteSize() const { void UninterpretedOption::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const UninterpretedOption* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const UninterpretedOption* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -11868,16 +12561,16 @@ void UninterpretedOption::InternalSwap(UninterpretedOption* other) { // UninterpretedOption_NamePart // required string name_part = 1; - bool UninterpretedOption_NamePart::has_name_part() const { +bool UninterpretedOption_NamePart::has_name_part() const { return (_has_bits_[0] & 0x00000001u) != 0; } - void UninterpretedOption_NamePart::set_has_name_part() { +void UninterpretedOption_NamePart::set_has_name_part() { _has_bits_[0] |= 0x00000001u; } - void UninterpretedOption_NamePart::clear_has_name_part() { +void UninterpretedOption_NamePart::clear_has_name_part() { _has_bits_[0] &= ~0x00000001u; } - void UninterpretedOption_NamePart::clear_name_part() { +void UninterpretedOption_NamePart::clear_name_part() { name_part_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_name_part(); } @@ -11921,16 +12614,16 @@ void UninterpretedOption::InternalSwap(UninterpretedOption* other) { } // required bool is_extension = 2; - bool UninterpretedOption_NamePart::has_is_extension() const { +bool UninterpretedOption_NamePart::has_is_extension() const { return (_has_bits_[0] & 0x00000002u) != 0; } - void UninterpretedOption_NamePart::set_has_is_extension() { +void UninterpretedOption_NamePart::set_has_is_extension() { _has_bits_[0] |= 0x00000002u; } - void UninterpretedOption_NamePart::clear_has_is_extension() { +void UninterpretedOption_NamePart::clear_has_is_extension() { _has_bits_[0] &= ~0x00000002u; } - void UninterpretedOption_NamePart::clear_is_extension() { +void UninterpretedOption_NamePart::clear_is_extension() { is_extension_ = false; clear_has_is_extension(); } @@ -11949,10 +12642,10 @@ void UninterpretedOption::InternalSwap(UninterpretedOption* other) { // UninterpretedOption // repeated .google.protobuf.UninterpretedOption.NamePart name = 2; - int UninterpretedOption::name_size() const { +int UninterpretedOption::name_size() const { return name_.size(); } - void UninterpretedOption::clear_name() { +void UninterpretedOption::clear_name() { name_.Clear(); } const ::google::protobuf::UninterpretedOption_NamePart& UninterpretedOption::name(int index) const { @@ -11979,16 +12672,16 @@ UninterpretedOption::mutable_name() { } // optional string identifier_value = 3; - bool UninterpretedOption::has_identifier_value() const { +bool UninterpretedOption::has_identifier_value() const { return (_has_bits_[0] & 0x00000002u) != 0; } - void UninterpretedOption::set_has_identifier_value() { +void UninterpretedOption::set_has_identifier_value() { _has_bits_[0] |= 0x00000002u; } - void UninterpretedOption::clear_has_identifier_value() { +void UninterpretedOption::clear_has_identifier_value() { _has_bits_[0] &= ~0x00000002u; } - void UninterpretedOption::clear_identifier_value() { +void UninterpretedOption::clear_identifier_value() { identifier_value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_identifier_value(); } @@ -12032,16 +12725,16 @@ UninterpretedOption::mutable_name() { } // optional uint64 positive_int_value = 4; - bool UninterpretedOption::has_positive_int_value() const { +bool UninterpretedOption::has_positive_int_value() const { return (_has_bits_[0] & 0x00000004u) != 0; } - void UninterpretedOption::set_has_positive_int_value() { +void UninterpretedOption::set_has_positive_int_value() { _has_bits_[0] |= 0x00000004u; } - void UninterpretedOption::clear_has_positive_int_value() { +void UninterpretedOption::clear_has_positive_int_value() { _has_bits_[0] &= ~0x00000004u; } - void UninterpretedOption::clear_positive_int_value() { +void UninterpretedOption::clear_positive_int_value() { positive_int_value_ = GOOGLE_ULONGLONG(0); clear_has_positive_int_value(); } @@ -12056,16 +12749,16 @@ UninterpretedOption::mutable_name() { } // optional int64 negative_int_value = 5; - bool UninterpretedOption::has_negative_int_value() const { +bool UninterpretedOption::has_negative_int_value() const { return (_has_bits_[0] & 0x00000008u) != 0; } - void UninterpretedOption::set_has_negative_int_value() { +void UninterpretedOption::set_has_negative_int_value() { _has_bits_[0] |= 0x00000008u; } - void UninterpretedOption::clear_has_negative_int_value() { +void UninterpretedOption::clear_has_negative_int_value() { _has_bits_[0] &= ~0x00000008u; } - void UninterpretedOption::clear_negative_int_value() { +void UninterpretedOption::clear_negative_int_value() { negative_int_value_ = GOOGLE_LONGLONG(0); clear_has_negative_int_value(); } @@ -12080,16 +12773,16 @@ UninterpretedOption::mutable_name() { } // optional double double_value = 6; - bool UninterpretedOption::has_double_value() const { +bool UninterpretedOption::has_double_value() const { return (_has_bits_[0] & 0x00000010u) != 0; } - void UninterpretedOption::set_has_double_value() { +void UninterpretedOption::set_has_double_value() { _has_bits_[0] |= 0x00000010u; } - void UninterpretedOption::clear_has_double_value() { +void UninterpretedOption::clear_has_double_value() { _has_bits_[0] &= ~0x00000010u; } - void UninterpretedOption::clear_double_value() { +void UninterpretedOption::clear_double_value() { double_value_ = 0; clear_has_double_value(); } @@ -12104,16 +12797,16 @@ UninterpretedOption::mutable_name() { } // optional bytes string_value = 7; - bool UninterpretedOption::has_string_value() const { +bool UninterpretedOption::has_string_value() const { return (_has_bits_[0] & 0x00000020u) != 0; } - void UninterpretedOption::set_has_string_value() { +void UninterpretedOption::set_has_string_value() { _has_bits_[0] |= 0x00000020u; } - void UninterpretedOption::clear_has_string_value() { +void UninterpretedOption::clear_has_string_value() { _has_bits_[0] &= ~0x00000020u; } - void UninterpretedOption::clear_string_value() { +void UninterpretedOption::clear_string_value() { string_value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_string_value(); } @@ -12157,16 +12850,16 @@ UninterpretedOption::mutable_name() { } // optional string aggregate_value = 8; - bool UninterpretedOption::has_aggregate_value() const { +bool UninterpretedOption::has_aggregate_value() const { return (_has_bits_[0] & 0x00000040u) != 0; } - void UninterpretedOption::set_has_aggregate_value() { +void UninterpretedOption::set_has_aggregate_value() { _has_bits_[0] |= 0x00000040u; } - void UninterpretedOption::clear_has_aggregate_value() { +void UninterpretedOption::clear_has_aggregate_value() { _has_bits_[0] &= ~0x00000040u; } - void UninterpretedOption::clear_aggregate_value() { +void UninterpretedOption::clear_aggregate_value() { aggregate_value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_aggregate_value(); } @@ -12222,7 +12915,7 @@ const int SourceCodeInfo_Location::kLeadingDetachedCommentsFieldNumber; #endif // !_MSC_VER SourceCodeInfo_Location::SourceCodeInfo_Location() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.SourceCodeInfo.Location) } @@ -12625,9 +13318,9 @@ int SourceCodeInfo_Location::ByteSize() const { void SourceCodeInfo_Location::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const SourceCodeInfo_Location* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const SourceCodeInfo_Location* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -12703,7 +13396,7 @@ const int SourceCodeInfo::kLocationFieldNumber; #endif // !_MSC_VER SourceCodeInfo::SourceCodeInfo() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.SourceCodeInfo) } @@ -12780,13 +13473,15 @@ bool SourceCodeInfo::MergePartialFromCodedStream( // repeated .google.protobuf.SourceCodeInfo.Location location = 1; case 1: { if (tag == 10) { - parse_location: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_location: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_location())); } else { goto handle_unusual; } - if (input->ExpectTag(10)) goto parse_location; + if (input->ExpectTag(10)) goto parse_loop_location; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectAtEnd()) goto success; break; } @@ -12871,9 +13566,9 @@ int SourceCodeInfo::ByteSize() const { void SourceCodeInfo::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const SourceCodeInfo* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const SourceCodeInfo* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -12929,10 +13624,10 @@ void SourceCodeInfo::InternalSwap(SourceCodeInfo* other) { // SourceCodeInfo_Location // repeated int32 path = 1 [packed = true]; - int SourceCodeInfo_Location::path_size() const { +int SourceCodeInfo_Location::path_size() const { return path_.size(); } - void SourceCodeInfo_Location::clear_path() { +void SourceCodeInfo_Location::clear_path() { path_.Clear(); } ::google::protobuf::int32 SourceCodeInfo_Location::path(int index) const { @@ -12959,10 +13654,10 @@ SourceCodeInfo_Location::mutable_path() { } // repeated int32 span = 2 [packed = true]; - int SourceCodeInfo_Location::span_size() const { +int SourceCodeInfo_Location::span_size() const { return span_.size(); } - void SourceCodeInfo_Location::clear_span() { +void SourceCodeInfo_Location::clear_span() { span_.Clear(); } ::google::protobuf::int32 SourceCodeInfo_Location::span(int index) const { @@ -12989,16 +13684,16 @@ SourceCodeInfo_Location::mutable_span() { } // optional string leading_comments = 3; - bool SourceCodeInfo_Location::has_leading_comments() const { +bool SourceCodeInfo_Location::has_leading_comments() const { return (_has_bits_[0] & 0x00000004u) != 0; } - void SourceCodeInfo_Location::set_has_leading_comments() { +void SourceCodeInfo_Location::set_has_leading_comments() { _has_bits_[0] |= 0x00000004u; } - void SourceCodeInfo_Location::clear_has_leading_comments() { +void SourceCodeInfo_Location::clear_has_leading_comments() { _has_bits_[0] &= ~0x00000004u; } - void SourceCodeInfo_Location::clear_leading_comments() { +void SourceCodeInfo_Location::clear_leading_comments() { leading_comments_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_leading_comments(); } @@ -13042,16 +13737,16 @@ SourceCodeInfo_Location::mutable_span() { } // optional string trailing_comments = 4; - bool SourceCodeInfo_Location::has_trailing_comments() const { +bool SourceCodeInfo_Location::has_trailing_comments() const { return (_has_bits_[0] & 0x00000008u) != 0; } - void SourceCodeInfo_Location::set_has_trailing_comments() { +void SourceCodeInfo_Location::set_has_trailing_comments() { _has_bits_[0] |= 0x00000008u; } - void SourceCodeInfo_Location::clear_has_trailing_comments() { +void SourceCodeInfo_Location::clear_has_trailing_comments() { _has_bits_[0] &= ~0x00000008u; } - void SourceCodeInfo_Location::clear_trailing_comments() { +void SourceCodeInfo_Location::clear_trailing_comments() { trailing_comments_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_trailing_comments(); } @@ -13095,10 +13790,10 @@ SourceCodeInfo_Location::mutable_span() { } // repeated string leading_detached_comments = 6; - int SourceCodeInfo_Location::leading_detached_comments_size() const { +int SourceCodeInfo_Location::leading_detached_comments_size() const { return leading_detached_comments_.size(); } - void SourceCodeInfo_Location::clear_leading_detached_comments() { +void SourceCodeInfo_Location::clear_leading_detached_comments() { leading_detached_comments_.Clear(); } const ::std::string& SourceCodeInfo_Location::leading_detached_comments(int index) const { @@ -13153,10 +13848,10 @@ SourceCodeInfo_Location::mutable_leading_detached_comments() { // SourceCodeInfo // repeated .google.protobuf.SourceCodeInfo.Location location = 1; - int SourceCodeInfo::location_size() const { +int SourceCodeInfo::location_size() const { return location_.size(); } - void SourceCodeInfo::clear_location() { +void SourceCodeInfo::clear_location() { location_.Clear(); } const ::google::protobuf::SourceCodeInfo_Location& SourceCodeInfo::location(int index) const { diff --git a/src/google/protobuf/descriptor.pb.h b/src/google/protobuf/descriptor.pb.h index 5bebf4fd..b887b8cb 100644 --- a/src/google/protobuf/descriptor.pb.h +++ b/src/google/protobuf/descriptor.pb.h @@ -42,6 +42,7 @@ class FileDescriptorSet; class FileDescriptorProto; class DescriptorProto; class DescriptorProto_ExtensionRange; +class DescriptorProto_ReservedRange; class FieldDescriptorProto; class OneofDescriptorProto; class EnumDescriptorProto; @@ -155,6 +156,26 @@ inline bool FieldOptions_CType_Parse( return ::google::protobuf::internal::ParseNamedEnum( FieldOptions_CType_descriptor(), name, value); } +enum FieldOptions_JSType { + FieldOptions_JSType_JS_NORMAL = 0, + FieldOptions_JSType_JS_STRING = 1, + FieldOptions_JSType_JS_NUMBER = 2 +}; +LIBPROTOBUF_EXPORT bool FieldOptions_JSType_IsValid(int value); +const FieldOptions_JSType FieldOptions_JSType_JSType_MIN = FieldOptions_JSType_JS_NORMAL; +const FieldOptions_JSType FieldOptions_JSType_JSType_MAX = FieldOptions_JSType_JS_NUMBER; +const int FieldOptions_JSType_JSType_ARRAYSIZE = FieldOptions_JSType_JSType_MAX + 1; + +LIBPROTOBUF_EXPORT const ::google::protobuf::EnumDescriptor* FieldOptions_JSType_descriptor(); +inline const ::std::string& FieldOptions_JSType_Name(FieldOptions_JSType value) { + return ::google::protobuf::internal::NameOfEnum( + FieldOptions_JSType_descriptor(), value); +} +inline bool FieldOptions_JSType_Parse( + const ::std::string& name, FieldOptions_JSType* value) { + return ::google::protobuf::internal::ParseNamedEnum( + FieldOptions_JSType_descriptor(), name, value); +} // =================================================================== class LIBPROTOBUF_EXPORT FileDescriptorSet : public ::google::protobuf::Message { @@ -591,6 +612,105 @@ class LIBPROTOBUF_EXPORT DescriptorProto_ExtensionRange : public ::google::proto }; // ------------------------------------------------------------------- +class LIBPROTOBUF_EXPORT DescriptorProto_ReservedRange : public ::google::protobuf::Message { + public: + DescriptorProto_ReservedRange(); + virtual ~DescriptorProto_ReservedRange(); + + DescriptorProto_ReservedRange(const DescriptorProto_ReservedRange& from); + + inline DescriptorProto_ReservedRange& operator=(const DescriptorProto_ReservedRange& from) { + CopyFrom(from); + return *this; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _internal_metadata_.unknown_fields(); + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return _internal_metadata_.mutable_unknown_fields(); + } + + static const ::google::protobuf::Descriptor* descriptor(); + static const DescriptorProto_ReservedRange& default_instance(); + + void Swap(DescriptorProto_ReservedRange* other); + + // implements Message ---------------------------------------------- + + inline DescriptorProto_ReservedRange* New() const { return New(NULL); } + + DescriptorProto_ReservedRange* New(::google::protobuf::Arena* arena) const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const DescriptorProto_ReservedRange& from); + void MergeFrom(const DescriptorProto_ReservedRange& from); + void Clear(); + bool IsInitialized() const; + + int ByteSize() const; + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + void SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const; + void InternalSwap(DescriptorProto_ReservedRange* other); + private: + inline ::google::protobuf::Arena* GetArenaNoVirtual() const { + return _internal_metadata_.arena(); + } + inline void* MaybeArenaPtr() const { + return _internal_metadata_.raw_arena_ptr(); + } + public: + + ::google::protobuf::Metadata GetMetadata() const; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // optional int32 start = 1; + bool has_start() const; + void clear_start(); + static const int kStartFieldNumber = 1; + ::google::protobuf::int32 start() const; + void set_start(::google::protobuf::int32 value); + + // optional int32 end = 2; + bool has_end() const; + void clear_end(); + static const int kEndFieldNumber = 2; + ::google::protobuf::int32 end() const; + void set_end(::google::protobuf::int32 value); + + // @@protoc_insertion_point(class_scope:google.protobuf.DescriptorProto.ReservedRange) + private: + inline void set_has_start(); + inline void clear_has_start(); + inline void set_has_end(); + inline void clear_has_end(); + + ::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_; + ::google::protobuf::uint32 _has_bits_[1]; + mutable int _cached_size_; + ::google::protobuf::int32 start_; + ::google::protobuf::int32 end_; + friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); + friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto(); + friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto(); + + void InitAsDefaultInstance(); + static DescriptorProto_ReservedRange* default_instance_; +}; +// ------------------------------------------------------------------- + class LIBPROTOBUF_EXPORT DescriptorProto : public ::google::protobuf::Message { public: DescriptorProto(); @@ -654,6 +774,7 @@ class LIBPROTOBUF_EXPORT DescriptorProto : public ::google::protobuf::Message { // nested types ---------------------------------------------------- typedef DescriptorProto_ExtensionRange ExtensionRange; + typedef DescriptorProto_ReservedRange ReservedRange; // accessors ------------------------------------------------------- @@ -750,6 +871,34 @@ class LIBPROTOBUF_EXPORT DescriptorProto : public ::google::protobuf::Message { ::google::protobuf::MessageOptions* release_options(); void set_allocated_options(::google::protobuf::MessageOptions* options); + // repeated .google.protobuf.DescriptorProto.ReservedRange reserved_range = 9; + int reserved_range_size() const; + void clear_reserved_range(); + static const int kReservedRangeFieldNumber = 9; + const ::google::protobuf::DescriptorProto_ReservedRange& reserved_range(int index) const; + ::google::protobuf::DescriptorProto_ReservedRange* mutable_reserved_range(int index); + ::google::protobuf::DescriptorProto_ReservedRange* add_reserved_range(); + const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ReservedRange >& + reserved_range() const; + ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ReservedRange >* + mutable_reserved_range(); + + // repeated string reserved_name = 10; + int reserved_name_size() const; + void clear_reserved_name(); + static const int kReservedNameFieldNumber = 10; + const ::std::string& reserved_name(int index) const; + ::std::string* mutable_reserved_name(int index); + void set_reserved_name(int index, const ::std::string& value); + void set_reserved_name(int index, const char* value); + void set_reserved_name(int index, const char* value, size_t size); + ::std::string* add_reserved_name(); + void add_reserved_name(const ::std::string& value); + void add_reserved_name(const char* value); + void add_reserved_name(const char* value, size_t size); + const ::google::protobuf::RepeatedPtrField< ::std::string>& reserved_name() const; + ::google::protobuf::RepeatedPtrField< ::std::string>* mutable_reserved_name(); + // @@protoc_insertion_point(class_scope:google.protobuf.DescriptorProto) private: inline void set_has_name(); @@ -768,6 +917,8 @@ class LIBPROTOBUF_EXPORT DescriptorProto : public ::google::protobuf::Message { ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ExtensionRange > extension_range_; ::google::protobuf::RepeatedPtrField< ::google::protobuf::OneofDescriptorProto > oneof_decl_; ::google::protobuf::MessageOptions* options_; + ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ReservedRange > reserved_range_; + ::google::protobuf::RepeatedPtrField< ::std::string> reserved_name_; friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto(); friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto(); @@ -2144,6 +2295,31 @@ class LIBPROTOBUF_EXPORT FieldOptions : public ::google::protobuf::Message { return FieldOptions_CType_Parse(name, value); } + typedef FieldOptions_JSType JSType; + static const JSType JS_NORMAL = FieldOptions_JSType_JS_NORMAL; + static const JSType JS_STRING = FieldOptions_JSType_JS_STRING; + static const JSType JS_NUMBER = FieldOptions_JSType_JS_NUMBER; + static inline bool JSType_IsValid(int value) { + return FieldOptions_JSType_IsValid(value); + } + static const JSType JSType_MIN = + FieldOptions_JSType_JSType_MIN; + static const JSType JSType_MAX = + FieldOptions_JSType_JSType_MAX; + static const int JSType_ARRAYSIZE = + FieldOptions_JSType_JSType_ARRAYSIZE; + static inline const ::google::protobuf::EnumDescriptor* + JSType_descriptor() { + return FieldOptions_JSType_descriptor(); + } + static inline const ::std::string& JSType_Name(JSType value) { + return FieldOptions_JSType_Name(value); + } + static inline bool JSType_Parse(const ::std::string& name, + JSType* value) { + return FieldOptions_JSType_Parse(name, value); + } + // accessors ------------------------------------------------------- // optional .google.protobuf.FieldOptions.CType ctype = 1 [default = STRING]; @@ -2160,6 +2336,13 @@ class LIBPROTOBUF_EXPORT FieldOptions : public ::google::protobuf::Message { bool packed() const; void set_packed(bool value); + // optional .google.protobuf.FieldOptions.JSType jstype = 6 [default = JS_NORMAL]; + bool has_jstype() const; + void clear_jstype(); + static const int kJstypeFieldNumber = 6; + ::google::protobuf::FieldOptions_JSType jstype() const; + void set_jstype(::google::protobuf::FieldOptions_JSType value); + // optional bool lazy = 5 [default = false]; bool has_lazy() const; void clear_lazy(); @@ -2200,6 +2383,8 @@ class LIBPROTOBUF_EXPORT FieldOptions : public ::google::protobuf::Message { inline void clear_has_ctype(); inline void set_has_packed(); inline void clear_has_packed(); + inline void set_has_jstype(); + inline void clear_has_jstype(); inline void set_has_lazy(); inline void clear_has_lazy(); inline void set_has_deprecated(); @@ -2213,11 +2398,12 @@ class LIBPROTOBUF_EXPORT FieldOptions : public ::google::protobuf::Message { ::google::protobuf::uint32 _has_bits_[1]; mutable int _cached_size_; int ctype_; + int jstype_; + ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption > uninterpreted_option_; bool packed_; bool lazy_; bool deprecated_; bool weak_; - ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption > uninterpreted_option_; friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto(); friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto(); friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto(); @@ -3754,6 +3940,58 @@ inline void DescriptorProto_ExtensionRange::set_end(::google::protobuf::int32 va // ------------------------------------------------------------------- +// DescriptorProto_ReservedRange + +// optional int32 start = 1; +inline bool DescriptorProto_ReservedRange::has_start() const { + return (_has_bits_[0] & 0x00000001u) != 0; +} +inline void DescriptorProto_ReservedRange::set_has_start() { + _has_bits_[0] |= 0x00000001u; +} +inline void DescriptorProto_ReservedRange::clear_has_start() { + _has_bits_[0] &= ~0x00000001u; +} +inline void DescriptorProto_ReservedRange::clear_start() { + start_ = 0; + clear_has_start(); +} +inline ::google::protobuf::int32 DescriptorProto_ReservedRange::start() const { + // @@protoc_insertion_point(field_get:google.protobuf.DescriptorProto.ReservedRange.start) + return start_; +} +inline void DescriptorProto_ReservedRange::set_start(::google::protobuf::int32 value) { + set_has_start(); + start_ = value; + // @@protoc_insertion_point(field_set:google.protobuf.DescriptorProto.ReservedRange.start) +} + +// optional int32 end = 2; +inline bool DescriptorProto_ReservedRange::has_end() const { + return (_has_bits_[0] & 0x00000002u) != 0; +} +inline void DescriptorProto_ReservedRange::set_has_end() { + _has_bits_[0] |= 0x00000002u; +} +inline void DescriptorProto_ReservedRange::clear_has_end() { + _has_bits_[0] &= ~0x00000002u; +} +inline void DescriptorProto_ReservedRange::clear_end() { + end_ = 0; + clear_has_end(); +} +inline ::google::protobuf::int32 DescriptorProto_ReservedRange::end() const { + // @@protoc_insertion_point(field_get:google.protobuf.DescriptorProto.ReservedRange.end) + return end_; +} +inline void DescriptorProto_ReservedRange::set_end(::google::protobuf::int32 value) { + set_has_end(); + end_ = value; + // @@protoc_insertion_point(field_set:google.protobuf.DescriptorProto.ReservedRange.end) +} + +// ------------------------------------------------------------------- + // DescriptorProto // optional string name = 1; @@ -4032,6 +4270,90 @@ inline void DescriptorProto::set_allocated_options(::google::protobuf::MessageOp // @@protoc_insertion_point(field_set_allocated:google.protobuf.DescriptorProto.options) } +// repeated .google.protobuf.DescriptorProto.ReservedRange reserved_range = 9; +inline int DescriptorProto::reserved_range_size() const { + return reserved_range_.size(); +} +inline void DescriptorProto::clear_reserved_range() { + reserved_range_.Clear(); +} +inline const ::google::protobuf::DescriptorProto_ReservedRange& DescriptorProto::reserved_range(int index) const { + // @@protoc_insertion_point(field_get:google.protobuf.DescriptorProto.reserved_range) + return reserved_range_.Get(index); +} +inline ::google::protobuf::DescriptorProto_ReservedRange* DescriptorProto::mutable_reserved_range(int index) { + // @@protoc_insertion_point(field_mutable:google.protobuf.DescriptorProto.reserved_range) + return reserved_range_.Mutable(index); +} +inline ::google::protobuf::DescriptorProto_ReservedRange* DescriptorProto::add_reserved_range() { + // @@protoc_insertion_point(field_add:google.protobuf.DescriptorProto.reserved_range) + return reserved_range_.Add(); +} +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ReservedRange >& +DescriptorProto::reserved_range() const { + // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.reserved_range) + return reserved_range_; +} +inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ReservedRange >* +DescriptorProto::mutable_reserved_range() { + // @@protoc_insertion_point(field_mutable_list:google.protobuf.DescriptorProto.reserved_range) + return &reserved_range_; +} + +// repeated string reserved_name = 10; +inline int DescriptorProto::reserved_name_size() const { + return reserved_name_.size(); +} +inline void DescriptorProto::clear_reserved_name() { + reserved_name_.Clear(); +} +inline const ::std::string& DescriptorProto::reserved_name(int index) const { + // @@protoc_insertion_point(field_get:google.protobuf.DescriptorProto.reserved_name) + return reserved_name_.Get(index); +} +inline ::std::string* DescriptorProto::mutable_reserved_name(int index) { + // @@protoc_insertion_point(field_mutable:google.protobuf.DescriptorProto.reserved_name) + return reserved_name_.Mutable(index); +} +inline void DescriptorProto::set_reserved_name(int index, const ::std::string& value) { + // @@protoc_insertion_point(field_set:google.protobuf.DescriptorProto.reserved_name) + reserved_name_.Mutable(index)->assign(value); +} +inline void DescriptorProto::set_reserved_name(int index, const char* value) { + reserved_name_.Mutable(index)->assign(value); + // @@protoc_insertion_point(field_set_char:google.protobuf.DescriptorProto.reserved_name) +} +inline void DescriptorProto::set_reserved_name(int index, const char* value, size_t size) { + reserved_name_.Mutable(index)->assign( + reinterpret_cast(value), size); + // @@protoc_insertion_point(field_set_pointer:google.protobuf.DescriptorProto.reserved_name) +} +inline ::std::string* DescriptorProto::add_reserved_name() { + return reserved_name_.Add(); +} +inline void DescriptorProto::add_reserved_name(const ::std::string& value) { + reserved_name_.Add()->assign(value); + // @@protoc_insertion_point(field_add:google.protobuf.DescriptorProto.reserved_name) +} +inline void DescriptorProto::add_reserved_name(const char* value) { + reserved_name_.Add()->assign(value); + // @@protoc_insertion_point(field_add_char:google.protobuf.DescriptorProto.reserved_name) +} +inline void DescriptorProto::add_reserved_name(const char* value, size_t size) { + reserved_name_.Add()->assign(reinterpret_cast(value), size); + // @@protoc_insertion_point(field_add_pointer:google.protobuf.DescriptorProto.reserved_name) +} +inline const ::google::protobuf::RepeatedPtrField< ::std::string>& +DescriptorProto::reserved_name() const { + // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.reserved_name) + return reserved_name_; +} +inline ::google::protobuf::RepeatedPtrField< ::std::string>* +DescriptorProto::mutable_reserved_name() { + // @@protoc_insertion_point(field_mutable_list:google.protobuf.DescriptorProto.reserved_name) + return &reserved_name_; +} + // ------------------------------------------------------------------- // FieldDescriptorProto @@ -5783,15 +6105,40 @@ inline void FieldOptions::set_packed(bool value) { // @@protoc_insertion_point(field_set:google.protobuf.FieldOptions.packed) } +// optional .google.protobuf.FieldOptions.JSType jstype = 6 [default = JS_NORMAL]; +inline bool FieldOptions::has_jstype() const { + return (_has_bits_[0] & 0x00000004u) != 0; +} +inline void FieldOptions::set_has_jstype() { + _has_bits_[0] |= 0x00000004u; +} +inline void FieldOptions::clear_has_jstype() { + _has_bits_[0] &= ~0x00000004u; +} +inline void FieldOptions::clear_jstype() { + jstype_ = 0; + clear_has_jstype(); +} +inline ::google::protobuf::FieldOptions_JSType FieldOptions::jstype() const { + // @@protoc_insertion_point(field_get:google.protobuf.FieldOptions.jstype) + return static_cast< ::google::protobuf::FieldOptions_JSType >(jstype_); +} +inline void FieldOptions::set_jstype(::google::protobuf::FieldOptions_JSType value) { + assert(::google::protobuf::FieldOptions_JSType_IsValid(value)); + set_has_jstype(); + jstype_ = value; + // @@protoc_insertion_point(field_set:google.protobuf.FieldOptions.jstype) +} + // optional bool lazy = 5 [default = false]; inline bool FieldOptions::has_lazy() const { - return (_has_bits_[0] & 0x00000004u) != 0; + return (_has_bits_[0] & 0x00000008u) != 0; } inline void FieldOptions::set_has_lazy() { - _has_bits_[0] |= 0x00000004u; + _has_bits_[0] |= 0x00000008u; } inline void FieldOptions::clear_has_lazy() { - _has_bits_[0] &= ~0x00000004u; + _has_bits_[0] &= ~0x00000008u; } inline void FieldOptions::clear_lazy() { lazy_ = false; @@ -5809,13 +6156,13 @@ inline void FieldOptions::set_lazy(bool value) { // optional bool deprecated = 3 [default = false]; inline bool FieldOptions::has_deprecated() const { - return (_has_bits_[0] & 0x00000008u) != 0; + return (_has_bits_[0] & 0x00000010u) != 0; } inline void FieldOptions::set_has_deprecated() { - _has_bits_[0] |= 0x00000008u; + _has_bits_[0] |= 0x00000010u; } inline void FieldOptions::clear_has_deprecated() { - _has_bits_[0] &= ~0x00000008u; + _has_bits_[0] &= ~0x00000010u; } inline void FieldOptions::clear_deprecated() { deprecated_ = false; @@ -5833,13 +6180,13 @@ inline void FieldOptions::set_deprecated(bool value) { // optional bool weak = 10 [default = false]; inline bool FieldOptions::has_weak() const { - return (_has_bits_[0] & 0x00000010u) != 0; + return (_has_bits_[0] & 0x00000020u) != 0; } inline void FieldOptions::set_has_weak() { - _has_bits_[0] |= 0x00000010u; + _has_bits_[0] |= 0x00000020u; } inline void FieldOptions::clear_has_weak() { - _has_bits_[0] &= ~0x00000010u; + _has_bits_[0] &= ~0x00000020u; } inline void FieldOptions::clear_weak() { weak_ = false; @@ -6746,6 +7093,48 @@ SourceCodeInfo::mutable_location() { } #endif // !PROTOBUF_INLINE_NOT_IN_HEADERS +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + // @@protoc_insertion_point(namespace_scope) @@ -6776,6 +7165,11 @@ template <> inline const EnumDescriptor* GetEnumDescriptor< ::google::protobuf::FieldOptions_CType>() { return ::google::protobuf::FieldOptions_CType_descriptor(); } +template <> struct is_proto_enum< ::google::protobuf::FieldOptions_JSType> : ::google::protobuf::internal::true_type {}; +template <> +inline const EnumDescriptor* GetEnumDescriptor< ::google::protobuf::FieldOptions_JSType>() { + return ::google::protobuf::FieldOptions_JSType_descriptor(); +} } // namespace protobuf } // namespace google diff --git a/src/google/protobuf/descriptor.proto b/src/google/protobuf/descriptor.proto index 7099135d..20a60080 100644 --- a/src/google/protobuf/descriptor.proto +++ b/src/google/protobuf/descriptor.proto @@ -106,6 +106,18 @@ message DescriptorProto { repeated OneofDescriptorProto oneof_decl = 8; optional MessageOptions options = 7; + + // Range of reserved tag numbers. Reserved tag numbers may not be used by + // fields or extension ranges in the same message. Reserved ranges may + // not overlap. + message ReservedRange { + optional int32 start = 1; // Inclusive. + optional int32 end = 2; // Exclusive. + } + repeated ReservedRange reserved_range = 9; + // Reserved field names, which may not be used by fields in the same message. + // A given name may only be reserved once. + repeated string reserved_name = 10; } // Describes a field within a message. @@ -251,11 +263,11 @@ message MethodDescriptorProto { // * For options which will be published and used publicly by multiple // independent entities, e-mail protobuf-global-extension-registry@google.com // to reserve extension numbers. Simply provide your project name (e.g. -// Object-C plugin) and your porject website (if available) -- there's no need -// to explain how you intend to use them. Usually you only need one extension -// number. You can declare multiple options with only one extension number by -// putting them in a sub-message. See the Custom Options section of the docs -// for examples: +// Objective-C plugin) and your project website (if available) -- there's no +// need to explain how you intend to use them. Usually you only need one +// extension number. You can declare multiple options with only one extension +// number by putting them in a sub-message. See the Custom Options section of +// the docs for examples: // https://developers.google.com/protocol-buffers/docs/proto#options // If this turns out to be popular, a web service will be set up // to automatically assign option numbers. @@ -442,10 +454,31 @@ message FieldOptions { // The packed option can be enabled for repeated primitive fields to enable // a more efficient representation on the wire. Rather than repeatedly // writing the tag and type for each element, the entire array is encoded as - // a single length-delimited blob. + // a single length-delimited blob. In proto3, only explicit setting it to + // false will avoid using packed encoding. optional bool packed = 2; + // The jstype option determines the JavaScript type used for values of the + // field. The option is permitted only for 64 bit integral and fixed types + // (int64, uint64, sint64, fixed64, sfixed64). By default these types are + // represented as JavaScript strings. This avoids loss of precision that can + // happen when a large value is converted to a floating point JavaScript + // numbers. Specifying JS_NUMBER for the jstype causes the generated + // JavaScript code to use the JavaScript "number" type instead of strings. + // This option is an enum to permit additional types to be added, + // e.g. goog.math.Integer. + optional JSType jstype = 6 [default = JS_NORMAL]; + enum JSType { + // Use the default type. + JS_NORMAL = 0; + + // Use JavaScript strings. + JS_STRING = 1; + + // Use JavaScript numbers. + JS_NUMBER = 2; + } // Should this field be parsed lazily? Lazy applies only to message-type // fields. It means that when the outer message is initially parsed, the diff --git a/src/google/protobuf/descriptor_database.h b/src/google/protobuf/descriptor_database.h index 934e4022..86002d56 100644 --- a/src/google/protobuf/descriptor_database.h +++ b/src/google/protobuf/descriptor_database.h @@ -312,7 +312,7 @@ class LIBPROTOBUF_EXPORT EncodedDescriptorDatabase : public DescriptorDatabase { // A DescriptorDatabase that fetches files from a given pool. class LIBPROTOBUF_EXPORT DescriptorPoolDatabase : public DescriptorDatabase { public: - DescriptorPoolDatabase(const DescriptorPool& pool); + explicit DescriptorPoolDatabase(const DescriptorPool& pool); ~DescriptorPoolDatabase(); // implements DescriptorDatabase ----------------------------------- @@ -341,7 +341,7 @@ class LIBPROTOBUF_EXPORT MergedDescriptorDatabase : public DescriptorDatabase { // Merge more than two databases. The sources remain property of the caller. // The vector may be deleted after the constructor returns but the // DescriptorDatabases need to stick around. - MergedDescriptorDatabase(const vector& sources); + explicit MergedDescriptorDatabase(const vector& sources); ~MergedDescriptorDatabase(); // implements DescriptorDatabase ----------------------------------- diff --git a/src/google/protobuf/descriptor_unittest.cc b/src/google/protobuf/descriptor_unittest.cc index fdce3d78..760df097 100644 --- a/src/google/protobuf/descriptor_unittest.cc +++ b/src/google/protobuf/descriptor_unittest.cc @@ -139,6 +139,14 @@ DescriptorProto::ExtensionRange* AddExtensionRange(DescriptorProto* parent, return result; } +DescriptorProto::ReservedRange* AddReservedRange(DescriptorProto* parent, + int start, int end) { + DescriptorProto::ReservedRange* result = parent->add_reserved_range(); + result->set_start(start); + result->set_end(end); + return result; +} + EnumValueDescriptorProto* AddEnumValue(EnumDescriptorProto* enum_proto, const string& name, int number) { EnumValueDescriptorProto* result = enum_proto->add_value(); @@ -1720,6 +1728,84 @@ TEST_F(ExtensionDescriptorTest, DuplicateFieldNumber) { // =================================================================== +// Test reserved fields. +class ReservedDescriptorTest : public testing::Test { + protected: + virtual void SetUp() { + // Build descriptors for the following definitions: + // + // message Foo { + // reserved 2, 9 to 11, 15; + // reserved "foo", "bar"; + // } + + FileDescriptorProto foo_file; + foo_file.set_name("foo.proto"); + + DescriptorProto* foo = AddMessage(&foo_file, "Foo"); + AddReservedRange(foo, 2, 3); + AddReservedRange(foo, 9, 12); + AddReservedRange(foo, 15, 16); + + foo->add_reserved_name("foo"); + foo->add_reserved_name("bar"); + + // Build the descriptors and get the pointers. + foo_file_ = pool_.BuildFile(foo_file); + ASSERT_TRUE(foo_file_ != NULL); + + ASSERT_EQ(1, foo_file_->message_type_count()); + foo_ = foo_file_->message_type(0); + } + + DescriptorPool pool_; + const FileDescriptor* foo_file_; + const Descriptor* foo_; +}; + +TEST_F(ReservedDescriptorTest, ReservedRanges) { + ASSERT_EQ(3, foo_->reserved_range_count()); + + EXPECT_EQ(2, foo_->reserved_range(0)->start); + EXPECT_EQ(3, foo_->reserved_range(0)->end); + + EXPECT_EQ(9, foo_->reserved_range(1)->start); + EXPECT_EQ(12, foo_->reserved_range(1)->end); + + EXPECT_EQ(15, foo_->reserved_range(2)->start); + EXPECT_EQ(16, foo_->reserved_range(2)->end); +}; + +TEST_F(ReservedDescriptorTest, IsReservedNumber) { + EXPECT_FALSE(foo_->IsReservedNumber(1)); + EXPECT_TRUE (foo_->IsReservedNumber(2)); + EXPECT_FALSE(foo_->IsReservedNumber(3)); + EXPECT_FALSE(foo_->IsReservedNumber(8)); + EXPECT_TRUE (foo_->IsReservedNumber(9)); + EXPECT_TRUE (foo_->IsReservedNumber(10)); + EXPECT_TRUE (foo_->IsReservedNumber(11)); + EXPECT_FALSE(foo_->IsReservedNumber(12)); + EXPECT_FALSE(foo_->IsReservedNumber(13)); + EXPECT_FALSE(foo_->IsReservedNumber(14)); + EXPECT_TRUE (foo_->IsReservedNumber(15)); + EXPECT_FALSE(foo_->IsReservedNumber(16)); +}; + +TEST_F(ReservedDescriptorTest, ReservedNames) { + ASSERT_EQ(2, foo_->reserved_name_count()); + + EXPECT_EQ("foo", foo_->reserved_name(0)); + EXPECT_EQ("bar", foo_->reserved_name(1)); +}; + +TEST_F(ReservedDescriptorTest, IsReservedName) { + EXPECT_TRUE (foo_->IsReservedName("foo")); + EXPECT_TRUE (foo_->IsReservedName("bar")); + EXPECT_FALSE(foo_->IsReservedName("baz")); +}; + +// =================================================================== + class MiscTest : public testing::Test { protected: // Function which makes a field descriptor of the given type. @@ -2997,10 +3083,10 @@ class ValidationErrorTest : public testing::Test { protected: // Parse file_text as a FileDescriptorProto in text format and add it // to the DescriptorPool. Expect no errors. - void BuildFile(const string& file_text) { + const FileDescriptor* BuildFile(const string& file_text) { FileDescriptorProto file_proto; - ASSERT_TRUE(TextFormat::ParseFromString(file_text, &file_proto)); - ASSERT_TRUE(pool_.BuildFile(file_proto) != NULL); + EXPECT_TRUE(TextFormat::ParseFromString(file_text, &file_proto)); + return GOOGLE_CHECK_NOTNULL(pool_.BuildFile(file_proto)); } // Parse file_text as a FileDescriptorProto in text format and add it @@ -3251,6 +3337,102 @@ TEST_F(ValidationErrorTest, OverlappingExtensionRanges) { "already-defined range 20 to 29.\n"); } +TEST_F(ValidationErrorTest, ReservedFieldError) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " field { name: \"foo\" number: 15 label:LABEL_OPTIONAL type:TYPE_INT32 }" + " reserved_range { start: 10 end: 20 }" + "}", + + "foo.proto: Foo.foo: NUMBER: Field \"foo\" uses reserved number 15.\n"); +} + +TEST_F(ValidationErrorTest, ReservedExtensionRangeError) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " extension_range { start: 10 end: 20 }" + " reserved_range { start: 5 end: 15 }" + "}", + + "foo.proto: Foo: NUMBER: Extension range 10 to 19" + " overlaps with reserved range 5 to 14.\n"); +} + +TEST_F(ValidationErrorTest, ReservedExtensionRangeAdjacent) { + BuildFile( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " extension_range { start: 10 end: 20 }" + " reserved_range { start: 5 end: 10 }" + "}"); +} + +TEST_F(ValidationErrorTest, ReservedRangeOverlap) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " reserved_range { start: 10 end: 20 }" + " reserved_range { start: 5 end: 15 }" + "}", + + "foo.proto: Foo: NUMBER: Reserved range 5 to 14" + " overlaps with already-defined range 10 to 19.\n"); +} + +TEST_F(ValidationErrorTest, ReservedNameError) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " field { name: \"foo\" number: 15 label:LABEL_OPTIONAL type:TYPE_INT32 }" + " field { name: \"bar\" number: 16 label:LABEL_OPTIONAL type:TYPE_INT32 }" + " field { name: \"baz\" number: 17 label:LABEL_OPTIONAL type:TYPE_INT32 }" + " reserved_name: \"foo\"" + " reserved_name: \"bar\"" + "}", + + "foo.proto: Foo.foo: NAME: Field name \"foo\" is reserved.\n" + "foo.proto: Foo.bar: NAME: Field name \"bar\" is reserved.\n"); +} + +TEST_F(ValidationErrorTest, ReservedNameRedundant) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " reserved_name: \"foo\"" + " reserved_name: \"foo\"" + "}", + + "foo.proto: foo: NAME: Field name \"foo\" is reserved multiple times.\n"); +} + +TEST_F(ValidationErrorTest, ReservedFieldsDebugString) { + const FileDescriptor* file = BuildFile( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " reserved_name: \"foo\"" + " reserved_name: \"bar\"" + " reserved_range { start: 5 end: 6 }" + " reserved_range { start: 10 end: 20 }" + "}"); + + ASSERT_EQ( + "syntax = \"proto2\";\n\n" + "message Foo {\n" + " reserved 5, 10 to 19;\n" + " reserved \"foo\", \"bar\";\n" + "}\n\n", + file->DebugString()); +} + TEST_F(ValidationErrorTest, InvalidDefaults) { BuildFileWithErrors( "name: \"foo.proto\" " @@ -3399,6 +3581,48 @@ TEST_F(ValidationErrorTest, FieldOneofIndexNegative) { "range for type \"Foo\".\n"); } +TEST_F(ValidationErrorTest, OneofFieldsConsecutiveDefinition) { + // Fields belonging to the same oneof must be defined consecutively. + BuildFileWithWarnings( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " field { name:\"foo1\" number: 1 label:LABEL_OPTIONAL type:TYPE_INT32 " + " oneof_index: 0 }" + " field { name:\"bar\" number: 2 label:LABEL_OPTIONAL type:TYPE_INT32 }" + " field { name:\"foo2\" number: 3 label:LABEL_OPTIONAL type:TYPE_INT32 " + " oneof_index: 0 }" + " oneof_decl { name:\"foos\" }" + "}", + + "foo.proto: Foo.bar: OTHER: Fields in the same oneof must be defined " + "consecutively. \"bar\" cannot be defined before the completion of the " + "\"foos\" oneof definition.\n"); + + // Prevent interleaved fields, which belong to different oneofs. + BuildFileWithWarnings( + "name: \"foo2.proto\" " + "message_type {" + " name: \"Foo2\"" + " field { name:\"foo1\" number: 1 label:LABEL_OPTIONAL type:TYPE_INT32 " + " oneof_index: 0 }" + " field { name:\"bar1\" number: 2 label:LABEL_OPTIONAL type:TYPE_INT32 " + " oneof_index: 1 }" + " field { name:\"foo2\" number: 3 label:LABEL_OPTIONAL type:TYPE_INT32 " + " oneof_index: 0 }" + " field { name:\"bar2\" number: 4 label:LABEL_OPTIONAL type:TYPE_INT32 " + " oneof_index: 1 }" + " oneof_decl { name:\"foos\" }" + " oneof_decl { name:\"bars\" }" + "}", + "foo2.proto: Foo2.bar1: OTHER: Fields in the same oneof must be defined " + "consecutively. \"bar1\" cannot be defined before the completion of the " + "\"foos\" oneof definition.\n" + "foo2.proto: Foo2.foo2: OTHER: Fields in the same oneof must be defined " + "consecutively. \"foo2\" cannot be defined before the completion of the " + "\"bars\" oneof definition.\n"); +} + TEST_F(ValidationErrorTest, FieldNumberConflict) { BuildFileWithErrors( "name: \"foo.proto\" " @@ -5293,6 +5517,22 @@ TEST_F(ValidationErrorTest, ValidateProto3LiteRuntime) { "in proto3.\n"); } +TEST_F(ValidationErrorTest, ValidateProto3Group) { + BuildFileWithErrors( + "name: 'foo.proto' " + "syntax: 'proto3' " + "message_type { " + " name: 'Foo' " + " nested_type { " + " name: 'FooGroup' " + " } " + " field { name:'foo_group' number: 1 label:LABEL_OPTIONAL " + " type: TYPE_GROUP type_name:'FooGroup' } " + "}", + "foo.proto: Foo.foo_group: TYPE: Groups are not supported in proto3 " + "syntax.\n"); +} + TEST_F(ValidationErrorTest, ValidateProto3EnumFromProto2) { // Define an enum in a proto2 file. diff --git a/src/google/protobuf/duration.pb.cc b/src/google/protobuf/duration.pb.cc index 4a11209e..f78fce21 100644 --- a/src/google/protobuf/duration.pb.cc +++ b/src/google/protobuf/duration.pb.cc @@ -117,7 +117,7 @@ const int Duration::kNanosFieldNumber; #endif // !_MSC_VER Duration::Duration() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.Duration) } @@ -310,9 +310,9 @@ int Duration::ByteSize() const { void Duration::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const Duration* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const Duration* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -370,7 +370,7 @@ void Duration::InternalSwap(Duration* other) { // Duration // optional int64 seconds = 1; - void Duration::clear_seconds() { +void Duration::clear_seconds() { seconds_ = GOOGLE_LONGLONG(0); } ::google::protobuf::int64 Duration::seconds() const { @@ -384,7 +384,7 @@ void Duration::InternalSwap(Duration* other) { } // optional int32 nanos = 2; - void Duration::clear_nanos() { +void Duration::clear_nanos() { nanos_ = 0; } ::google::protobuf::int32 Duration::nanos() const { diff --git a/src/google/protobuf/empty.pb.cc b/src/google/protobuf/empty.pb.cc index f7ca5013..1a9a6661 100644 --- a/src/google/protobuf/empty.pb.cc +++ b/src/google/protobuf/empty.pb.cc @@ -111,7 +111,7 @@ static void MergeFromFail(int line) { #endif // !_MSC_VER Empty::Empty() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.Empty) } @@ -221,9 +221,9 @@ int Empty::ByteSize() const { void Empty::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const Empty* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const Empty* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { diff --git a/src/google/protobuf/extension_set.cc b/src/google/protobuf/extension_set.cc index 03b38dd0..649ae184 100644 --- a/src/google/protobuf/extension_set.cc +++ b/src/google/protobuf/extension_set.cc @@ -314,7 +314,7 @@ void ExtensionSet::Add##CAMELCASE(int number, FieldType type, \ extension->is_repeated = true; \ extension->is_packed = packed; \ extension->repeated_##LOWERCASE##_value = \ - Arena::Create >(arena_, arena_); \ + Arena::CreateMessage >(arena_); \ } else { \ GOOGLE_DCHECK_TYPE(*extension, REPEATED, UPPERCASE); \ GOOGLE_DCHECK_EQ(extension->is_packed, packed); \ @@ -359,43 +359,43 @@ void* ExtensionSet::MutableRawRepeatedField(int number, FieldType field_type, static_cast(field_type))) { case WireFormatLite::CPPTYPE_INT32: extension->repeated_int32_value = - Arena::Create >(arena_, arena_); + Arena::CreateMessage >(arena_); break; case WireFormatLite::CPPTYPE_INT64: extension->repeated_int64_value = - Arena::Create >(arena_, arena_); + Arena::CreateMessage >(arena_); break; case WireFormatLite::CPPTYPE_UINT32: extension->repeated_uint32_value = - Arena::Create >(arena_, arena_); + Arena::CreateMessage >(arena_); break; case WireFormatLite::CPPTYPE_UINT64: extension->repeated_uint64_value = - Arena::Create >(arena_, arena_); + Arena::CreateMessage >(arena_); break; case WireFormatLite::CPPTYPE_DOUBLE: extension->repeated_double_value = - Arena::Create >(arena_, arena_); + Arena::CreateMessage >(arena_); break; case WireFormatLite::CPPTYPE_FLOAT: extension->repeated_float_value = - Arena::Create >(arena_, arena_); + Arena::CreateMessage >(arena_); break; case WireFormatLite::CPPTYPE_BOOL: extension->repeated_bool_value = - Arena::Create >(arena_, arena_); + Arena::CreateMessage >(arena_); break; case WireFormatLite::CPPTYPE_ENUM: extension->repeated_enum_value = - Arena::Create >(arena_, arena_); + Arena::CreateMessage >(arena_); break; case WireFormatLite::CPPTYPE_STRING: extension->repeated_string_value = - Arena::Create >(arena_, arena_); + Arena::CreateMessage >(arena_); break; case WireFormatLite::CPPTYPE_MESSAGE: extension->repeated_message_value = - Arena::Create >(arena_, arena_); + Arena::CreateMessage >(arena_); break; } } @@ -468,7 +468,7 @@ void ExtensionSet::AddEnum(int number, FieldType type, extension->is_repeated = true; extension->is_packed = packed; extension->repeated_enum_value = - Arena::Create >(arena_, arena_); + Arena::CreateMessage >(arena_); } else { GOOGLE_DCHECK_TYPE(*extension, REPEATED, ENUM); GOOGLE_DCHECK_EQ(extension->is_packed, packed); @@ -529,7 +529,7 @@ string* ExtensionSet::AddString(int number, FieldType type, extension->is_repeated = true; extension->is_packed = false; extension->repeated_string_value = - Arena::Create >(arena_, arena_); + Arena::CreateMessage >(arena_); } else { GOOGLE_DCHECK_TYPE(*extension, REPEATED, STRING); } @@ -632,6 +632,35 @@ void ExtensionSet::SetAllocatedMessage(int number, FieldType type, extension->is_cleared = false; } +void ExtensionSet::UnsafeArenaSetAllocatedMessage( + int number, FieldType type, const FieldDescriptor* descriptor, + MessageLite* message) { + if (message == NULL) { + ClearExtension(number); + return; + } + Extension* extension; + if (MaybeNewExtension(number, descriptor, &extension)) { + extension->type = type; + GOOGLE_DCHECK_EQ(cpp_type(extension->type), WireFormatLite::CPPTYPE_MESSAGE); + extension->is_repeated = false; + extension->is_lazy = false; + extension->message_value = message; + } else { + GOOGLE_DCHECK_TYPE(*extension, OPTIONAL, MESSAGE); + if (extension->is_lazy) { + extension->lazymessage_value->UnsafeArenaSetAllocatedMessage(message); + } else { + if (arena_ == NULL) { + delete extension->message_value; + } + extension->message_value = message; + } + } + extension->is_cleared = false; +} + + MessageLite* ExtensionSet::ReleaseMessage(int number, const MessageLite& prototype) { map::iterator iter = extensions_.find(number); @@ -712,7 +741,7 @@ MessageLite* ExtensionSet::AddMessage(int number, FieldType type, GOOGLE_DCHECK_EQ(cpp_type(extension->type), WireFormatLite::CPPTYPE_MESSAGE); extension->is_repeated = true; extension->repeated_message_value = - Arena::Create >(arena_, arena_); + Arena::CreateMessage >(arena_); } else { GOOGLE_DCHECK_TYPE(*extension, REPEATED, MESSAGE); } @@ -866,7 +895,7 @@ void ExtensionSet::InternalExtensionMergeFrom( case WireFormatLite::CPPTYPE_##UPPERCASE: \ if (is_new) { \ extension->repeated_##LOWERCASE##_value = \ - Arena::Create(arena_, arena_); \ + Arena::CreateMessage(arena_); \ } \ extension->repeated_##LOWERCASE##_value->MergeFrom( \ *other_extension.repeated_##LOWERCASE##_value); \ @@ -886,7 +915,7 @@ void ExtensionSet::InternalExtensionMergeFrom( case WireFormatLite::CPPTYPE_MESSAGE: if (is_new) { extension->repeated_message_value = - Arena::Create >(arena_, arena_); + Arena::CreateMessage >(arena_); } // We can't call RepeatedPtrField::MergeFrom() because // it would attempt to allocate new objects. diff --git a/src/google/protobuf/extension_set.h b/src/google/protobuf/extension_set.h index 6d6702b3..c371e011 100644 --- a/src/google/protobuf/extension_set.h +++ b/src/google/protobuf/extension_set.h @@ -194,7 +194,7 @@ class LIBPROTOBUF_EXPORT ExtensionSet { // directly, unless you are doing low-level memory management. // // When calling any of these accessors, the extension number requested - // MUST exist in the DescriptorPool provided to the constructor. Otheriwse, + // MUST exist in the DescriptorPool provided to the constructor. Otherwise, // the method will fail an assert. Normally, though, you would not call // these directly; you would either call the generated accessors of your // message class (e.g. GetExtension()) or you would call the accessors @@ -262,6 +262,9 @@ class LIBPROTOBUF_EXPORT ExtensionSet { void SetAllocatedMessage(int number, FieldType type, const FieldDescriptor* descriptor, MessageLite* message); + void UnsafeArenaSetAllocatedMessage(int number, FieldType type, + const FieldDescriptor* descriptor, + MessageLite* message); MessageLite* ReleaseMessage(int number, const MessageLite& prototype); MessageLite* UnsafeArenaReleaseMessage( int number, const MessageLite& prototype); @@ -432,6 +435,7 @@ class LIBPROTOBUF_EXPORT ExtensionSet { const MessageLite& prototype) const = 0; virtual MessageLite* MutableMessage(const MessageLite& prototype) = 0; virtual void SetAllocatedMessage(MessageLite *message) = 0; + virtual void UnsafeArenaSetAllocatedMessage(MessageLite *message) = 0; virtual MessageLite* ReleaseMessage(const MessageLite& prototype) = 0; virtual MessageLite* UnsafeArenaReleaseMessage( const MessageLite& prototype) = 0; diff --git a/src/google/protobuf/extension_set_heavy.cc b/src/google/protobuf/extension_set_heavy.cc index 796e7a5f..330bd828 100644 --- a/src/google/protobuf/extension_set_heavy.cc +++ b/src/google/protobuf/extension_set_heavy.cc @@ -221,7 +221,7 @@ MessageLite* ExtensionSet::AddMessage(const FieldDescriptor* descriptor, GOOGLE_DCHECK_EQ(cpp_type(extension->type), FieldDescriptor::CPPTYPE_MESSAGE); extension->is_repeated = true; extension->repeated_message_value = - ::google::protobuf::Arena::Create >(arena_, arena_); + ::google::protobuf::Arena::CreateMessage >(arena_); } else { GOOGLE_DCHECK_TYPE(*extension, REPEATED, MESSAGE); } diff --git a/src/google/protobuf/field_mask.pb.cc b/src/google/protobuf/field_mask.pb.cc index b00461d5..68314fd3 100644 --- a/src/google/protobuf/field_mask.pb.cc +++ b/src/google/protobuf/field_mask.pb.cc @@ -114,7 +114,7 @@ const int FieldMask::kPathsFieldNumber; #endif // !_MSC_VER FieldMask::FieldMask() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.FieldMask) } @@ -277,9 +277,9 @@ int FieldMask::ByteSize() const { void FieldMask::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const FieldMask* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const FieldMask* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -331,10 +331,10 @@ void FieldMask::InternalSwap(FieldMask* other) { // FieldMask // repeated string paths = 1; - int FieldMask::paths_size() const { +int FieldMask::paths_size() const { return paths_.size(); } - void FieldMask::clear_paths() { +void FieldMask::clear_paths() { paths_.Clear(); } const ::std::string& FieldMask::paths(int index) const { diff --git a/src/google/protobuf/generated_message_reflection.h b/src/google/protobuf/generated_message_reflection.h index 4dddf6c7..dc8abb98 100644 --- a/src/google/protobuf/generated_message_reflection.h +++ b/src/google/protobuf/generated_message_reflection.h @@ -40,6 +40,7 @@ #include #include +#include #include // TODO(jasonh): Remove this once the compiler change to directly include this // is released to components. @@ -597,6 +598,42 @@ inline To dynamic_cast_if_available(From from) { #endif } +// Tries to downcast this message to a generated message type. +// Returns NULL if this class is not an instance of T. +// +// This is like dynamic_cast_if_available, except it works even when +// dynamic_cast is not available by using Reflection. However it only works +// with Message objects. +// +// TODO(haberman): can we remove dynamic_cast_if_available in favor of this? +template +T* DynamicCastToGenerated(const Message* from) { + // Compile-time assert that T is a generated type that has a + // default_instance() accessor, but avoid actually calling it. + const T&(*get_default_instance)() = &T::default_instance; + (void)get_default_instance; + + // Compile-time assert that T is a subclass of google::protobuf::Message. + const Message* unused = static_cast(NULL); + (void)unused; + +#if defined(GOOGLE_PROTOBUF_NO_RTTI) || \ + (defined(_MSC_VER) && !defined(_CPPRTTI)) + bool ok = &T::default_instance() == + from->GetReflection()->GetMessageFactory()->GetPrototype( + from->GetDescriptor()); + return ok ? down_cast(from) : NULL; +#else + return dynamic_cast(from); +#endif +} + +template +T* DynamicCastToGenerated(Message* from) { + const Message* message_const = from; + return const_cast(DynamicCastToGenerated(message_const)); +} + } // namespace internal } // namespace protobuf diff --git a/src/google/protobuf/generated_message_util.h b/src/google/protobuf/generated_message_util.h index 678f92a7..6357e27d 100644 --- a/src/google/protobuf/generated_message_util.h +++ b/src/google/protobuf/generated_message_util.h @@ -47,6 +47,10 @@ namespace google { namespace protobuf { + +class Arena; +namespace io { class CodedInputStream; } + namespace internal { @@ -106,6 +110,15 @@ template bool AllAreInitialized(const Type& t) { return true; } +class ArenaString; + +// Read a length (varint32), followed by a string, from *input. Return a +// pointer to a copy of the string that resides in *arena. Requires both +// args to be non-NULL. If something goes wrong while reading the data +// then NULL is returned (e.g., input does not start with a valid varint). +ArenaString* ReadArenaString(::google::protobuf::io::CodedInputStream* input, + ::google::protobuf::Arena* arena); + } // namespace internal } // namespace protobuf diff --git a/src/google/protobuf/io/coded_stream.cc b/src/google/protobuf/io/coded_stream.cc index 93e1a22e..3b8650d6 100644 --- a/src/google/protobuf/io/coded_stream.cc +++ b/src/google/protobuf/io/coded_stream.cc @@ -156,6 +156,11 @@ CodedInputStream::IncrementRecursionDepthAndPushLimit(int byte_limit) { return std::make_pair(PushLimit(byte_limit), --recursion_budget_); } +CodedInputStream::Limit CodedInputStream::ReadLengthAndPushLimit() { + uint32 length; + return PushLimit(ReadVarint32(&length) ? length : 0); +} + bool CodedInputStream::DecrementRecursionDepthAndPopLimit(Limit limit) { bool result = ConsumedEntireMessage(); PopLimit(limit); @@ -164,6 +169,12 @@ bool CodedInputStream::DecrementRecursionDepthAndPopLimit(Limit limit) { return result; } +bool CodedInputStream::CheckEntireMessageConsumedAndPopLimit(Limit limit) { + bool result = ConsumedEntireMessage(); + PopLimit(limit); + return result; +} + int CodedInputStream::BytesUntilLimit() const { if (current_limit_ == INT_MAX) return -1; int current_position = CurrentPosition(); @@ -245,20 +256,7 @@ bool CodedInputStream::GetDirectBufferPointer(const void** data, int* size) { } bool CodedInputStream::ReadRaw(void* buffer, int size) { - int current_buffer_size; - while ((current_buffer_size = BufferSize()) < size) { - // Reading past end of buffer. Copy what we have, then refresh. - memcpy(buffer, buffer_, current_buffer_size); - buffer = reinterpret_cast(buffer) + current_buffer_size; - size -= current_buffer_size; - Advance(current_buffer_size); - if (!Refresh()) return false; - } - - memcpy(buffer, buffer_, size); - Advance(size); - - return true; + return InternalReadRawInline(buffer, size); } bool CodedInputStream::ReadString(string* buffer, int size) { @@ -336,17 +334,23 @@ bool CodedInputStream::ReadLittleEndian64Fallback(uint64* value) { namespace { -inline const uint8* ReadVarint32FromArray( - const uint8* buffer, uint32* value) GOOGLE_ATTRIBUTE_ALWAYS_INLINE; -inline const uint8* ReadVarint32FromArray(const uint8* buffer, uint32* value) { +// Read a varint from the given buffer, write it to *value, and return a pair. +// The first part of the pair is true iff the read was successful. The second +// part is buffer + (number of bytes read). This function is always inlined, +// so returning a pair is costless. +inline ::std::pair ReadVarint32FromArray( + uint32 first_byte, const uint8* buffer, + uint32* value) GOOGLE_ATTRIBUTE_ALWAYS_INLINE; +inline ::std::pair ReadVarint32FromArray( + uint32 first_byte, const uint8* buffer, uint32* value) { // Fast path: We have enough bytes left in the buffer to guarantee that // this read won't cross the end, so we can skip the checks. + GOOGLE_DCHECK_EQ(*buffer, first_byte); + GOOGLE_DCHECK_EQ(first_byte & 0x80, 0x80) << first_byte; const uint8* ptr = buffer; uint32 b; - uint32 result; - - b = *(ptr++); result = b ; if (!(b & 0x80)) goto done; - result -= 0x80; + uint32 result = first_byte - 0x80; + ++ptr; // We just processed the first byte. Move on to the second. b = *(ptr++); result += b << 7; if (!(b & 0x80)) goto done; result -= 0x80 << 7; b = *(ptr++); result += b << 14; if (!(b & 0x80)) goto done; @@ -364,38 +368,42 @@ inline const uint8* ReadVarint32FromArray(const uint8* buffer, uint32* value) { // We have overrun the maximum size of a varint (10 bytes). Assume // the data is corrupt. - return NULL; + return std::make_pair(false, ptr); done: *value = result; - return ptr; + return std::make_pair(true, ptr); } } // namespace bool CodedInputStream::ReadVarint32Slow(uint32* value) { - uint64 result; // Directly invoke ReadVarint64Fallback, since we already tried to optimize // for one-byte varints. - if (!ReadVarint64Fallback(&result)) return false; - *value = (uint32)result; - return true; + std::pair p = ReadVarint64Fallback(); + *value = static_cast(p.first); + return p.second; } -bool CodedInputStream::ReadVarint32Fallback(uint32* value) { +int64 CodedInputStream::ReadVarint32Fallback(uint32 first_byte_or_zero) { if (BufferSize() >= kMaxVarintBytes || // Optimization: We're also safe if the buffer is non-empty and it ends // with a byte that would terminate a varint. (buffer_end_ > buffer_ && !(buffer_end_[-1] & 0x80))) { - const uint8* end = ReadVarint32FromArray(buffer_, value); - if (end == NULL) return false; - buffer_ = end; - return true; + GOOGLE_DCHECK_NE(first_byte_or_zero, 0) + << "Caller should provide us with *buffer_ when buffer is non-empty"; + uint32 temp; + ::std::pair p = + ReadVarint32FromArray(first_byte_or_zero, buffer_, &temp); + if (!p.first) return -1; + buffer_ = p.second; + return temp; } else { // Really slow case: we will incur the cost of an extra function call here, // but moving this out of line reduces the size of this function, which // improves the common case. In micro benchmarks, this is worth about 10-15% - return ReadVarint32Slow(value); + uint32 temp; + return ReadVarint32Slow(&temp) ? static_cast(temp) : -1; } } @@ -425,18 +433,24 @@ uint32 CodedInputStream::ReadTagSlow() { return static_cast(result); } -uint32 CodedInputStream::ReadTagFallback() { +uint32 CodedInputStream::ReadTagFallback(uint32 first_byte_or_zero) { const int buf_size = BufferSize(); if (buf_size >= kMaxVarintBytes || // Optimization: We're also safe if the buffer is non-empty and it ends // with a byte that would terminate a varint. (buf_size > 0 && !(buffer_end_[-1] & 0x80))) { + GOOGLE_DCHECK_EQ(first_byte_or_zero, buffer_[0]); + if (first_byte_or_zero == 0) { + ++buffer_; + return 0; + } uint32 tag; - const uint8* end = ReadVarint32FromArray(buffer_, &tag); - if (end == NULL) { + ::std::pair p = + ReadVarint32FromArray(first_byte_or_zero, buffer_, &tag); + if (!p.first) { return 0; } - buffer_ = end; + buffer_ = p.second; return tag; } else { // We are commonly at a limit when attempting to read tags. Try to quickly @@ -479,7 +493,7 @@ bool CodedInputStream::ReadVarint64Slow(uint64* value) { return true; } -bool CodedInputStream::ReadVarint64Fallback(uint64* value) { +std::pair CodedInputStream::ReadVarint64Fallback() { if (BufferSize() >= kMaxVarintBytes || // Optimization: We're also safe if the buffer is non-empty and it ends // with a byte that would terminate a varint. @@ -517,16 +531,18 @@ bool CodedInputStream::ReadVarint64Fallback(uint64* value) { // We have overrun the maximum size of a varint (10 bytes). The data // must be corrupt. - return false; + return std::make_pair(0, false); done: Advance(ptr - buffer_); - *value = (static_cast(part0) ) | - (static_cast(part1) << 28) | - (static_cast(part2) << 56); - return true; + return std::make_pair((static_cast(part0)) | + (static_cast(part1) << 28) | + (static_cast(part2) << 56), + true); } else { - return ReadVarint64Slow(value); + uint64 temp; + bool success = ReadVarint64Slow(&temp); + return std::make_pair(temp, success); } } diff --git a/src/google/protobuf/io/coded_stream.h b/src/google/protobuf/io/coded_stream.h index dea4b650..961c1a3f 100644 --- a/src/google/protobuf/io/coded_stream.h +++ b/src/google/protobuf/io/coded_stream.h @@ -197,6 +197,11 @@ class LIBPROTOBUF_EXPORT CodedInputStream { // Read raw bytes, copying them into the given buffer. bool ReadRaw(void* buffer, int size); + // Like the above, with inlined optimizations. This should only be used + // by the protobuf implementation. + inline bool InternalReadRawInline(void* buffer, + int size) GOOGLE_ATTRIBUTE_ALWAYS_INLINE; + // Like ReadRaw, but reads into a string. // // Implementation Note: ReadString() grows the string gradually as it @@ -387,9 +392,14 @@ class LIBPROTOBUF_EXPORT CodedInputStream { // under the limit, false if it has gone over. bool IncrementRecursionDepth(); - // Decrements the recursion depth. + // Decrements the recursion depth if possible. void DecrementRecursionDepth(); + // Decrements the recursion depth blindly. This is faster than + // DecrementRecursionDepth(). It should be used only if all previous + // increments to recursion depth were successful. + void UnsafeDecrementRecursionDepth(); + // Shorthand for make_pair(PushLimit(byte_limit), --recursion_budget_). // Using this can reduce code size and complexity in some cases. The caller // is expected to check that the second part of the result is non-negative (to @@ -398,15 +408,25 @@ class LIBPROTOBUF_EXPORT CodedInputStream { std::pair IncrementRecursionDepthAndPushLimit( int byte_limit); + // Shorthand for PushLimit(ReadVarint32(&length) ? length : 0). + Limit ReadLengthAndPushLimit(); + // Helper that is equivalent to: { // bool result = ConsumedEntireMessage(); // PopLimit(limit); - // DecrementRecursionDepth(); + // UnsafeDecrementRecursionDepth(); // return result; } // Using this can reduce code size and complexity in some cases. // Do not use unless the current recursion depth is greater than zero. bool DecrementRecursionDepthAndPopLimit(Limit limit); + // Helper that is equivalent to: { + // bool result = ConsumedEntireMessage(); + // PopLimit(limit); + // return result; } + // Using this can reduce code size and complexity in some cases. + bool CheckEntireMessageConsumedAndPopLimit(Limit limit); + // Extension Registry ---------------------------------------------- // ADVANCED USAGE: 99.9% of people can ignore this section. // @@ -568,9 +588,13 @@ class LIBPROTOBUF_EXPORT CodedInputStream { // optimization. The Slow method is yet another fallback when the buffer is // not large enough. Making the slow path out-of-line speeds up the common // case by 10-15%. The slow path is fairly uncommon: it only triggers when a - // message crosses multiple buffers. - bool ReadVarint32Fallback(uint32* value); - bool ReadVarint64Fallback(uint64* value); + // message crosses multiple buffers. Note: ReadVarint32Fallback() and + // ReadVarint64Fallback() are called frequently and generally not inlined, so + // they have been optimized to avoid "out" parameters. The former returns -1 + // if it fails and the uint32 it read otherwise. The latter has a bool + // indicating success or failure as part of its return type. + int64 ReadVarint32Fallback(uint32 first_byte_or_zero); + std::pair ReadVarint64Fallback(); bool ReadVarint32Slow(uint32* value); bool ReadVarint64Slow(uint64* value); bool ReadLittleEndian32Fallback(uint32* value); @@ -578,7 +602,7 @@ class LIBPROTOBUF_EXPORT CodedInputStream { // Fallback/slow methods for reading tags. These do not update last_tag_, // but will set legitimate_message_end_ if we are at the end of the input // stream. - uint32 ReadTagFallback(); + uint32 ReadTagFallback(uint32 first_byte_or_zero); uint32 ReadTagSlow(); bool ReadStringFallback(string* buffer, int size); @@ -818,13 +842,18 @@ class LIBPROTOBUF_EXPORT CodedOutputStream { // methods optimize for that case. inline bool CodedInputStream::ReadVarint32(uint32* value) { - if (GOOGLE_PREDICT_TRUE(buffer_ < buffer_end_) && *buffer_ < 0x80) { - *value = *buffer_; - Advance(1); - return true; - } else { - return ReadVarint32Fallback(value); + uint32 v = 0; + if (GOOGLE_PREDICT_TRUE(buffer_ < buffer_end_)) { + v = *buffer_; + if (v < 0x80) { + *value = v; + Advance(1); + return true; + } } + int64 result = ReadVarint32Fallback(v); + *value = static_cast(result); + return result >= 0; } inline bool CodedInputStream::ReadVarint64(uint64* value) { @@ -832,9 +861,10 @@ inline bool CodedInputStream::ReadVarint64(uint64* value) { *value = *buffer_; Advance(1); return true; - } else { - return ReadVarint64Fallback(value); } + std::pair p = ReadVarint64Fallback(); + *value = p.first; + return p.second; } // static @@ -903,14 +933,17 @@ inline bool CodedInputStream::ReadLittleEndian64(uint64* value) { } inline uint32 CodedInputStream::ReadTag() { - if (GOOGLE_PREDICT_TRUE(buffer_ < buffer_end_) && buffer_[0] < 0x80) { - last_tag_ = buffer_[0]; - Advance(1); - return last_tag_; - } else { - last_tag_ = ReadTagFallback(); - return last_tag_; + uint32 v = 0; + if (GOOGLE_PREDICT_TRUE(buffer_ < buffer_end_)) { + v = *buffer_; + if (v < 0x80) { + last_tag_ = v; + Advance(1); + return v; + } } + last_tag_ = ReadTagFallback(v); + return last_tag_; } inline std::pair CodedInputStream::ReadTagWithCutoff( @@ -918,10 +951,12 @@ inline std::pair CodedInputStream::ReadTagWithCutoff( // In performance-sensitive code we can expect cutoff to be a compile-time // constant, and things like "cutoff >= kMax1ByteVarint" to be evaluated at // compile time. + uint32 first_byte_or_zero = 0; if (GOOGLE_PREDICT_TRUE(buffer_ < buffer_end_)) { // Hot case: buffer_ non_empty, buffer_[0] in [1, 128). // TODO(gpike): Is it worth rearranging this? E.g., if the number of fields // is large enough then is it better to check for the two-byte case first? + first_byte_or_zero = buffer_[0]; if (static_cast(buffer_[0]) > 0) { const uint32 kMax1ByteVarint = 0x7f; uint32 tag = last_tag_ = buffer_[0]; @@ -948,7 +983,7 @@ inline std::pair CodedInputStream::ReadTagWithCutoff( } } // Slow path - last_tag_ = ReadTagFallback(); + last_tag_ = ReadTagFallback(first_byte_or_zero); return std::make_pair(last_tag_, static_cast(last_tag_ - 1) < cutoff); } @@ -1177,6 +1212,11 @@ inline void CodedInputStream::DecrementRecursionDepth() { if (recursion_budget_ < recursion_limit_) ++recursion_budget_; } +inline void CodedInputStream::UnsafeDecrementRecursionDepth() { + assert(recursion_budget_ < recursion_limit_); + ++recursion_budget_; +} + inline void CodedInputStream::SetExtensionRegistry(const DescriptorPool* pool, MessageFactory* factory) { extension_pool_ = pool; diff --git a/src/google/protobuf/io/coded_stream_inl.h b/src/google/protobuf/io/coded_stream_inl.h index cd8d1746..fa20f208 100644 --- a/src/google/protobuf/io/coded_stream_inl.h +++ b/src/google/protobuf/io/coded_stream_inl.h @@ -66,6 +66,23 @@ inline bool CodedInputStream::InternalReadStringInline(string* buffer, return ReadStringFallback(buffer, size); } +inline bool CodedInputStream::InternalReadRawInline(void* buffer, int size) { + int current_buffer_size; + while ((current_buffer_size = BufferSize()) < size) { + // Reading past end of buffer. Copy what we have, then refresh. + memcpy(buffer, buffer_, current_buffer_size); + buffer = reinterpret_cast(buffer) + current_buffer_size; + size -= current_buffer_size; + Advance(current_buffer_size); + if (!Refresh()) return false; + } + + memcpy(buffer, buffer_, size); + Advance(size); + + return true; +} + } // namespace io } // namespace protobuf } // namespace google diff --git a/src/google/protobuf/io/printer.cc b/src/google/protobuf/io/printer.cc index e621ba1d..3ae8c268 100644 --- a/src/google/protobuf/io/printer.cc +++ b/src/google/protobuf/io/printer.cc @@ -155,6 +155,78 @@ void Printer::Print(const char* text, Print(vars, text); } +void Printer::Print(const char* text, + const char* variable1, const string& value1, + const char* variable2, const string& value2, + const char* variable3, const string& value3, + const char* variable4, const string& value4, + const char* variable5, const string& value5) { + map vars; + vars[variable1] = value1; + vars[variable2] = value2; + vars[variable3] = value3; + vars[variable4] = value4; + vars[variable5] = value5; + Print(vars, text); +} + +void Printer::Print(const char* text, + const char* variable1, const string& value1, + const char* variable2, const string& value2, + const char* variable3, const string& value3, + const char* variable4, const string& value4, + const char* variable5, const string& value5, + const char* variable6, const string& value6) { + map vars; + vars[variable1] = value1; + vars[variable2] = value2; + vars[variable3] = value3; + vars[variable4] = value4; + vars[variable5] = value5; + vars[variable6] = value6; + Print(vars, text); +} + +void Printer::Print(const char* text, + const char* variable1, const string& value1, + const char* variable2, const string& value2, + const char* variable3, const string& value3, + const char* variable4, const string& value4, + const char* variable5, const string& value5, + const char* variable6, const string& value6, + const char* variable7, const string& value7) { + map vars; + vars[variable1] = value1; + vars[variable2] = value2; + vars[variable3] = value3; + vars[variable4] = value4; + vars[variable5] = value5; + vars[variable6] = value6; + vars[variable7] = value7; + Print(vars, text); +} + +void Printer::Print(const char* text, + const char* variable1, const string& value1, + const char* variable2, const string& value2, + const char* variable3, const string& value3, + const char* variable4, const string& value4, + const char* variable5, const string& value5, + const char* variable6, const string& value6, + const char* variable7, const string& value7, + const char* variable8, const string& value8) { + map vars; + vars[variable1] = value1; + vars[variable2] = value2; + vars[variable3] = value3; + vars[variable4] = value4; + vars[variable5] = value5; + vars[variable6] = value6; + vars[variable7] = value7; + vars[variable8] = value8; + Print(vars, text); +} + void Printer::Indent() { indent_ += " "; } diff --git a/src/google/protobuf/io/printer.h b/src/google/protobuf/io/printer.h index 92ce3409..f1490bbe 100644 --- a/src/google/protobuf/io/printer.h +++ b/src/google/protobuf/io/printer.h @@ -91,8 +91,36 @@ class LIBPROTOBUF_EXPORT Printer { const char* variable2, const string& value2, const char* variable3, const string& value3, const char* variable4, const string& value4); - // TODO(kenton): Overloaded versions with more variables? Three seems - // to be enough. + // Like the first Print(), except the substitutions are given as parameters. + void Print(const char* text, const char* variable1, const string& value1, + const char* variable2, const string& value2, + const char* variable3, const string& value3, + const char* variable4, const string& value4, + const char* variable5, const string& value5); + // Like the first Print(), except the substitutions are given as parameters. + void Print(const char* text, const char* variable1, const string& value1, + const char* variable2, const string& value2, + const char* variable3, const string& value3, + const char* variable4, const string& value4, + const char* variable5, const string& value5, + const char* variable6, const string& value6); + // Like the first Print(), except the substitutions are given as parameters. + void Print(const char* text, const char* variable1, const string& value1, + const char* variable2, const string& value2, + const char* variable3, const string& value3, + const char* variable4, const string& value4, + const char* variable5, const string& value5, + const char* variable6, const string& value6, + const char* variable7, const string& value7); + // Like the first Print(), except the substitutions are given as parameters. + void Print(const char* text, const char* variable1, const string& value1, + const char* variable2, const string& value2, + const char* variable3, const string& value3, + const char* variable4, const string& value4, + const char* variable5, const string& value5, + const char* variable6, const string& value6, + const char* variable7, const string& value7, + const char* variable8, const string& value8); // Indent text by two spaces. After calling Indent(), two spaces will be // inserted at the beginning of each line of text. Indent() may be called diff --git a/src/google/protobuf/lite_unittest.cc b/src/google/protobuf/lite_unittest.cc index 4ea21007..1995cf08 100644 --- a/src/google/protobuf/lite_unittest.cc +++ b/src/google/protobuf/lite_unittest.cc @@ -34,12 +34,16 @@ #include #include +#include +#include +#include #include #include #include #include #include #include +#include using namespace std; @@ -84,6 +88,12 @@ void SetSomeTypesInEmptyMessageUnknownFields( } // namespace +#define EXPECT_TRUE GOOGLE_CHECK +#define ASSERT_TRUE GOOGLE_CHECK +#define EXPECT_FALSE(COND) GOOGLE_CHECK(!(COND)) +#define EXPECT_EQ GOOGLE_CHECK_EQ +#define ASSERT_EQ GOOGLE_CHECK_EQ + int main(int argc, char* argv[]) { string data, data2, packed_data; @@ -345,6 +355,374 @@ int main(int argc, char* argv[]) { GOOGLE_CHECK_EQ(0, empty_message.unknown_fields().size()); } + // Tests for map lite ============================================= + + { + // Accessors + protobuf_unittest::TestMapLite message; + + google::protobuf::MapLiteTestUtil::SetMapFields(&message); + google::protobuf::MapLiteTestUtil::ExpectMapFieldsSet(message); + + google::protobuf::MapLiteTestUtil::ModifyMapFields(&message); + google::protobuf::MapLiteTestUtil::ExpectMapFieldsModified(message); + } + + { + // SetMapFieldsInitialized + protobuf_unittest::TestMapLite message; + + google::protobuf::MapLiteTestUtil::SetMapFieldsInitialized(&message); + google::protobuf::MapLiteTestUtil::ExpectMapFieldsSetInitialized(message); + } + + { + // Proto2SetMapFieldsInitialized + protobuf_unittest::TestEnumStartWithNonZeroMapLite message; + EXPECT_EQ(protobuf_unittest::PROTO2_NON_ZERO_MAP_ENUM_FOO_LITE, + (*message.mutable_map_field())[0]); + } + + { + // Clear + protobuf_unittest::TestMapLite message; + + google::protobuf::MapLiteTestUtil::SetMapFields(&message); + message.Clear(); + google::protobuf::MapLiteTestUtil::ExpectClear(message); + } + + { + // ClearMessageMap + protobuf_unittest::TestMessageMapLite message; + + // Creates a TestAllTypes with default value + google::protobuf::TestUtilLite::ExpectClear( + (*message.mutable_map_int32_message())[0]); + } + + { + // CopyFrom + protobuf_unittest::TestMapLite message1, message2; + + google::protobuf::MapLiteTestUtil::SetMapFields(&message1); + message2.CopyFrom(message1); + google::protobuf::MapLiteTestUtil::ExpectMapFieldsSet(message2); + + // Copying from self should be a no-op. + message2.CopyFrom(message2); + google::protobuf::MapLiteTestUtil::ExpectMapFieldsSet(message2); + } + + { + // CopyFromMessageMap + protobuf_unittest::TestMessageMapLite message1, message2; + + (*message1.mutable_map_int32_message())[0].add_repeated_int32(100); + (*message2.mutable_map_int32_message())[0].add_repeated_int32(101); + + message1.CopyFrom(message2); + + // Checks repeated field is overwritten. + EXPECT_EQ(1, message1.map_int32_message().at(0).repeated_int32_size()); + EXPECT_EQ(101, message1.map_int32_message().at(0).repeated_int32(0)); + } + + { + // SwapWithEmpty + protobuf_unittest::TestMapLite message1, message2; + + google::protobuf::MapLiteTestUtil::SetMapFields(&message1); + google::protobuf::MapLiteTestUtil::ExpectMapFieldsSet(message1); + google::protobuf::MapLiteTestUtil::ExpectClear(message2); + + message1.Swap(&message2); + google::protobuf::MapLiteTestUtil::ExpectMapFieldsSet(message2); + google::protobuf::MapLiteTestUtil::ExpectClear(message1); + } + + { + // SwapWithSelf + protobuf_unittest::TestMapLite message; + + google::protobuf::MapLiteTestUtil::SetMapFields(&message); + google::protobuf::MapLiteTestUtil::ExpectMapFieldsSet(message); + + message.Swap(&message); + google::protobuf::MapLiteTestUtil::ExpectMapFieldsSet(message); + } + + { + // SwapWithOther + protobuf_unittest::TestMapLite message1, message2; + + google::protobuf::MapLiteTestUtil::SetMapFields(&message1); + google::protobuf::MapLiteTestUtil::SetMapFields(&message2); + google::protobuf::MapLiteTestUtil::ModifyMapFields(&message2); + + message1.Swap(&message2); + google::protobuf::MapLiteTestUtil::ExpectMapFieldsModified(message1); + google::protobuf::MapLiteTestUtil::ExpectMapFieldsSet(message2); + } + + { + // CopyConstructor + protobuf_unittest::TestMapLite message1; + google::protobuf::MapLiteTestUtil::SetMapFields(&message1); + + protobuf_unittest::TestMapLite message2(message1); + google::protobuf::MapLiteTestUtil::ExpectMapFieldsSet(message2); + } + + { + // CopyAssignmentOperator + protobuf_unittest::TestMapLite message1; + google::protobuf::MapLiteTestUtil::SetMapFields(&message1); + + protobuf_unittest::TestMapLite message2; + message2 = message1; + google::protobuf::MapLiteTestUtil::ExpectMapFieldsSet(message2); + + // Make sure that self-assignment does something sane. + message2.operator=(message2); + google::protobuf::MapLiteTestUtil::ExpectMapFieldsSet(message2); + } + + { + // NonEmptyMergeFrom + protobuf_unittest::TestMapLite message1, message2; + + google::protobuf::MapLiteTestUtil::SetMapFields(&message1); + + // This field will test merging into an empty spot. + (*message2.mutable_map_int32_int32())[1] = 1; + message1.mutable_map_int32_int32()->erase(1); + + // This tests overwriting. + (*message2.mutable_map_int32_double())[1] = 1; + (*message1.mutable_map_int32_double())[1] = 2; + + message1.MergeFrom(message2); + google::protobuf::MapLiteTestUtil::ExpectMapFieldsSet(message1); + } + + { + // MergeFromMessageMap + protobuf_unittest::TestMessageMapLite message1, message2; + + (*message1.mutable_map_int32_message())[0].add_repeated_int32(100); + (*message2.mutable_map_int32_message())[0].add_repeated_int32(101); + + message1.MergeFrom(message2); + + // Checks repeated field is overwritten. + EXPECT_EQ(1, message1.map_int32_message().at(0).repeated_int32_size()); + EXPECT_EQ(101, message1.map_int32_message().at(0).repeated_int32(0)); + } + + { + // Test the generated SerializeWithCachedSizesToArray() + protobuf_unittest::TestMapLite message1, message2; + string data; + google::protobuf::MapLiteTestUtil::SetMapFields(&message1); + int size = message1.ByteSize(); + data.resize(size); + ::google::protobuf::uint8* start = reinterpret_cast< ::google::protobuf::uint8*>(::google::protobuf::string_as_array(&data)); + ::google::protobuf::uint8* end = message1.SerializeWithCachedSizesToArray(start); + EXPECT_EQ(size, end - start); + EXPECT_TRUE(message2.ParseFromString(data)); + google::protobuf::MapLiteTestUtil::ExpectMapFieldsSet(message2); + } + + { + // Test the generated SerializeWithCachedSizes() + protobuf_unittest::TestMapLite message1, message2; + google::protobuf::MapLiteTestUtil::SetMapFields(&message1); + int size = message1.ByteSize(); + string data; + data.resize(size); + { + // Allow the output stream to buffer only one byte at a time. + google::protobuf::io::ArrayOutputStream array_stream( + ::google::protobuf::string_as_array(&data), size, 1); + google::protobuf::io::CodedOutputStream output_stream(&array_stream); + message1.SerializeWithCachedSizes(&output_stream); + EXPECT_FALSE(output_stream.HadError()); + EXPECT_EQ(size, output_stream.ByteCount()); + } + EXPECT_TRUE(message2.ParseFromString(data)); + google::protobuf::MapLiteTestUtil::ExpectMapFieldsSet(message2); + } + + + { + // Proto2UnknownEnum + protobuf_unittest::TestEnumMapPlusExtraLite from; + (*from.mutable_known_map_field())[0] = + protobuf_unittest::E_PROTO2_MAP_ENUM_FOO_LITE; + (*from.mutable_unknown_map_field())[0] = + protobuf_unittest::E_PROTO2_MAP_ENUM_EXTRA_LITE; + string data; + from.SerializeToString(&data); + + protobuf_unittest::TestEnumMapLite to; + EXPECT_TRUE(to.ParseFromString(data)); + EXPECT_EQ(0, to.unknown_map_field().size()); + EXPECT_FALSE(to.mutable_unknown_fields()->empty()); + EXPECT_EQ(1, to.known_map_field().size()); + EXPECT_EQ(protobuf_unittest::PROTO2_MAP_ENUM_FOO_LITE, + to.known_map_field().at(0)); + + data.clear(); + from.Clear(); + to.SerializeToString(&data); + EXPECT_TRUE(from.ParseFromString(data)); + EXPECT_EQ(1, from.known_map_field().size()); + EXPECT_EQ(protobuf_unittest::E_PROTO2_MAP_ENUM_FOO_LITE, + from.known_map_field().at(0)); + EXPECT_EQ(1, from.unknown_map_field().size()); + EXPECT_EQ(protobuf_unittest::E_PROTO2_MAP_ENUM_EXTRA_LITE, + from.unknown_map_field().at(0)); + } + + { + // StandardWireFormat + protobuf_unittest::TestMapLite message; + string data = "\x0A\x04\x08\x01\x10\x01"; + + EXPECT_TRUE(message.ParseFromString(data)); + EXPECT_EQ(1, message.map_int32_int32().size()); + EXPECT_EQ(1, message.map_int32_int32().at(1)); + } + + { + // UnorderedWireFormat + protobuf_unittest::TestMapLite message; + + // put value before key in wire format + string data = "\x0A\x04\x10\x01\x08\x02"; + + EXPECT_TRUE(message.ParseFromString(data)); + EXPECT_EQ(1, message.map_int32_int32().size()); + EXPECT_EQ(1, message.map_int32_int32().at(2)); + } + + { + // DuplicatedKeyWireFormat + protobuf_unittest::TestMapLite message; + + // Two key fields in wire format + string data = "\x0A\x06\x08\x01\x08\x02\x10\x01"; + + EXPECT_TRUE(message.ParseFromString(data)); + EXPECT_EQ(1, message.map_int32_int32().size()); + EXPECT_EQ(1, message.map_int32_int32().at(2)); + } + + { + // DuplicatedValueWireFormat + protobuf_unittest::TestMapLite message; + + // Two value fields in wire format + string data = "\x0A\x06\x08\x01\x10\x01\x10\x02"; + + EXPECT_TRUE(message.ParseFromString(data)); + EXPECT_EQ(1, message.map_int32_int32().size()); + EXPECT_EQ(2, message.map_int32_int32().at(1)); + } + + { + // MissedKeyWireFormat + protobuf_unittest::TestMapLite message; + + // No key field in wire format + string data = "\x0A\x02\x10\x01"; + + EXPECT_TRUE(message.ParseFromString(data)); + EXPECT_EQ(1, message.map_int32_int32().size()); + EXPECT_EQ(1, message.map_int32_int32().at(0)); + } + + { + // MissedValueWireFormat + protobuf_unittest::TestMapLite message; + + // No value field in wire format + string data = "\x0A\x02\x08\x01"; + + EXPECT_TRUE(message.ParseFromString(data)); + EXPECT_EQ(1, message.map_int32_int32().size()); + EXPECT_EQ(0, message.map_int32_int32().at(1)); + } + + { + // UnknownFieldWireFormat + protobuf_unittest::TestMapLite message; + + // Unknown field in wire format + string data = "\x0A\x06\x08\x02\x10\x03\x18\x01"; + + EXPECT_TRUE(message.ParseFromString(data)); + EXPECT_EQ(1, message.map_int32_int32().size()); + EXPECT_EQ(3, message.map_int32_int32().at(2)); + } + + { + // CorruptedWireFormat + protobuf_unittest::TestMapLite message; + + // corrupted data in wire format + string data = "\x0A\x06\x08\x02\x11\x03"; + + EXPECT_FALSE(message.ParseFromString(data)); + } + + { + // IsInitialized + protobuf_unittest::TestRequiredMessageMapLite map_message; + + // Add an uninitialized message. + (*map_message.mutable_map_field())[0]; + EXPECT_FALSE(map_message.IsInitialized()); + + // Initialize uninitialized message + (*map_message.mutable_map_field())[0].set_a(0); + (*map_message.mutable_map_field())[0].set_b(0); + (*map_message.mutable_map_field())[0].set_c(0); + EXPECT_TRUE(map_message.IsInitialized()); + } + + // arena support for map ========================================= + + { + // ParsingAndSerializingNoHeapAllocation + + // Allocate a large initial block to avoid mallocs during hooked test. + std::vector arena_block(128 * 1024); + google::protobuf::ArenaOptions options; + options.initial_block = arena_block.data(); + options.initial_block_size = arena_block.size(); + google::protobuf::Arena arena(options); + string data; + data.reserve(128 * 1024); + + { + google::protobuf::internal::NoHeapChecker no_heap; + + protobuf_unittest::TestArenaMapLite* from = + google::protobuf::Arena::CreateMessage( + &arena); + google::protobuf::MapLiteTestUtil::SetArenaMapFields(from); + from->SerializeToString(&data); + + protobuf_unittest::TestArenaMapLite* to = + google::protobuf::Arena::CreateMessage( + &arena); + to->ParseFromString(data); + google::protobuf::MapLiteTestUtil::ExpectArenaMapFieldsSet(*to); + } + } + std::cout << "PASS" << std::endl; return 0; } diff --git a/src/google/protobuf/map.h b/src/google/protobuf/map.h index e56af3fc..1858e2f9 100644 --- a/src/google/protobuf/map.h +++ b/src/google/protobuf/map.h @@ -422,6 +422,7 @@ class Map { int default_enum_value_; friend class ::google::protobuf::Arena; + typedef void InternalArenaConstructable_; typedef void DestructorSkippable_; template >( - arena, key, value, arena); + arena, key, value); } // Like above, but for all the other types. This avoids value copy to create // MapEntryLite from google::protobuf::Map in serialization. static MapEntryLite* Wrap(const Key& key, const Value& value, Arena* arena) { - return Arena::Create >( - arena, key, value, arena); + return Arena::CreateMessage >( + arena, key, value); } protected: @@ -308,7 +309,7 @@ class LIBPROTOBUF_EXPORT MapEntryLite : public MessageLite { typedef typename Base::ValCppType ValCppType; public: - MapEntryWrapper(const K& key, const V& value, Arena* arena) + MapEntryWrapper(Arena* arena, const K& key, const V& value) : MapEntryLite(arena), key_(key), value_(value) { @@ -323,6 +324,7 @@ class LIBPROTOBUF_EXPORT MapEntryLite : public MessageLite { const Value& value_; friend class ::google::protobuf::Arena; + typedef void InternalArenaConstructable_; typedef void DestructorSkippable_; }; @@ -341,7 +343,7 @@ class LIBPROTOBUF_EXPORT MapEntryLite : public MessageLite { typedef typename Base::ValCppType ValCppType; public: - MapEnumEntryWrapper(const K& key, const V& value, Arena* arena) + MapEnumEntryWrapper(Arena* arena, const K& key, const V& value) : MapEntryLite(arena), key_(key), value_(value) { @@ -355,7 +357,7 @@ class LIBPROTOBUF_EXPORT MapEntryLite : public MessageLite { const KeyCppType& key_; const ValCppType value_; - friend class ::google::protobuf::Arena; + friend class LIBPROTOBUF_EXPORT google::protobuf::Arena; typedef void DestructorSkippable_; }; diff --git a/src/google/protobuf/map_field.cc b/src/google/protobuf/map_field.cc index fd40c0d8..6ff1936e 100644 --- a/src/google/protobuf/map_field.cc +++ b/src/google/protobuf/map_field.cc @@ -104,14 +104,18 @@ void MapFieldBase::SetRepeatedDirty() { state_ = STATE_MODIFIED_REPEATED; } void* MapFieldBase::MutableRepeatedPtrField() const { return repeated_field_; } void MapFieldBase::SyncRepeatedFieldWithMap() const { - Atomic32 state = google::protobuf::internal::NoBarrier_Load(&state_); + // "Acquire" insures the operation after SyncRepeatedFieldWithMap won't get + // executed before state_ is checked. + Atomic32 state = google::protobuf::internal::Acquire_Load(&state_); if (state == STATE_MODIFIED_MAP) { mutex_.Lock(); // Double check state, because another thread may have seen the same state // and done the synchronization before the current thread. if (state_ == STATE_MODIFIED_MAP) { SyncRepeatedFieldWithMapNoLock(); - google::protobuf::internal::NoBarrier_Store(&state_, CLEAN); + // "Release" insures state_ can only be changed "after" + // SyncRepeatedFieldWithMapNoLock is finished. + google::protobuf::internal::Release_Store(&state_, CLEAN); } mutex_.Unlock(); } @@ -119,19 +123,23 @@ void MapFieldBase::SyncRepeatedFieldWithMap() const { void MapFieldBase::SyncRepeatedFieldWithMapNoLock() const { if (repeated_field_ == NULL) { - repeated_field_ = Arena::Create >(arena_, arena_); + repeated_field_ = Arena::CreateMessage >(arena_); } } void MapFieldBase::SyncMapWithRepeatedField() const { - Atomic32 state = google::protobuf::internal::NoBarrier_Load(&state_); + // "Acquire" insures the operation after SyncMapWithRepeatedField won't get + // executed before state_ is checked. + Atomic32 state = google::protobuf::internal::Acquire_Load(&state_); if (state == STATE_MODIFIED_REPEATED) { mutex_.Lock(); // Double check state, because another thread may have seen the same state // and done the synchronization before the current thread. if (state_ == STATE_MODIFIED_REPEATED) { SyncMapWithRepeatedFieldNoLock(); - google::protobuf::internal::NoBarrier_Store(&state_, CLEAN); + // "Release" insures state_ can only be changed "after" + // SyncRepeatedFieldWithMapNoLock is finished. + google::protobuf::internal::Release_Store(&state_, CLEAN); } mutex_.Unlock(); } diff --git a/src/google/protobuf/map_field.h b/src/google/protobuf/map_field.h index 6d8b6ec8..f3504f1b 100644 --- a/src/google/protobuf/map_field.h +++ b/src/google/protobuf/map_field.h @@ -208,6 +208,7 @@ class LIBPROTOBUF_EXPORT MapField : public MapFieldBase, void SetAssignDescriptorCallback(void (*callback)()); private: + typedef void InternalArenaConstructable_; typedef void DestructorSkippable_; // MapField needs MapEntry's default instance to create new MapEntry. diff --git a/src/google/protobuf/map_field_inl.h b/src/google/protobuf/map_field_inl.h index ae63c721..cbfc0c8f 100644 --- a/src/google/protobuf/map_field_inl.h +++ b/src/google/protobuf/map_field_inl.h @@ -216,7 +216,7 @@ MapField(); } else { repeated_field_ = - Arena::Create >(arena_, arena_); + Arena::CreateMessage >(arena_); } } const Map& map = GetInternalMap(); diff --git a/src/google/protobuf/map_field_lite.h b/src/google/protobuf/map_field_lite.h index 549ecc08..40322851 100644 --- a/src/google/protobuf/map_field_lite.h +++ b/src/google/protobuf/map_field_lite.h @@ -109,7 +109,7 @@ template ::MapFieldLite(Arena* arena) : arena_(arena) { - map_ = Arena::Create >(arena, arena); + map_ = Arena::CreateMessage >(arena); SetDefaultEnumValue(); } diff --git a/src/google/protobuf/map_field_test.cc b/src/google/protobuf/map_field_test.cc index 61344cbb..f4681866 100644 --- a/src/google/protobuf/map_field_test.cc +++ b/src/google/protobuf/map_field_test.cc @@ -56,6 +56,7 @@ using unittest::TestAllTypes; class MapFieldBaseStub : public MapFieldBase { public: + typedef void InternalArenaConstructable_; typedef void DestructorSkippable_; MapFieldBaseStub() {} explicit MapFieldBaseStub(Arena* arena) : MapFieldBase(arena) {} @@ -149,10 +150,12 @@ TEST_F(MapFieldBasePrimitiveTest, Arena) { Arena arena(options); { - NoHeapChecker no_heap; + // TODO(liujisi): Re-write the test to ensure the memory for the map and + // repeated fields are allocated from arenas. + // NoHeapChecker no_heap; MapFieldType* map_field = - Arena::Create(&arena, &arena, default_entry_); + Arena::CreateMessage(&arena, default_entry_); // Set content in map (*map_field->MutableMap())[100] = 101; @@ -162,10 +165,12 @@ TEST_F(MapFieldBasePrimitiveTest, Arena) { } { - NoHeapChecker no_heap; + // TODO(liujisi): Re-write the test to ensure the memory for the map and + // repeated fields are allocated from arenas. + // NoHeapChecker no_heap; MapFieldBaseStub* map_field = - Arena::Create(&arena, &arena); + Arena::CreateMessage(&arena); // Trigger conversion to repeated field. EXPECT_TRUE(map_field->MutableRepeatedField() != NULL); diff --git a/src/google/protobuf/map_test.cc b/src/google/protobuf/map_test.cc index 88cba1f2..447f52d9 100644 --- a/src/google/protobuf/map_test.cc +++ b/src/google/protobuf/map_test.cc @@ -2258,6 +2258,27 @@ TEST(TextFormatMapTest, SerializeAndParse) { MapTestUtil::ExpectMapFieldsSet(dest); } +TEST(TextFormatMapTest, Sorted) { + unittest::TestMap message; + MapTestUtil::MapReflectionTester tester(message.GetDescriptor()); + tester.SetMapFieldsViaReflection(&message); + + string expected_text; + GOOGLE_CHECK_OK(File::GetContents( + TestSourceDir() + + "/google/protobuf/" + "testdata/map_test_data.txt", + &expected_text, true)); + + EXPECT_EQ(message.DebugString(), expected_text); + + // Test again on the reverse order. + unittest::TestMap message2; + tester.SetMapFieldsViaReflection(&message2); + tester.SwapMapsViaReflection(&message2); + EXPECT_EQ(message2.DebugString(), expected_text); +} + // arena support ================================================= TEST(ArenaTest, ParsingAndSerializingNoHeapAllocation) { diff --git a/src/google/protobuf/map_type_handler.h b/src/google/protobuf/map_type_handler.h index 278b78ae..ffdb6dfb 100644 --- a/src/google/protobuf/map_type_handler.h +++ b/src/google/protobuf/map_type_handler.h @@ -129,11 +129,11 @@ class MapCppTypeHandler : public MapCommonTypeHandler { // Return bytes used by value in Map. static int SpaceUsedInMap(const Type& value) { return value.SpaceUsed(); } static inline void Clear(Type** value) { - if (*value != NULL) (*value)->Type::Clear(); + if (*value != NULL) (*value)->Clear(); } static inline void ClearMaybeByDefaultEnum(Type** value, int default_enum_value) { - if (*value != NULL) (*value)->Type::Clear(); + if (*value != NULL) (*value)->Clear(); } static inline void Merge(const Type& from, Type** to) { (*to)->MergeFrom(from); diff --git a/src/google/protobuf/map_unittest.proto b/src/google/protobuf/map_unittest.proto index b6a988b3..cbf747d9 100644 --- a/src/google/protobuf/map_unittest.proto +++ b/src/google/protobuf/map_unittest.proto @@ -62,7 +62,7 @@ message TestMap { } message TestMapSubmessage { - optional TestMap test_map = 1; + TestMap test_map = 1; } message TestMessageMap { @@ -104,3 +104,17 @@ message TestArenaMap { map map_int32_enum = 14; map map_int32_foreign_message = 15; } + +// Previously, message containing enum called Type cannot be used as value of +// map field. +message MessageContainingEnumCalledType { + enum Type { + TYPE_FOO = 0; + } + map type = 1; +} + +// Previously, message cannot contain map field called "entry". +message MessageContainingMapCalledEntry { + map entry = 1; +} diff --git a/src/google/protobuf/message.cc b/src/google/protobuf/message.cc index f58be848..276d7de5 100644 --- a/src/google/protobuf/message.cc +++ b/src/google/protobuf/message.cc @@ -465,10 +465,21 @@ struct ShutdownRepeatedFieldRegister { } // namespace internal namespace internal { -// Macro defined in repeated_field.h. We can only define the Message-specific -// GenericTypeHandler specializations here because we depend on Message, which -// is not part of proto2-lite hence is not available in repeated_field.h. -DEFINE_SPECIALIZATIONS_FOR_BASE_PROTO_TYPES_NOINLINE(Message); +template<> +Message* GenericTypeHandler::NewFromPrototype( + const Message* prototype, google::protobuf::Arena* arena) { + return prototype->New(arena); +} +template<> +google::protobuf::Arena* GenericTypeHandler::GetArena( + Message* value) { + return value->GetArena(); +} +template<> +void* GenericTypeHandler::GetMaybeArenaPointer( + Message* value) { + return value->GetMaybeArenaPointer(); +} } // namespace internal } // namespace protobuf diff --git a/src/google/protobuf/message.h b/src/google/protobuf/message.h index 6e1929e5..18c092d0 100644 --- a/src/google/protobuf/message.h +++ b/src/google/protobuf/message.h @@ -98,11 +98,11 @@ // // // Use the reflection interface to examine the contents. // const Reflection* reflection = foo->GetReflection(); -// assert(reflection->GetString(foo, text_field) == "Hello World!"); -// assert(reflection->FieldSize(foo, numbers_field) == 3); -// assert(reflection->GetRepeatedInt32(foo, numbers_field, 0) == 1); -// assert(reflection->GetRepeatedInt32(foo, numbers_field, 1) == 5); -// assert(reflection->GetRepeatedInt32(foo, numbers_field, 2) == 42); +// assert(reflection->GetString(*foo, text_field) == "Hello World!"); +// assert(reflection->FieldSize(*foo, numbers_field) == 3); +// assert(reflection->GetRepeatedInt32(*foo, numbers_field, 0) == 1); +// assert(reflection->GetRepeatedInt32(*foo, numbers_field, 1) == 5); +// assert(reflection->GetRepeatedInt32(*foo, numbers_field, 2) == 42); // // delete foo; // } @@ -229,6 +229,11 @@ class LIBPROTOBUF_EXPORT Message : public MessageLite { // Computes (an estimate of) the total number of bytes currently used for // storing the message in memory. The default implementation calls the // Reflection object's SpaceUsed() method. + // + // SpaceUsed() is noticeably slower than ByteSize(), as it is implemented + // using reflection (rather than the generated code implementation for + // ByteSize()). Like ByteSize(), its CPU time is linear in the number of + // fields defined for the proto. virtual int SpaceUsed() const; // Debugging & Testing---------------------------------------------- diff --git a/src/google/protobuf/message_lite.cc b/src/google/protobuf/message_lite.cc index 63be0e9b..4f63ad2b 100644 --- a/src/google/protobuf/message_lite.cc +++ b/src/google/protobuf/message_lite.cc @@ -337,7 +337,7 @@ bool MessageLite::SerializePartialToArray(void* data, int size) const { string MessageLite::SerializeAsString() const { // If the compiler implements the (Named) Return Value Optimization, - // the local variable 'result' will not actually reside on the stack + // the local variable 'output' will not actually reside on the stack // of this function, but will be overlaid with the object that the // caller supplied for the return value to be constructed in. string output; diff --git a/src/google/protobuf/message_lite.h b/src/google/protobuf/message_lite.h index eab61c14..4c16f4c0 100644 --- a/src/google/protobuf/message_lite.h +++ b/src/google/protobuf/message_lite.h @@ -238,6 +238,9 @@ class LIBPROTOBUF_EXPORT MessageLite { // Computes the serialized size of the message. This recursively calls // ByteSize() on all embedded messages. If a subclass does not override // this, it MUST override SetCachedSize(). + // + // ByteSize() is generally linear in the number of fields defined for the + // proto. virtual int ByteSize() const = 0; // Serializes the message without recomputing the size. The message must diff --git a/src/google/protobuf/message_unittest.cc b/src/google/protobuf/message_unittest.cc index ebfb4321..75d60b8b 100644 --- a/src/google/protobuf/message_unittest.cc +++ b/src/google/protobuf/message_unittest.cc @@ -45,12 +45,13 @@ #include #include -#include +#include +#include #include -#include +#include #include -#include -#include +#include +#include #include #include @@ -205,6 +206,28 @@ TEST(MessageTest, InitializationErrorString) { EXPECT_EQ("a, b, c", message.InitializationErrorString()); } +TEST(MessageTest, DynamicCastToGenerated) { + unittest::TestAllTypes test_all_types; + + google::protobuf::Message* test_all_types_pointer = &test_all_types; + EXPECT_EQ(&test_all_types, + google::protobuf::internal::DynamicCastToGenerated( + test_all_types_pointer)); + EXPECT_EQ(NULL, + google::protobuf::internal::DynamicCastToGenerated( + test_all_types_pointer)); + + const google::protobuf::Message* test_all_types_pointer_const = &test_all_types; + EXPECT_EQ( + &test_all_types, + google::protobuf::internal::DynamicCastToGenerated( + test_all_types_pointer_const)); + EXPECT_EQ( + NULL, + google::protobuf::internal::DynamicCastToGenerated( + test_all_types_pointer_const)); +} + #ifdef PROTOBUF_HAS_DEATH_TEST // death tests do not work on Windows yet. TEST(MessageTest, SerializeFailsIfNotInitialized) { diff --git a/src/google/protobuf/proto3_arena_unittest.cc b/src/google/protobuf/proto3_arena_unittest.cc index c3b5996f..da4be673 100644 --- a/src/google/protobuf/proto3_arena_unittest.cc +++ b/src/google/protobuf/proto3_arena_unittest.cc @@ -180,6 +180,16 @@ TEST(ArenaTest, ReleaseMessage) { EXPECT_EQ(118, nested->bb()); } +TEST(ArenaTest, MessageFieldClear) { + // GitHub issue #310: https://github.com/google/protobuf/issues/310 + Arena arena; + TestAllTypes* arena_message = Arena::CreateMessage(&arena); + arena_message->mutable_optional_nested_message()->set_bb(118); + // This should not crash, but prior to the bugfix, it tried to use `operator + // delete` the nested message (which is on the arena): + arena_message->Clear(); +} + } // namespace } // namespace protobuf } // namespace google diff --git a/src/google/protobuf/reflection.h b/src/google/protobuf/reflection.h index 03c761c1..4ff0f6b4 100755 --- a/src/google/protobuf/reflection.h +++ b/src/google/protobuf/reflection.h @@ -39,6 +39,7 @@ #endif #include +#include namespace google { namespace protobuf { diff --git a/src/google/protobuf/repeated_field.h b/src/google/protobuf/repeated_field.h index 7bfdc40a..5a2fb409 100644 --- a/src/google/protobuf/repeated_field.h +++ b/src/google/protobuf/repeated_field.h @@ -576,26 +576,21 @@ class GenericTypeHandler { } }; -// Macros for specializing GenericTypeHandler for base proto types, these are -// are defined here, to allow inlining them at their callsites. -#define DEFINE_SPECIALIZATIONS_FOR_BASE_PROTO_TYPES(Inline, TypeName) \ - template<> \ - Inline TypeName* GenericTypeHandler::NewFromPrototype( \ - const TypeName* prototype, google::protobuf::Arena* arena) { \ - return prototype->New(arena); \ - } \ - template<> \ - Inline google::protobuf::Arena* GenericTypeHandler::GetArena( \ - TypeName* value) { \ - return value->GetArena(); \ - } \ - template<> \ - Inline void* GenericTypeHandler::GetMaybeArenaPointer( \ - TypeName* value) { \ - return value->GetMaybeArenaPointer(); \ - } -#define DEFINE_SPECIALIZATIONS_FOR_BASE_PROTO_TYPES_NOINLINE(TypeName) \ - DEFINE_SPECIALIZATIONS_FOR_BASE_PROTO_TYPES(, TypeName) +template<> +inline MessageLite* GenericTypeHandler::NewFromPrototype( + const MessageLite* prototype, google::protobuf::Arena* arena) { + return prototype->New(arena); +} +template<> +inline google::protobuf::Arena* GenericTypeHandler::GetArena( + MessageLite* value) { + return value->GetArena(); +} +template<> +inline void* GenericTypeHandler::GetMaybeArenaPointer( + MessageLite* value) { + return value->GetMaybeArenaPointer(); +} // Implements GenericTypeHandler specialization required by RepeatedPtrFields // to work with MessageLite type. @@ -605,8 +600,6 @@ inline void GenericTypeHandler::Merge( to->CheckTypeAndMergeFrom(from); } -DEFINE_SPECIALIZATIONS_FOR_BASE_PROTO_TYPES(inline, MessageLite); - // Declarations of the specialization as we cannot define them here, as the // header that defines ProtocolMessage depends on types defined in this header. #define DECLARE_SPECIALIZATIONS_FOR_BASE_PROTO_TYPES(TypeName) \ @@ -1235,6 +1228,7 @@ void RepeatedField::Reserve(int new_size) { kRepHeaderSize + sizeof(Element)*new_size)); } rep_->arena = arena; + int old_total_size = total_size_; total_size_ = new_size; // Invoke placement-new on newly allocated elements. We shouldn't have to do // this, since Element is supposed to be POD, but a previous version of this @@ -1253,15 +1247,17 @@ void RepeatedField::Reserve(int new_size) { if (current_size_ > 0) { MoveArray(rep_->elements, old_rep->elements, current_size_); } - // Likewise, we need to invoke destructors on the old array. If Element has no - // destructor, this loop will disappear. - e = &old_rep->elements[0]; - limit = &old_rep->elements[current_size_]; - for (; e < limit; e++) { - e->Element::~Element(); - } - if (arena == NULL) { - delete[] reinterpret_cast(old_rep); + if (old_rep) { + // Likewise, we need to invoke destructors on the old array. If Element has + // no destructor, this loop will disappear. + e = &old_rep->elements[0]; + limit = &old_rep->elements[old_total_size]; + for (; e < limit; e++) { + e->Element::~Element(); + } + if (arena == NULL) { + delete[] reinterpret_cast(old_rep); + } } } @@ -1418,7 +1414,7 @@ void RepeatedPtrFieldBase::Clear() { const int n = current_size_; GOOGLE_DCHECK_GE(n, 0); if (n > 0) { - void* const* elements = raw_data(); + void* const* elements = rep_->elements; int i = 0; do { TypeHandler::Clear(cast(elements[i++])); diff --git a/src/google/protobuf/repeated_field_unittest.cc b/src/google/protobuf/repeated_field_unittest.cc index 66e74523..af397932 100644 --- a/src/google/protobuf/repeated_field_unittest.cc +++ b/src/google/protobuf/repeated_field_unittest.cc @@ -456,6 +456,28 @@ TEST(RepeatedField, ExtractSubrange) { } } +TEST(RepeatedField, ClearThenReserveMore) { + // Test that Reserve properly destroys the old internal array when it's forced + // to allocate a new one, even when cleared-but-not-deleted objects are + // present. Use a 'string' and > 16 bytes length so that the elements are + // non-POD and allocate -- the leak checker will catch any skipped destructor + // calls here. + RepeatedField field; + for (int i = 0; i < 32; i++) { + field.Add(string("abcdefghijklmnopqrstuvwxyz0123456789")); + } + EXPECT_EQ(32, field.size()); + field.Clear(); + EXPECT_EQ(0, field.size()); + EXPECT_EQ(32, field.Capacity()); + + field.Reserve(1024); + EXPECT_EQ(0, field.size()); + EXPECT_EQ(1024, field.Capacity()); + // Finish test -- |field| should destroy the cleared-but-not-yet-destroyed + // strings. +} + // =================================================================== // RepeatedPtrField tests. These pretty much just mirror the RepeatedField // tests above. diff --git a/src/google/protobuf/source_context.pb.cc b/src/google/protobuf/source_context.pb.cc index 0926e747..3b3799a9 100644 --- a/src/google/protobuf/source_context.pb.cc +++ b/src/google/protobuf/source_context.pb.cc @@ -114,7 +114,7 @@ const int SourceContext::kFileNameFieldNumber; #endif // !_MSC_VER SourceContext::SourceContext() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.SourceContext) } @@ -277,9 +277,9 @@ int SourceContext::ByteSize() const { void SourceContext::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const SourceContext* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const SourceContext* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -334,7 +334,7 @@ void SourceContext::InternalSwap(SourceContext* other) { // SourceContext // optional string file_name = 1; - void SourceContext::clear_file_name() { +void SourceContext::clear_file_name() { file_name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } const ::std::string& SourceContext::file_name() const { diff --git a/src/google/protobuf/struct.pb.cc b/src/google/protobuf/struct.pb.cc index 4405a5bf..30113cdb 100644 --- a/src/google/protobuf/struct.pb.cc +++ b/src/google/protobuf/struct.pb.cc @@ -217,7 +217,7 @@ const int Struct::kFieldsFieldNumber; #endif // !_MSC_VER Struct::Struct() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.Struct) } @@ -295,7 +295,8 @@ bool Struct::MergePartialFromCodedStream( // map fields = 1; case 1: { if (tag == 10) { - parse_fields: + DO_(input->IncrementRecursionDepth()); + parse_loop_fields: ::google::protobuf::scoped_ptr entry(fields_.NewEntry()); DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( input, entry.get())); @@ -303,7 +304,8 @@ bool Struct::MergePartialFromCodedStream( } else { goto handle_unusual; } - if (input->ExpectTag(10)) goto parse_fields; + if (input->ExpectTag(10)) goto parse_loop_fields; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectAtEnd()) goto success; break; } @@ -336,7 +338,8 @@ void Struct::SerializeWithCachedSizes( { ::google::protobuf::scoped_ptr entry; for (::google::protobuf::Map< ::std::string, ::google::protobuf::Value >::const_iterator - it = fields().begin(); it != fields().end(); ++it) { + it = this->fields().begin(); + it != this->fields().end(); ++it) { entry.reset(fields_.NewEntryWrapper(it->first, it->second)); ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( 1, *entry, output); @@ -353,7 +356,8 @@ void Struct::SerializeWithCachedSizes( { ::google::protobuf::scoped_ptr entry; for (::google::protobuf::Map< ::std::string, ::google::protobuf::Value >::const_iterator - it = fields().begin(); it != fields().end(); ++it) { + it = this->fields().begin(); + it != this->fields().end(); ++it) { entry.reset(fields_.NewEntryWrapper(it->first, it->second)); target = ::google::protobuf::internal::WireFormatLite:: WriteMessageNoVirtualToArray( @@ -373,7 +377,8 @@ int Struct::ByteSize() const { { ::google::protobuf::scoped_ptr entry; for (::google::protobuf::Map< ::std::string, ::google::protobuf::Value >::const_iterator - it = fields().begin(); it != fields().end(); ++it) { + it = this->fields().begin(); + it != this->fields().end(); ++it) { entry.reset(fields_.NewEntryWrapper(it->first, it->second)); total_size += ::google::protobuf::internal::WireFormatLite:: MessageSizeNoVirtual(*entry); @@ -388,9 +393,9 @@ int Struct::ByteSize() const { void Struct::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const Struct* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const Struct* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -442,10 +447,10 @@ void Struct::InternalSwap(Struct* other) { // Struct // map fields = 1; - int Struct::fields_size() const { +int Struct::fields_size() const { return fields_.size(); } - void Struct::clear_fields() { +void Struct::clear_fields() { fields_.Clear(); } const ::google::protobuf::Map< ::std::string, ::google::protobuf::Value >& @@ -473,7 +478,7 @@ const int Value::kListValueFieldNumber; #endif // !_MSC_VER Value::Value() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.Value) } @@ -845,9 +850,9 @@ int Value::ByteSize() const { void Value::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const Value* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const Value* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -928,13 +933,13 @@ void Value::InternalSwap(Value* other) { // Value // optional .google.protobuf.NullValue null_value = 1; - bool Value::has_null_value() const { +bool Value::has_null_value() const { return kind_case() == kNullValue; } - void Value::set_has_null_value() { +void Value::set_has_null_value() { _oneof_case_[0] = kNullValue; } - void Value::clear_null_value() { +void Value::clear_null_value() { if (has_null_value()) { kind_.null_value_ = 0; clear_has_kind(); @@ -957,13 +962,13 @@ void Value::InternalSwap(Value* other) { } // optional double number_value = 2; - bool Value::has_number_value() const { +bool Value::has_number_value() const { return kind_case() == kNumberValue; } - void Value::set_has_number_value() { +void Value::set_has_number_value() { _oneof_case_[0] = kNumberValue; } - void Value::clear_number_value() { +void Value::clear_number_value() { if (has_number_value()) { kind_.number_value_ = 0; clear_has_kind(); @@ -986,13 +991,13 @@ void Value::InternalSwap(Value* other) { } // optional string string_value = 3; - bool Value::has_string_value() const { +bool Value::has_string_value() const { return kind_case() == kStringValue; } - void Value::set_has_string_value() { +void Value::set_has_string_value() { _oneof_case_[0] = kStringValue; } - void Value::clear_string_value() { +void Value::clear_string_value() { if (has_string_value()) { kind_.string_value_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); clear_has_kind(); @@ -1066,13 +1071,13 @@ void Value::InternalSwap(Value* other) { } // optional bool bool_value = 4; - bool Value::has_bool_value() const { +bool Value::has_bool_value() const { return kind_case() == kBoolValue; } - void Value::set_has_bool_value() { +void Value::set_has_bool_value() { _oneof_case_[0] = kBoolValue; } - void Value::clear_bool_value() { +void Value::clear_bool_value() { if (has_bool_value()) { kind_.bool_value_ = false; clear_has_kind(); @@ -1095,13 +1100,13 @@ void Value::InternalSwap(Value* other) { } // optional .google.protobuf.Struct struct_value = 5; - bool Value::has_struct_value() const { +bool Value::has_struct_value() const { return kind_case() == kStructValue; } - void Value::set_has_struct_value() { +void Value::set_has_struct_value() { _oneof_case_[0] = kStructValue; } - void Value::clear_struct_value() { +void Value::clear_struct_value() { if (has_struct_value()) { delete kind_.struct_value_; clear_has_kind(); @@ -1141,13 +1146,13 @@ void Value::InternalSwap(Value* other) { } // optional .google.protobuf.ListValue list_value = 6; - bool Value::has_list_value() const { +bool Value::has_list_value() const { return kind_case() == kListValue; } - void Value::set_has_list_value() { +void Value::set_has_list_value() { _oneof_case_[0] = kListValue; } - void Value::clear_list_value() { +void Value::clear_list_value() { if (has_list_value()) { delete kind_.list_value_; clear_has_kind(); @@ -1186,10 +1191,10 @@ void Value::InternalSwap(Value* other) { // @@protoc_insertion_point(field_set_allocated:google.protobuf.Value.list_value) } - bool Value::has_kind() const { +bool Value::has_kind() const { return kind_case() != KIND_NOT_SET; } - void Value::clear_has_kind() { +void Value::clear_has_kind() { _oneof_case_[0] = KIND_NOT_SET; } Value::KindCase Value::kind_case() const { @@ -1204,7 +1209,7 @@ const int ListValue::kValuesFieldNumber; #endif // !_MSC_VER ListValue::ListValue() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.ListValue) } @@ -1278,13 +1283,15 @@ bool ListValue::MergePartialFromCodedStream( // repeated .google.protobuf.Value values = 1; case 1: { if (tag == 10) { - parse_values: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_values: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_values())); } else { goto handle_unusual; } - if (input->ExpectTag(10)) goto parse_values; + if (input->ExpectTag(10)) goto parse_loop_values; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectAtEnd()) goto success; break; } @@ -1355,9 +1362,9 @@ int ListValue::ByteSize() const { void ListValue::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const ListValue* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const ListValue* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -1409,10 +1416,10 @@ void ListValue::InternalSwap(ListValue* other) { // ListValue // repeated .google.protobuf.Value values = 1; - int ListValue::values_size() const { +int ListValue::values_size() const { return values_.size(); } - void ListValue::clear_values() { +void ListValue::clear_values() { values_.Clear(); } const ::google::protobuf::Value& ListValue::values(int index) const { diff --git a/src/google/protobuf/struct.pb.h b/src/google/protobuf/struct.pb.h index 46482142..2889c8fe 100644 --- a/src/google/protobuf/struct.pb.h +++ b/src/google/protobuf/struct.pb.h @@ -735,6 +735,10 @@ ListValue::mutable_values() { } #endif // !PROTOBUF_INLINE_NOT_IN_HEADERS +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + // @@protoc_insertion_point(namespace_scope) diff --git a/src/google/protobuf/stubs/common.h b/src/google/protobuf/stubs/common.h index 37123c7b..3acaeba1 100644 --- a/src/google/protobuf/stubs/common.h +++ b/src/google/protobuf/stubs/common.h @@ -1141,6 +1141,14 @@ class LIBPROTOBUF_EXPORT Mutex { GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Mutex); }; +// Undefine the macros to workaround the conflicts with Google internal +// MutexLock implementation. +// TODO(liujisi): Remove the undef once internal macros are removed. +#undef MutexLock +#undef ReaderMutexLock +#undef WriterMutexLock +#undef MutexLockMaybe + // MutexLock(mu) acquires mu when constructed and releases it when destroyed. class LIBPROTOBUF_EXPORT MutexLock { public: diff --git a/src/google/protobuf/stubs/strutil.cc b/src/google/protobuf/stubs/strutil.cc index 7955d261..7ecc17ee 100644 --- a/src/google/protobuf/stubs/strutil.cc +++ b/src/google/protobuf/stubs/strutil.cc @@ -1285,24 +1285,6 @@ char* FloatToBuffer(float value, char* buffer) { return buffer; } -string ToHex(uint64 num) { - if (num == 0) { - return string("0"); - } - - // Compute hex bytes in reverse order, writing to the back of the - // buffer. - char buf[16]; // No more than 16 hex digits needed. - char* bufptr = buf + 16; - static const char kHexChars[] = "0123456789abcdef"; - while (num != 0) { - *--bufptr = kHexChars[num & 0xf]; - num >>= 4; - } - - return string(bufptr, buf + 16 - bufptr); -} - namespace strings { AlphaNum::AlphaNum(strings::Hex hex) { diff --git a/src/google/protobuf/stubs/strutil.h b/src/google/protobuf/stubs/strutil.h index 920701eb..5faa81e0 100644 --- a/src/google/protobuf/stubs/strutil.h +++ b/src/google/protobuf/stubs/strutil.h @@ -682,12 +682,6 @@ string Join(const Range& components, return result; } -// ---------------------------------------------------------------------- -// ToHex() -// Return a lower-case hex string representation of the given integer. -// ---------------------------------------------------------------------- -LIBPROTOBUF_EXPORT string ToHex(uint64 num); - // ---------------------------------------------------------------------- // GlobalReplaceSubstring() // Replaces all instances of a substring in a string. Does nothing diff --git a/src/google/protobuf/testdata/golden_message_proto3 b/src/google/protobuf/testdata/golden_message_proto3 index 934f36fa..bd646a0d 100644 Binary files a/src/google/protobuf/testdata/golden_message_proto3 and b/src/google/protobuf/testdata/golden_message_proto3 differ diff --git a/src/google/protobuf/testdata/map_test_data.txt b/src/google/protobuf/testdata/map_test_data.txt new file mode 100644 index 00000000..bc272321 --- /dev/null +++ b/src/google/protobuf/testdata/map_test_data.txt @@ -0,0 +1,140 @@ +map_int32_int32 { + key: 0 + value: 0 +} +map_int32_int32 { + key: 1 + value: 1 +} +map_int64_int64 { + key: 0 + value: 0 +} +map_int64_int64 { + key: 1 + value: 1 +} +map_uint32_uint32 { + key: 0 + value: 0 +} +map_uint32_uint32 { + key: 1 + value: 1 +} +map_uint64_uint64 { + key: 0 + value: 0 +} +map_uint64_uint64 { + key: 1 + value: 1 +} +map_sint32_sint32 { + key: 0 + value: 0 +} +map_sint32_sint32 { + key: 1 + value: 1 +} +map_sint64_sint64 { + key: 0 + value: 0 +} +map_sint64_sint64 { + key: 1 + value: 1 +} +map_fixed32_fixed32 { + key: 0 + value: 0 +} +map_fixed32_fixed32 { + key: 1 + value: 1 +} +map_fixed64_fixed64 { + key: 0 + value: 0 +} +map_fixed64_fixed64 { + key: 1 + value: 1 +} +map_sfixed32_sfixed32 { + key: 0 + value: 0 +} +map_sfixed32_sfixed32 { + key: 1 + value: 1 +} +map_sfixed64_sfixed64 { + key: 0 + value: 0 +} +map_sfixed64_sfixed64 { + key: 1 + value: 1 +} +map_int32_float { + key: 0 + value: 0 +} +map_int32_float { + key: 1 + value: 1 +} +map_int32_double { + key: 0 + value: 0 +} +map_int32_double { + key: 1 + value: 1 +} +map_bool_bool { + key: false + value: false +} +map_bool_bool { + key: true + value: true +} +map_string_string { + key: "0" + value: "0" +} +map_string_string { + key: "1" + value: "1" +} +map_int32_bytes { + key: 0 + value: "0" +} +map_int32_bytes { + key: 1 + value: "1" +} +map_int32_enum { + key: 0 + value: MAP_ENUM_BAR +} +map_int32_enum { + key: 1 + value: MAP_ENUM_BAZ +} +map_int32_foreign_message { + key: 0 + value { + c: 0 + } +} +map_int32_foreign_message { + key: 1 + value { + c: 1 + } +} diff --git a/src/google/protobuf/text_format.cc b/src/google/protobuf/text_format.cc index ec070c51..61dfa5d6 100644 --- a/src/google/protobuf/text_format.cc +++ b/src/google/protobuf/text_format.cc @@ -43,6 +43,8 @@ #include #include +#include +#include #include #include #include @@ -50,6 +52,7 @@ #include #include #include +#include #include #include #include @@ -70,6 +73,18 @@ inline bool IsOctNumber(const string& str) { (str[1] >= '0' && str[1] < '8')); } +inline bool GetAnyFieldDescriptors(const Message& message, + const FieldDescriptor** type_url_field, + const FieldDescriptor** value_field) { + const Descriptor* descriptor = message.GetDescriptor(); + *type_url_field = descriptor->FindFieldByNumber(1); + *value_field = descriptor->FindFieldByNumber(2); + return (*type_url_field != NULL && + (*type_url_field)->type() == FieldDescriptor::TYPE_STRING && + *value_field != NULL && + (*value_field)->type() == FieldDescriptor::TYPE_BYTES); +} + } // namespace string Message::DebugString() const { @@ -330,7 +345,17 @@ class TextFormat::Parser::ParserImpl { // Confirm that we have a valid ending delimiter. DO(Consume(delimiter)); + return true; + } + // Consume either "<" or "{". + bool ConsumeMessageDelimiter(string* delimiter) { + if (TryConsume("<")) { + *delimiter = ">"; + } else { + DO(Consume("{")); + *delimiter = "}"; + } return true; } @@ -347,15 +372,28 @@ class TextFormat::Parser::ParserImpl { int start_line = tokenizer_.current().line; int start_column = tokenizer_.current().column; + const FieldDescriptor* any_type_url_field; + const FieldDescriptor* any_value_field; + if (internal::GetAnyFieldDescriptors(*message, &any_type_url_field, + &any_value_field) && + TryConsume("[")) { + string full_type_name; + DO(ConsumeAnyTypeUrl(&full_type_name)); + DO(Consume("]")); + string serialized_value; + DO(ConsumeAnyValue(full_type_name, + message->GetDescriptor()->file()->pool(), + &serialized_value)); + reflection->SetString( + message, any_type_url_field, + string(internal::kTypeGoogleApisComPrefix) + full_type_name); + reflection->SetString(message, any_value_field, serialized_value); + return true; + // Fall through. + } if (TryConsume("[")) { // Extension. - DO(ConsumeIdentifier(&field_name)); - while (TryConsume(".")) { - string part; - DO(ConsumeIdentifier(&part)); - field_name += "."; - field_name += part; - } + DO(ConsumeFullTypeName(&field_name)); DO(Consume("]")); field = (finder_ != NULL @@ -512,13 +550,7 @@ class TextFormat::Parser::ParserImpl { string field_name; if (TryConsume("[")) { // Extension name. - DO(ConsumeIdentifier(&field_name)); - while (TryConsume(".")) { - string part; - DO(ConsumeIdentifier(&part)); - field_name += "."; - field_name += part; - } + DO(ConsumeFullTypeName(&field_name)); DO(Consume("]")); } else { DO(ConsumeIdentifier(&field_name)); @@ -553,13 +585,7 @@ class TextFormat::Parser::ParserImpl { } string delimiter; - if (TryConsume("<")) { - delimiter = ">"; - } else { - DO(Consume("{")); - delimiter = "}"; - } - + DO(ConsumeMessageDelimiter(&delimiter)); if (field->is_repeated()) { DO(ConsumeMessage(reflection->AddMessage(message, field), delimiter)); } else { @@ -576,12 +602,7 @@ class TextFormat::Parser::ParserImpl { // the ending delimiter. bool SkipFieldMessage() { string delimiter; - if (TryConsume("<")) { - delimiter = ">"; - } else { - DO(Consume("{")); - delimiter = "}"; - } + DO(ConsumeMessageDelimiter(&delimiter)); while (!LookingAt(">") && !LookingAt("}")) { DO(SkipField()); } @@ -808,6 +829,18 @@ class TextFormat::Parser::ParserImpl { return false; } + // Consume a string of form ".....". + bool ConsumeFullTypeName(string* name) { + DO(ConsumeIdentifier(name)); + while (TryConsume(".")) { + string part; + DO(ConsumeIdentifier(&part)); + *name += "."; + *name += part; + } + return true; + } + // Consumes a string and saves its value in the text parameter. // Returns false if the token is not of type STRING. bool ConsumeString(string* text) { @@ -947,6 +980,54 @@ class TextFormat::Parser::ParserImpl { return true; } + // Consumes Any::type_url value, of form "type.googleapis.com/full.type.Name" + bool ConsumeAnyTypeUrl(string* full_type_name) { + // TODO(saito) Extend Consume() to consume multiple tokens at once, so that + // this code can be written as just DO(Consume(kGoogleApisTypePrefix)). + string url1, url2, url3; + DO(ConsumeIdentifier(&url1)); // type + DO(Consume(".")); + DO(ConsumeIdentifier(&url2)); // googleapis + DO(Consume(".")); + DO(ConsumeIdentifier(&url3)); // com + DO(Consume("/")); + DO(ConsumeFullTypeName(full_type_name)); + + const string prefix = url1 + "." + url2 + "." + url3 + "/"; + if (prefix != internal::kTypeGoogleApisComPrefix) { + ReportError("TextFormat::Parser for Any supports only " + "type.googleapi.com, but found \"" + prefix + "\""); + return false; + } + return true; + } + + // A helper function for reconstructing Any::value. Consumes a text of + // full_type_name, then serializes it into serialized_value. "pool" is used to + // look up and create a temporary object with full_type_name. + bool ConsumeAnyValue(const string& full_type_name, const DescriptorPool* pool, + string* serialized_value) { + const Descriptor* value_descriptor = + pool->FindMessageTypeByName(full_type_name); + if (value_descriptor == NULL) { + ReportError("Could not find type \"" + full_type_name + + "\" stored in google.protobuf.Any."); + return false; + } + DynamicMessageFactory factory; + const Message* value_prototype = factory.GetPrototype(value_descriptor); + if (value_prototype == NULL) { + return false; + } + google::protobuf::scoped_ptr value(value_prototype->New()); + string sub_delimiter; + DO(ConsumeMessageDelimiter(&sub_delimiter)); + DO(ConsumeMessage(value.get(), sub_delimiter)); + + value->AppendToString(serialized_value); + return true; + } + // Consumes a token and confirms that it matches that specified in the // value parameter. Returns false if the token found does not match that // which was specified. @@ -1338,7 +1419,8 @@ TextFormat::Printer::Printer() use_field_number_(false), use_short_repeated_primitives_(false), hide_unknown_fields_(false), - print_message_fields_in_index_order_(false) { + print_message_fields_in_index_order_(false), + expand_any_(false) { SetUseUtf8StringEscaping(false); } @@ -1413,11 +1495,63 @@ struct FieldIndexSorter { return left->index() < right->index(); } }; + } // namespace +bool TextFormat::Printer::PrintAny(const Message& message, + TextGenerator& generator) const { + const FieldDescriptor* type_url_field; + const FieldDescriptor* value_field; + if (!internal::GetAnyFieldDescriptors(message, &type_url_field, + &value_field)) { + return false; + } + + const Reflection* reflection = message.GetReflection(); + + // Extract the full type name from the type_url field. + const string& type_url = reflection->GetString(message, type_url_field); + string full_type_name; + if (!internal::ParseAnyTypeUrl(type_url, &full_type_name)) { + return false; + } + + // Print the "value" in text. + const google::protobuf::Descriptor* value_descriptor = + message.GetDescriptor()->file()->pool()->FindMessageTypeByName( + full_type_name); + if (value_descriptor == NULL) { + GOOGLE_LOG(WARNING) << "Proto type " << type_url << " not found"; + return false; + } + DynamicMessageFactory factory; + google::protobuf::scoped_ptr value_message( + factory.GetPrototype(value_descriptor)->New()); + string serialized_value = reflection->GetString(message, value_field); + if (!value_message->ParseFromString(serialized_value)) { + GOOGLE_LOG(WARNING) << type_url << ": failed to parse contents"; + return false; + } + generator.Print(StrCat("[", type_url, "]")); + const FieldValuePrinter* printer = FindWithDefault( + custom_printers_, value_field, default_field_value_printer_.get()); + generator.Print( + printer->PrintMessageStart(message, -1, 0, single_line_mode_)); + generator.Indent(); + Print(*value_message, generator); + generator.Outdent(); + generator.Print(printer->PrintMessageEnd(message, -1, 0, single_line_mode_)); + return true; +} + void TextFormat::Printer::Print(const Message& message, TextGenerator& generator) const { + const Descriptor* descriptor = message.GetDescriptor(); const Reflection* reflection = message.GetReflection(); + if (descriptor->full_name() == internal::kAnyFullTypeName && expand_any_ && + PrintAny(message, generator)) { + return; + } vector fields; reflection->ListFields(message, &fields); if (print_message_fields_in_index_order_) { @@ -1446,6 +1580,54 @@ void TextFormat::Printer::PrintFieldValueToString( PrintFieldValue(message, message.GetReflection(), field, index, generator); } +class MapEntryMessageComparator { + public: + explicit MapEntryMessageComparator(const Descriptor* descriptor) + : field_(descriptor->field(0)) {} + + bool operator()(const Message* a, const Message* b) { + const Reflection* reflection = a->GetReflection(); + switch (field_->cpp_type()) { + case FieldDescriptor::CPPTYPE_BOOL: { + bool first = reflection->GetBool(*a, field_); + bool second = reflection->GetBool(*b, field_); + return first < second; + } + case FieldDescriptor::CPPTYPE_INT32: { + int32 first = reflection->GetInt32(*a, field_); + int32 second = reflection->GetInt32(*b, field_); + return first < second; + } + case FieldDescriptor::CPPTYPE_INT64: { + int64 first = reflection->GetInt64(*a, field_); + int64 second = reflection->GetInt64(*b, field_); + return first < second; + } + case FieldDescriptor::CPPTYPE_UINT32: { + uint32 first = reflection->GetUInt32(*a, field_); + uint32 second = reflection->GetUInt32(*b, field_); + return first < second; + } + case FieldDescriptor::CPPTYPE_UINT64: { + uint64 first = reflection->GetUInt64(*a, field_); + uint64 second = reflection->GetUInt64(*b, field_); + return first < second; + } + case FieldDescriptor::CPPTYPE_STRING: { + string first = reflection->GetString(*a, field_); + string second = reflection->GetString(*b, field_); + return first < second; + } + default: + GOOGLE_LOG(DFATAL) << "Invalid key for map field."; + return true; + } + } + + private: + const FieldDescriptor* field_; +}; + void TextFormat::Printer::PrintField(const Message& message, const Reflection* reflection, const FieldDescriptor* field, @@ -1466,6 +1648,21 @@ void TextFormat::Printer::PrintField(const Message& message, count = 1; } + std::vector sorted_map_field; + if (field->is_map()) { + const RepeatedPtrField& map_field = + reflection->GetRepeatedPtrField(message, field); + for (RepeatedPtrField::const_pointer_iterator it = + map_field.pointer_begin(); + it != map_field.pointer_end(); ++it) { + sorted_map_field.push_back(*it); + } + + MapEntryMessageComparator comparator(field->message_type()); + std::stable_sort(sorted_map_field.begin(), sorted_map_field.end(), + comparator); + } + for (int j = 0; j < count; ++j) { const int field_index = field->is_repeated() ? j : -1; @@ -1475,8 +1672,10 @@ void TextFormat::Printer::PrintField(const Message& message, const FieldValuePrinter* printer = FindWithDefault( custom_printers_, field, default_field_value_printer_.get()); const Message& sub_message = - field->is_repeated() - ? reflection->GetRepeatedMessage(message, field, j) + field->is_repeated() + ? (field->is_map() + ? *sorted_map_field[j] + : reflection->GetRepeatedMessage(message, field, j)) : reflection->GetMessage(message, field); generator.Print( printer->PrintMessageStart( @@ -1680,8 +1879,8 @@ void TextFormat::Printer::PrintUnknownFields( case UnknownField::TYPE_FIXED32: { generator.Print(field_number); generator.Print(": 0x"); - char buffer[kFastToBufferSize]; - generator.Print(FastHex32ToBuffer(field.fixed32(), buffer)); + generator.Print( + StrCat(strings::Hex(field.fixed32(), strings::Hex::ZERO_PAD_8))); if (single_line_mode_) { generator.Print(" "); } else { @@ -1692,8 +1891,8 @@ void TextFormat::Printer::PrintUnknownFields( case UnknownField::TYPE_FIXED64: { generator.Print(field_number); generator.Print(": 0x"); - char buffer[kFastToBufferSize]; - generator.Print(FastHex64ToBuffer(field.fixed64(), buffer)); + generator.Print( + StrCat(strings::Hex(field.fixed64(), strings::Hex::ZERO_PAD_16))); if (single_line_mode_) { generator.Print(" "); } else { diff --git a/src/google/protobuf/text_format.h b/src/google/protobuf/text_format.h index 9e2cb070..6717aecd 100644 --- a/src/google/protobuf/text_format.h +++ b/src/google/protobuf/text_format.h @@ -208,6 +208,17 @@ class LIBPROTOBUF_EXPORT TextFormat { print_message_fields_in_index_order; } + // If expand==true, expand google.protobuf.Any payloads. The output + // will be of form + // [type_url] { } + // + // If expand==false, print Any using the default printer. The output will + // look like + // type_url: "" value: "serialized_content" + void SetExpandAny(bool expand) { + expand_any_ = expand; + } + // Register a custom field-specific FieldValuePrinter for fields // with a particular FieldDescriptor. // Returns "true" if the registration succeeded, or "false", if there is @@ -259,6 +270,8 @@ class LIBPROTOBUF_EXPORT TextFormat { void PrintUnknownFields(const UnknownFieldSet& unknown_fields, TextGenerator& generator) const; + bool PrintAny(const Message& message, TextGenerator& generator) const; + int initial_indent_level_; bool single_line_mode_; @@ -271,6 +284,8 @@ class LIBPROTOBUF_EXPORT TextFormat { bool print_message_fields_in_index_order_; + bool expand_any_; + google::protobuf::scoped_ptr default_field_value_printer_; typedef map CustomPrinterMap; diff --git a/src/google/protobuf/text_format_unittest.cc b/src/google/protobuf/text_format_unittest.cc index 477fdcbd..1b18c5ed 100644 --- a/src/google/protobuf/text_format_unittest.cc +++ b/src/google/protobuf/text_format_unittest.cc @@ -32,23 +32,24 @@ // Based on original Protocol Buffers design by // Sanjay Ghemawat, Jeff Dean, and others. +#include + #include #include #include -#include -#include -#include -#include -#include -#include - #include #include -#include -#include +#include +#include +#include +#include +#include #include #include +#include +#include + namespace google { namespace protobuf { @@ -451,7 +452,7 @@ TEST_F(TextFormatTest, ErrorCasesRegisteringFieldValuePrinterShouldFail) { class CustomMessageFieldValuePrinter : public TextFormat::FieldValuePrinter { public: virtual string PrintInt32(int32 v) const { - return StrCat(FieldValuePrinter::PrintInt32(v), " # x", ToHex(v)); + return StrCat(FieldValuePrinter::PrintInt32(v), " # x", strings::Hex(v)); } virtual string PrintMessageStart(const Message& message, diff --git a/src/google/protobuf/timestamp.pb.cc b/src/google/protobuf/timestamp.pb.cc index 6edfe056..877dc8f3 100644 --- a/src/google/protobuf/timestamp.pb.cc +++ b/src/google/protobuf/timestamp.pb.cc @@ -117,7 +117,7 @@ const int Timestamp::kNanosFieldNumber; #endif // !_MSC_VER Timestamp::Timestamp() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.Timestamp) } @@ -310,9 +310,9 @@ int Timestamp::ByteSize() const { void Timestamp::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const Timestamp* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const Timestamp* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -370,7 +370,7 @@ void Timestamp::InternalSwap(Timestamp* other) { // Timestamp // optional int64 seconds = 1; - void Timestamp::clear_seconds() { +void Timestamp::clear_seconds() { seconds_ = GOOGLE_LONGLONG(0); } ::google::protobuf::int64 Timestamp::seconds() const { @@ -384,7 +384,7 @@ void Timestamp::InternalSwap(Timestamp* other) { } // optional int32 nanos = 2; - void Timestamp::clear_nanos() { +void Timestamp::clear_nanos() { nanos_ = 0; } ::google::protobuf::int32 Timestamp::nanos() const { diff --git a/src/google/protobuf/type.pb.cc b/src/google/protobuf/type.pb.cc index d77bc6ce..8b08909e 100644 --- a/src/google/protobuf/type.pb.cc +++ b/src/google/protobuf/type.pb.cc @@ -267,7 +267,7 @@ const int Type::kSourceContextFieldNumber; #endif // !_MSC_VER Type::Type() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.Type) } @@ -332,7 +332,7 @@ Type* Type::New(::google::protobuf::Arena* arena) const { void Type::Clear() { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); - if (source_context_ != NULL) delete source_context_; + if (GetArenaNoVirtual() == NULL && source_context_ != NULL) delete source_context_; source_context_ = NULL; fields_.Clear(); oneofs_.Clear(); @@ -369,12 +369,15 @@ bool Type::MergePartialFromCodedStream( case 2: { if (tag == 18) { parse_fields: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_fields: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_fields())); } else { goto handle_unusual; } - if (input->ExpectTag(18)) goto parse_fields; + if (input->ExpectTag(18)) goto parse_loop_fields; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectTag(26)) goto parse_oneofs; break; } @@ -402,12 +405,15 @@ bool Type::MergePartialFromCodedStream( case 4: { if (tag == 34) { parse_options: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_options: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_options())); } else { goto handle_unusual; } - if (input->ExpectTag(34)) goto parse_options; + if (input->ExpectTag(34)) goto parse_loop_options; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectTag(42)) goto parse_source_context; break; } @@ -587,9 +593,9 @@ int Type::ByteSize() const { void Type::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const Type* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const Type* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -654,7 +660,7 @@ void Type::InternalSwap(Type* other) { // Type // optional string name = 1; - void Type::clear_name() { +void Type::clear_name() { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } const ::std::string& Type::name() const { @@ -697,10 +703,10 @@ void Type::InternalSwap(Type* other) { } // repeated .google.protobuf.Field fields = 2; - int Type::fields_size() const { +int Type::fields_size() const { return fields_.size(); } - void Type::clear_fields() { +void Type::clear_fields() { fields_.Clear(); } const ::google::protobuf::Field& Type::fields(int index) const { @@ -727,10 +733,10 @@ Type::mutable_fields() { } // repeated string oneofs = 3; - int Type::oneofs_size() const { +int Type::oneofs_size() const { return oneofs_.size(); } - void Type::clear_oneofs() { +void Type::clear_oneofs() { oneofs_.Clear(); } const ::std::string& Type::oneofs(int index) const { @@ -781,10 +787,10 @@ Type::mutable_oneofs() { } // repeated .google.protobuf.Option options = 4; - int Type::options_size() const { +int Type::options_size() const { return options_.size(); } - void Type::clear_options() { +void Type::clear_options() { options_.Clear(); } const ::google::protobuf::Option& Type::options(int index) const { @@ -811,11 +817,11 @@ Type::mutable_options() { } // optional .google.protobuf.SourceContext source_context = 5; - bool Type::has_source_context() const { +bool Type::has_source_context() const { return !_is_default_instance_ && source_context_ != NULL; } - void Type::clear_source_context() { - if (source_context_ != NULL) delete source_context_; +void Type::clear_source_context() { + if (GetArenaNoVirtual() == NULL && source_context_ != NULL) delete source_context_; source_context_ = NULL; } const ::google::protobuf::SourceContext& Type::source_context() const { @@ -941,7 +947,7 @@ const int Field::kOptionsFieldNumber; #endif // !_MSC_VER Field::Field() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.Field) } @@ -1153,12 +1159,15 @@ bool Field::MergePartialFromCodedStream( case 9: { if (tag == 74) { parse_options: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_options: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_options())); } else { goto handle_unusual; } - if (input->ExpectTag(74)) goto parse_options; + if (input->ExpectTag(74)) goto parse_loop_options; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectAtEnd()) goto success; break; } @@ -1370,9 +1379,9 @@ int Field::ByteSize() const { void Field::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const Field* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const Field* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -1454,7 +1463,7 @@ void Field::InternalSwap(Field* other) { // Field // optional .google.protobuf.Field.Kind kind = 1; - void Field::clear_kind() { +void Field::clear_kind() { kind_ = 0; } ::google::protobuf::Field_Kind Field::kind() const { @@ -1468,7 +1477,7 @@ void Field::InternalSwap(Field* other) { } // optional .google.protobuf.Field.Cardinality cardinality = 2; - void Field::clear_cardinality() { +void Field::clear_cardinality() { cardinality_ = 0; } ::google::protobuf::Field_Cardinality Field::cardinality() const { @@ -1482,7 +1491,7 @@ void Field::InternalSwap(Field* other) { } // optional int32 number = 3; - void Field::clear_number() { +void Field::clear_number() { number_ = 0; } ::google::protobuf::int32 Field::number() const { @@ -1496,7 +1505,7 @@ void Field::InternalSwap(Field* other) { } // optional string name = 4; - void Field::clear_name() { +void Field::clear_name() { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } const ::std::string& Field::name() const { @@ -1539,7 +1548,7 @@ void Field::InternalSwap(Field* other) { } // optional string type_url = 6; - void Field::clear_type_url() { +void Field::clear_type_url() { type_url_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } const ::std::string& Field::type_url() const { @@ -1582,7 +1591,7 @@ void Field::InternalSwap(Field* other) { } // optional int32 oneof_index = 7; - void Field::clear_oneof_index() { +void Field::clear_oneof_index() { oneof_index_ = 0; } ::google::protobuf::int32 Field::oneof_index() const { @@ -1596,7 +1605,7 @@ void Field::InternalSwap(Field* other) { } // optional bool packed = 8; - void Field::clear_packed() { +void Field::clear_packed() { packed_ = false; } bool Field::packed() const { @@ -1610,10 +1619,10 @@ void Field::InternalSwap(Field* other) { } // repeated .google.protobuf.Option options = 9; - int Field::options_size() const { +int Field::options_size() const { return options_.size(); } - void Field::clear_options() { +void Field::clear_options() { options_.Clear(); } const ::google::protobuf::Option& Field::options(int index) const { @@ -1651,7 +1660,7 @@ const int Enum::kSourceContextFieldNumber; #endif // !_MSC_VER Enum::Enum() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.Enum) } @@ -1716,7 +1725,7 @@ Enum* Enum::New(::google::protobuf::Arena* arena) const { void Enum::Clear() { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); - if (source_context_ != NULL) delete source_context_; + if (GetArenaNoVirtual() == NULL && source_context_ != NULL) delete source_context_; source_context_ = NULL; enumvalue_.Clear(); options_.Clear(); @@ -1752,26 +1761,31 @@ bool Enum::MergePartialFromCodedStream( case 2: { if (tag == 18) { parse_enumvalue: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_enumvalue: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_enumvalue())); } else { goto handle_unusual; } - if (input->ExpectTag(18)) goto parse_enumvalue; - if (input->ExpectTag(26)) goto parse_options; + if (input->ExpectTag(18)) goto parse_loop_enumvalue; + if (input->ExpectTag(26)) goto parse_loop_options; + input->UnsafeDecrementRecursionDepth(); break; } // repeated .google.protobuf.Option options = 3; case 3: { if (tag == 26) { - parse_options: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_options: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_options())); } else { goto handle_unusual; } - if (input->ExpectTag(26)) goto parse_options; + if (input->ExpectTag(26)) goto parse_loop_options; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectTag(34)) goto parse_source_context; break; } @@ -1924,9 +1938,9 @@ int Enum::ByteSize() const { void Enum::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const Enum* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const Enum* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -1989,7 +2003,7 @@ void Enum::InternalSwap(Enum* other) { // Enum // optional string name = 1; - void Enum::clear_name() { +void Enum::clear_name() { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } const ::std::string& Enum::name() const { @@ -2032,10 +2046,10 @@ void Enum::InternalSwap(Enum* other) { } // repeated .google.protobuf.EnumValue enumvalue = 2; - int Enum::enumvalue_size() const { +int Enum::enumvalue_size() const { return enumvalue_.size(); } - void Enum::clear_enumvalue() { +void Enum::clear_enumvalue() { enumvalue_.Clear(); } const ::google::protobuf::EnumValue& Enum::enumvalue(int index) const { @@ -2062,10 +2076,10 @@ Enum::mutable_enumvalue() { } // repeated .google.protobuf.Option options = 3; - int Enum::options_size() const { +int Enum::options_size() const { return options_.size(); } - void Enum::clear_options() { +void Enum::clear_options() { options_.Clear(); } const ::google::protobuf::Option& Enum::options(int index) const { @@ -2092,11 +2106,11 @@ Enum::mutable_options() { } // optional .google.protobuf.SourceContext source_context = 4; - bool Enum::has_source_context() const { +bool Enum::has_source_context() const { return !_is_default_instance_ && source_context_ != NULL; } - void Enum::clear_source_context() { - if (source_context_ != NULL) delete source_context_; +void Enum::clear_source_context() { + if (GetArenaNoVirtual() == NULL && source_context_ != NULL) delete source_context_; source_context_ = NULL; } const ::google::protobuf::SourceContext& Enum::source_context() const { @@ -2139,7 +2153,7 @@ const int EnumValue::kOptionsFieldNumber; #endif // !_MSC_VER EnumValue::EnumValue() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.EnumValue) } @@ -2251,12 +2265,15 @@ bool EnumValue::MergePartialFromCodedStream( case 3: { if (tag == 26) { parse_options: - DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual( + DO_(input->IncrementRecursionDepth()); + parse_loop_options: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( input, add_options())); } else { goto handle_unusual; } - if (input->ExpectTag(26)) goto parse_options; + if (input->ExpectTag(26)) goto parse_loop_options; + input->UnsafeDecrementRecursionDepth(); if (input->ExpectAtEnd()) goto success; break; } @@ -2372,9 +2389,9 @@ int EnumValue::ByteSize() const { void EnumValue::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const EnumValue* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const EnumValue* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -2435,7 +2452,7 @@ void EnumValue::InternalSwap(EnumValue* other) { // EnumValue // optional string name = 1; - void EnumValue::clear_name() { +void EnumValue::clear_name() { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } const ::std::string& EnumValue::name() const { @@ -2478,7 +2495,7 @@ void EnumValue::InternalSwap(EnumValue* other) { } // optional int32 number = 2; - void EnumValue::clear_number() { +void EnumValue::clear_number() { number_ = 0; } ::google::protobuf::int32 EnumValue::number() const { @@ -2492,10 +2509,10 @@ void EnumValue::InternalSwap(EnumValue* other) { } // repeated .google.protobuf.Option options = 3; - int EnumValue::options_size() const { +int EnumValue::options_size() const { return options_.size(); } - void EnumValue::clear_options() { +void EnumValue::clear_options() { options_.Clear(); } const ::google::protobuf::Option& EnumValue::options(int index) const { @@ -2531,7 +2548,7 @@ const int Option::kValueFieldNumber; #endif // !_MSC_VER Option::Option() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.Option) } @@ -2596,7 +2613,7 @@ Option* Option::New(::google::protobuf::Arena* arena) const { void Option::Clear() { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); - if (value_ != NULL) delete value_; + if (GetArenaNoVirtual() == NULL && value_ != NULL) delete value_; value_ = NULL; } @@ -2732,9 +2749,9 @@ int Option::ByteSize() const { void Option::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const Option* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const Option* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -2793,7 +2810,7 @@ void Option::InternalSwap(Option* other) { // Option // optional string name = 1; - void Option::clear_name() { +void Option::clear_name() { name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } const ::std::string& Option::name() const { @@ -2836,11 +2853,11 @@ void Option::InternalSwap(Option* other) { } // optional .google.protobuf.Any value = 2; - bool Option::has_value() const { +bool Option::has_value() const { return !_is_default_instance_ && value_ != NULL; } - void Option::clear_value() { - if (value_ != NULL) delete value_; +void Option::clear_value() { + if (GetArenaNoVirtual() == NULL && value_ != NULL) delete value_; value_ = NULL; } const ::google::protobuf::Any& Option::value() const { diff --git a/src/google/protobuf/type.pb.h b/src/google/protobuf/type.pb.h index a14edd4a..c9952efa 100644 --- a/src/google/protobuf/type.pb.h +++ b/src/google/protobuf/type.pb.h @@ -936,7 +936,7 @@ inline bool Type::has_source_context() const { return !_is_default_instance_ && source_context_ != NULL; } inline void Type::clear_source_context() { - if (source_context_ != NULL) delete source_context_; + if (GetArenaNoVirtual() == NULL && source_context_ != NULL) delete source_context_; source_context_ = NULL; } inline const ::google::protobuf::SourceContext& Type::source_context() const { @@ -1270,7 +1270,7 @@ inline bool Enum::has_source_context() const { return !_is_default_instance_ && source_context_ != NULL; } inline void Enum::clear_source_context() { - if (source_context_ != NULL) delete source_context_; + if (GetArenaNoVirtual() == NULL && source_context_ != NULL) delete source_context_; source_context_ = NULL; } inline const ::google::protobuf::SourceContext& Enum::source_context() const { @@ -1445,7 +1445,7 @@ inline bool Option::has_value() const { return !_is_default_instance_ && value_ != NULL; } inline void Option::clear_value() { - if (value_ != NULL) delete value_; + if (GetArenaNoVirtual() == NULL && value_ != NULL) delete value_; value_ = NULL; } inline const ::google::protobuf::Any& Option::value() const { @@ -1478,6 +1478,14 @@ inline void Option::set_allocated_value(::google::protobuf::Any* value) { } #endif // !PROTOBUF_INLINE_NOT_IN_HEADERS +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + // @@protoc_insertion_point(namespace_scope) diff --git a/src/google/protobuf/unittest.proto b/src/google/protobuf/unittest.proto index a32291e0..834c9b56 100644 --- a/src/google/protobuf/unittest.proto +++ b/src/google/protobuf/unittest.proto @@ -185,6 +185,7 @@ message TestAllTypes { message NestedTestAllTypes { optional NestedTestAllTypes child = 1; optional TestAllTypes payload = 2; + repeated NestedTestAllTypes repeated_child = 3; } message TestDeprecatedFields { @@ -203,6 +204,11 @@ enum ForeignEnum { FOREIGN_BAZ = 6; } +message TestReservedFields { + reserved 2, 15, 9 to 11; + reserved "bar", "baz"; +} + message TestAllExtensions { extensions 1 to max; } diff --git a/src/google/protobuf/unittest_drop_unknown_fields.proto b/src/google/protobuf/unittest_drop_unknown_fields.proto index 1b35fad0..faaddc6e 100644 --- a/src/google/protobuf/unittest_drop_unknown_fields.proto +++ b/src/google/protobuf/unittest_drop_unknown_fields.proto @@ -41,8 +41,8 @@ message Foo { BAR = 1; BAZ = 2; } - optional int32 int32_value = 1; - optional NestedEnum enum_value = 2; + int32 int32_value = 1; + NestedEnum enum_value = 2; } message FooWithExtraFields { @@ -52,7 +52,7 @@ message FooWithExtraFields { BAZ = 2; QUX = 3; } - optional int32 int32_value = 1; - optional NestedEnum enum_value = 2; - optional int32 extra_int32_value = 3; + int32 int32_value = 1; + NestedEnum enum_value = 2; + int32 extra_int32_value = 3; } diff --git a/src/google/protobuf/unittest_no_field_presence.proto b/src/google/protobuf/unittest_no_field_presence.proto index f261b58e..f5cc4cc3 100644 --- a/src/google/protobuf/unittest_no_field_presence.proto +++ b/src/google/protobuf/unittest_no_field_presence.proto @@ -43,7 +43,7 @@ option csharp_namespace = "Google.ProtocolBuffers.TestProtos.Proto3"; // forms. message TestAllTypes { message NestedMessage { - optional int32 bb = 1; + int32 bb = 1; } enum NestedEnum { @@ -55,36 +55,36 @@ message TestAllTypes { // Singular // TODO: remove 'optional' labels as soon as CL 69188077 is LGTM'd to make // 'optional' optional. - optional int32 optional_int32 = 1; - optional int64 optional_int64 = 2; - optional uint32 optional_uint32 = 3; - optional uint64 optional_uint64 = 4; - optional sint32 optional_sint32 = 5; - optional sint64 optional_sint64 = 6; - optional fixed32 optional_fixed32 = 7; - optional fixed64 optional_fixed64 = 8; - optional sfixed32 optional_sfixed32 = 9; - optional sfixed64 optional_sfixed64 = 10; - optional float optional_float = 11; - optional double optional_double = 12; - optional bool optional_bool = 13; - optional string optional_string = 14; - optional bytes optional_bytes = 15; - - optional NestedMessage optional_nested_message = 18; - optional ForeignMessage optional_foreign_message = 19; - optional protobuf_unittest.TestAllTypes optional_proto2_message = 20; - - optional NestedEnum optional_nested_enum = 21; - optional ForeignEnum optional_foreign_enum = 22; + int32 optional_int32 = 1; + int64 optional_int64 = 2; + uint32 optional_uint32 = 3; + uint64 optional_uint64 = 4; + sint32 optional_sint32 = 5; + sint64 optional_sint64 = 6; + fixed32 optional_fixed32 = 7; + fixed64 optional_fixed64 = 8; + sfixed32 optional_sfixed32 = 9; + sfixed64 optional_sfixed64 = 10; + float optional_float = 11; + double optional_double = 12; + bool optional_bool = 13; + string optional_string = 14; + bytes optional_bytes = 15; + + NestedMessage optional_nested_message = 18; + ForeignMessage optional_foreign_message = 19; + protobuf_unittest.TestAllTypes optional_proto2_message = 20; + + NestedEnum optional_nested_enum = 21; + ForeignEnum optional_foreign_enum = 22; // N.B.: proto2-enum-type fields not allowed, because their default values // might not be zero. //optional protobuf_unittest.ForeignEnum optional_proto2_enum = 23; - optional string optional_string_piece = 24 [ctype=STRING_PIECE]; - optional string optional_cord = 25 [ctype=CORD]; + string optional_string_piece = 24 [ctype=STRING_PIECE]; + string optional_cord = 25 [ctype=CORD]; - optional NestedMessage optional_lazy_message = 30 [lazy=true]; + NestedMessage optional_lazy_message = 30 [lazy=true]; // Repeated repeated int32 repeated_int32 = 31; @@ -124,13 +124,13 @@ message TestAllTypes { } message TestProto2Required { - optional protobuf_unittest.TestRequired proto2 = 1; + protobuf_unittest.TestRequired proto2 = 1; } // Define these after TestAllTypes to make sure the compiler can handle // that. message ForeignMessage { - optional int32 c = 1; + int32 c = 1; } enum ForeignEnum { diff --git a/src/google/protobuf/unittest_preserve_unknown_enum.proto b/src/google/protobuf/unittest_preserve_unknown_enum.proto index 24e6828f..abc3de28 100644 --- a/src/google/protobuf/unittest_preserve_unknown_enum.proto +++ b/src/google/protobuf/unittest_preserve_unknown_enum.proto @@ -49,7 +49,7 @@ enum MyEnumPlusExtra { } message MyMessage { - optional MyEnum e = 1; + MyEnum e = 1; repeated MyEnum repeated_e = 2; repeated MyEnum repeated_packed_e = 3 [packed=true]; repeated MyEnumPlusExtra repeated_packed_unexpected_e = 4; // not packed @@ -60,7 +60,7 @@ message MyMessage { } message MyMessagePlusExtra { - optional MyEnumPlusExtra e = 1; + MyEnumPlusExtra e = 1; repeated MyEnumPlusExtra repeated_e = 2; repeated MyEnumPlusExtra repeated_packed_e = 3 [packed=true]; repeated MyEnumPlusExtra repeated_packed_unexpected_e = 4 [packed=true]; diff --git a/src/google/protobuf/unittest_proto3_arena.proto b/src/google/protobuf/unittest_proto3_arena.proto index 6d7bada8..b835a6ba 100644 --- a/src/google/protobuf/unittest_proto3_arena.proto +++ b/src/google/protobuf/unittest_proto3_arena.proto @@ -43,7 +43,7 @@ message TestAllTypes { // The field name "b" fails to compile in proto1 because it conflicts with // a local variable named "b" in one of the generated methods. Doh. // This file needs to compile in proto1 to test backwards-compatibility. - optional int32 bb = 1; + int32 bb = 1; } enum NestedEnum { @@ -55,46 +55,47 @@ message TestAllTypes { } // Singular - optional int32 optional_int32 = 1; - optional int64 optional_int64 = 2; - optional uint32 optional_uint32 = 3; - optional uint64 optional_uint64 = 4; - optional sint32 optional_sint32 = 5; - optional sint64 optional_sint64 = 6; - optional fixed32 optional_fixed32 = 7; - optional fixed64 optional_fixed64 = 8; - optional sfixed32 optional_sfixed32 = 9; - optional sfixed64 optional_sfixed64 = 10; - optional float optional_float = 11; - optional double optional_double = 12; - optional bool optional_bool = 13; - optional string optional_string = 14; - optional bytes optional_bytes = 15; - - optional group OptionalGroup = 16 { - optional int32 a = 17; - } - - optional NestedMessage optional_nested_message = 18; - optional ForeignMessage optional_foreign_message = 19; - optional protobuf_unittest_import.ImportMessage optional_import_message = 20; - - optional NestedEnum optional_nested_enum = 21; - optional ForeignEnum optional_foreign_enum = 22; + int32 optional_int32 = 1; + int64 optional_int64 = 2; + uint32 optional_uint32 = 3; + uint64 optional_uint64 = 4; + sint32 optional_sint32 = 5; + sint64 optional_sint64 = 6; + fixed32 optional_fixed32 = 7; + fixed64 optional_fixed64 = 8; + sfixed32 optional_sfixed32 = 9; + sfixed64 optional_sfixed64 = 10; + float optional_float = 11; + double optional_double = 12; + bool optional_bool = 13; + string optional_string = 14; + bytes optional_bytes = 15; + + // Groups are not allowed in proto3. + // optional group OptionalGroup = 16 { + // optional int32 a = 17; + // } + + NestedMessage optional_nested_message = 18; + ForeignMessage optional_foreign_message = 19; + protobuf_unittest_import.ImportMessage optional_import_message = 20; + + NestedEnum optional_nested_enum = 21; + ForeignEnum optional_foreign_enum = 22; // Omitted (compared to unittest.proto) because proto2 enums are not allowed // inside proto2 messages. // // optional protobuf_unittest_import.ImportEnum optional_import_enum = 23; - optional string optional_string_piece = 24 [ctype=STRING_PIECE]; - optional string optional_cord = 25 [ctype=CORD]; + string optional_string_piece = 24 [ctype=STRING_PIECE]; + string optional_cord = 25 [ctype=CORD]; // Defined in unittest_import_public.proto - optional protobuf_unittest_import.PublicImportMessage + protobuf_unittest_import.PublicImportMessage optional_public_import_message = 26; - optional NestedMessage optional_lazy_message = 27 [lazy=true]; + NestedMessage optional_lazy_message = 27 [lazy=true]; // Repeated repeated int32 repeated_int32 = 31; @@ -113,9 +114,10 @@ message TestAllTypes { repeated string repeated_string = 44; repeated bytes repeated_bytes = 45; - repeated group RepeatedGroup = 46 { - optional int32 a = 47; - } + // Groups are not allowed in proto3. + // repeated group RepeatedGroup = 46 { + // optional int32 a = 47; + // } repeated NestedMessage repeated_nested_message = 48; repeated ForeignMessage repeated_foreign_message = 49; @@ -161,6 +163,24 @@ message TestPackedTypes { repeated ForeignEnum packed_enum = 103 [packed = true]; } +// Explicitly set packed to false +message TestUnpackedTypes { + repeated int32 repeated_int32 = 1 [packed = false]; + repeated int64 repeated_int64 = 2 [packed = false]; + repeated uint32 repeated_uint32 = 3 [packed = false]; + repeated uint64 repeated_uint64 = 4 [packed = false]; + repeated sint32 repeated_sint32 = 5 [packed = false]; + repeated sint64 repeated_sint64 = 6 [packed = false]; + repeated fixed32 repeated_fixed32 = 7 [packed = false]; + repeated fixed64 repeated_fixed64 = 8 [packed = false]; + repeated sfixed32 repeated_sfixed32 = 9 [packed = false]; + repeated sfixed64 repeated_sfixed64 = 10 [packed = false]; + repeated float repeated_float = 11 [packed = false]; + repeated double repeated_double = 12 [packed = false]; + repeated bool repeated_bool = 13 [packed = false]; + repeated TestAllTypes.NestedEnum repeated_nested_enum = 14 [packed = false]; +} + // This proto includes a recusively nested message. message NestedTestAllTypes { NestedTestAllTypes child = 1; @@ -170,7 +190,7 @@ message NestedTestAllTypes { // Define these after TestAllTypes to make sure the compiler can handle // that. message ForeignMessage { - optional int32 c = 1; + int32 c = 1; } enum ForeignEnum { diff --git a/src/google/protobuf/unknown_field_set.cc b/src/google/protobuf/unknown_field_set.cc index e15280c8..76644900 100644 --- a/src/google/protobuf/unknown_field_set.cc +++ b/src/google/protobuf/unknown_field_set.cc @@ -93,7 +93,7 @@ void UnknownFieldSet::InternalMergeFrom(const UnknownFieldSet& other) { fields_ = new vector(); for (int i = 0; i < other_field_count; i++) { fields_->push_back((*other.fields_)[i]); - fields_->back().DeepCopy(); + fields_->back().DeepCopy((*other.fields_)[i]); } } } @@ -104,7 +104,7 @@ void UnknownFieldSet::MergeFrom(const UnknownFieldSet& other) { if (fields_ == NULL) fields_ = new vector(); for (int i = 0; i < other_field_count; i++) { fields_->push_back((*other.fields_)[i]); - fields_->back().DeepCopy(); + fields_->back().DeepCopy((*other.fields_)[i]); } } } @@ -202,7 +202,7 @@ UnknownFieldSet* UnknownFieldSet::AddGroup(int number) { void UnknownFieldSet::AddField(const UnknownField& field) { if (fields_ == NULL) fields_ = new vector(); fields_->push_back(field); - fields_->back().DeepCopy(); + fields_->back().DeepCopy(field); } void UnknownFieldSet::DeleteSubrange(int start, int num) { @@ -303,7 +303,7 @@ void UnknownField::Reset() { } } -void UnknownField::DeepCopy() { +void UnknownField::DeepCopy(const UnknownField& other) { switch (type()) { case UnknownField::TYPE_LENGTH_DELIMITED: length_delimited_.string_value_ = new string( diff --git a/src/google/protobuf/unknown_field_set.h b/src/google/protobuf/unknown_field_set.h index 987b1979..6781cd0f 100644 --- a/src/google/protobuf/unknown_field_set.h +++ b/src/google/protobuf/unknown_field_set.h @@ -216,7 +216,7 @@ class LIBPROTOBUF_EXPORT UnknownField { void Reset(); // Make a deep copy of any pointers in this UnknownField. - void DeepCopy(); + void DeepCopy(const UnknownField& other); // Set the wire type of this UnknownField. Should only be used when this // UnknownField is being created. diff --git a/src/google/protobuf/wire_format.cc b/src/google/protobuf/wire_format.cc index c5bbbf2e..54cd653a 100644 --- a/src/google/protobuf/wire_format.cc +++ b/src/google/protobuf/wire_format.cc @@ -829,7 +829,7 @@ void WireFormat::SerializeFieldWithCachedSizes( count = 1; } - const bool is_packed = field->options().packed(); + const bool is_packed = field->is_packed(); if (is_packed && count > 0) { WireFormatLite::WriteTag(field->number(), WireFormatLite::WIRETYPE_LENGTH_DELIMITED, output); @@ -996,7 +996,7 @@ int WireFormat::FieldByteSize( const int data_size = FieldDataOnlyByteSize(field, message); int our_size = data_size; - if (field->options().packed()) { + if (field->is_packed()) { if (data_size > 0) { // Packed fields get serialized like a string, not their native type. // Technically this doesn't really matter; the size only changes if it's diff --git a/src/google/protobuf/wire_format_lite.h b/src/google/protobuf/wire_format_lite.h index 76bc75a1..ac83abdc 100644 --- a/src/google/protobuf/wire_format_lite.h +++ b/src/google/protobuf/wire_format_lite.h @@ -330,6 +330,17 @@ class LIBPROTOBUF_EXPORT WireFormatLite { template static inline bool ReadMessageNoVirtual(input, MessageType* value); + // The same, but do not modify input's recursion depth. This is useful + // when reading a bunch of groups or messages in a loop, because then the + // recursion depth can be incremented before the loop and decremented after. + template + static inline bool ReadGroupNoVirtualNoRecursionDepth(field_number, input, + MessageType* value); + + template + static inline bool ReadMessageNoVirtualNoRecursionDepth(input, + MessageType* value); + // Write a tag. The Write*() functions typically include the tag, so // normally there's no need to call this unless using the Write*NoTag() // variants. diff --git a/src/google/protobuf/wire_format_lite_inl.h b/src/google/protobuf/wire_format_lite_inl.h index 129fc63f..d073ff92 100644 --- a/src/google/protobuf/wire_format_lite_inl.h +++ b/src/google/protobuf/wire_format_lite_inl.h @@ -470,7 +470,7 @@ inline bool WireFormatLite::ReadGroupNoVirtual( if (!value-> MessageType_WorkAroundCppLookupDefect::MergePartialFromCodedStream(input)) return false; - input->DecrementRecursionDepth(); + input->UnsafeDecrementRecursionDepth(); // Make sure the last thing read was an end tag for this group. if (!input->LastTagWas(MakeTag(field_number, WIRETYPE_END_GROUP))) { return false; @@ -478,6 +478,14 @@ inline bool WireFormatLite::ReadGroupNoVirtual( return true; } template +inline bool WireFormatLite::ReadGroupNoVirtualNoRecursionDepth( + int field_number, io::CodedInputStream* input, + MessageType_WorkAroundCppLookupDefect* value) { + return value->MessageType_WorkAroundCppLookupDefect:: + MergePartialFromCodedStream(input) && + input->LastTagWas(MakeTag(field_number, WIRETYPE_END_GROUP)); +} +template inline bool WireFormatLite::ReadMessageNoVirtual( io::CodedInputStream* input, MessageType_WorkAroundCppLookupDefect* value) { uint32 length; @@ -491,6 +499,17 @@ inline bool WireFormatLite::ReadMessageNoVirtual( // tag. return input->DecrementRecursionDepthAndPopLimit(p.first); } +template +inline bool WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( + io::CodedInputStream* input, MessageType_WorkAroundCppLookupDefect* value) { + io::CodedInputStream::Limit old_limit = input->ReadLengthAndPushLimit(); + if (!value-> + MessageType_WorkAroundCppLookupDefect::MergePartialFromCodedStream(input)) + return false; + // Make sure that parsing stopped when the limit was hit, not at an endgroup + // tag. + return input->CheckEntireMessageConsumedAndPopLimit(old_limit); +} // =================================================================== diff --git a/src/google/protobuf/wire_format_unittest.cc b/src/google/protobuf/wire_format_unittest.cc index a3062a6a..e80c5a71 100644 --- a/src/google/protobuf/wire_format_unittest.cc +++ b/src/google/protobuf/wire_format_unittest.cc @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -794,6 +795,137 @@ TEST(WireFormatTest, CompatibleTypes) { ASSERT_EQ(static_cast(data), msg5.data()); } +class Proto3PrimitiveRepeatedWireFormatTest + : public ::testing::TestWithParam { + protected: + template + void SetProto3PrimitiveRepeatedFields(Proto* message) { + message->add_repeated_int32(1); + message->add_repeated_int64(1); + message->add_repeated_uint32(1); + message->add_repeated_uint64(1); + message->add_repeated_sint32(1); + message->add_repeated_sint64(1); + message->add_repeated_fixed32(1); + message->add_repeated_fixed64(1); + message->add_repeated_sfixed32(1); + message->add_repeated_sfixed64(1); + message->add_repeated_float(1.0); + message->add_repeated_double(1.0); + message->add_repeated_bool(true); + message->add_repeated_nested_enum( + proto3_arena_unittest::TestAllTypes_NestedEnum_FOO); + } + + template + void ExpectProto3PrimitiveRepeatedFieldsSet(const Proto& message) { + EXPECT_EQ(1, message.repeated_int32(0)); + EXPECT_EQ(1, message.repeated_int64(0)); + EXPECT_EQ(1, message.repeated_uint32(0)); + EXPECT_EQ(1, message.repeated_uint64(0)); + EXPECT_EQ(1, message.repeated_sint32(0)); + EXPECT_EQ(1, message.repeated_sint64(0)); + EXPECT_EQ(1, message.repeated_fixed32(0)); + EXPECT_EQ(1, message.repeated_fixed64(0)); + EXPECT_EQ(1, message.repeated_sfixed32(0)); + EXPECT_EQ(1, message.repeated_sfixed64(0)); + EXPECT_EQ(1.0, message.repeated_float(0)); + EXPECT_EQ(1.0, message.repeated_double(0)); + EXPECT_EQ(true, message.repeated_bool(0)); + EXPECT_EQ(proto3_arena_unittest::TestAllTypes_NestedEnum_FOO, + message.repeated_nested_enum(0)); + } + + template + void TestProto3PrimitiveRepeatedFields(Proto* message, + const string& expected) { + SetProto3PrimitiveRepeatedFields(message); + + int size = message->ByteSize(); + + // Serialize using the generated code. + string generated_data; + { + io::StringOutputStream raw_output(&generated_data); + io::CodedOutputStream output(&raw_output); + message->SerializeWithCachedSizes(&output); + ASSERT_FALSE(output.HadError()); + } + + EXPECT_TRUE(expected == generated_data); + + message->Clear(); + message->ParseFromString(generated_data); + ExpectProto3PrimitiveRepeatedFieldsSet(*message); + + // Serialize using the dynamic code. + string dynamic_data; + { + io::StringOutputStream raw_output(&dynamic_data); + io::CodedOutputStream output(&raw_output); + WireFormat::SerializeWithCachedSizes(*message, size, &output); + ASSERT_FALSE(output.HadError()); + } + + EXPECT_TRUE(expected == dynamic_data); + + message->Clear(); + io::CodedInputStream input( + reinterpret_cast(dynamic_data.data()), + dynamic_data.size()); + WireFormat::ParseAndMergePartial(&input, message); + ExpectProto3PrimitiveRepeatedFieldsSet(*message); + } +}; +INSTANTIATE_TEST_CASE_P(SetPacked, + Proto3PrimitiveRepeatedWireFormatTest, + ::testing::Values(false, true)); + +TEST_P(Proto3PrimitiveRepeatedWireFormatTest, Proto3PrimitiveRepeated) { + proto3_arena_unittest::TestAllTypes packed_message; + proto3_arena_unittest::TestUnpackedTypes unpacked_message; + + const string packedExpected( + "\xFA\x01\x01\x01" + "\x82\x02\x01\x01" + "\x8A\x02\x01\x01" + "\x92\x02\x01\x01" + "\x9A\x02\x01\x02" + "\xA2\x02\x01\x02" + "\xAA\x02\x04\x01\x00\x00\x00" + "\xB2\x02\x08\x01\x00\x00\x00\x00\x00\x00\x00" + "\xBA\x02\x04\x01\x00\x00\x00" + "\xC2\x02\x08\x01\x00\x00\x00\x00\x00\x00\x00" + "\xCA\x02\x04\x00\x00\x80\x3f" + "\xD2\x02\x08\x00\x00\x00\x00\x00\x00\xf0\x3f" + "\xDA\x02\x01\x01" + "\x9A\x03\x01\x01", + 86); + + const string unpackedExpected( + "\x08\x01" + "\x10\x01" + "\x18\x01" + "\x20\x01" + "\x28\x02" + "\x30\x02" + "\x3D\x01\x00\x00\x00" + "\x41\x01\x00\x00\x00\x00\x00\x00\x00" + "\x4D\x01\x00\x00\x00" + "\x51\x01\x00\x00\x00\x00\x00\x00\x00" + "\x5D\x00\x00\x80\x3f" + "\x61\x00\x00\x00\x00\x00\x00\xf0\x3f" + "\x68\x01" + "\x70\x01", + 58); + + if (GetParam()) { + TestProto3PrimitiveRepeatedFields(&packed_message, packedExpected); + } else { + TestProto3PrimitiveRepeatedFields(&unpacked_message, unpackedExpected); + } +} + class WireFormatInvalidInputTest : public testing::Test { protected: // Make a serialized TestAllTypes in which the field optional_nested_message diff --git a/src/google/protobuf/wrappers.pb.cc b/src/google/protobuf/wrappers.pb.cc index 4d91ed56..ffc77f1c 100644 --- a/src/google/protobuf/wrappers.pb.cc +++ b/src/google/protobuf/wrappers.pb.cc @@ -312,7 +312,7 @@ const int DoubleValue::kValueFieldNumber; #endif // !_MSC_VER DoubleValue::DoubleValue() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.DoubleValue) } @@ -458,9 +458,9 @@ int DoubleValue::ByteSize() const { void DoubleValue::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const DoubleValue* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const DoubleValue* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -514,7 +514,7 @@ void DoubleValue::InternalSwap(DoubleValue* other) { // DoubleValue // optional double value = 1; - void DoubleValue::clear_value() { +void DoubleValue::clear_value() { value_ = 0; } double DoubleValue::value() const { @@ -536,7 +536,7 @@ const int FloatValue::kValueFieldNumber; #endif // !_MSC_VER FloatValue::FloatValue() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.FloatValue) } @@ -682,9 +682,9 @@ int FloatValue::ByteSize() const { void FloatValue::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const FloatValue* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const FloatValue* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -738,7 +738,7 @@ void FloatValue::InternalSwap(FloatValue* other) { // FloatValue // optional float value = 1; - void FloatValue::clear_value() { +void FloatValue::clear_value() { value_ = 0; } float FloatValue::value() const { @@ -760,7 +760,7 @@ const int Int64Value::kValueFieldNumber; #endif // !_MSC_VER Int64Value::Int64Value() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.Int64Value) } @@ -908,9 +908,9 @@ int Int64Value::ByteSize() const { void Int64Value::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const Int64Value* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const Int64Value* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -964,7 +964,7 @@ void Int64Value::InternalSwap(Int64Value* other) { // Int64Value // optional int64 value = 1; - void Int64Value::clear_value() { +void Int64Value::clear_value() { value_ = GOOGLE_LONGLONG(0); } ::google::protobuf::int64 Int64Value::value() const { @@ -986,7 +986,7 @@ const int UInt64Value::kValueFieldNumber; #endif // !_MSC_VER UInt64Value::UInt64Value() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.UInt64Value) } @@ -1134,9 +1134,9 @@ int UInt64Value::ByteSize() const { void UInt64Value::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const UInt64Value* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const UInt64Value* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -1190,7 +1190,7 @@ void UInt64Value::InternalSwap(UInt64Value* other) { // UInt64Value // optional uint64 value = 1; - void UInt64Value::clear_value() { +void UInt64Value::clear_value() { value_ = GOOGLE_ULONGLONG(0); } ::google::protobuf::uint64 UInt64Value::value() const { @@ -1212,7 +1212,7 @@ const int Int32Value::kValueFieldNumber; #endif // !_MSC_VER Int32Value::Int32Value() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.Int32Value) } @@ -1360,9 +1360,9 @@ int Int32Value::ByteSize() const { void Int32Value::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const Int32Value* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const Int32Value* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -1416,7 +1416,7 @@ void Int32Value::InternalSwap(Int32Value* other) { // Int32Value // optional int32 value = 1; - void Int32Value::clear_value() { +void Int32Value::clear_value() { value_ = 0; } ::google::protobuf::int32 Int32Value::value() const { @@ -1438,7 +1438,7 @@ const int UInt32Value::kValueFieldNumber; #endif // !_MSC_VER UInt32Value::UInt32Value() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.UInt32Value) } @@ -1586,9 +1586,9 @@ int UInt32Value::ByteSize() const { void UInt32Value::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const UInt32Value* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const UInt32Value* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -1642,7 +1642,7 @@ void UInt32Value::InternalSwap(UInt32Value* other) { // UInt32Value // optional uint32 value = 1; - void UInt32Value::clear_value() { +void UInt32Value::clear_value() { value_ = 0u; } ::google::protobuf::uint32 UInt32Value::value() const { @@ -1664,7 +1664,7 @@ const int BoolValue::kValueFieldNumber; #endif // !_MSC_VER BoolValue::BoolValue() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.BoolValue) } @@ -1810,9 +1810,9 @@ int BoolValue::ByteSize() const { void BoolValue::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const BoolValue* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const BoolValue* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -1866,7 +1866,7 @@ void BoolValue::InternalSwap(BoolValue* other) { // BoolValue // optional bool value = 1; - void BoolValue::clear_value() { +void BoolValue::clear_value() { value_ = false; } bool BoolValue::value() const { @@ -1888,7 +1888,7 @@ const int StringValue::kValueFieldNumber; #endif // !_MSC_VER StringValue::StringValue() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.StringValue) } @@ -2051,9 +2051,9 @@ int StringValue::ByteSize() const { void StringValue::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const StringValue* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const StringValue* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -2108,7 +2108,7 @@ void StringValue::InternalSwap(StringValue* other) { // StringValue // optional string value = 1; - void StringValue::clear_value() { +void StringValue::clear_value() { value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } const ::std::string& StringValue::value() const { @@ -2159,7 +2159,7 @@ const int BytesValue::kValueFieldNumber; #endif // !_MSC_VER BytesValue::BytesValue() - : ::google::protobuf::Message() , _internal_metadata_(NULL) { + : ::google::protobuf::Message(), _internal_metadata_(NULL) { SharedCtor(); // @@protoc_insertion_point(constructor:google.protobuf.BytesValue) } @@ -2310,9 +2310,9 @@ int BytesValue::ByteSize() const { void BytesValue::MergeFrom(const ::google::protobuf::Message& from) { if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); - const BytesValue* source = - ::google::protobuf::internal::dynamic_cast_if_available( - &from); + const BytesValue* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); if (source == NULL) { ::google::protobuf::internal::ReflectionOps::Merge(from, this); } else { @@ -2367,7 +2367,7 @@ void BytesValue::InternalSwap(BytesValue* other) { // BytesValue // optional bytes value = 1; - void BytesValue::clear_value() { +void BytesValue::clear_value() { value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } const ::std::string& BytesValue::value() const { diff --git a/src/google/protobuf/wrappers.pb.h b/src/google/protobuf/wrappers.pb.h index c318f950..387ebd7c 100644 --- a/src/google/protobuf/wrappers.pb.h +++ b/src/google/protobuf/wrappers.pb.h @@ -984,6 +984,22 @@ inline void BytesValue::set_allocated_value(::std::string* value) { } #endif // !PROTOBUF_INLINE_NOT_IN_HEADERS +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + // @@protoc_insertion_point(namespace_scope) diff --git a/vsprojects/libprotoc.vcproj b/vsprojects/libprotoc.vcproj index d1f065e2..3474d21f 100644 --- a/vsprojects/libprotoc.vcproj +++ b/vsprojects/libprotoc.vcproj @@ -295,6 +295,10 @@ RelativePath="..\src\google\protobuf\compiler\java\java_enum_field.h" > + + @@ -323,14 +327,34 @@ RelativePath="..\src\google\protobuf\compiler\java\java_lazy_message_field.h" > + + + + + + + + + + @@ -339,6 +363,10 @@ RelativePath="..\src\google\protobuf\compiler\java\java_primitive_field.h" > + + @@ -351,6 +379,10 @@ RelativePath="..\src\google\protobuf\compiler\java\java_string_field.h" > + + @@ -607,18 +639,42 @@ RelativePath="..\src\google\protobuf\compiler\java\java_lazy_message_field.cc" > + + + + + + + + + + + + @@ -627,6 +683,10 @@ RelativePath="..\src\google\protobuf\compiler\java\java_primitive_field.cc" > + + @@ -639,6 +699,10 @@ RelativePath="..\src\google\protobuf\compiler\java\java_string_field.cc" > + + -- cgit v1.2.3 From 9839c0c2c964f2c3881be042758536e9bd68bd8a Mon Sep 17 00:00:00 2001 From: teboring Date: Fri, 22 May 2015 22:22:21 -0700 Subject: Update version number to 3.0.0-alpha-3 --- configure.ac | 2 +- java/pom.xml | 4 ++-- javanano/pom.xml | 4 ++-- protoc-artifacts/pom.xml | 2 +- python/google/protobuf/__init__.py | 2 +- ruby/Gemfile.lock | 2 +- ruby/google-protobuf.gemspec | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) (limited to 'ruby') diff --git a/configure.ac b/configure.ac index 8338c18d..92bc0471 100644 --- a/configure.ac +++ b/configure.ac @@ -12,7 +12,7 @@ AC_PREREQ(2.59) # In the SVN trunk, the version should always be the next anticipated release # version with the "-pre" suffix. (We used to use "-SNAPSHOT" but this pushed # the size of one file name in the dist tarfile over the 99-char limit.) -AC_INIT([Protocol Buffers],[3.0.0-alpha-3-pre],[protobuf@googlegroups.com],[protobuf]) +AC_INIT([Protocol Buffers],[3.0.0-alpha-3],[protobuf@googlegroups.com],[protobuf]) AM_MAINTAINER_MODE([enable]) diff --git a/java/pom.xml b/java/pom.xml index 112bd9c3..cb41977c 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -10,7 +10,7 @@ com.google.protobuf protobuf-java - 3.0.0-alpha-3-pre + 3.0.0-alpha-3 bundle Protocol Buffer Java API @@ -164,7 +164,7 @@ https://developers.google.com/protocol-buffers/ com.google.protobuf - com.google.protobuf;version=3.0.0-alpha-3-pre + com.google.protobuf;version=3.0.0-alpha-3 diff --git a/javanano/pom.xml b/javanano/pom.xml index 3d8cfb9f..73496779 100644 --- a/javanano/pom.xml +++ b/javanano/pom.xml @@ -10,7 +10,7 @@ com.google.protobuf.nano protobuf-javanano - 3.0.0-alpha-3-pre + 3.0.0-alpha-3 bundle Protocol Buffer JavaNano API @@ -165,7 +165,7 @@ https://developers.google.com/protocol-buffers/ com.google.protobuf - com.google.protobuf;version=3.0.0-alpha-3-pre + com.google.protobuf;version=3.0.0-alpha-3 diff --git a/protoc-artifacts/pom.xml b/protoc-artifacts/pom.xml index fad81b57..28ba0049 100644 --- a/protoc-artifacts/pom.xml +++ b/protoc-artifacts/pom.xml @@ -10,7 +10,7 @@ com.google.protobuf protoc - 3.0.0-alpha-3-pre + 3.0.0-alpha-3 pom Protobuf Compiler diff --git a/python/google/protobuf/__init__.py b/python/google/protobuf/__init__.py index 1345bd5f..46d29fa4 100755 --- a/python/google/protobuf/__init__.py +++ b/python/google/protobuf/__init__.py @@ -32,4 +32,4 @@ # # Copyright 2007 Google Inc. All Rights Reserved. -__version__ = '3.0.0a3.dev0' +__version__ = '3.0.0a3' diff --git a/ruby/Gemfile.lock b/ruby/Gemfile.lock index 6f349276..2cf5e472 100644 --- a/ruby/Gemfile.lock +++ b/ruby/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - google-protobuf (3.0.0.alpha.3.1.pre) + google-protobuf (3.0.0.alpha.3) GEM remote: https://rubygems.org/ diff --git a/ruby/google-protobuf.gemspec b/ruby/google-protobuf.gemspec index 28cdebf5..3f98a37b 100644 --- a/ruby/google-protobuf.gemspec +++ b/ruby/google-protobuf.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |s| s.name = "google-protobuf" - s.version = "3.0.0.alpha.3.1.pre" + s.version = "3.0.0.alpha.3" s.licenses = ["BSD"] s.summary = "Protocol Buffers" s.description = "Protocol Buffers are Google's data interchange format." -- cgit v1.2.3 From e107e2d68edb550bc7763db2cbbc463ae264dcce Mon Sep 17 00:00:00 2001 From: Bo Yang Date: Fri, 29 May 2015 11:00:57 -0700 Subject: Update version number to 3.0.0-alpha-4 --- configure.ac | 2 +- java/pom.xml | 2 +- javanano/pom.xml | 2 +- protoc-artifacts/pom.xml | 2 +- python/google/protobuf/__init__.py | 2 +- ruby/google-protobuf.gemspec | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) (limited to 'ruby') diff --git a/configure.ac b/configure.ac index 4e1c0d24..8018cc75 100644 --- a/configure.ac +++ b/configure.ac @@ -12,7 +12,7 @@ AC_PREREQ(2.59) # In the SVN trunk, the version should always be the next anticipated release # version with the "-pre" suffix. (We used to use "-SNAPSHOT" but this pushed # the size of one file name in the dist tarfile over the 99-char limit.) -AC_INIT([Protocol Buffers],[3.0.0-alpha-3],[protobuf@googlegroups.com],[protobuf]) +AC_INIT([Protocol Buffers],[3.0.0-alpha-4-pre],[protobuf@googlegroups.com],[protobuf]) AM_MAINTAINER_MODE([enable]) diff --git a/java/pom.xml b/java/pom.xml index cb41977c..f0969db7 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -10,7 +10,7 @@ com.google.protobuf protobuf-java - 3.0.0-alpha-3 + 3.0.0-alpha-4-pre bundle Protocol Buffer Java API diff --git a/javanano/pom.xml b/javanano/pom.xml index 73496779..3b3813ab 100644 --- a/javanano/pom.xml +++ b/javanano/pom.xml @@ -10,7 +10,7 @@ com.google.protobuf.nano protobuf-javanano - 3.0.0-alpha-3 + 3.0.0-alpha-4-pre bundle Protocol Buffer JavaNano API diff --git a/protoc-artifacts/pom.xml b/protoc-artifacts/pom.xml index 28ba0049..52a7d2d6 100644 --- a/protoc-artifacts/pom.xml +++ b/protoc-artifacts/pom.xml @@ -10,7 +10,7 @@ com.google.protobuf protoc - 3.0.0-alpha-3 + 3.0.0-alpha-4-pre pom Protobuf Compiler diff --git a/python/google/protobuf/__init__.py b/python/google/protobuf/__init__.py index 46d29fa4..3f67dfc7 100755 --- a/python/google/protobuf/__init__.py +++ b/python/google/protobuf/__init__.py @@ -32,4 +32,4 @@ # # Copyright 2007 Google Inc. All Rights Reserved. -__version__ = '3.0.0a3' +__version__ = '3.0.0a4.dev0' diff --git a/ruby/google-protobuf.gemspec b/ruby/google-protobuf.gemspec index 3f98a37b..a9e570ec 100644 --- a/ruby/google-protobuf.gemspec +++ b/ruby/google-protobuf.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |s| s.name = "google-protobuf" - s.version = "3.0.0.alpha.3" + s.version = "3.0.0.alpha.4.0.pre" s.licenses = ["BSD"] s.summary = "Protocol Buffers" s.description = "Protocol Buffers are Google's data interchange format." -- cgit v1.2.3 From e8ed021ee717cb8cc4e9de68e61d4dae2e1dd832 Mon Sep 17 00:00:00 2001 From: Josh Haberman Date: Mon, 8 Jun 2015 17:56:03 -0700 Subject: Updated upb to latest version (C89). Since this version of upb supports C89, all of the extra compiler flags are no longer required. --- ruby/ext/google/protobuf_c/extconf.rb | 4 +- ruby/ext/google/protobuf_c/map.c | 2 +- ruby/ext/google/protobuf_c/upb.c | 4894 +++++++++--------- ruby/ext/google/protobuf_c/upb.h | 8748 +++++++++++++++++---------------- 4 files changed, 7012 insertions(+), 6636 deletions(-) (limited to 'ruby') diff --git a/ruby/ext/google/protobuf_c/extconf.rb b/ruby/ext/google/protobuf_c/extconf.rb index 74203b07..9675f57f 100644 --- a/ruby/ext/google/protobuf_c/extconf.rb +++ b/ruby/ext/google/protobuf_c/extconf.rb @@ -2,9 +2,7 @@ require 'mkmf' -$CFLAGS += " -O3 -std=c99 -Wno-unused-function " + - "-Wno-declaration-after-statement -Wno-unused-variable " + - "-Wno-sign-compare -DNDEBUG " +$CFLAGS += " -O3 -DNDEBUG" $objs = ["protobuf.o", "defs.o", "storage.o", "message.o", "repeated_field.o", "map.o", "encode_decode.o", "upb.o"] diff --git a/ruby/ext/google/protobuf_c/map.c b/ruby/ext/google/protobuf_c/map.c index 12e7a9d9..3436e09a 100644 --- a/ruby/ext/google/protobuf_c/map.c +++ b/ruby/ext/google/protobuf_c/map.c @@ -120,7 +120,7 @@ static VALUE table_key_to_ruby(Map* self, const char* buf, size_t length) { } static void* value_memory(upb_value* v) { - return (void*)(&v->val.uint64); + return (void*)(&v->val); } // ----------------------------------------------------------------------------- diff --git a/ruby/ext/google/protobuf_c/upb.c b/ruby/ext/google/protobuf_c/upb.c index 55a99cfe..f50ff6ad 100644 --- a/ruby/ext/google/protobuf_c/upb.c +++ b/ruby/ext/google/protobuf_c/upb.c @@ -13,7 +13,7 @@ typedef struct { size_t len; - char str[1]; // Null-terminated string data follows. + char str[1]; /* Null-terminated string data follows. */ } str_t; static str_t *newstr(const char *data, size_t len) { @@ -27,7 +27,7 @@ static str_t *newstr(const char *data, size_t len) { static void freestr(str_t *s) { free(s); } -// isalpha() etc. from are locale-dependent, which we don't want. +/* isalpha() etc. from are locale-dependent, which we don't want. */ static bool upb_isbetween(char c, char low, char high) { return c >= low && c <= high; } @@ -42,7 +42,8 @@ static bool upb_isalphanum(char c) { static bool upb_isident(const char *str, size_t len, bool full, upb_status *s) { bool start = true; - for (size_t i = 0; i < len; i++) { + size_t i; + for (i = 0; i < len; i++) { char c = str[i]; if (c == '.') { if (start || !full) { @@ -87,39 +88,22 @@ bool upb_def_setfullname(upb_def *def, const char *fullname, upb_status *s) { upb_def *upb_def_dup(const upb_def *def, const void *o) { switch (def->type) { case UPB_DEF_MSG: - return UPB_UPCAST(upb_msgdef_dup(upb_downcast_msgdef(def), o)); + return upb_msgdef_upcast_mutable( + upb_msgdef_dup(upb_downcast_msgdef(def), o)); case UPB_DEF_FIELD: - return UPB_UPCAST(upb_fielddef_dup(upb_downcast_fielddef(def), o)); + return upb_fielddef_upcast_mutable( + upb_fielddef_dup(upb_downcast_fielddef(def), o)); case UPB_DEF_ENUM: - return UPB_UPCAST(upb_enumdef_dup(upb_downcast_enumdef(def), o)); + return upb_enumdef_upcast_mutable( + upb_enumdef_dup(upb_downcast_enumdef(def), o)); default: assert(false); return NULL; } } -bool upb_def_isfrozen(const upb_def *def) { - return upb_refcounted_isfrozen(UPB_UPCAST(def)); -} - -void upb_def_ref(const upb_def *def, const void *owner) { - upb_refcounted_ref(UPB_UPCAST(def), owner); -} - -void upb_def_unref(const upb_def *def, const void *owner) { - upb_refcounted_unref(UPB_UPCAST(def), owner); -} - -void upb_def_donateref(const upb_def *def, const void *from, const void *to) { - upb_refcounted_donateref(UPB_UPCAST(def), from, to); -} - -void upb_def_checkref(const upb_def *def, const void *owner) { - upb_refcounted_checkref(UPB_UPCAST(def), owner); -} - static bool upb_def_init(upb_def *def, upb_deftype_t type, const struct upb_refcounted_vtbl *vtbl, const void *owner) { - if (!upb_refcounted_init(UPB_UPCAST(def), vtbl, owner)) return false; + if (!upb_refcounted_init(upb_def_upcast_mutable(def), vtbl, owner)) return false; def->type = type; def->fullname = NULL; def->came_from_user = false; @@ -131,7 +115,7 @@ static void upb_def_uninit(upb_def *def) { } static const char *msgdef_name(const upb_msgdef *m) { - const char *name = upb_def_fullname(UPB_UPCAST(m)); + const char *name = upb_def_fullname(upb_msgdef_upcast(m)); return name ? name : "(anonymous)"; } @@ -154,13 +138,15 @@ static bool upb_validate_field(upb_fielddef *f, upb_status *s) { } if (upb_fielddef_hassubdef(f)) { + const upb_def *subdef; + if (f->subdef_is_symbolic) { upb_status_seterrf(s, "field '%s.%s' has not been resolved", msgdef_name(f->msg.def), upb_fielddef_name(f)); return false; } - const upb_def *subdef = upb_fielddef_subdef(f); + subdef = upb_fielddef_subdef(f); if (subdef == NULL) { upb_status_seterrf(s, "field %s.%s is missing required subdef", msgdef_name(f->msg.def), upb_fielddef_name(f)); @@ -179,14 +165,14 @@ static bool upb_validate_field(upb_fielddef *f, upb_status *s) { bool has_default_name = upb_fielddef_enumhasdefaultstr(f); bool has_default_number = upb_fielddef_enumhasdefaultint32(f); - // Previously verified by upb_validate_enumdef(). + /* Previously verified by upb_validate_enumdef(). */ assert(upb_enumdef_numvals(upb_fielddef_enumsubdef(f)) > 0); - // We've already validated that we have an associated enumdef and that it - // has at least one member, so at least one of these should be true. - // Because if the user didn't set anything, we'll pick up the enum's - // default, but if the user *did* set something we should at least pick up - // the one they set (int32 or string). + /* We've already validated that we have an associated enumdef and that it + * has at least one member, so at least one of these should be true. + * Because if the user didn't set anything, we'll pick up the enum's + * default, but if the user *did* set something we should at least pick up + * the one they set (int32 or string). */ assert(has_default_name || has_default_number); if (!has_default_name) { @@ -205,13 +191,13 @@ static bool upb_validate_field(upb_fielddef *f, upb_status *s) { return false; } - // Lift the effective numeric default into the field's default slot, in case - // we were only getting it "by reference" from the enumdef. + /* Lift the effective numeric default into the field's default slot, in case + * we were only getting it "by reference" from the enumdef. */ upb_fielddef_setdefaultint32(f, upb_fielddef_defaultint32(f)); } - // Ensure that MapEntry submessages only appear as repeated fields, not - // optional/required (singular) fields. + /* 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); @@ -238,8 +224,8 @@ static bool upb_validate_enumdef(const upb_enumdef *e, upb_status *s) { return true; } -// All submessage fields are lower than all other fields. -// Secondly, fields are increasing in order. +/* All submessage fields are lower than all other fields. + * Secondly, fields are increasing in order. */ uint32_t field_rank(const upb_fielddef *f) { uint32_t ret = upb_fielddef_number(f); const uint32_t high_bit = 1 << 30; @@ -256,14 +242,15 @@ int cmp_fields(const void *p1, const void *p2) { } static bool assign_msg_indices(upb_msgdef *m, upb_status *s) { - // Sort fields. upb internally relies on UPB_TYPE_MESSAGE fields having the - // lowest indexes, but we do not publicly guarantee this. + /* Sort fields. upb internally relies on UPB_TYPE_MESSAGE fields having the + * lowest indexes, but we do not publicly guarantee this. */ + upb_msg_field_iter j; + int i; + uint32_t selector; int n = upb_msgdef_numfields(m); upb_fielddef **fields = malloc(n * sizeof(*fields)); if (!fields) return false; - upb_msg_field_iter j; - int i; m->submsg_field_count = 0; for(i = 0, upb_msg_field_begin(&j, m); !upb_msg_field_done(&j); @@ -282,7 +269,7 @@ static bool assign_msg_indices(upb_msgdef *m, upb_status *s) { qsort(fields, n, sizeof(*fields), cmp_fields); - uint32_t selector = UPB_STATIC_SELECTOR_COUNT + m->submsg_field_count; + selector = UPB_STATIC_SELECTOR_COUNT + m->submsg_field_count; for (i = 0; i < n; i++) { upb_fielddef *f = fields[i]; f->index_ = i; @@ -292,38 +279,42 @@ static bool assign_msg_indices(upb_msgdef *m, upb_status *s) { m->selector_count = selector; #ifndef NDEBUG - // Verify that all selectors for the message are distinct. - // + { + /* Verify that all selectors for the message are distinct. */ #define TRY(type) \ - if (upb_handlers_getselector(f, type, &sel)) upb_inttable_insert(&t, sel, v); + if (upb_handlers_getselector(f, type, &sel)) upb_inttable_insert(&t, sel, v); - upb_inttable t; - upb_inttable_init(&t, UPB_CTYPE_BOOL); - upb_value v = upb_value_bool(true); - upb_selector_t sel; - upb_inttable_insert(&t, UPB_STARTMSG_SELECTOR, v); - upb_inttable_insert(&t, UPB_ENDMSG_SELECTOR, v); - 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); - TRY(UPB_HANDLER_INT64) - TRY(UPB_HANDLER_UINT32) - TRY(UPB_HANDLER_UINT64) - TRY(UPB_HANDLER_FLOAT) - TRY(UPB_HANDLER_DOUBLE) - TRY(UPB_HANDLER_BOOL) - TRY(UPB_HANDLER_STARTSTR) - TRY(UPB_HANDLER_STRING) - TRY(UPB_HANDLER_ENDSTR) - TRY(UPB_HANDLER_STARTSUBMSG) - TRY(UPB_HANDLER_ENDSUBMSG) - TRY(UPB_HANDLER_STARTSEQ) - TRY(UPB_HANDLER_ENDSEQ) - } - upb_inttable_uninit(&t); + upb_inttable t; + upb_value v; + upb_selector_t sel; + + upb_inttable_init(&t, UPB_CTYPE_BOOL); + v = upb_value_bool(true); + upb_inttable_insert(&t, UPB_STARTMSG_SELECTOR, v); + upb_inttable_insert(&t, UPB_ENDMSG_SELECTOR, v); + 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); + TRY(UPB_HANDLER_INT64) + TRY(UPB_HANDLER_UINT32) + TRY(UPB_HANDLER_UINT64) + TRY(UPB_HANDLER_FLOAT) + TRY(UPB_HANDLER_DOUBLE) + TRY(UPB_HANDLER_BOOL) + TRY(UPB_HANDLER_STARTSTR) + TRY(UPB_HANDLER_STRING) + TRY(UPB_HANDLER_ENDSTR) + TRY(UPB_HANDLER_STARTSUBMSG) + TRY(UPB_HANDLER_ENDSUBMSG) + TRY(UPB_HANDLER_STARTSEQ) + TRY(UPB_HANDLER_ENDSEQ) + } + upb_inttable_uninit(&t); + } #undef TRY #endif @@ -332,14 +323,17 @@ static bool assign_msg_indices(upb_msgdef *m, upb_status *s) { } bool upb_def_freeze(upb_def *const* defs, int n, upb_status *s) { + int i; + int maxdepth; + bool ret; upb_status_clear(s); - // First perform validation, in two passes so we can check that we have a - // transitive closure without needing to search. - for (int i = 0; i < n; i++) { + /* First perform validation, in two passes so we can check that we have a + * transitive closure without needing to search. */ + for (i = 0; i < n; i++) { upb_def *def = defs[i]; if (upb_def_isfrozen(def)) { - // Could relax this requirement if it's annoying. + /* Could relax this requirement if it's annoying. */ upb_status_seterrmsg(s, "def is already frozen"); goto err; } else if (def->type == UPB_DEF_FIELD) { @@ -350,14 +344,14 @@ bool upb_def_freeze(upb_def *const* defs, int n, upb_status *s) { goto err; } } else { - // Set now to detect transitive closure in the second pass. + /* Set now to detect transitive closure in the second pass. */ def->came_from_user = true; } } - // Second pass of validation. Also assign selector bases and indexes, and - // compact tables. - for (int i = 0; i < n; i++) { + /* Second pass of validation. Also assign selector bases and indexes, and + * compact tables. */ + for (i = 0; i < n; i++) { upb_msgdef *m = upb_dyncast_msgdef_mutable(defs[i]); upb_enumdef *e = upb_dyncast_enumdef_mutable(defs[i]); if (m) { @@ -370,17 +364,17 @@ bool upb_def_freeze(upb_def *const* defs, int n, upb_status *s) { } } - // Def graph contains FieldDefs between each MessageDef, so double the limit. - int maxdepth = UPB_MAX_MESSAGE_DEPTH * 2; + /* Def graph contains FieldDefs between each MessageDef, so double the + * limit. */ + maxdepth = UPB_MAX_MESSAGE_DEPTH * 2; - // Validation all passed; freeze the defs. - bool ret = - upb_refcounted_freeze((upb_refcounted * const *)defs, n, s, maxdepth); + /* Validation all passed; freeze the defs. */ + ret = upb_refcounted_freeze((upb_refcounted * const *)defs, n, s, maxdepth); assert(!(s && ret != upb_ok(s))); return ret; err: - for (int i = 0; i < n; i++) { + for (i = 0; i < n; i++) { defs[i]->came_from_user = false; } assert(!(s && upb_ok(s))); @@ -395,12 +389,12 @@ static void upb_enumdef_free(upb_refcounted *r) { upb_inttable_iter i; upb_inttable_begin(&i, &e->iton); for( ; !upb_inttable_done(&i); upb_inttable_next(&i)) { - // To clean up the upb_strdup() from upb_enumdef_addval(). + /* To clean up the upb_strdup() from upb_enumdef_addval(). */ free(upb_value_getcstr(upb_inttable_iter_value(&i))); } upb_strtable_uninit(&e->ntoi); upb_inttable_uninit(&e->iton); - upb_def_uninit(UPB_UPCAST(e)); + upb_def_uninit(upb_enumdef_upcast_mutable(e)); free(e); } @@ -408,7 +402,8 @@ upb_enumdef *upb_enumdef_new(const void *owner) { static const struct upb_refcounted_vtbl vtbl = {NULL, &upb_enumdef_free}; upb_enumdef *e = malloc(sizeof(*e)); if (!e) return NULL; - if (!upb_def_init(UPB_UPCAST(e), UPB_DEF_ENUM, &vtbl, owner)) goto err2; + if (!upb_def_init(upb_enumdef_upcast_mutable(e), UPB_DEF_ENUM, &vtbl, owner)) + goto err2; if (!upb_strtable_init(&e->ntoi, UPB_CTYPE_INT32)) goto err2; if (!upb_inttable_init(&e->iton, UPB_CTYPE_CSTR)) goto err1; return e; @@ -421,9 +416,9 @@ err2: } upb_enumdef *upb_enumdef_dup(const upb_enumdef *e, const void *owner) { + upb_enum_iter i; upb_enumdef *new_e = upb_enumdef_new(owner); if (!new_e) return NULL; - upb_enum_iter i; for(upb_enum_begin(&i, e); !upb_enum_done(&i); upb_enum_next(&i)) { bool success = upb_enumdef_addval( new_e, upb_enum_iter_name(&i),upb_enum_iter_number(&i), NULL); @@ -435,39 +430,18 @@ upb_enumdef *upb_enumdef_dup(const upb_enumdef *e, const void *owner) { return new_e; } -bool upb_enumdef_isfrozen(const upb_enumdef *e) { - return upb_def_isfrozen(UPB_UPCAST(e)); -} - -void upb_enumdef_ref(const upb_enumdef *e, const void *owner) { - upb_def_ref(UPB_UPCAST(e), owner); -} - -void upb_enumdef_unref(const upb_enumdef *e, const void *owner) { - upb_def_unref(UPB_UPCAST(e), owner); -} - -void upb_enumdef_donateref( - const upb_enumdef *e, const void *from, const void *to) { - upb_def_donateref(UPB_UPCAST(e), from, to); -} - -void upb_enumdef_checkref(const upb_enumdef *e, const void *owner) { - upb_def_checkref(UPB_UPCAST(e), owner); -} - bool upb_enumdef_freeze(upb_enumdef *e, upb_status *status) { - upb_def *d = UPB_UPCAST(e); + upb_def *d = upb_enumdef_upcast_mutable(e); return upb_def_freeze(&d, 1, status); } const char *upb_enumdef_fullname(const upb_enumdef *e) { - return upb_def_fullname(UPB_UPCAST(e)); + return upb_def_fullname(upb_enumdef_upcast(e)); } bool upb_enumdef_setfullname(upb_enumdef *e, const char *fullname, upb_status *s) { - return upb_def_setfullname(UPB_UPCAST(e), fullname, s); + return upb_def_setfullname(upb_enumdef_upcast_mutable(e), fullname, s); } bool upb_enumdef_addval(upb_enumdef *e, const char *name, int32_t num, @@ -516,7 +490,7 @@ int upb_enumdef_numvals(const upb_enumdef *e) { } void upb_enum_begin(upb_enum_iter *i, const upb_enumdef *e) { - // We iterate over the ntoi table, to account for duplicate numbers. + /* We iterate over the ntoi table, to account for duplicate numbers. */ upb_strtable_begin(i, &e->ntoi); } @@ -561,13 +535,13 @@ static void visitfield(const upb_refcounted *r, upb_refcounted_visit *visit, void *closure) { const upb_fielddef *f = (const upb_fielddef*)r; if (upb_fielddef_containingtype(f)) { - visit(r, UPB_UPCAST2(upb_fielddef_containingtype(f)), closure); + visit(r, upb_msgdef_upcast2(upb_fielddef_containingtype(f)), closure); } if (upb_fielddef_containingoneof(f)) { - visit(r, UPB_UPCAST2(upb_fielddef_containingoneof(f)), closure); + visit(r, upb_oneofdef_upcast2(upb_fielddef_containingoneof(f)), closure); } if (upb_fielddef_subdef(f)) { - visit(r, UPB_UPCAST(upb_fielddef_subdef(f)), closure); + visit(r, upb_def_upcast(upb_fielddef_subdef(f)), closure); } } @@ -576,26 +550,27 @@ static void freefield(upb_refcounted *r) { upb_fielddef_uninit_default(f); if (f->subdef_is_symbolic) free(f->sub.name); - upb_def_uninit(UPB_UPCAST(f)); + upb_def_uninit(upb_fielddef_upcast_mutable(f)); free(f); } static const char *enumdefaultstr(const upb_fielddef *f) { + const upb_enumdef *e; assert(f->type_is_set_ && f->type_ == UPB_TYPE_ENUM); - const upb_enumdef *e = upb_fielddef_enumsubdef(f); + e = upb_fielddef_enumsubdef(f); if (f->default_is_string && f->defaultval.bytes) { - // Default was explicitly set as a string. + /* Default was explicitly set as a string. */ str_t *s = f->defaultval.bytes; return s->str; } else if (e) { if (!f->default_is_string) { - // Default was explicitly set as an integer; look it up in enumdef. + /* Default was explicitly set as an integer; look it up in enumdef. */ const char *name = upb_enumdef_iton(e, f->defaultval.sint); if (name) { return name; } } else { - // Default is completely unset; pull enumdef default. + /* Default is completely unset; pull enumdef default. */ if (upb_enumdef_numvals(e) > 0) { const char *name = upb_enumdef_iton(e, upb_enumdef_default(e)); assert(name); @@ -607,21 +582,22 @@ static const char *enumdefaultstr(const upb_fielddef *f) { } static bool enumdefaultint32(const upb_fielddef *f, int32_t *val) { + const upb_enumdef *e; assert(f->type_is_set_ && f->type_ == UPB_TYPE_ENUM); - const upb_enumdef *e = upb_fielddef_enumsubdef(f); + e = upb_fielddef_enumsubdef(f); if (!f->default_is_string) { - // Default was explicitly set as an integer. + /* Default was explicitly set as an integer. */ *val = f->defaultval.sint; return true; } else if (e) { if (f->defaultval.bytes) { - // Default was explicitly set as a str; try to lookup corresponding int. + /* Default was explicitly set as a str; try to lookup corresponding int. */ str_t *s = f->defaultval.bytes; if (upb_enumdef_ntoiz(e, s->str, val)) { return true; } } else { - // Default is unset; try to pull in enumdef default. + /* Default is unset; try to pull in enumdef default. */ if (upb_enumdef_numvals(e) > 0) { *val = upb_enumdef_default(e); return true; @@ -631,11 +607,11 @@ static bool enumdefaultint32(const upb_fielddef *f, int32_t *val) { return false; } -upb_fielddef *upb_fielddef_new(const void *owner) { +upb_fielddef *upb_fielddef_new(const void *o) { static const struct upb_refcounted_vtbl vtbl = {visitfield, freefield}; upb_fielddef *f = malloc(sizeof(*f)); if (!f) return NULL; - if (!upb_def_init(UPB_UPCAST(f), UPB_DEF_FIELD, &vtbl, owner)) { + if (!upb_def_init(upb_fielddef_upcast_mutable(f), UPB_DEF_FIELD, &vtbl, o)) { free(f); return NULL; } @@ -653,19 +629,20 @@ upb_fielddef *upb_fielddef_new(const void *owner) { f->lazy_ = false; f->packed_ = true; - // For the moment we default this to UPB_INTFMT_VARIABLE, since it will work - // with all integer types and is in some since more "default" since the most - // normal-looking proto2 types int32/int64/uint32/uint64 use variable. - // - // Other options to consider: - // - there is no default; users must set this manually (like type). - // - default signed integers to UPB_INTFMT_ZIGZAG, since it's more likely to - // be an optimal default for signed integers. + /* For the moment we default this to UPB_INTFMT_VARIABLE, since it will work + * with all integer types and is in some since more "default" since the most + * normal-looking proto2 types int32/int64/uint32/uint64 use variable. + * + * Other options to consider: + * - there is no default; users must set this manually (like type). + * - default signed integers to UPB_INTFMT_ZIGZAG, since it's more likely to + * be an optimal default for signed integers. */ f->intfmt = UPB_INTFMT_VARIABLE; return f; } upb_fielddef *upb_fielddef_dup(const upb_fielddef *f, const void *owner) { + const char *srcname; upb_fielddef *newf = upb_fielddef_new(owner); if (!newf) return NULL; upb_fielddef_settype(newf, upb_fielddef_type(f)); @@ -680,9 +657,8 @@ upb_fielddef *upb_fielddef_dup(const upb_fielddef *f, const void *owner) { newf->defaultval = f->defaultval; } - const char *srcname; if (f->subdef_is_symbolic) { - srcname = f->sub.name; // Might be NULL. + srcname = f->sub.name; /* Might be NULL. */ } else { srcname = f->sub.def ? upb_def_fullname(f->sub.def) : NULL; } @@ -701,27 +677,6 @@ upb_fielddef *upb_fielddef_dup(const upb_fielddef *f, const void *owner) { return newf; } -bool upb_fielddef_isfrozen(const upb_fielddef *f) { - return upb_def_isfrozen(UPB_UPCAST(f)); -} - -void upb_fielddef_ref(const upb_fielddef *f, const void *owner) { - upb_def_ref(UPB_UPCAST(f), owner); -} - -void upb_fielddef_unref(const upb_fielddef *f, const void *owner) { - upb_def_unref(UPB_UPCAST(f), owner); -} - -void upb_fielddef_donateref( - const upb_fielddef *f, const void *from, const void *to) { - upb_def_donateref(UPB_UPCAST(f), from, to); -} - -void upb_fielddef_checkref(const upb_fielddef *f, const void *owner) { - upb_def_checkref(UPB_UPCAST(f), owner); -} - bool upb_fielddef_typeisset(const upb_fielddef *f) { return f->type_is_set_; } @@ -764,7 +719,7 @@ bool upb_fielddef_packed(const upb_fielddef *f) { } const char *upb_fielddef_name(const upb_fielddef *f) { - return upb_def_fullname(UPB_UPCAST(f)); + return upb_def_fullname(upb_fielddef_upcast(f)); } const upb_msgdef *upb_fielddef_containingtype(const upb_fielddef *f) { @@ -794,8 +749,8 @@ bool upb_fielddef_setcontainingtypename(upb_fielddef *f, const char *name, upb_status_seterrmsg(s, "field has already been added to a message."); return false; } - // TODO: validate name (upb_isident() doesn't quite work atm because this name - // may have a leading "."). + /* TODO: validate name (upb_isident() doesn't quite work atm because this name + * may have a leading "."). */ release_containingtype(f); f->msg.name = upb_strdup(name); f->msg_is_symbolic = true; @@ -807,7 +762,7 @@ bool upb_fielddef_setname(upb_fielddef *f, const char *name, upb_status *s) { upb_status_seterrmsg(s, "Already added to message or oneof"); return false; } - return upb_def_setfullname(UPB_UPCAST(f), name, s); + return upb_def_setfullname(upb_fielddef_upcast_mutable(f), name, s); } static void chkdefaulttype(const upb_fielddef *f, upb_fieldtype_t type) { @@ -867,7 +822,7 @@ const char *upb_fielddef_defaultstr(const upb_fielddef *f, size_t *len) { if (upb_fielddef_type(f) == UPB_TYPE_ENUM) { const char *ret = enumdefaultstr(f); assert(ret); - // Enum defaults can't have embedded NULLs. + /* Enum defaults can't have embedded NULLs. */ if (len) *len = strlen(ret); return ret; } @@ -898,7 +853,7 @@ static void upb_fielddef_init_default(upb_fielddef *f) { break; case UPB_TYPE_MESSAGE: break; case UPB_TYPE_ENUM: - // This is our special sentinel that indicates "not set" for an enum. + /* This is our special sentinel that indicates "not set" for an enum. */ f->default_is_string = true; f->defaultval.bytes = NULL; break; @@ -1144,6 +1099,7 @@ void upb_fielddef_setdefaultdouble(upb_fielddef *f, double value) { bool upb_fielddef_setdefaultstr(upb_fielddef *f, const void *str, size_t len, upb_status *s) { + str_t *str2; assert(upb_fielddef_isstring(f) || f->type_ == UPB_TYPE_ENUM); if (f->type_ == UPB_TYPE_ENUM && !upb_isident(str, len, false, s)) return false; @@ -1156,7 +1112,7 @@ bool upb_fielddef_setdefaultstr(upb_fielddef *f, const void *str, size_t len, assert(f->type_ == UPB_TYPE_ENUM); } - str_t *str2 = newstr(str, len); + str2 = newstr(str, len); f->defaultval.bytes = str2; f->default_is_string = true; return true; @@ -1169,8 +1125,8 @@ void upb_fielddef_setdefaultcstr(upb_fielddef *f, const char *str, } bool upb_fielddef_enumhasdefaultint32(const upb_fielddef *f) { - assert(f->type_is_set_ && f->type_ == UPB_TYPE_ENUM); int32_t val; + assert(f->type_is_set_ && f->type_ == UPB_TYPE_ENUM); return enumdefaultint32(f, &val); } @@ -1217,12 +1173,12 @@ bool upb_fielddef_setsubdef(upb_fielddef *f, const upb_def *subdef, bool upb_fielddef_setmsgsubdef(upb_fielddef *f, const upb_msgdef *subdef, upb_status *s) { - return upb_fielddef_setsubdef(f, UPB_UPCAST(subdef), s); + return upb_fielddef_setsubdef(f, upb_msgdef_upcast(subdef), s); } bool upb_fielddef_setenumsubdef(upb_fielddef *f, const upb_enumdef *subdef, upb_status *s) { - return upb_fielddef_setsubdef(f, UPB_UPCAST(subdef), s); + return upb_fielddef_setsubdef(f, upb_enumdef_upcast(subdef), s); } bool upb_fielddef_setsubdefname(upb_fielddef *f, const char *name, @@ -1232,8 +1188,8 @@ bool upb_fielddef_setsubdefname(upb_fielddef *f, const char *name, upb_status_seterrmsg(s, "field type does not accept a subdef"); return false; } - // TODO: validate name (upb_isident() doesn't quite work atm because this name - // may have a leading "."). + /* TODO: validate name (upb_isident() doesn't quite work atm because this name + * may have a leading "."). */ release_subdef(f); f->sub.name = upb_strdup(name); f->subdef_is_symbolic = true; @@ -1282,20 +1238,20 @@ bool upb_fielddef_checkdescriptortype(int32_t type) { static void visitmsg(const upb_refcounted *r, upb_refcounted_visit *visit, void *closure) { + upb_msg_oneof_iter o; const upb_msgdef *m = (const upb_msgdef*)r; 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); + visit(r, upb_fielddef_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); + visit(r, upb_oneofdef_upcast2(f), closure); } } @@ -1304,7 +1260,7 @@ static void freemsg(upb_refcounted *r) { upb_strtable_uninit(&m->ntoo); upb_strtable_uninit(&m->ntof); upb_inttable_uninit(&m->itof); - upb_def_uninit(UPB_UPCAST(m)); + upb_def_uninit(upb_msgdef_upcast_mutable(m)); free(m); } @@ -1312,7 +1268,8 @@ upb_msgdef *upb_msgdef_new(const void *owner) { static const struct upb_refcounted_vtbl vtbl = {visitmsg, freemsg}; 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_def_init(upb_msgdef_upcast_mutable(m), UPB_DEF_MSG, &vtbl, owner)) + goto err2; 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; @@ -1329,25 +1286,28 @@ err3: } upb_msgdef *upb_msgdef_dup(const upb_msgdef *m, const void *owner) { + bool ok; + upb_msg_field_iter i; + upb_msg_oneof_iter o; + upb_msgdef *newm = upb_msgdef_new(owner); if (!newm) return NULL; - bool ok = upb_def_setfullname(UPB_UPCAST(newm), - upb_def_fullname(UPB_UPCAST(m)), NULL); + ok = upb_def_setfullname(upb_msgdef_upcast_mutable(newm), + upb_def_fullname(upb_msgdef_upcast(m)), + NULL); newm->map_entry = m->map_entry; UPB_ASSERT_VAR(ok, ok); - 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. + /* 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)) { @@ -1360,43 +1320,22 @@ upb_msgdef *upb_msgdef_dup(const upb_msgdef *m, const void *owner) { return newm; } -bool upb_msgdef_isfrozen(const upb_msgdef *m) { - return upb_def_isfrozen(UPB_UPCAST(m)); -} - -void upb_msgdef_ref(const upb_msgdef *m, const void *owner) { - upb_def_ref(UPB_UPCAST(m), owner); -} - -void upb_msgdef_unref(const upb_msgdef *m, const void *owner) { - upb_def_unref(UPB_UPCAST(m), owner); -} - -void upb_msgdef_donateref( - const upb_msgdef *m, const void *from, const void *to) { - upb_def_donateref(UPB_UPCAST(m), from, to); -} - -void upb_msgdef_checkref(const upb_msgdef *m, const void *owner) { - upb_def_checkref(UPB_UPCAST(m), owner); -} - bool upb_msgdef_freeze(upb_msgdef *m, upb_status *status) { - upb_def *d = UPB_UPCAST(m); + upb_def *d = upb_msgdef_upcast_mutable(m); return upb_def_freeze(&d, 1, status); } const char *upb_msgdef_fullname(const upb_msgdef *m) { - return upb_def_fullname(UPB_UPCAST(m)); + return upb_def_fullname(upb_msgdef_upcast(m)); } bool upb_msgdef_setfullname(upb_msgdef *m, const char *fullname, upb_status *s) { - return upb_def_setfullname(UPB_UPCAST(m), fullname, s); + return upb_def_setfullname(upb_msgdef_upcast_mutable(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. +/* 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) { @@ -1426,40 +1365,42 @@ static void add_field(upb_msgdef *m, upb_fielddef *f, const void *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 - // top-level extension (ie. one not in any package) to have the same name as a - // field from the message. - // - // This also implies that there needs to be a separate lookup-by-name method - // for extensions. It seems desirable for iteration to return both extensions - // and non-extensions though. - // - // 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. + /* TODO: extensions need to have a separate namespace, because proto2 allows a + * top-level extension (ie. one not in any package) to have the same name as a + * field from the message. + * + * This also implies that there needs to be a separate lookup-by-name method + * for extensions. It seems desirable for iteration to return both extensions + * and non-extensions though. + * + * 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. + /* Check constraints for all fields before performing any action. */ if (!check_field_add(m, f, s)) { return false; } else if (upb_fielddef_containingoneof(f) != NULL) { - // Fields in a oneof can only be added by adding the oneof to the msgdef. + /* 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. + /* Constraint checks ok, perform the action. */ 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. + upb_oneof_iter it; + + /* 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; @@ -1471,9 +1412,8 @@ bool upb_msgdef_addoneof(upb_msgdef *m, upb_oneofdef *o, const void *ref_donor, 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; + /* Check that all of the oneof's fields do not conflict with names or numbers + * of fields already in the message. */ 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)) { @@ -1481,15 +1421,15 @@ bool upb_msgdef_addoneof(upb_msgdef *m, upb_oneofdef *o, const void *ref_donor, } } - // Everything checks out -- commit now. + /* Everything checks out -- commit now. */ - // Add oneof itself first. + /* 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. + /* 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); @@ -1581,10 +1521,10 @@ static void visitoneof(const upb_refcounted *r, upb_refcounted_visit *visit, 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); + visit(r, upb_fielddef_upcast2(f), closure); } if (o->parent) { - visit(r, UPB_UPCAST2(o->parent), closure); + visit(r, upb_msgdef_upcast2(o->parent), closure); } } @@ -1592,7 +1532,7 @@ 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)); + upb_def_uninit(upb_oneofdef_upcast_mutable(o)); free(o); } @@ -1601,7 +1541,9 @@ upb_oneofdef *upb_oneofdef_new(const void *owner) { 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_def_init(upb_oneofdef_upcast_mutable(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; @@ -1614,12 +1556,13 @@ err2: } upb_oneofdef *upb_oneofdef_dup(const upb_oneofdef *o, const void *owner) { + bool ok; + upb_oneof_iter i; 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); + ok = upb_def_setfullname(upb_oneofdef_upcast_mutable(newo), + upb_def_fullname(upb_oneofdef_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)) { @@ -1630,29 +1573,8 @@ upb_oneofdef *upb_oneofdef_dup(const upb_oneofdef *o, const void *owner) { 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)); + return upb_def_fullname(upb_oneofdef_upcast(o)); } bool upb_oneofdef_setname(upb_oneofdef *o, const char *fullname, @@ -1661,7 +1583,7 @@ bool upb_oneofdef_setname(upb_oneofdef *o, const char *fullname, upb_status_seterrmsg(s, "oneof already added to a message"); return false; } - return upb_def_setfullname(UPB_UPCAST(o), fullname, s); + return upb_def_setfullname(upb_oneofdef_upcast_mutable(o), fullname, s); } const upb_msgdef *upb_oneofdef_containingtype(const upb_oneofdef *o) { @@ -1678,20 +1600,20 @@ bool upb_oneofdef_addfield(upb_oneofdef *o, upb_fielddef *f, 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. + /* 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. + /* 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. + /* 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; @@ -1704,21 +1626,21 @@ bool upb_oneofdef_addfield(upb_oneofdef *o, upb_fielddef *f, 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. + /* 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 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 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 " @@ -1727,8 +1649,8 @@ bool upb_oneofdef_addfield(upb_oneofdef *o, upb_fielddef *f, } } - // Commit phase. First add the field to our parent msgdef, if any, because - // that may fail; then add the field to our own tables. + /* 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)) { @@ -1801,18 +1723,17 @@ static void *seeded_alloc(void *ud, void *ptr, size_t oldsize, size_t size); /* Default allocator **********************************************************/ -// Just use realloc, keeping all allocated blocks in a linked list to destroy at -// the end. +/* Just use realloc, keeping all allocated blocks in a linked list to destroy at + * the end. */ typedef struct mem_block { - // List is doubly-linked, because in cases where realloc() moves an existing - // block, we need to be able to remove the old pointer from the list - // efficiently. + /* List is doubly-linked, because in cases where realloc() moves an existing + * block, we need to be able to remove the old pointer from the list + * efficiently. */ struct mem_block *prev, *next; #ifndef NDEBUG - size_t size; // Doesn't include mem_block structure. + size_t size; /* Doesn't include mem_block structure. */ #endif - char data[]; } mem_block; typedef struct { @@ -1820,10 +1741,12 @@ typedef struct { } default_alloc_ud; static void *default_alloc(void *_ud, void *ptr, size_t oldsize, size_t size) { - UPB_UNUSED(oldsize); default_alloc_ud *ud = _ud; + mem_block *from, *block; + void *ret; + UPB_UNUSED(oldsize); - mem_block *from = ptr ? (void*)((char*)ptr - sizeof(mem_block)) : NULL; + from = ptr ? (void*)((char*)ptr - sizeof(mem_block)) : NULL; #ifndef NDEBUG if (from) { @@ -1831,8 +1754,11 @@ static void *default_alloc(void *_ud, void *ptr, size_t oldsize, size_t size) { } #endif - mem_block *block = realloc(from, size + sizeof(mem_block)); + /* TODO(haberman): we probably need to provide even better alignment here, + * like 16-byte alignment of the returned data pointer. */ + block = realloc(from, size + sizeof(mem_block)); if (!block) return NULL; + ret = (char*)block + sizeof(*block); #ifndef NDEBUG block->size = size; @@ -1840,20 +1766,20 @@ static void *default_alloc(void *_ud, void *ptr, size_t oldsize, size_t size) { if (from) { if (block != from) { - // The block was moved, so pointers in next and prev blocks must be - // updated to its new location. + /* The block was moved, so pointers in next and prev blocks must be + * updated to its new location. */ if (block->next) block->next->prev = block; if (block->prev) block->prev->next = block; } } else { - // Insert at head of linked list. + /* Insert at head of linked list. */ block->prev = NULL; block->next = ud->head; if (block->next) block->next->prev = block; ud->head = block; } - return &block->data; + return ret; } static void default_alloc_cleanup(void *_ud) { @@ -1886,14 +1812,14 @@ static bool write_err_to(void *ud, const upb_status *status) { /* upb_env ********************************************************************/ void upb_env_init(upb_env *e) { + default_alloc_ud *ud = (default_alloc_ud*)&e->default_alloc_ud; e->ok_ = true; e->bytes_allocated = 0; e->cleanup_head = NULL; - default_alloc_ud *ud = (default_alloc_ud*)&e->default_alloc_ud; ud->head = NULL; - // Set default functions. + /* Set default functions. */ upb_env_setallocfunc(e, default_alloc, ud); upb_env_seterrorfunc(e, default_err, NULL); } @@ -1906,8 +1832,8 @@ void upb_env_uninit(upb_env *e) { ent = ent->next; } - // Must do this after running cleanup functions, because this will delete - // the memory we store our cleanup entries in! + /* Must do this after running cleanup functions, because this will delete + the memory we store our cleanup entries in! */ if (e->alloc == default_alloc) { default_alloc_cleanup(e->alloc_ud); } @@ -1954,8 +1880,8 @@ bool upb_env_addcleanup(upb_env *e, upb_cleanup_func *func, void *ud) { void *upb_env_malloc(upb_env *e, size_t size) { e->bytes_allocated += size; if (e->alloc == seeded_alloc) { - // This is equivalent to the next branch, but allows inlining for a - // measurable perf benefit. + /* This is equivalent to the next branch, but allows inlining for a + * measurable perf benefit. */ return seeded_alloc(e->alloc_ud, NULL, 0, size); } else { return e->alloc(e->alloc_ud, NULL, 0, size); @@ -1963,12 +1889,13 @@ void *upb_env_malloc(upb_env *e, size_t size) { } void *upb_env_realloc(upb_env *e, void *ptr, size_t oldsize, size_t size) { + char *ret; assert(oldsize <= size); - char *ret = e->alloc(e->alloc_ud, ptr, oldsize, size); + ret = e->alloc(e->alloc_ud, ptr, oldsize, size); #ifndef NDEBUG - // Overwrite non-preserved memory to ensure callers are passing the oldsize - // that they truly require. + /* Overwrite non-preserved memory to ensure callers are passing the oldsize + * that they truly require. */ memset(ret + oldsize, 0xff, size - oldsize); #endif @@ -1982,7 +1909,7 @@ size_t upb_env_bytesallocated(const upb_env *e) { /* upb_seededalloc ************************************************************/ -// Be conservative and choose 16 in case anyone is using SSE. +/* Be conservative and choose 16 in case anyone is using SSE. */ static const size_t maxalign = 16; static size_t align_up(size_t size) { @@ -1991,24 +1918,24 @@ static size_t align_up(size_t size) { UPB_FORCEINLINE static void *seeded_alloc(void *ud, void *ptr, size_t oldsize, size_t size) { + upb_seededalloc *a = ud; UPB_UNUSED(ptr); - upb_seededalloc *a = ud; size = align_up(size); assert(a->mem_limit >= a->mem_ptr); if (oldsize == 0 && size <= (size_t)(a->mem_limit - a->mem_ptr)) { - // Fast path: we can satisfy from the initial allocation. + /* Fast path: we can satisfy from the initial allocation. */ void *ret = a->mem_ptr; a->mem_ptr += size; return ret; } else { - // Slow path: fallback to other allocator. - a->need_cleanup = true; - // Is `ptr` part of the user-provided initial block? Don't pass it to the - // default allocator if so; otherwise, it may try to realloc() the block. char *chptr = ptr; + /* Slow path: fallback to other allocator. */ + a->need_cleanup = true; + /* Is `ptr` part of the user-provided initial block? Don't pass it to the + * default allocator if so; otherwise, it may try to realloc() the block. */ if (chptr >= a->mem_base && chptr < a->mem_limit) { return a->alloc(a->alloc_ud, NULL, 0, size); } else { @@ -2018,13 +1945,13 @@ UPB_FORCEINLINE static void *seeded_alloc(void *ud, void *ptr, size_t oldsize, } void upb_seededalloc_init(upb_seededalloc *a, void *mem, size_t len) { + default_alloc_ud *ud = (default_alloc_ud*)&a->default_alloc_ud; a->mem_base = mem; a->mem_ptr = mem; a->mem_limit = (char*)mem + len; a->need_cleanup = false; a->returned_allocfunc = false; - default_alloc_ud *ud = (default_alloc_ud*)&a->default_alloc_ud; ud->head = NULL; upb_seededalloc_setfallbackalloc(a, default_alloc, ud); @@ -2063,8 +1990,9 @@ upb_alloc_func *upb_seededalloc_getallocfunc(upb_seededalloc *a) { #include -// Defined for the sole purpose of having a unique pointer value for -// UPB_NO_CLOSURE. + +/* Defined for the sole purpose of having a unique pointer value for + * UPB_NO_CLOSURE. */ char _upb_noclosure; static void freehandlers(upb_refcounted *r) { @@ -2093,42 +2021,45 @@ static void visithandlers(const upb_refcounted *r, upb_refcounted_visit *visit, !upb_msg_field_done(&i); upb_msg_field_next(&i)) { upb_fielddef *f = upb_msg_iter_field(&i); + const upb_handlers *sub; if (!upb_fielddef_issubmsg(f)) continue; - const upb_handlers *sub = upb_handlers_getsubhandlers(h, f); - if (sub) visit(r, UPB_UPCAST(sub), closure); + sub = upb_handlers_getsubhandlers(h, f); + if (sub) visit(r, upb_handlers_upcast(sub), closure); } } static const struct upb_refcounted_vtbl vtbl = {visithandlers, freehandlers}; typedef struct { - upb_inttable tab; // maps upb_msgdef* -> upb_handlers*. + upb_inttable tab; /* maps upb_msgdef* -> upb_handlers*. */ upb_handlers_callback *callback; const void *closure; } dfs_state; -// TODO(haberman): discard upb_handlers* objects that do not actually have any -// handlers set and cannot reach any upb_handlers* object that does. This is -// slightly tricky to do correctly. +/* TODO(haberman): discard upb_handlers* objects that do not actually have any + * handlers set and cannot reach any upb_handlers* object that does. This is + * slightly tricky to do correctly. */ static upb_handlers *newformsg(const upb_msgdef *m, const void *owner, dfs_state *s) { + upb_msg_field_iter i; upb_handlers *h = upb_handlers_new(m, owner); if (!h) return NULL; if (!upb_inttable_insertptr(&s->tab, m, upb_value_ptr(h))) goto oom; s->callback(s->closure, h); - // For each submessage field, get or create a handlers object and set it as - // the subhandlers. - upb_msg_field_iter i; + /* For each submessage field, get or create a handlers object and set it as + * the subhandlers. */ 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); + const upb_msgdef *subdef; + upb_value subm_ent; + if (!upb_fielddef_issubmsg(f)) continue; - const upb_msgdef *subdef = upb_downcast_msgdef(upb_fielddef_subdef(f)); - upb_value subm_ent; + subdef = upb_downcast_msgdef(upb_fielddef_subdef(f)); if (upb_inttable_lookupptr(&s->tab, subdef, &subm_ent)) { upb_handlers_setsubhandlers(h, f, upb_value_getptr(subm_ent)); } else { @@ -2145,11 +2076,11 @@ oom: return NULL; } -// Given a selector for a STARTSUBMSG handler, resolves to a pointer to the -// subhandlers for this submessage field. +/* Given a selector for a STARTSUBMSG handler, resolves to a pointer to the + * subhandlers for this submessage field. */ #define SUBH(h, selector) (h->sub[selector]) -// The selector for a submessage field is the field index. +/* The selector for a submessage field is the field index. */ #define SUBH_F(h, f) SUBH(h, f->index_) static int32_t trygetsel(upb_handlers *h, const upb_fielddef *f, @@ -2187,6 +2118,10 @@ static const void **returntype(upb_handlers *h, const upb_fielddef *f, static bool doset(upb_handlers *h, int32_t sel, const upb_fielddef *f, upb_handlertype_t type, upb_func *func, upb_handlerattr *attr) { + upb_handlerattr set_attr = UPB_HANDLERATTR_INITIALIZER; + const void *closure_type; + const void **context_closure_type; + assert(!upb_handlers_isfrozen(h)); if (sel < 0) { @@ -2201,15 +2136,13 @@ static bool doset(upb_handlers *h, int32_t sel, const upb_fielddef *f, return false; } - upb_handlerattr set_attr = UPB_HANDLERATTR_INITIALIZER; if (attr) { set_attr = *attr; } - // Check that the given closure type matches the closure type that has been - // established for this context (if any). - const void *closure_type = upb_handlerattr_closuretype(&set_attr); - const void **context_closure_type; + /* Check that the given closure type matches the closure type that has been + * established for this context (if any). */ + closure_type = upb_handlerattr_closuretype(&set_attr); if (type == UPB_HANDLER_STRING) { context_closure_type = returntype(h, f, UPB_HANDLER_STARTSTR); @@ -2223,7 +2156,7 @@ static bool doset(upb_handlers *h, int32_t sel, const upb_fielddef *f, if (closure_type && *context_closure_type && closure_type != *context_closure_type) { - // TODO(haberman): better message for debugging. + /* TODO(haberman): better message for debugging. */ if (f) { upb_status_seterrf(&h->status_, "closure type does not match for field %s", @@ -2238,8 +2171,8 @@ static bool doset(upb_handlers *h, int32_t sel, const upb_fielddef *f, if (closure_type) *context_closure_type = closure_type; - // If this is a STARTSEQ or STARTSTR handler, check that the returned pointer - // matches any pre-existing expectations about what type is expected. + /* If this is a STARTSEQ or STARTSTR handler, check that the returned pointer + * matches any pre-existing expectations about what type is expected. */ if (type == UPB_HANDLER_STARTSEQ || type == UPB_HANDLER_STARTSTR) { const void *return_type = upb_handlerattr_returnclosuretype(&set_attr); const void *table_return_type = @@ -2258,17 +2191,20 @@ static bool doset(upb_handlers *h, int32_t sel, const upb_fielddef *f, return true; } -// Returns the effective closure type for this handler (which will propagate -// from outer frames if this frame has no START* handler). Not implemented for -// UPB_HANDLER_STRING at the moment since this is not needed. Returns NULL is -// the effective closure type is unspecified (either no handler was registered -// to specify it or the handler that was registered did not specify the closure -// type). +/* Returns the effective closure type for this handler (which will propagate + * from outer frames if this frame has no START* handler). Not implemented for + * UPB_HANDLER_STRING at the moment since this is not needed. Returns NULL is + * the effective closure type is unspecified (either no handler was registered + * to specify it or the handler that was registered did not specify the closure + * type). */ const void *effective_closure_type(upb_handlers *h, const upb_fielddef *f, upb_handlertype_t type) { - assert(type != UPB_HANDLER_STRING); - const void *ret = h->top_closure_type; + const void *ret; upb_selector_t sel; + + assert(type != UPB_HANDLER_STRING); + ret = h->top_closure_type; + if (upb_fielddef_isseq(f) && type != UPB_HANDLER_STARTSEQ && type != UPB_HANDLER_ENDSEQ && @@ -2281,26 +2217,30 @@ const void *effective_closure_type(upb_handlers *h, const upb_fielddef *f, ret = upb_handlerattr_returnclosuretype(&h->table[sel].attr); } - // The effective type of the submessage; not used yet. - // if (type == SUBMESSAGE && - // h->table[sel = handlers_getsel(h, f, UPB_HANDLER_STARTSUBMSG)].func) { - // ret = upb_handlerattr_returnclosuretype(&h->table[sel].attr); - // } + /* The effective type of the submessage; not used yet. + * if (type == SUBMESSAGE && + * h->table[sel = handlers_getsel(h, f, UPB_HANDLER_STARTSUBMSG)].func) { + * ret = upb_handlerattr_returnclosuretype(&h->table[sel].attr); + * } */ return ret; } -// Checks whether the START* handler specified by f & type is missing even -// though it is required to convert the established type of an outer frame -// ("closure_type") into the established type of an inner frame (represented in -// the return closure type of this handler's attr. +/* Checks whether the START* handler specified by f & type is missing even + * though it is required to convert the established type of an outer frame + * ("closure_type") into the established type of an inner frame (represented in + * the return closure type of this handler's attr. */ bool checkstart(upb_handlers *h, const upb_fielddef *f, upb_handlertype_t type, upb_status *status) { + const void *closure_type; + const upb_handlerattr *attr; + const void *return_closure_type; + upb_selector_t sel = handlers_getsel(h, f, type); if (h->table[sel].func) return true; - const void *closure_type = effective_closure_type(h, f, type); - const upb_handlerattr *attr = &h->table[sel].attr; - const void *return_closure_type = upb_handlerattr_returnclosuretype(attr); + closure_type = effective_closure_type(h, f, type); + attr = &h->table[sel].attr; + return_closure_type = upb_handlerattr_returnclosuretype(attr); if (closure_type && return_closure_type && closure_type != return_closure_type) { upb_status_seterrf(status, @@ -2313,32 +2253,14 @@ bool checkstart(upb_handlers *h, const upb_fielddef *f, upb_handlertype_t type, /* Public interface ***********************************************************/ -bool upb_handlers_isfrozen(const upb_handlers *h) { - return upb_refcounted_isfrozen(UPB_UPCAST(h)); -} - -void upb_handlers_ref(const upb_handlers *h, const void *owner) { - upb_refcounted_ref(UPB_UPCAST(h), owner); -} - -void upb_handlers_unref(const upb_handlers *h, const void *owner) { - upb_refcounted_unref(UPB_UPCAST(h), owner); -} - -void upb_handlers_donateref( - const upb_handlers *h, const void *from, const void *to) { - upb_refcounted_donateref(UPB_UPCAST(h), from, to); -} - -void upb_handlers_checkref(const upb_handlers *h, const void *owner) { - upb_refcounted_checkref(UPB_UPCAST(h), owner); -} - upb_handlers *upb_handlers_new(const upb_msgdef *md, const void *owner) { + int extra; + upb_handlers *h; + assert(upb_msgdef_isfrozen(md)); - int extra = sizeof(upb_handlers_tabent) * (md->selector_count - 1); - upb_handlers *h = calloc(sizeof(*h) + extra, 1); + extra = sizeof(upb_handlers_tabent) * (md->selector_count - 1); + h = calloc(sizeof(*h) + extra, 1); if (!h) return NULL; h->msg = md; @@ -2346,14 +2268,15 @@ upb_handlers *upb_handlers_new(const upb_msgdef *md, const void *owner) { upb_status_clear(&h->status_); h->sub = calloc(md->submsg_field_count, sizeof(*h->sub)); if (!h->sub) goto oom; - if (!upb_refcounted_init(UPB_UPCAST(h), &vtbl, owner)) goto oom; + if (!upb_refcounted_init(upb_handlers_upcast_mutable(h), &vtbl, owner)) + goto oom; if (!upb_inttable_init(&h->cleanup_, UPB_CTYPE_FPTR)) goto oom; - // calloc() above initialized all handlers to NULL. + /* calloc() above initialized all handlers to NULL. */ return h; oom: - freehandlers(UPB_UPCAST(h)); + freehandlers(upb_handlers_upcast_mutable(h)); return NULL; } @@ -2362,17 +2285,21 @@ const upb_handlers *upb_handlers_newfrozen(const upb_msgdef *m, upb_handlers_callback *callback, const void *closure) { dfs_state state; + upb_handlers *ret; + bool ok; + upb_refcounted *r; + state.callback = callback; state.closure = closure; if (!upb_inttable_init(&state.tab, UPB_CTYPE_PTR)) return NULL; - upb_handlers *ret = newformsg(m, owner, &state); + ret = newformsg(m, owner, &state); upb_inttable_uninit(&state.tab); if (!ret) return NULL; - upb_refcounted *r = UPB_UPCAST(ret); - bool ok = upb_refcounted_freeze(&r, 1, NULL, UPB_MAX_HANDLER_DEPTH); + r = upb_handlers_upcast_mutable(ret); + ok = upb_refcounted_freeze(&r, 1, NULL, UPB_MAX_HANDLER_DEPTH); UPB_ASSERT_VAR(ok, ok); return ret; @@ -2395,20 +2322,20 @@ void upb_handlers_clearerr(upb_handlers *h) { return doset(h, sel, f, handlertype, (upb_func*)func, attr); \ } -SETTER(int32, upb_int32_handlerfunc*, UPB_HANDLER_INT32); -SETTER(int64, upb_int64_handlerfunc*, UPB_HANDLER_INT64); -SETTER(uint32, upb_uint32_handlerfunc*, UPB_HANDLER_UINT32); -SETTER(uint64, upb_uint64_handlerfunc*, UPB_HANDLER_UINT64); -SETTER(float, upb_float_handlerfunc*, UPB_HANDLER_FLOAT); -SETTER(double, upb_double_handlerfunc*, UPB_HANDLER_DOUBLE); -SETTER(bool, upb_bool_handlerfunc*, UPB_HANDLER_BOOL); -SETTER(startstr, upb_startstr_handlerfunc*, UPB_HANDLER_STARTSTR); -SETTER(string, upb_string_handlerfunc*, UPB_HANDLER_STRING); -SETTER(endstr, upb_endfield_handlerfunc*, UPB_HANDLER_ENDSTR); -SETTER(startseq, upb_startfield_handlerfunc*, UPB_HANDLER_STARTSEQ); -SETTER(startsubmsg, upb_startfield_handlerfunc*, UPB_HANDLER_STARTSUBMSG); -SETTER(endsubmsg, upb_endfield_handlerfunc*, UPB_HANDLER_ENDSUBMSG); -SETTER(endseq, upb_endfield_handlerfunc*, UPB_HANDLER_ENDSEQ); +SETTER(int32, upb_int32_handlerfunc*, UPB_HANDLER_INT32) +SETTER(int64, upb_int64_handlerfunc*, UPB_HANDLER_INT64) +SETTER(uint32, upb_uint32_handlerfunc*, UPB_HANDLER_UINT32) +SETTER(uint64, upb_uint64_handlerfunc*, UPB_HANDLER_UINT64) +SETTER(float, upb_float_handlerfunc*, UPB_HANDLER_FLOAT) +SETTER(double, upb_double_handlerfunc*, UPB_HANDLER_DOUBLE) +SETTER(bool, upb_bool_handlerfunc*, UPB_HANDLER_BOOL) +SETTER(startstr, upb_startstr_handlerfunc*, UPB_HANDLER_STARTSTR) +SETTER(string, upb_string_handlerfunc*, UPB_HANDLER_STRING) +SETTER(endstr, upb_endfield_handlerfunc*, UPB_HANDLER_ENDSTR) +SETTER(startseq, upb_startfield_handlerfunc*, UPB_HANDLER_STARTSEQ) +SETTER(startsubmsg, upb_startfield_handlerfunc*, UPB_HANDLER_STARTSUBMSG) +SETTER(endsubmsg, upb_endfield_handlerfunc*, UPB_HANDLER_ENDSUBMSG) +SETTER(endseq, upb_endfield_handlerfunc*, UPB_HANDLER_ENDSEQ) #undef SETTER @@ -2430,8 +2357,8 @@ bool upb_handlers_setsubhandlers(upb_handlers *h, const upb_fielddef *f, assert(sub); assert(!upb_handlers_isfrozen(h)); assert(upb_fielddef_issubmsg(f)); - if (SUBH_F(h, f)) return false; // Can't reset. - if (UPB_UPCAST(upb_handlers_msgdef(sub)) != upb_fielddef_subdef(f)) { + if (SUBH_F(h, f)) return false; /* Can't reset. */ + if (upb_msgdef_upcast(upb_handlers_msgdef(sub)) != upb_fielddef_subdef(f)) { return false; } SUBH_F(h, f) = sub; @@ -2455,17 +2382,18 @@ bool upb_handlers_getattr(const upb_handlers *h, upb_selector_t sel, const upb_handlers *upb_handlers_getsubhandlers_sel(const upb_handlers *h, upb_selector_t sel) { - // STARTSUBMSG selector in sel is the field's selector base. + /* STARTSUBMSG selector in sel is the field's selector base. */ return SUBH(h, sel - UPB_STATIC_SELECTOR_COUNT); } const upb_msgdef *upb_handlers_msgdef(const upb_handlers *h) { return h->msg; } bool upb_handlers_addcleanup(upb_handlers *h, void *p, upb_handlerfree *func) { + bool ok; if (upb_inttable_lookupptr(&h->cleanup_, p, NULL)) { return false; } - bool ok = upb_inttable_insertptr(&h->cleanup_, p, upb_value_fptr(func)); + ok = upb_inttable_insertptr(&h->cleanup_, p, upb_value_fptr(func)); UPB_ASSERT_VAR(ok, ok); return true; } @@ -2474,8 +2402,10 @@ bool upb_handlers_addcleanup(upb_handlers *h, void *p, upb_handlerfree *func) { /* "Static" methods ***********************************************************/ bool upb_handlers_freeze(upb_handlers *const*handlers, int n, upb_status *s) { - // TODO: verify we have a transitive closure. - for (int i = 0; i < n; i++) { + /* TODO: verify we have a transitive closure. */ + int i; + for (i = 0; i < n; i++) { + upb_msg_field_iter j; upb_handlers *h = handlers[i]; if (!upb_ok(&h->status_)) { @@ -2485,9 +2415,8 @@ bool upb_handlers_freeze(upb_handlers *const*handlers, int n, upb_status *s) { return false; } - // Check that there are no closure mismatches due to missing Start* handlers - // or subhandlers with different type-level types. - upb_msg_field_iter j; + /* Check that there are no closure mismatches due to missing Start* handlers + * or subhandlers with different type-level types. */ for(upb_msg_field_begin(&j, h->msg); !upb_msg_field_done(&j); upb_msg_field_next(&j)) { @@ -2521,29 +2450,29 @@ bool upb_handlers_freeze(upb_handlers *const*handlers, int n, upb_status *s) { } if (hashandler && !upb_handlers_getsubhandlers(h, f)) { - // For now we add an empty subhandlers in this case. It makes the - // decoder code generator simpler, because it only has to handle two - // cases (submessage has handlers or not) as opposed to three - // (submessage has handlers in enclosing message but no subhandlers). - // - // This makes parsing less efficient in the case that we want to - // notice a submessage but skip its contents (like if we're testing - // for submessage presence or counting the number of repeated - // submessages). In this case we will end up parsing the submessage - // field by field and throwing away the results for each, instead of - // skipping the whole delimited thing at once. If this is an issue we - // can revisit it, but do remember that this only arises when you have - // handlers (startseq/startsubmsg/endsubmsg/endseq) set for the - // submessage but no subhandlers. The uses cases for this are - // limited. + /* For now we add an empty subhandlers in this case. It makes the + * decoder code generator simpler, because it only has to handle two + * cases (submessage has handlers or not) as opposed to three + * (submessage has handlers in enclosing message but no subhandlers). + * + * This makes parsing less efficient in the case that we want to + * notice a submessage but skip its contents (like if we're testing + * for submessage presence or counting the number of repeated + * submessages). In this case we will end up parsing the submessage + * field by field and throwing away the results for each, instead of + * skipping the whole delimited thing at once. If this is an issue we + * can revisit it, but do remember that this only arises when you have + * handlers (startseq/startsubmsg/endsubmsg/endseq) set for the + * submessage but no subhandlers. The uses cases for this are + * limited. */ upb_handlers *sub = upb_handlers_new(upb_fielddef_msgsubdef(f), &sub); upb_handlers_setsubhandlers(h, f, sub); upb_handlers_unref(sub, &sub); } - // TODO(haberman): check type of submessage. - // This is slightly tricky; also consider whether we should check that - // they match at setsubhandlers time. + /* TODO(haberman): check type of submessage. + * This is slightly tricky; also consider whether we should check that + * they match at setsubhandlers time. */ } } } @@ -2566,7 +2495,7 @@ upb_handlertype_t upb_handlers_getprimitivehandlertype(const upb_fielddef *f) { case UPB_TYPE_FLOAT: return UPB_HANDLER_FLOAT; case UPB_TYPE_DOUBLE: return UPB_HANDLER_DOUBLE; case UPB_TYPE_BOOL: return UPB_HANDLER_BOOL; - default: assert(false); return -1; // Invalid input. + default: assert(false); return -1; /* Invalid input. */ } } @@ -2618,10 +2547,10 @@ bool upb_handlers_getselector(const upb_fielddef *f, upb_handlertype_t type, break; case UPB_HANDLER_STARTSUBMSG: if (!upb_fielddef_issubmsg(f)) return false; - // Selectors for STARTSUBMSG are at the beginning of the table so that the - // selector can also be used as an index into the "sub" array of - // subhandlers. The indexes for the two into these two tables are the - // same, except that in the handler table the static selectors come first. + /* Selectors for STARTSUBMSG are at the beginning of the table so that the + * selector can also be used as an index into the "sub" array of + * subhandlers. The indexes for the two into these two tables are the + * same, except that in the handler table the static selectors come first. */ *s = f->index_ + UPB_STATIC_SELECTOR_COUNT; break; case UPB_HANDLER_ENDSUBMSG: @@ -2639,13 +2568,13 @@ uint32_t upb_handlers_selectorbaseoffset(const upb_fielddef *f) { uint32_t upb_handlers_selectorcount(const upb_fielddef *f) { uint32_t ret = 1; - if (upb_fielddef_isseq(f)) ret += 2; // STARTSEQ/ENDSEQ - if (upb_fielddef_isstring(f)) ret += 2; // [STRING]/STARTSTR/ENDSTR + if (upb_fielddef_isseq(f)) ret += 2; /* STARTSEQ/ENDSEQ */ + if (upb_fielddef_isstring(f)) ret += 2; /* [STRING]/STARTSTR/ENDSTR */ if (upb_fielddef_issubmsg(f)) { - // ENDSUBMSG (STARTSUBMSG is at table beginning) + /* ENDSUBMSG (STARTSUBMSG is at table beginning) */ ret += 0; if (upb_fielddef_lazy(f)) { - // STARTSTR/ENDSTR/STRING (for lazy) + /* STARTSTR/ENDSTR/STRING (for lazy) */ ret += 3; } } @@ -2709,7 +2638,7 @@ void upb_byteshandler_init(upb_byteshandler* h) { memset(h, 0, sizeof(*h)); } -// For when we support handlerfree callbacks. +/* For when we support handlerfree callbacks. */ void upb_byteshandler_uninit(upb_byteshandler* h) { UPB_UNUSED(h); } @@ -2765,17 +2694,17 @@ const void *UPB_UNTRACKED_REF = &untracked_val; /* arch-specific atomic primitives *******************************************/ -#ifdef UPB_THREAD_UNSAFE ////////////////////////////////////////////////////// +#ifdef UPB_THREAD_UNSAFE /*---------------------------------------------------*/ static void atomic_inc(uint32_t *a) { (*a)++; } static bool atomic_dec(uint32_t *a) { return --(*a) == 0; } -#elif defined(__GNUC__) || defined(__clang__) ////////////////////////////////// +#elif defined(__GNUC__) || defined(__clang__) /*------------------------------*/ static void atomic_inc(uint32_t *a) { __sync_fetch_and_add(a, 1); } static bool atomic_dec(uint32_t *a) { return __sync_sub_and_fetch(a, 1) == 0; } -#elif defined(WIN32) /////////////////////////////////////////////////////////// +#elif defined(WIN32) /*-------------------------------------------------------*/ #include @@ -2789,13 +2718,13 @@ static bool atomic_dec(upb_atomic_t *a) { Implement them or compile with UPB_THREAD_UNSAFE. #endif -// All static objects point to this refcount. -// It is special-cased in ref/unref below. +/* All static objects point to this refcount. + * It is special-cased in ref/unref below. */ uint32_t static_refcount = -1; -// We can avoid atomic ops for statically-declared objects. -// This is a minor optimization but nice since we can avoid degrading under -// contention in this case. +/* We can avoid atomic ops for statically-declared objects. + * This is a minor optimization but nice since we can avoid degrading under + * contention in this case. */ static void refgroup(uint32_t *group) { if (group != &static_refcount) @@ -2822,21 +2751,21 @@ static void upb_unlock() {} #else -// User must define functions that lock/unlock a global mutex and link this -// file against them. +/* User must define functions that lock/unlock a global mutex and link this + * file against them. */ void upb_lock(); void upb_unlock(); #endif -// UPB_DEBUG_REFS mode counts on being able to malloc() memory in some -// code-paths that can normally never fail, like upb_refcounted_ref(). Since -// we have no way to propagage out-of-memory errors back to the user, and since -// these errors can only occur in UPB_DEBUG_REFS mode, we immediately fail. +/* UPB_DEBUG_REFS mode counts on being able to malloc() memory in some + * code-paths that can normally never fail, like upb_refcounted_ref(). Since + * we have no way to propagage out-of-memory errors back to the user, and since + * these errors can only occur in UPB_DEBUG_REFS mode, we immediately fail. */ #define CHECK_OOM(predicate) if (!(predicate)) { assert(predicate); exit(1); } typedef struct { - int count; // How many refs there are (duplicates only allowed for ref2). + int count; /* How many refs there are (duplicates only allowed for ref2). */ bool is_ref2; } trackedref; @@ -2849,18 +2778,19 @@ static trackedref *trackedref_new(bool is_ref2) { } static void track(const upb_refcounted *r, const void *owner, bool ref2) { + upb_value v; + assert(owner); if (owner == UPB_UNTRACKED_REF) return; upb_lock(); - upb_value v; if (upb_inttable_lookupptr(r->refs, owner, &v)) { trackedref *ref = upb_value_getptr(v); - // Since we allow multiple ref2's for the same to/from pair without - // allocating separate memory for each one, we lose the fine-grained - // tracking behavior we get with regular refs. Since ref2s only happen - // inside upb, we'll accept this limitation until/unless there is a really - // difficult upb-internal bug that can't be figured out without it. + /* Since we allow multiple ref2's for the same to/from pair without + * allocating separate memory for each one, we lose the fine-grained + * tracking behavior we get with regular refs. Since ref2s only happen + * inside upb, we'll accept this limitation until/unless there is a really + * difficult upb-internal bug that can't be figured out without it. */ assert(ref2); assert(ref->is_ref2); ref->count++; @@ -2869,8 +2799,8 @@ static void track(const upb_refcounted *r, const void *owner, bool ref2) { bool ok = upb_inttable_insertptr(r->refs, owner, upb_value_ptr(ref)); CHECK_OOM(ok); if (ref2) { - // We know this cast is safe when it is a ref2, because it's coming from - // another refcounted object. + /* We know this cast is safe when it is a ref2, because it's coming from + * another refcounted object. */ const upb_refcounted *from = owner; assert(!upb_inttable_lookupptr(from->ref2s, r, NULL)); ok = upb_inttable_insertptr(from->ref2s, r, upb_value_ptr(NULL)); @@ -2881,22 +2811,25 @@ static void track(const upb_refcounted *r, const void *owner, bool ref2) { } static void untrack(const upb_refcounted *r, const void *owner, bool ref2) { + upb_value v; + bool found; + trackedref *ref; + assert(owner); if (owner == UPB_UNTRACKED_REF) return; upb_lock(); - upb_value v; - bool found = upb_inttable_lookupptr(r->refs, owner, &v); - // This assert will fail if an owner attempts to release a ref it didn't have. + found = upb_inttable_lookupptr(r->refs, owner, &v); + /* This assert will fail if an owner attempts to release a ref it didn't have. */ UPB_ASSERT_VAR(found, found); - trackedref *ref = upb_value_getptr(v); + ref = upb_value_getptr(v); assert(ref->is_ref2 == ref2); if (--ref->count == 0) { free(ref); upb_inttable_removeptr(r->refs, owner, NULL); if (ref2) { - // We know this cast is safe when it is a ref2, because it's coming from - // another refcounted object. + /* We know this cast is safe when it is a ref2, because it's coming from + * another refcounted object. */ const upb_refcounted *from = owner; bool removed = upb_inttable_removeptr(from->ref2s, r, NULL); assert(removed); @@ -2906,32 +2839,41 @@ static void untrack(const upb_refcounted *r, const void *owner, bool ref2) { } static void checkref(const upb_refcounted *r, const void *owner, bool ref2) { - upb_lock(); upb_value v; - bool found = upb_inttable_lookupptr(r->refs, owner, &v); + bool found; + trackedref *ref; + + upb_lock(); + found = upb_inttable_lookupptr(r->refs, owner, &v); UPB_ASSERT_VAR(found, found); - trackedref *ref = upb_value_getptr(v); + ref = upb_value_getptr(v); assert(ref->is_ref2 == ref2); upb_unlock(); } -// Populates the given UPB_CTYPE_INT32 inttable with counts of ref2's that -// originate from the given owner. +/* Populates the given UPB_CTYPE_INT32 inttable with counts of ref2's that + * originate from the given owner. */ static void getref2s(const upb_refcounted *owner, upb_inttable *tab) { - upb_lock(); upb_inttable_iter i; + + upb_lock(); upb_inttable_begin(&i, owner->ref2s); for(; !upb_inttable_done(&i); upb_inttable_next(&i)) { + upb_value v; + upb_value count; + trackedref *ref; + bool ok; + bool found; + upb_refcounted *to = (upb_refcounted*)upb_inttable_iter_key(&i); - // To get the count we need to look in the target's table. - upb_value v; - bool found = upb_inttable_lookupptr(to->refs, owner, &v); + /* To get the count we need to look in the target's table. */ + found = upb_inttable_lookupptr(to->refs, owner, &v); assert(found); - trackedref *ref = upb_value_getptr(v); - upb_value count = upb_value_int32(ref->count); + ref = upb_value_getptr(v); + count = upb_value_int32(ref->count); - bool ok = upb_inttable_insertptr(tab, to, count); + ok = upb_inttable_insertptr(tab, to, count); CHECK_OOM(ok); } upb_unlock(); @@ -2945,15 +2887,18 @@ typedef struct { static void visit_check(const upb_refcounted *obj, const upb_refcounted *subobj, void *closure) { check_state *s = closure; - assert(obj == s->obj); - assert(subobj); upb_inttable *ref2 = &s->ref2; upb_value v; - bool removed = upb_inttable_removeptr(ref2, subobj, &v); - // The following assertion will fail if the visit() function visits a subobj - // that it did not have a ref2 on, or visits the same subobj too many times. + bool removed; + int32_t newcount; + + assert(obj == s->obj); + assert(subobj); + removed = upb_inttable_removeptr(ref2, subobj, &v); + /* The following assertion will fail if the visit() function visits a subobj + * that it did not have a ref2 on, or visits the same subobj too many times. */ assert(removed); - int32_t newcount = upb_value_getint32(v) - 1; + newcount = upb_value_getint32(v) - 1; if (newcount > 0) { upb_inttable_insert(ref2, (uintptr_t)subobj, upb_value_int32(newcount)); } @@ -2961,19 +2906,21 @@ static void visit_check(const upb_refcounted *obj, const upb_refcounted *subobj, static void visit(const upb_refcounted *r, upb_refcounted_visit *v, void *closure) { - // In DEBUG_REFS mode we know what existing ref2 refs there are, so we know - // exactly the set of nodes that visit() should visit. So we verify visit()'s - // correctness here. + bool ok; + + /* In DEBUG_REFS mode we know what existing ref2 refs there are, so we know + * exactly the set of nodes that visit() should visit. So we verify visit()'s + * correctness here. */ check_state state; state.obj = r; - bool ok = upb_inttable_init(&state.ref2, UPB_CTYPE_INT32); + ok = upb_inttable_init(&state.ref2, UPB_CTYPE_INT32); CHECK_OOM(ok); getref2s(r, &state.ref2); - // This should visit any children in the ref2 table. + /* This should visit any children in the ref2 table. */ if (r->vtbl->visit) r->vtbl->visit(r, visit_check, &state); - // This assertion will fail if the visit() function missed any children. + /* This assertion will fail if the visit() function missed any children. */ assert(upb_inttable_count(&state.ref2) == 0); upb_inttable_uninit(&state.ref2); if (r->vtbl->visit) r->vtbl->visit(r, v, closure); @@ -3037,27 +2984,27 @@ static void visit(const upb_refcounted *r, upb_refcounted_visit *v, if (r->vtbl->visit) r->vtbl->visit(r, v, closure); } -#endif // UPB_DEBUG_REFS +#endif /* UPB_DEBUG_REFS */ /* freeze() *******************************************************************/ -// The freeze() operation is by far the most complicated part of this scheme. -// We compute strongly-connected components and then mutate the graph such that -// we preserve the invariants documented at the top of this file. And we must -// handle out-of-memory errors gracefully (without leaving the graph -// inconsistent), which adds to the fun. +/* The freeze() operation is by far the most complicated part of this scheme. + * We compute strongly-connected components and then mutate the graph such that + * we preserve the invariants documented at the top of this file. And we must + * handle out-of-memory errors gracefully (without leaving the graph + * inconsistent), which adds to the fun. */ -// The state used by the freeze operation (shared across many functions). +/* The state used by the freeze operation (shared across many functions). */ typedef struct { int depth; int maxdepth; uint64_t index; - // Maps upb_refcounted* -> attributes (color, etc). attr layout varies by - // color. + /* Maps upb_refcounted* -> attributes (color, etc). attr layout varies by + * color. */ upb_inttable objattr; - upb_inttable stack; // stack of upb_refcounted* for Tarjan's algorithm. - upb_inttable groups; // array of uint32_t*, malloc'd refcounts for new groups + upb_inttable stack; /* stack of upb_refcounted* for Tarjan's algorithm. */ + upb_inttable groups; /* array of uint32_t*, malloc'd refcounts for new groups */ upb_status *status; jmp_buf err; } tarjan; @@ -3066,15 +3013,15 @@ static void release_ref2(const upb_refcounted *obj, const upb_refcounted *subobj, void *closure); -// Node attributes ///////////////////////////////////////////////////////////// +/* Node attributes -----------------------------------------------------------*/ -// After our analysis phase all nodes will be either GRAY or WHITE. +/* After our analysis phase all nodes will be either GRAY or WHITE. */ typedef enum { - BLACK = 0, // Object has not been seen. - GRAY, // Object has been found via a refgroup but may not be reachable. - GREEN, // Object is reachable and is currently on the Tarjan stack. - WHITE, // Object is reachable and has been assigned a group (SCC). + BLACK = 0, /* Object has not been seen. */ + GRAY, /* Object has been found via a refgroup but may not be reachable. */ + GREEN, /* Object is reachable and is currently on the Tarjan stack. */ + WHITE /* Object is reachable and has been assigned a group (SCC). */ } color_t; UPB_NORETURN static void err(tarjan *t) { longjmp(t->err, 1); } @@ -3102,7 +3049,7 @@ static void setattr(tarjan *t, const upb_refcounted *r, uint64_t attr) { } static color_t color(tarjan *t, const upb_refcounted *r) { - return trygetattr(t, r) & 0x3; // Color is always stored in the low 2 bits. + return trygetattr(t, r) & 0x3; /* Color is always stored in the low 2 bits. */ } static void set_gray(tarjan *t, const upb_refcounted *r) { @@ -3110,11 +3057,11 @@ static void set_gray(tarjan *t, const upb_refcounted *r) { setattr(t, r, GRAY); } -// Pushes an obj onto the Tarjan stack and sets it to GREEN. +/* Pushes an obj onto the Tarjan stack and sets it to GREEN. */ static void push(tarjan *t, const upb_refcounted *r) { assert(color(t, r) == BLACK || color(t, r) == GRAY); - // This defines the attr layout for the GREEN state. "index" and "lowlink" - // get 31 bits, which is plenty (limit of 2B objects frozen at a time). + /* This defines the attr layout for the GREEN state. "index" and "lowlink" + * get 31 bits, which is plenty (limit of 2B objects frozen at a time). */ setattr(t, r, GREEN | (t->index << 2) | (t->index << 33)); if (++t->index == 0x80000000) { upb_status_seterrmsg(t->status, "too many objects to freeze"); @@ -3123,13 +3070,13 @@ static void push(tarjan *t, const upb_refcounted *r) { upb_inttable_push(&t->stack, upb_value_ptr((void*)r)); } -// Pops an obj from the Tarjan stack and sets it to WHITE, with a ptr to its -// SCC group. +/* Pops an obj from the Tarjan stack and sets it to WHITE, with a ptr to its + * SCC group. */ static upb_refcounted *pop(tarjan *t) { upb_refcounted *r = upb_value_getptr(upb_inttable_pop(&t->stack)); assert(color(t, r) == GREEN); - // This defines the attr layout for nodes in the WHITE state. - // Top of group stack is [group, NULL]; we point at group. + /* This defines the attr layout for nodes in the WHITE state. + * Top of group stack is [group, NULL]; we point at group. */ setattr(t, r, WHITE | (upb_inttable_count(&t->groups) - 2) << 8); return r; } @@ -3137,7 +3084,7 @@ static upb_refcounted *pop(tarjan *t) { static void tarjan_newgroup(tarjan *t) { uint32_t *group = malloc(sizeof(*group)); if (!group) oom(t); - // Push group and empty group leader (we'll fill in leader later). + /* Push group and empty group leader (we'll fill in leader later). */ if (!upb_inttable_push(&t->groups, upb_value_ptr(group)) || !upb_inttable_push(&t->groups, upb_value_ptr(NULL))) { free(group); @@ -3165,21 +3112,27 @@ static void set_lowlink(tarjan *t, const upb_refcounted *r, uint32_t lowlink) { } static uint32_t *group(tarjan *t, upb_refcounted *r) { - assert(color(t, r) == WHITE); - uint64_t groupnum = getattr(t, r) >> 8; + uint64_t groupnum; upb_value v; - bool found = upb_inttable_lookup(&t->groups, groupnum, &v); + bool found; + + assert(color(t, r) == WHITE); + groupnum = getattr(t, r) >> 8; + found = upb_inttable_lookup(&t->groups, groupnum, &v); UPB_ASSERT_VAR(found, found); return upb_value_getptr(v); } -// If the group leader for this object's group has not previously been set, -// the given object is assigned to be its leader. +/* If the group leader for this object's group has not previously been set, + * the given object is assigned to be its leader. */ static upb_refcounted *groupleader(tarjan *t, upb_refcounted *r) { - assert(color(t, r) == WHITE); - uint64_t leader_slot = (getattr(t, r) >> 8) + 1; + uint64_t leader_slot; upb_value v; - bool found = upb_inttable_lookup(&t->groups, leader_slot, &v); + bool found; + + assert(color(t, r) == WHITE); + leader_slot = (getattr(t, r) >> 8) + 1; + found = upb_inttable_lookup(&t->groups, leader_slot, &v); UPB_ASSERT_VAR(found, found); if (upb_value_getptr(v)) { return upb_value_getptr(v); @@ -3191,10 +3144,10 @@ static upb_refcounted *groupleader(tarjan *t, upb_refcounted *r) { } -// Tarjan's algorithm ////////////////////////////////////////////////////////// +/* Tarjan's algorithm --------------------------------------------------------*/ -// See: -// http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm +/* See: + * http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm */ static void do_tarjan(const upb_refcounted *obj, tarjan *t); static void tarjan_visit(const upb_refcounted *obj, @@ -3205,14 +3158,14 @@ static void tarjan_visit(const upb_refcounted *obj, upb_status_seterrf(t->status, "graph too deep to freeze (%d)", t->maxdepth); err(t); } else if (subobj->is_frozen || color(t, subobj) == WHITE) { - // Do nothing: we don't want to visit or color already-frozen nodes, - // and WHITE nodes have already been assigned a SCC. + /* Do nothing: we don't want to visit or color already-frozen nodes, + * and WHITE nodes have already been assigned a SCC. */ } else if (color(t, subobj) < GREEN) { - // Subdef has not yet been visited; recurse on it. + /* Subdef has not yet been visited; recurse on it. */ do_tarjan(subobj, t); set_lowlink(t, obj, UPB_MIN(lowlink(t, obj), lowlink(t, subobj))); } else if (color(t, subobj) == GREEN) { - // Subdef is in the stack and hence in the current SCC. + /* Subdef is in the stack and hence in the current SCC. */ set_lowlink(t, obj, UPB_MIN(lowlink(t, obj), idx(t, subobj))); } --t->depth; @@ -3220,7 +3173,7 @@ static void tarjan_visit(const upb_refcounted *obj, static void do_tarjan(const upb_refcounted *obj, tarjan *t) { if (color(t, obj) == BLACK) { - // We haven't seen this object's group; mark the whole group GRAY. + /* We haven't seen this object's group; mark the whole group GRAY. */ const upb_refcounted *o = obj; do { set_gray(t, o); } while ((o = o->next) != obj); } @@ -3235,15 +3188,15 @@ static void do_tarjan(const upb_refcounted *obj, tarjan *t) { } -// freeze() //////////////////////////////////////////////////////////////////// +/* freeze() ------------------------------------------------------------------*/ static void crossref(const upb_refcounted *r, const upb_refcounted *subobj, void *_t) { tarjan *t = _t; assert(color(t, r) > BLACK); if (color(t, subobj) > BLACK && r->group != subobj->group) { - // Previously this ref was not reflected in subobj->group because they - // were in the same group; now that they are split a ref must be taken. + /* Previously this ref was not reflected in subobj->group because they + * were in the same group; now that they are split a ref must be taken. */ refgroup(subobj->group); } } @@ -3251,10 +3204,12 @@ static void crossref(const upb_refcounted *r, const upb_refcounted *subobj, static bool freeze(upb_refcounted *const*roots, int n, upb_status *s, int maxdepth) { volatile bool ret = false; + int i; + upb_inttable_iter iter; - // We run in two passes so that we can allocate all memory before performing - // any mutation of the input -- this allows us to leave the input unchanged - // in the case of memory allocation failure. + /* We run in two passes so that we can allocate all memory before performing + * any mutation of the input -- this allows us to leave the input unchanged + * in the case of memory allocation failure. */ tarjan t; t.index = 0; t.depth = 0; @@ -3266,64 +3221,65 @@ static bool freeze(upb_refcounted *const*roots, int n, upb_status *s, if (setjmp(t.err) != 0) goto err4; - for (int i = 0; i < n; i++) { + for (i = 0; i < n; i++) { if (color(&t, roots[i]) < GREEN) { do_tarjan(roots[i], &t); } } - // If we've made it this far, no further errors are possible so it's safe to - // mutate the objects without risk of leaving them in an inconsistent state. + /* If we've made it this far, no further errors are possible so it's safe to + * mutate the objects without risk of leaving them in an inconsistent state. */ ret = true; - // The transformation that follows requires care. The preconditions are: - // - all objects in attr map are WHITE or GRAY, and are in mutable groups - // (groups of all mutable objs) - // - no ref2(to, from) refs have incremented count(to) if both "to" and - // "from" are in our attr map (this follows from invariants (2) and (3)) - - // Pass 1: we remove WHITE objects from their mutable groups, and add them to - // new groups according to the SCC's we computed. These new groups will - // consist of only frozen objects. None will be immediately collectible, - // because WHITE objects are by definition reachable from one of "roots", - // which the caller must own refs on. - upb_inttable_iter i; - upb_inttable_begin(&i, &t.objattr); - for(; !upb_inttable_done(&i); upb_inttable_next(&i)) { - upb_refcounted *obj = (upb_refcounted*)upb_inttable_iter_key(&i); - // Since removal from a singly-linked list requires access to the object's - // predecessor, we consider obj->next instead of obj for moving. With the - // while() loop we guarantee that we will visit every node's predecessor. - // Proof: - // 1. every node's predecessor is in our attr map. - // 2. though the loop body may change a node's predecessor, it will only - // change it to be the node we are currently operating on, so with a - // while() loop we guarantee ourselves the chance to remove each node. + /* The transformation that follows requires care. The preconditions are: + * - all objects in attr map are WHITE or GRAY, and are in mutable groups + * (groups of all mutable objs) + * - no ref2(to, from) refs have incremented count(to) if both "to" and + * "from" are in our attr map (this follows from invariants (2) and (3)) */ + + /* Pass 1: we remove WHITE objects from their mutable groups, and add them to + * new groups according to the SCC's we computed. These new groups will + * consist of only frozen objects. None will be immediately collectible, + * because WHITE objects are by definition reachable from one of "roots", + * which the caller must own refs on. */ + upb_inttable_begin(&iter, &t.objattr); + for(; !upb_inttable_done(&iter); upb_inttable_next(&iter)) { + upb_refcounted *obj = (upb_refcounted*)upb_inttable_iter_key(&iter); + /* Since removal from a singly-linked list requires access to the object's + * predecessor, we consider obj->next instead of obj for moving. With the + * while() loop we guarantee that we will visit every node's predecessor. + * Proof: + * 1. every node's predecessor is in our attr map. + * 2. though the loop body may change a node's predecessor, it will only + * change it to be the node we are currently operating on, so with a + * while() loop we guarantee ourselves the chance to remove each node. */ while (color(&t, obj->next) == WHITE && group(&t, obj->next) != obj->next->group) { - // Remove from old group. + upb_refcounted *leader; + + /* Remove from old group. */ upb_refcounted *move = obj->next; if (obj == move) { - // Removing the last object from a group. + /* Removing the last object from a group. */ assert(*obj->group == obj->individual_count); free(obj->group); } else { obj->next = move->next; - // This may decrease to zero; we'll collect GRAY objects (if any) that - // remain in the group in the third pass. + /* This may decrease to zero; we'll collect GRAY objects (if any) that + * remain in the group in the third pass. */ assert(*move->group >= move->individual_count); *move->group -= move->individual_count; } - // Add to new group. - upb_refcounted *leader = groupleader(&t, move); + /* Add to new group. */ + leader = groupleader(&t, move); if (move == leader) { - // First object added to new group is its leader. + /* First object added to new group is its leader. */ move->group = group(&t, move); move->next = move; *move->group = move->individual_count; } else { - // Group already has at least one object in it. + /* Group already has at least one object in it. */ assert(leader->group == group(&t, move)); move->group = group(&t, move); move->next = leader->next; @@ -3335,40 +3291,42 @@ static bool freeze(upb_refcounted *const*roots, int n, upb_status *s, } } - // Pass 2: GRAY and WHITE objects "obj" with ref2(to, obj) references must - // increment count(to) if group(obj) != group(to) (which could now be the - // case if "to" was just frozen). - upb_inttable_begin(&i, &t.objattr); - for(; !upb_inttable_done(&i); upb_inttable_next(&i)) { - upb_refcounted *obj = (upb_refcounted*)upb_inttable_iter_key(&i); + /* Pass 2: GRAY and WHITE objects "obj" with ref2(to, obj) references must + * increment count(to) if group(obj) != group(to) (which could now be the + * case if "to" was just frozen). */ + upb_inttable_begin(&iter, &t.objattr); + for(; !upb_inttable_done(&iter); upb_inttable_next(&iter)) { + upb_refcounted *obj = (upb_refcounted*)upb_inttable_iter_key(&iter); visit(obj, crossref, &t); } - // Pass 3: GRAY objects are collected if their group's refcount dropped to - // zero when we removed its white nodes. This can happen if they had only - // been kept alive by virtue of sharing a group with an object that was just - // frozen. - // - // It is important that we do this last, since the GRAY object's free() - // function could call unref2() on just-frozen objects, which will decrement - // refs that were added in pass 2. - upb_inttable_begin(&i, &t.objattr); - for(; !upb_inttable_done(&i); upb_inttable_next(&i)) { - upb_refcounted *obj = (upb_refcounted*)upb_inttable_iter_key(&i); + /* Pass 3: GRAY objects are collected if their group's refcount dropped to + * zero when we removed its white nodes. This can happen if they had only + * been kept alive by virtue of sharing a group with an object that was just + * frozen. + * + * It is important that we do this last, since the GRAY object's free() + * function could call unref2() on just-frozen objects, which will decrement + * refs that were added in pass 2. */ + upb_inttable_begin(&iter, &t.objattr); + for(; !upb_inttable_done(&iter); upb_inttable_next(&iter)) { + upb_refcounted *obj = (upb_refcounted*)upb_inttable_iter_key(&iter); if (obj->group == NULL || *obj->group == 0) { if (obj->group) { - // We eagerly free() the group's count (since we can't easily determine - // the group's remaining size it's the easiest way to ensure it gets - // done). + upb_refcounted *o; + + /* We eagerly free() the group's count (since we can't easily determine + * the group's remaining size it's the easiest way to ensure it gets + * done). */ free(obj->group); - // Visit to release ref2's (done in a separate pass since release_ref2 - // depends on o->group being unmodified so it can test merged()). - upb_refcounted *o = obj; + /* Visit to release ref2's (done in a separate pass since release_ref2 + * depends on o->group being unmodified so it can test merged()). */ + o = obj; do { visit(o, release_ref2, NULL); } while ((o = o->next) != obj); - // Mark "group" fields as NULL so we know to free the objects later in - // this loop, but also don't try to delete the group twice. + /* Mark "group" fields as NULL so we know to free the objects later in + * this loop, but also don't try to delete the group twice. */ o = obj; do { o->group = NULL; } while ((o = o->next) != obj); } @@ -3378,9 +3336,9 @@ static bool freeze(upb_refcounted *const*roots, int n, upb_status *s, err4: if (!ret) { - upb_inttable_begin(&i, &t.groups); - for(; !upb_inttable_done(&i); upb_inttable_next(&i)) - free(upb_value_getptr(upb_inttable_iter_value(&i))); + upb_inttable_begin(&iter, &t.groups); + for(; !upb_inttable_done(&iter); upb_inttable_next(&iter)) + free(upb_value_getptr(upb_inttable_iter_value(&iter))); } upb_inttable_uninit(&t.groups); err3: @@ -3399,21 +3357,24 @@ static bool merged(const upb_refcounted *r, const upb_refcounted *r2) { } static void merge(upb_refcounted *r, upb_refcounted *from) { + upb_refcounted *base; + upb_refcounted *tmp; + if (merged(r, from)) return; *r->group += *from->group; free(from->group); - upb_refcounted *base = from; - - // Set all refcount pointers in the "from" chain to the merged refcount. - // - // TODO(haberman): this linear algorithm can result in an overall O(n^2) bound - // if the user continuously extends a group by one object. Prevent this by - // using one of the techniques in this paper: - // ftp://www.ncedc.org/outgoing/geomorph/dino/orals/p245-tarjan.pdf + base = from; + + /* Set all refcount pointers in the "from" chain to the merged refcount. + * + * TODO(haberman): this linear algorithm can result in an overall O(n^2) bound + * if the user continuously extends a group by one object. Prevent this by + * using one of the techniques in this paper: + * ftp://www.ncedc.org/outgoing/geomorph/dino/orals/p245-tarjan.pdf */ do { from->group = r->group; } while ((from = from->next) != base); - // Merge the two circularly linked lists by swapping their next pointers. - upb_refcounted *tmp = r->next; + /* Merge the two circularly linked lists by swapping their next pointers. */ + tmp = r->next; r->next = base->next; base->next = tmp; } @@ -3433,11 +3394,13 @@ static void release_ref2(const upb_refcounted *obj, static void unref(const upb_refcounted *r) { if (unrefgroup(r->group)) { + const upb_refcounted *o; + free(r->group); - // In two passes, since release_ref2 needs a guarantee that any subobjs - // are alive. - const upb_refcounted *o = r; + /* In two passes, since release_ref2 needs a guarantee that any subobjs + * are alive. */ + o = r; do { visit(o, release_ref2, NULL); } while((o = o->next) != r); o = r; @@ -3461,6 +3424,18 @@ static void freeobj(upb_refcounted *o) { bool upb_refcounted_init(upb_refcounted *r, const struct upb_refcounted_vtbl *vtbl, const void *owner) { +#ifndef NDEBUG + /* Endianness check. This is unrelated to upb_refcounted, it's just a + * convenient place to put the check that we can be assured will run for + * basically every program using upb. */ + const int x = 1; +#ifdef UPB_BIG_ENDIAN + assert(*(char*)&x != 1); +#else + assert(*(char*)&x == 1); +#endif +#endif + r->next = r; r->vtbl = vtbl; r->individual_count = 0; @@ -3495,7 +3470,7 @@ void upb_refcounted_unref(const upb_refcounted *r, const void *owner) { } void upb_refcounted_ref2(const upb_refcounted *r, upb_refcounted *from) { - assert(!from->is_frozen); // Non-const pointer implies this. + assert(!from->is_frozen); /* Non-const pointer implies this. */ track(r, from, true); if (r->is_frozen) { refgroup(r->group); @@ -3505,7 +3480,7 @@ void upb_refcounted_ref2(const upb_refcounted *r, upb_refcounted *from) { } void upb_refcounted_unref2(const upb_refcounted *r, upb_refcounted *from) { - assert(!from->is_frozen); // Non-const pointer implies this. + assert(!from->is_frozen); /* Non-const pointer implies this. */ untrack(r, from, true); if (r->is_frozen) { unref(r); @@ -3529,7 +3504,8 @@ void upb_refcounted_checkref(const upb_refcounted *r, const void *owner) { bool upb_refcounted_freeze(upb_refcounted *const*roots, int n, upb_status *s, int maxdepth) { - for (int i = 0; i < n; i++) { + int i; + for (i = 0; i < n; i++) { assert(!roots[i]->is_frozen); } return freeze(roots, n, s, maxdepth); @@ -3544,7 +3520,7 @@ bool upb_refcounted_freeze(upb_refcounted *const*roots, int n, upb_status *s, #include -// Fallback implementation if the shim is not specialized by the JIT. +/* Fallback implementation if the shim is not specialized by the JIT. */ #define SHIM_WRITER(type, ctype) \ bool upb_shim_set ## type (void *c, const void *hd, ctype val) { \ uint8_t *m = c; \ @@ -3566,12 +3542,14 @@ SHIM_WRITER(bool, bool) bool upb_shim_set(upb_handlers *h, const upb_fielddef *f, size_t offset, int32_t hasbit) { + upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER; + bool ok; + upb_shim_data *d = malloc(sizeof(*d)); if (!d) return false; d->offset = offset; d->hasbit = hasbit; - upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER; upb_handlerattr_sethandlerdata(&attr, d); upb_handlerattr_setalwaysok(&attr, true); upb_handlers_addcleanup(h, d, free); @@ -3580,7 +3558,7 @@ bool upb_shim_set(upb_handlers *h, const upb_fielddef *f, size_t offset, case UPB_TYPE_##u: \ ok = upb_handlers_set##l(h, f, upb_shim_set##l, &attr); break; - bool ok = false; + ok = false; switch (upb_fielddef_type(f)) { TYPE(INT64, int64); @@ -3634,27 +3612,6 @@ const upb_shim_data *upb_shim_getdata(const upb_handlers *h, upb_selector_t s, #include #include -bool upb_symtab_isfrozen(const upb_symtab *s) { - return upb_refcounted_isfrozen(UPB_UPCAST(s)); -} - -void upb_symtab_ref(const upb_symtab *s, const void *owner) { - upb_refcounted_ref(UPB_UPCAST(s), owner); -} - -void upb_symtab_unref(const upb_symtab *s, const void *owner) { - upb_refcounted_unref(UPB_UPCAST(s), owner); -} - -void upb_symtab_donateref( - const upb_symtab *s, const void *from, const void *to) { - upb_refcounted_donateref(UPB_UPCAST(s), from, to); -} - -void upb_symtab_checkref(const upb_symtab *s, const void *owner) { - upb_refcounted_checkref(UPB_UPCAST(s), owner); -} - static void upb_symtab_free(upb_refcounted *r) { upb_symtab *s = (upb_symtab*)r; upb_strtable_iter i; @@ -3671,18 +3628,21 @@ static void upb_symtab_free(upb_refcounted *r) { upb_symtab *upb_symtab_new(const void *owner) { static const struct upb_refcounted_vtbl vtbl = {NULL, &upb_symtab_free}; upb_symtab *s = malloc(sizeof(*s)); - upb_refcounted_init(UPB_UPCAST(s), &vtbl, owner); + upb_refcounted_init(upb_symtab_upcast_mutable(s), &vtbl, owner); upb_strtable_init(&s->symtab, UPB_CTYPE_PTR); return s; } void upb_symtab_freeze(upb_symtab *s) { + upb_refcounted *r; + bool ok; + assert(!upb_symtab_isfrozen(s)); - upb_refcounted *r = UPB_UPCAST(s); - // The symtab does not take ref2's (see refcounted.h) on the defs, because - // defs cannot refer back to the table and therefore cannot create cycles. So - // 0 will suffice for maxdepth here. - bool ok = upb_refcounted_freeze(&r, 1, NULL, 0); + r = upb_symtab_upcast_mutable(s); + /* The symtab does not take ref2's (see refcounted.h) on the defs, because + * defs cannot refer back to the table and therefore cannot create cycles. So + * 0 will suffice for maxdepth here. */ + ok = upb_refcounted_freeze(&r, 1, NULL, 0); UPB_ASSERT_VAR(ok, ok); } @@ -3707,19 +3667,19 @@ const upb_enumdef *upb_symtab_lookupenum(const upb_symtab *s, const char *sym) { return def ? upb_dyncast_enumdef(def) : NULL; } -// Given a symbol and the base symbol inside which it is defined, find the -// symbol's definition in t. +/* Given a symbol and the base symbol inside which it is defined, find the + * symbol's definition in t. */ static upb_def *upb_resolvename(const upb_strtable *t, const char *base, const char *sym) { if(strlen(sym) == 0) return NULL; if(sym[0] == '.') { - // Symbols starting with '.' are absolute, so we do a single lookup. - // Slice to omit the leading '.' + /* Symbols starting with '.' are absolute, so we do a single lookup. + * Slice to omit the leading '.' */ upb_value v; return upb_strtable_lookup(t, sym + 1, &v) ? upb_value_getptr(v) : NULL; } else { - // Remove components from base until we find an entry or run out. - // TODO: This branch is totally broken, but currently not used. + /* Remove components from base until we find an entry or run out. + * TODO: This branch is totally broken, but currently not used. */ (void)base; assert(false); return NULL; @@ -3732,36 +3692,41 @@ const upb_def *upb_symtab_resolve(const upb_symtab *s, const char *base, return ret; } -// Searches def and its children to find defs that have the same name as any -// def in "addtab." Returns true if any where found, and as a side-effect adds -// duplicates of these defs into addtab. -// -// We use a modified depth-first traversal that traverses each SCC (which we -// already computed) as if it were a single node. This allows us to traverse -// the possibly-cyclic graph as if it were a DAG and to dup the correct set of -// nodes with O(n) time. +/* Searches def and its children to find defs that have the same name as any + * def in "addtab." Returns true if any where found, and as a side-effect adds + * duplicates of these defs into addtab. + * + * We use a modified depth-first traversal that traverses each SCC (which we + * already computed) as if it were a single node. This allows us to traverse + * the possibly-cyclic graph as if it were a DAG and to dup the correct set of + * nodes with O(n) time. */ static bool upb_resolve_dfs(const upb_def *def, upb_strtable *addtab, const void *new_owner, upb_inttable *seen, upb_status *s) { - // Memoize results of this function for efficiency (since we're traversing a - // DAG this is not needed to limit the depth of the search). + /* Memoize results of this function for efficiency (since we're traversing a + * DAG this is not needed to limit the depth of the search). */ upb_value v; + bool need_dup; + const upb_def *base; + if (upb_inttable_lookup(seen, (uintptr_t)def, &v)) return upb_value_getbool(v); - // Visit submessages for all messages in the SCC. - bool need_dup = false; - const upb_def *base = def; + /* Visit submessages for all messages in the SCC. */ + need_dup = false; + base = def; do { + upb_value v; + const upb_msgdef *m; + assert(upb_def_isfrozen(def)); if (def->type == UPB_DEF_FIELD) continue; - upb_value v; if (upb_strtable_lookup(addtab, upb_def_fullname(def), &v)) { need_dup = true; } - // For messages, continue the recursion by visiting all subdefs. - const upb_msgdef *m = upb_dyncast_msgdef(def); + /* For messages, continue the recursion by visiting all subdefs. */ + m = upb_dyncast_msgdef(def); if (m) { upb_msg_field_iter i; for(upb_msg_field_begin(&i, m); @@ -3769,7 +3734,7 @@ static bool upb_resolve_dfs(const upb_def *def, upb_strtable *addtab, 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. + /* |= to avoid short-circuit; we need its side-effects. */ need_dup |= upb_resolve_dfs( upb_fielddef_subdef(f), addtab, new_owner, seen, s); if (!upb_ok(s)) return false; @@ -3778,11 +3743,13 @@ static bool upb_resolve_dfs(const upb_def *def, upb_strtable *addtab, } while ((def = (upb_def*)def->base.next) != base); if (need_dup) { - // Dup any defs that don't already have entries in addtab. + /* Dup any defs that don't already have entries in addtab. */ def = base; do { + const char *name; + if (def->type == UPB_DEF_FIELD) continue; - const char *name = upb_def_fullname(def); + name = upb_def_fullname(def); if (!upb_strtable_lookup(addtab, name, NULL)) { upb_def *newdef = upb_def_dup(def, new_owner); if (!newdef) goto oom; @@ -3801,34 +3768,41 @@ oom: return false; } -// TODO(haberman): we need a lot more testing of error conditions. -// The came_from_user stuff in particular is not tested. +/* TODO(haberman): we need a lot more testing of error conditions. + * The came_from_user stuff in particular is not tested. */ bool upb_symtab_add(upb_symtab *s, upb_def *const*defs, int n, void *ref_donor, upb_status *status) { - assert(!upb_symtab_isfrozen(s)); + int i; + upb_strtable_iter iter; upb_def **add_defs = NULL; upb_strtable addtab; + upb_inttable seen; + + assert(!upb_symtab_isfrozen(s)); if (!upb_strtable_init(&addtab, UPB_CTYPE_PTR)) { upb_status_seterrmsg(status, "out of memory"); return false; } - // Add new defs to our "add" set. - for (int i = 0; i < n; i++) { + /* Add new defs to our "add" set. */ + for (i = 0; i < n; i++) { upb_def *def = defs[i]; + const char *fullname; + upb_fielddef *f; + if (upb_def_isfrozen(def)) { upb_status_seterrmsg(status, "added defs must be mutable"); goto err; } assert(!upb_def_isfrozen(def)); - const char *fullname = upb_def_fullname(def); + fullname = upb_def_fullname(def); if (!fullname) { upb_status_seterrmsg( status, "Anonymous defs cannot be added to a symtab"); goto err; } - upb_fielddef *f = upb_dyncast_fielddef_mutable(def); + f = upb_dyncast_fielddef_mutable(def); if (f) { if (!upb_fielddef_containingtypename(f)) { @@ -3842,8 +3816,8 @@ bool upb_symtab_add(upb_symtab *s, upb_def *const*defs, int n, void *ref_donor, upb_status_seterrf(status, "Conflicting defs named '%s'", fullname); goto err; } - // We need this to back out properly, because if there is a failure we - // need to donate the ref back to the caller. + /* We need this to back out properly, because if there is a failure we + * need to donate the ref back to the caller. */ def->came_from_user = true; upb_def_donateref(def, ref_donor, s); if (!upb_strtable_insert(&addtab, fullname, upb_value_ptr(def))) @@ -3851,31 +3825,33 @@ bool upb_symtab_add(upb_symtab *s, upb_def *const*defs, int n, void *ref_donor, } } - // Add standalone fielddefs (ie. extensions) to the appropriate messages. - // If the appropriate message only exists in the existing symtab, duplicate - // it so we have a mutable copy we can add the fields to. - for (int i = 0; i < n; i++) { + /* Add standalone fielddefs (ie. extensions) to the appropriate messages. + * If the appropriate message only exists in the existing symtab, duplicate + * it so we have a mutable copy we can add the fields to. */ + for (i = 0; i < n; i++) { upb_def *def = defs[i]; upb_fielddef *f = upb_dyncast_fielddef_mutable(def); + const char *msgname; + upb_value v; + upb_msgdef *m; + if (!f) continue; - const char *msgname = upb_fielddef_containingtypename(f); - // We validated this earlier in this function. + msgname = upb_fielddef_containingtypename(f); + /* We validated this earlier in this function. */ assert(msgname); - // If the extendee name is absolutely qualified, move past the initial ".". - // TODO(haberman): it is not obvious what it would mean if this was not - // absolutely qualified. + /* If the extendee name is absolutely qualified, move past the initial ".". + * TODO(haberman): it is not obvious what it would mean if this was not + * absolutely qualified. */ if (msgname[0] == '.') { msgname++; } - upb_value v; - upb_msgdef *m; if (upb_strtable_lookup(&addtab, msgname, &v)) { - // Extendee is in the set of defs the user asked us to add. + /* Extendee is in the set of defs the user asked us to add. */ m = upb_value_getptr(v); } else { - // Need to find and dup the extendee from the existing symtab. + /* Need to find and dup the extendee from the existing symtab. */ const upb_msgdef *frozen_m = upb_symtab_lookupmsg(s, msgname); if (!frozen_m) { upb_status_seterrf(status, @@ -3897,37 +3873,37 @@ bool upb_symtab_add(upb_symtab *s, upb_def *const*defs, int n, void *ref_donor, } } - // Add dups of any existing def that can reach a def with the same name as - // anything in our "add" set. - upb_inttable seen; + /* Add dups of any existing def that can reach a def with the same name as + * anything in our "add" set. */ if (!upb_inttable_init(&seen, UPB_CTYPE_BOOL)) goto oom_err; - upb_strtable_iter i; - upb_strtable_begin(&i, &s->symtab); - for (; !upb_strtable_done(&i); upb_strtable_next(&i)) { - upb_def *def = upb_value_getptr(upb_strtable_iter_value(&i)); + upb_strtable_begin(&iter, &s->symtab); + for (; !upb_strtable_done(&iter); upb_strtable_next(&iter)) { + upb_def *def = upb_value_getptr(upb_strtable_iter_value(&iter)); upb_resolve_dfs(def, &addtab, s, &seen, status); if (!upb_ok(status)) goto err; } upb_inttable_uninit(&seen); - // Now using the table, resolve symbolic references for subdefs. - upb_strtable_begin(&i, &addtab); - for (; !upb_strtable_done(&i); upb_strtable_next(&i)) { - upb_def *def = upb_value_getptr(upb_strtable_iter_value(&i)); + /* Now using the table, resolve symbolic references for subdefs. */ + upb_strtable_begin(&iter, &addtab); + for (; !upb_strtable_done(&iter); upb_strtable_next(&iter)) { + const char *base; + upb_def *def = upb_value_getptr(upb_strtable_iter_value(&iter)); upb_msgdef *m = upb_dyncast_msgdef_mutable(def); + upb_msg_field_iter j; + if (!m) continue; - // Type names are resolved relative to the message in which they appear. - const char *base = upb_msgdef_fullname(m); + /* Type names are resolved relative to the message in which they appear. */ + base = upb_msgdef_fullname(m); - 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)) { - // Try the lookup in the current set of to-be-added defs first. If not - // there, try existing defs. + /* Try the lookup in the current set of to-be-added defs first. If not + * there, try existing defs. */ upb_def *subdef = upb_resolvename(&addtab, base, name); if (subdef == NULL) { subdef = upb_resolvename(&s->symtab, base, name); @@ -3943,31 +3919,33 @@ bool upb_symtab_add(upb_symtab *s, upb_def *const*defs, int n, void *ref_donor, } } - // We need an array of the defs in addtab, for passing to upb_def_freeze. + /* We need an array of the defs in addtab, for passing to upb_def_freeze. */ add_defs = malloc(sizeof(void*) * upb_strtable_count(&addtab)); if (add_defs == NULL) goto oom_err; - upb_strtable_begin(&i, &addtab); - for (n = 0; !upb_strtable_done(&i); upb_strtable_next(&i)) { - add_defs[n++] = upb_value_getptr(upb_strtable_iter_value(&i)); + upb_strtable_begin(&iter, &addtab); + for (n = 0; !upb_strtable_done(&iter); upb_strtable_next(&iter)) { + add_defs[n++] = upb_value_getptr(upb_strtable_iter_value(&iter)); } if (!upb_def_freeze(add_defs, n, status)) goto err; - // This must be delayed until all errors have been detected, since error - // recovery code uses this table to cleanup defs. + /* This must be delayed until all errors have been detected, since error + * recovery code uses this table to cleanup defs. */ upb_strtable_uninit(&addtab); - // TODO(haberman) we don't properly handle errors after this point (like - // OOM in upb_strtable_insert() below). - for (int i = 0; i < n; i++) { + /* TODO(haberman) we don't properly handle errors after this point (like + * OOM in upb_strtable_insert() below). */ + for (i = 0; i < n; i++) { upb_def *def = add_defs[i]; const char *name = upb_def_fullname(def); upb_value v; + bool success; + if (upb_strtable_remove(&s->symtab, name, &v)) { const upb_def *def = upb_value_getptr(v); upb_def_unref(def, s); } - bool success = upb_strtable_insert(&s->symtab, name, upb_value_ptr(def)); + success = upb_strtable_insert(&s->symtab, name, upb_value_ptr(def)); UPB_ASSERT_VAR(success, success == true); } free(add_defs); @@ -3976,12 +3954,11 @@ bool upb_symtab_add(upb_symtab *s, upb_def *const*defs, int n, void *ref_donor, oom_err: upb_status_seterrmsg(status, "out of memory"); err: { - // For defs the user passed in, we need to donate the refs back. For defs - // we dup'd, we need to just unref them. - upb_strtable_iter i; - upb_strtable_begin(&i, &addtab); - for (; !upb_strtable_done(&i); upb_strtable_next(&i)) { - upb_def *def = upb_value_getptr(upb_strtable_iter_value(&i)); + /* For defs the user passed in, we need to donate the refs back. For defs + * we dup'd, we need to just unref them. */ + upb_strtable_begin(&iter, &addtab); + for (; !upb_strtable_done(&iter); upb_strtable_next(&iter)) { + upb_def *def = upb_value_getptr(upb_strtable_iter_value(&iter)); bool came_from_user = def->came_from_user; def->came_from_user = false; if (came_from_user) { @@ -3997,7 +3974,7 @@ err: { return false; } -// Iteration. +/* Iteration. */ static void advance_to_matching(upb_symtab_iter *iter) { if (iter->type == UPB_DEF_ANY) @@ -4041,17 +4018,17 @@ const upb_def *upb_symtab_iter_def(const upb_symtab_iter *iter) { #include #include -#define UPB_MAXARRSIZE 16 // 64k. +#define UPB_MAXARRSIZE 16 /* 64k. */ -// From Chromium. +/* From Chromium. */ #define ARRAY_SIZE(x) \ ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x]))))) static const double MAX_LOAD = 0.85; -// The minimum utilization of the array part of a mixed hash/array table. This -// is a speed/memory-usage tradeoff (though it's not straightforward because of -// cache effects). The lower this is, the more memory we'll use. +/* The minimum utilization of the array part of a mixed hash/array table. This + * is a speed/memory-usage tradeoff (though it's not straightforward because of + * cache effects). The lower this is, the more memory we'll use. */ static const double MIN_DENSITY = 0.1; bool is_pow2(uint64_t v) { return v == 0 || (v & (v - 1)) == 0; } @@ -4060,7 +4037,7 @@ int log2ceil(uint64_t v) { int ret = 0; bool pow2 = is_pow2(v); while (v >>= 1) ret++; - ret = pow2 ? ret : ret + 1; // Ceiling. + ret = pow2 ? ret : ret + 1; /* Ceiling. */ return UPB_MIN(UPB_MAXARRSIZE, ret); } @@ -4069,12 +4046,15 @@ char *upb_strdup(const char *s) { } char *upb_strdup2(const char *s, size_t len) { - // Prevent overflow errors. + size_t n; + char *p; + + /* Prevent overflow errors. */ if (len == SIZE_MAX) return NULL; - // Always null-terminate, even if binary data; but don't rely on the input to - // have a null-terminating byte since it may be a raw binary buffer. - size_t n = len + 1; - char *p = malloc(n); + /* Always null-terminate, even if binary data; but don't rely on the input to + * have a null-terminating byte since it may be a raw binary buffer. */ + n = len + 1; + p = malloc(n); if (p) { memcpy(p, s, len); p[len] = 0; @@ -4082,21 +4062,25 @@ char *upb_strdup2(const char *s, size_t len) { return p; } -// A type to represent the lookup key of either a strtable or an inttable. -typedef struct { - upb_tabkey key; +/* A type to represent the lookup key of either a strtable or an inttable. */ +typedef union { + uintptr_t num; + struct { + const char *str; + size_t len; + } str; } lookupkey_t; static lookupkey_t strkey2(const char *str, size_t len) { lookupkey_t k; - k.key.s.str = (char*)str; - k.key.s.length = len; + k.str.str = str; + k.str.len = len; return k; } static lookupkey_t intkey(uintptr_t key) { lookupkey_t k; - k.key = upb_intkey(key); + k.num = key; return k; } @@ -4105,7 +4089,7 @@ typedef bool eqlfunc_t(upb_tabkey k1, lookupkey_t k2); /* Base table (shared code) ***************************************************/ -// For when we need to cast away const. +/* For when we need to cast away const. */ static upb_tabent *mutable_entries(upb_table *t) { return (upb_tabent*)t->entries; } @@ -4115,11 +4099,13 @@ static bool isfull(upb_table *t) { } static bool init(upb_table *t, upb_ctype_t ctype, uint8_t size_lg2) { + size_t bytes; + t->count = 0; t->ctype = ctype; t->size_lg2 = size_lg2; t->mask = upb_table_size(t) ? upb_table_size(t) - 1 : 0; - size_t bytes = upb_table_size(t) * sizeof(upb_tabent); + bytes = upb_table_size(t) * sizeof(upb_tabent); if (bytes > 0) { t->entries = malloc(bytes); if (!t->entries) return false; @@ -4143,8 +4129,10 @@ static upb_tabent *getentry_mutable(upb_table *t, uint32_t hash) { static const upb_tabent *findentry(const upb_table *t, lookupkey_t key, uint32_t hash, eqlfunc_t *eql) { + const upb_tabent *e; + if (t->size_lg2 == 0) return NULL; - const upb_tabent *e = upb_getentry(t, hash); + e = upb_getentry(t, hash); if (upb_tabent_isempty(e)) return NULL; while (1) { if (eql(e->key, key)) return e; @@ -4162,7 +4150,7 @@ static bool lookup(const upb_table *t, lookupkey_t key, upb_value *v, const upb_tabent *e = findentry(t, key, hash, eql); if (e) { if (v) { - _upb_value_setval(v, e->val, t->ctype); + _upb_value_setval(v, e->val.val, t->ctype); } return true; } else { @@ -4170,34 +4158,41 @@ static bool lookup(const upb_table *t, lookupkey_t key, upb_value *v, } } -// The given key must not already exist in the table. -static void insert(upb_table *t, lookupkey_t key, upb_value val, - uint32_t hash, hashfunc_t *hashfunc, eqlfunc_t *eql) { +/* The given key must not already exist in the table. */ +static void insert(upb_table *t, lookupkey_t key, upb_tabkey tabkey, + upb_value val, uint32_t hash, + hashfunc_t *hashfunc, eqlfunc_t *eql) { + upb_tabent *mainpos_e; + upb_tabent *our_e; + UPB_UNUSED(eql); + UPB_UNUSED(key); assert(findentry(t, key, hash, eql) == NULL); assert(val.ctype == t->ctype); + t->count++; - upb_tabent *mainpos_e = getentry_mutable(t, hash); - upb_tabent *our_e = mainpos_e; + mainpos_e = getentry_mutable(t, hash); + our_e = mainpos_e; + if (upb_tabent_isempty(mainpos_e)) { - // Our main position is empty; use it. + /* Our main position is empty; use it. */ our_e->next = NULL; } else { - // Collision. + /* Collision. */ upb_tabent *new_e = emptyent(t); - // Head of collider's chain. + /* Head of collider's chain. */ upb_tabent *chain = getentry_mutable(t, hashfunc(mainpos_e->key)); if (chain == mainpos_e) { - // Existing ent is in its main posisiton (it has the same hash as us, and - // is the head of our chain). Insert to new ent and append to this chain. + /* Existing ent is in its main posisiton (it has the same hash as us, and + * is the head of our chain). Insert to new ent and append to this chain. */ new_e->next = mainpos_e->next; mainpos_e->next = new_e; our_e = new_e; } else { - // Existing ent is not in its main position (it is a node in some other - // chain). This implies that no existing ent in the table has our hash. - // Evict it (updating its chain) and use its ent for head of our chain. - *new_e = *mainpos_e; // copies next. + /* Existing ent is not in its main position (it is a node in some other + * chain). This implies that no existing ent in the table has our hash. + * Evict it (updating its chain) and use its ent for head of our chain. */ + *new_e = *mainpos_e; /* copies next. */ while (chain->next != mainpos_e) { chain = (upb_tabent*)chain->next; assert(chain); @@ -4207,8 +4202,8 @@ static void insert(upb_table *t, lookupkey_t key, upb_value val, our_e->next = NULL; } } - our_e->key = key.key; - our_e->val = val.val; + our_e->key = tabkey; + our_e->val.val = val.val; assert(findentry(t, key, hash, eql) == our_e); } @@ -4217,33 +4212,36 @@ static bool rm(upb_table *t, lookupkey_t key, upb_value *val, upb_tabent *chain = getentry_mutable(t, hash); if (upb_tabent_isempty(chain)) return false; if (eql(chain->key, key)) { - // Element to remove is at the head of its chain. + /* Element to remove is at the head of its chain. */ t->count--; if (val) { - _upb_value_setval(val, chain->val, t->ctype); + _upb_value_setval(val, chain->val.val, t->ctype); } if (chain->next) { upb_tabent *move = (upb_tabent*)chain->next; *chain = *move; if (removed) *removed = move->key; - move->key.num = 0; // Make the slot empty. + move->key = 0; /* Make the slot empty. */ } else { if (removed) *removed = chain->key; - chain->key.num = 0; // Make the slot empty. + chain->key = 0; /* Make the slot empty. */ } return true; } else { - // Element to remove is either in a non-head position or not in the table. + /* Element to remove is either in a non-head position or not in the + * table. */ while (chain->next && !eql(chain->next->key, key)) chain = (upb_tabent*)chain->next; if (chain->next) { - // Found element to remove. + /* Found element to remove. */ + upb_tabent *rm; + if (val) { - _upb_value_setval(val, chain->next->val, t->ctype); + _upb_value_setval(val, chain->next->val.val, t->ctype); } - upb_tabent *rm = (upb_tabent*)chain->next; + rm = (upb_tabent*)chain->next; if (removed) *removed = rm->key; - rm->key.num = 0; + rm->key = 0; chain->next = rm->next; t->count--; return true; @@ -4269,15 +4267,26 @@ static size_t begin(const upb_table *t) { /* upb_strtable ***************************************************************/ -// A simple "subclass" of upb_table that only adds a hash function for strings. +/* A simple "subclass" of upb_table that only adds a hash function for strings. */ + +static upb_tabkey strcopy(lookupkey_t k2) { + char *str = malloc(k2.str.len + sizeof(uint32_t) + 1); + if (str == NULL) return 0; + memcpy(str, &k2.str.len, sizeof(uint32_t)); + memcpy(str + sizeof(uint32_t), k2.str.str, k2.str.len + 1); + return (uintptr_t)str; +} static uint32_t strhash(upb_tabkey key) { - return MurmurHash2(key.s.str, key.s.length, 0); + uint32_t len; + char *str = upb_tabstr(key, &len); + return MurmurHash2(str, len, 0); } static bool streql(upb_tabkey k1, lookupkey_t k2) { - return k1.s.length == k2.key.s.length && - memcmp(k1.s.str, k2.key.s.str, k1.s.length) == 0; + uint32_t len; + char *str = upb_tabstr(k1, &len); + return len == k2.str.len && memcmp(str, k2.str.str, len) == 0; } bool upb_strtable_init(upb_strtable *t, upb_ctype_t ctype) { @@ -4285,16 +4294,18 @@ bool upb_strtable_init(upb_strtable *t, upb_ctype_t ctype) { } void upb_strtable_uninit(upb_strtable *t) { - for (size_t i = 0; i < upb_table_size(&t->t); i++) - free((void*)t->t.entries[i].key.s.str); + size_t i; + for (i = 0; i < upb_table_size(&t->t); i++) + free((void*)t->t.entries[i].key); uninit(&t->t); } bool upb_strtable_resize(upb_strtable *t, size_t size_lg2) { upb_strtable new_table; + upb_strtable_iter i; + if (!init(&new_table.t, t->t.ctype, size_lg2)) return false; - upb_strtable_iter i; upb_strtable_begin(&i, t); for ( ; !upb_strtable_done(&i); upb_strtable_next(&i)) { upb_strtable_insert2( @@ -4310,17 +4321,23 @@ bool upb_strtable_resize(upb_strtable *t, size_t size_lg2) { bool upb_strtable_insert2(upb_strtable *t, const char *k, size_t len, upb_value v) { + lookupkey_t key; + upb_tabkey tabkey; + uint32_t hash; + if (isfull(&t->t)) { - // Need to resize. New table of double the size, add old elements to it. + /* Need to resize. New table of double the size, add old elements to it. */ if (!upb_strtable_resize(t, t->t.size_lg2 + 1)) { return false; } } - if ((k = upb_strdup2(k, len)) == NULL) return false; - lookupkey_t key = strkey2(k, len); - uint32_t hash = MurmurHash2(key.key.s.str, key.key.s.length, 0); - insert(&t->t, key, v, hash, &strhash, &streql); + key = strkey2(k, len); + tabkey = strcopy(key); + if (tabkey == 0) return false; + + hash = MurmurHash2(key.str.str, key.str.len, 0); + insert(&t->t, key, tabkey, v, hash, &strhash, &streql); return true; } @@ -4335,14 +4352,14 @@ bool upb_strtable_remove2(upb_strtable *t, const char *key, size_t len, uint32_t hash = MurmurHash2(key, strlen(key), 0); upb_tabkey tabkey; if (rm(&t->t, strkey2(key, len), val, &tabkey, hash, &streql)) { - free((void*)tabkey.s.str); + free((void*)tabkey); return true; } else { return false; } } -// Iteration +/* Iteration */ static const upb_tabent *str_tabent(const upb_strtable_iter *i) { return &i->t->t.entries[i->index]; @@ -4364,17 +4381,19 @@ bool upb_strtable_done(const upb_strtable_iter *i) { const char *upb_strtable_iter_key(upb_strtable_iter *i) { assert(!upb_strtable_done(i)); - return str_tabent(i)->key.s.str; + return upb_tabstr(str_tabent(i)->key, NULL); } size_t upb_strtable_iter_keylength(upb_strtable_iter *i) { + uint32_t len; assert(!upb_strtable_done(i)); - return str_tabent(i)->key.s.length; + upb_tabstr(str_tabent(i)->key, &len); + return len; } upb_value upb_strtable_iter_value(const upb_strtable_iter *i) { assert(!upb_strtable_done(i)); - return _upb_value_val(str_tabent(i)->val, i->t->t.ctype); + return _upb_value_val(str_tabent(i)->val.val, i->t->t.ctype); } void upb_strtable_iter_setdone(upb_strtable_iter *i) { @@ -4391,20 +4410,20 @@ bool upb_strtable_iter_isequal(const upb_strtable_iter *i1, /* upb_inttable ***************************************************************/ -// For inttables we use a hybrid structure where small keys are kept in an -// array and large keys are put in the hash table. +/* For inttables we use a hybrid structure where small keys are kept in an + * array and large keys are put in the hash table. */ -static uint32_t inthash(upb_tabkey key) { return upb_inthash(key.num); } +static uint32_t inthash(upb_tabkey key) { return upb_inthash(key); } static bool inteql(upb_tabkey k1, lookupkey_t k2) { - return k1.num == k2.key.num; + return k1 == k2.num; } -static _upb_value *mutable_array(upb_inttable *t) { - return (_upb_value*)t->array; +static upb_tabval *mutable_array(upb_inttable *t) { + return (upb_tabval*)t->array; } -static _upb_value *inttable_val(upb_inttable *t, uintptr_t key) { +static upb_tabval *inttable_val(upb_inttable *t, uintptr_t key) { if (key < t->array_size) { return upb_arrhas(t->array[key]) ? &(mutable_array(t)[key]) : NULL; } else { @@ -4414,7 +4433,7 @@ static _upb_value *inttable_val(upb_inttable *t, uintptr_t key) { } } -static const _upb_value *inttable_val_const(const upb_inttable *t, +static const upb_tabval *inttable_val_const(const upb_inttable *t, uintptr_t key) { return inttable_val((upb_inttable*)t, key); } @@ -4426,25 +4445,29 @@ size_t upb_inttable_count(const upb_inttable *t) { static void check(upb_inttable *t) { UPB_UNUSED(t); #if defined(UPB_DEBUG_TABLE) && !defined(NDEBUG) - // This check is very expensive (makes inserts/deletes O(N)). - size_t count = 0; - upb_inttable_iter i; - upb_inttable_begin(&i, t); - for(; !upb_inttable_done(&i); upb_inttable_next(&i), count++) { - assert(upb_inttable_lookup(t, upb_inttable_iter_key(&i), NULL)); + { + /* This check is very expensive (makes inserts/deletes O(N)). */ + size_t count = 0; + upb_inttable_iter i; + upb_inttable_begin(&i, t); + for(; !upb_inttable_done(&i); upb_inttable_next(&i), count++) { + assert(upb_inttable_lookup(t, upb_inttable_iter_key(&i), NULL)); + } + assert(count == upb_inttable_count(t)); } - assert(count == upb_inttable_count(t)); #endif } bool upb_inttable_sizedinit(upb_inttable *t, upb_ctype_t ctype, size_t asize, int hsize_lg2) { + size_t array_bytes; + if (!init(&t->t, ctype, hsize_lg2)) return false; - // Always make the array part at least 1 long, so that we know key 0 - // won't be in the hash part, which simplifies things. + /* Always make the array part at least 1 long, so that we know key 0 + * won't be in the hash part, which simplifies things. */ t->array_size = UPB_MAX(1, asize); t->array_count = 0; - size_t array_bytes = t->array_size * sizeof(upb_value); + array_bytes = t->array_size * sizeof(upb_value); t->array = malloc(array_bytes); if (!t->array) { uninit(&t->t); @@ -4465,24 +4488,32 @@ void upb_inttable_uninit(upb_inttable *t) { } bool upb_inttable_insert(upb_inttable *t, uintptr_t key, upb_value val) { - assert(upb_arrhas(val.val)); + /* XXX: Table can't store value (uint64_t)-1. Need to somehow statically + * guarantee that this is not necessary, or fix the limitation. */ + upb_tabval tabval; + tabval.val = val.val; + UPB_UNUSED(tabval); + assert(upb_arrhas(tabval)); + if (key < t->array_size) { assert(!upb_arrhas(t->array[key])); t->array_count++; - mutable_array(t)[key] = val.val; + mutable_array(t)[key].val = val.val; } else { if (isfull(&t->t)) { - // Need to resize the hash part, but we re-use the array part. + /* Need to resize the hash part, but we re-use the array part. */ + size_t i; upb_table new_table; if (!init(&new_table, t->t.ctype, t->t.size_lg2 + 1)) return false; - size_t i; for (i = begin(&t->t); i < upb_table_size(&t->t); i = next(&t->t, i)) { const upb_tabent *e = &t->t.entries[i]; + uint32_t hash; upb_value v; - _upb_value_setval(&v, e->val, t->t.ctype); - uint32_t hash = upb_inthash(e->key.num); - insert(&new_table, intkey(e->key.num), v, hash, &inthash, &inteql); + + _upb_value_setval(&v, e->val.val, t->t.ctype); + hash = upb_inthash(e->key); + insert(&new_table, intkey(e->key), e->key, v, hash, &inthash, &inteql); } assert(t->t.count == new_table.count); @@ -4490,23 +4521,23 @@ bool upb_inttable_insert(upb_inttable *t, uintptr_t key, upb_value val) { uninit(&t->t); t->t = new_table; } - insert(&t->t, intkey(key), val, upb_inthash(key), &inthash, &inteql); + insert(&t->t, intkey(key), key, val, upb_inthash(key), &inthash, &inteql); } check(t); return true; } bool upb_inttable_lookup(const upb_inttable *t, uintptr_t key, upb_value *v) { - const _upb_value *table_v = inttable_val_const(t, key); + const upb_tabval *table_v = inttable_val_const(t, key); if (!table_v) return false; - if (v) _upb_value_setval(v, *table_v, t->t.ctype); + if (v) _upb_value_setval(v, table_v->val, t->t.ctype); return true; } bool upb_inttable_replace(upb_inttable *t, uintptr_t key, upb_value val) { - _upb_value *table_v = inttable_val(t, key); + upb_tabval *table_v = inttable_val(t, key); if (!table_v) return false; - *table_v = val.val; + table_v->val = val.val; return true; } @@ -4514,11 +4545,11 @@ bool upb_inttable_remove(upb_inttable *t, uintptr_t key, upb_value *val) { bool success; if (key < t->array_size) { if (upb_arrhas(t->array[key])) { + upb_tabval empty = UPB_TABVALUE_EMPTY_INIT; t->array_count--; if (val) { - _upb_value_setval(val, t->array[key], t->t.ctype); + _upb_value_setval(val, t->array[key].val, t->t.ctype); } - _upb_value empty = UPB_ARRAY_EMPTYENT; mutable_array(t)[key] = empty; success = true; } else { @@ -4558,10 +4589,14 @@ bool upb_inttable_removeptr(upb_inttable *t, const void *key, upb_value *val) { } void upb_inttable_compact(upb_inttable *t) { - // Create a power-of-two histogram of the table keys. + /* Create a power-of-two histogram of the table keys. */ int counts[UPB_MAXARRSIZE + 1] = {0}; uintptr_t max_key = 0; upb_inttable_iter i; + size_t arr_size; + int arr_count; + upb_inttable new_t; + upb_inttable_begin(&i, t); for (; !upb_inttable_done(&i); upb_inttable_next(&i)) { uintptr_t key = upb_inttable_iter_key(&i); @@ -4571,15 +4606,17 @@ void upb_inttable_compact(upb_inttable *t) { counts[log2ceil(key)]++; } - size_t arr_size = 1; - int arr_count = upb_inttable_count(t); + arr_size = 1; + arr_count = upb_inttable_count(t); if (upb_inttable_count(t) >= max_key * MIN_DENSITY) { - // We can put 100% of the entries in the array part. + /* We can put 100% of the entries in the array part. */ arr_size = max_key + 1; } else { - // Find the largest power of two that satisfies the MIN_DENSITY definition. - for (int size_lg2 = ARRAY_SIZE(counts) - 1; size_lg2 > 1; size_lg2--) { + /* Find the largest power of two that satisfies the MIN_DENSITY + * definition. */ + int size_lg2; + for (size_lg2 = ARRAY_SIZE(counts) - 1; size_lg2 > 1; size_lg2--) { arr_size = 1 << size_lg2; arr_count -= counts[size_lg2]; if (arr_count >= arr_size * MIN_DENSITY) { @@ -4588,38 +4625,39 @@ void upb_inttable_compact(upb_inttable *t) { } } - // Array part must always be at least 1 entry large to catch lookups of key - // 0. Key 0 must always be in the array part because "0" in the hash part - // denotes an empty entry. + /* Array part must always be at least 1 entry large to catch lookups of key + * 0. Key 0 must always be in the array part because "0" in the hash part + * denotes an empty entry. */ arr_size = UPB_MAX(arr_size, 1); - // Insert all elements into new, perfectly-sized table. - int hash_count = upb_inttable_count(t) - arr_count; - int hash_size = hash_count ? (hash_count / MAX_LOAD) + 1 : 0; - int hashsize_lg2 = log2ceil(hash_size); - assert(hash_count >= 0); - - upb_inttable new_t; - upb_inttable_sizedinit(&new_t, t->t.ctype, arr_size, hashsize_lg2); - upb_inttable_begin(&i, t); - for (; !upb_inttable_done(&i); upb_inttable_next(&i)) { - uintptr_t k = upb_inttable_iter_key(&i); - upb_inttable_insert(&new_t, k, upb_inttable_iter_value(&i)); + { + /* Insert all elements into new, perfectly-sized table. */ + int hash_count = upb_inttable_count(t) - arr_count; + int hash_size = hash_count ? (hash_count / MAX_LOAD) + 1 : 0; + int hashsize_lg2 = log2ceil(hash_size); + + assert(hash_count >= 0); + upb_inttable_sizedinit(&new_t, t->t.ctype, arr_size, hashsize_lg2); + upb_inttable_begin(&i, t); + for (; !upb_inttable_done(&i); upb_inttable_next(&i)) { + uintptr_t k = upb_inttable_iter_key(&i); + upb_inttable_insert(&new_t, k, upb_inttable_iter_value(&i)); + } + assert(new_t.array_size == arr_size); + assert(new_t.t.size_lg2 == hashsize_lg2); } - assert(new_t.array_size == arr_size); - assert(new_t.t.size_lg2 == hashsize_lg2); upb_inttable_uninit(t); *t = new_t; } -// Iteration. +/* Iteration. */ static const upb_tabent *int_tabent(const upb_inttable_iter *i) { assert(!i->array_part); return &i->t->t.entries[i->index]; } -static _upb_value int_arrent(const upb_inttable_iter *i) { +static upb_tabval int_arrent(const upb_inttable_iter *i) { assert(i->array_part); return i->t->array[i->index]; } @@ -4658,13 +4696,13 @@ bool upb_inttable_done(const upb_inttable_iter *i) { uintptr_t upb_inttable_iter_key(const upb_inttable_iter *i) { assert(!upb_inttable_done(i)); - return i->array_part ? i->index : int_tabent(i)->key.num; + return i->array_part ? i->index : int_tabent(i)->key; } upb_value upb_inttable_iter_value(const upb_inttable_iter *i) { assert(!upb_inttable_done(i)); return _upb_value_val( - i->array_part ? i->t->array[i->index] : int_tabent(i)->val, + i->array_part ? i->t->array[i->index].val : int_tabent(i)->val.val, i->t->t.ctype); } @@ -4682,26 +4720,26 @@ bool upb_inttable_iter_isequal(const upb_inttable_iter *i1, } #ifdef UPB_UNALIGNED_READS_OK -//----------------------------------------------------------------------------- -// MurmurHash2, by Austin Appleby (released as public domain). -// Reformatted and C99-ified by Joshua Haberman. -// Note - This code makes a few assumptions about how your machine behaves - -// 1. We can read a 4-byte value from any address without crashing -// 2. sizeof(int) == 4 (in upb this limitation is removed by using uint32_t -// And it has a few limitations - -// 1. It will not work incrementally. -// 2. It will not produce the same results on little-endian and big-endian -// machines. +/* ----------------------------------------------------------------------------- + * MurmurHash2, by Austin Appleby (released as public domain). + * Reformatted and C99-ified by Joshua Haberman. + * Note - This code makes a few assumptions about how your machine behaves - + * 1. We can read a 4-byte value from any address without crashing + * 2. sizeof(int) == 4 (in upb this limitation is removed by using uint32_t + * And it has a few limitations - + * 1. It will not work incrementally. + * 2. It will not produce the same results on little-endian and big-endian + * machines. */ uint32_t MurmurHash2(const void *key, size_t len, uint32_t seed) { - // 'm' and 'r' are mixing constants generated offline. - // They're not really 'magic', they just happen to work well. + /* 'm' and 'r' are mixing constants generated offline. + * They're not really 'magic', they just happen to work well. */ const uint32_t m = 0x5bd1e995; const int32_t r = 24; - // Initialize the hash to a 'random' value + /* Initialize the hash to a 'random' value */ uint32_t h = seed ^ len; - // Mix 4 bytes at a time into the hash + /* Mix 4 bytes at a time into the hash */ const uint8_t * data = (const uint8_t *)key; while(len >= 4) { uint32_t k = *(uint32_t *)data; @@ -4717,15 +4755,15 @@ uint32_t MurmurHash2(const void *key, size_t len, uint32_t seed) { len -= 4; } - // Handle the last few bytes of the input array + /* Handle the last few bytes of the input array */ switch(len) { case 3: h ^= data[2] << 16; case 2: h ^= data[1] << 8; case 1: h ^= data[0]; h *= m; }; - // Do a few final mixes of the hash to ensure the last few - // bytes are well-incorporated. + /* Do a few final mixes of the hash to ensure the last few + * bytes are well-incorporated. */ h ^= h >> 13; h *= m; h ^= h >> 15; @@ -4733,13 +4771,13 @@ uint32_t MurmurHash2(const void *key, size_t len, uint32_t seed) { return h; } -#else // !UPB_UNALIGNED_READS_OK +#else /* !UPB_UNALIGNED_READS_OK */ -//----------------------------------------------------------------------------- -// MurmurHashAligned2, by Austin Appleby -// Same algorithm as MurmurHash2, but only does aligned reads - should be safer -// on certain platforms. -// Performance will be lower than MurmurHash2 +/* ----------------------------------------------------------------------------- + * MurmurHashAligned2, by Austin Appleby + * Same algorithm as MurmurHash2, but only does aligned reads - should be safer + * on certain platforms. + * Performance will be lower than MurmurHash2 */ #define MIX(h,k,m) { k *= m; k ^= k >> r; k *= m; h *= m; h ^= k; } @@ -4751,8 +4789,10 @@ uint32_t MurmurHash2(const void * key, size_t len, uint32_t seed) { uint8_t align = (uintptr_t)data & 3; if(align && (len >= 4)) { - // Pre-load the temp registers + /* Pre-load the temp registers */ uint32_t t = 0, d = 0; + int32_t sl; + int32_t sr; switch(align) { case 1: t |= data[2] << 16; @@ -4765,16 +4805,18 @@ uint32_t MurmurHash2(const void * key, size_t len, uint32_t seed) { data += 4-align; len -= 4-align; - int32_t sl = 8 * (4-align); - int32_t sr = 8 * align; + sl = 8 * (4-align); + sr = 8 * align; - // Mix + /* Mix */ while(len >= 4) { + uint32_t k; + d = *(uint32_t *)data; t = (t >> sr) | (d << sl); - uint32_t k = t; + k = t; MIX(h,k,m); @@ -4784,25 +4826,27 @@ uint32_t MurmurHash2(const void * key, size_t len, uint32_t seed) { len -= 4; } - // Handle leftover data in temp registers + /* Handle leftover data in temp registers */ d = 0; if(len >= align) { + uint32_t k; + switch(align) { case 3: d |= data[2] << 16; case 2: d |= data[1] << 8; case 1: d |= data[0]; } - uint32_t k = (t >> sr) | (d << sl); + k = (t >> sr) | (d << sl); MIX(h,k,m); data += align; len -= align; - //---------- - // Handle tail bytes + /* ---------- + * Handle tail bytes */ switch(len) { case 3: h ^= data[2] << 16; @@ -4833,8 +4877,8 @@ uint32_t MurmurHash2(const void * key, size_t len, uint32_t seed) { len -= 4; } - //---------- - // Handle tail bytes + /* ---------- + * Handle tail bytes */ switch(len) { case 3: h ^= data[2] << 16; @@ -4851,7 +4895,7 @@ uint32_t MurmurHash2(const void * key, size_t len, uint32_t seed) { } #undef MIX -#endif // UPB_UNALIGNED_READS_OK +#endif /* UPB_UNALIGNED_READS_OK */ /* * upb - a minimalist implementation of protocol buffers. * @@ -4873,10 +4917,10 @@ bool upb_dumptostderr(void *closure, const upb_status* status) { return false; } -// Guarantee null-termination and provide ellipsis truncation. -// It may be tempting to "optimize" this by initializing these final -// four bytes up-front and then being careful never to overwrite them, -// this is safer and simpler. +/* Guarantee null-termination and provide ellipsis truncation. + * It may be tempting to "optimize" this by initializing these final + * four bytes up-front and then being careful never to overwrite them, + * this is safer and simpler. */ static void nullz(upb_status *status) { const char *ellipsis = "..."; size_t len = strlen(ellipsis); @@ -4918,7 +4962,7 @@ void upb_status_seterrf(upb_status *status, const char *fmt, ...) { void upb_status_vseterrf(upb_status *status, const char *fmt, va_list args) { if (!status) return; status->ok_ = false; - vsnprintf(status->msg, sizeof(status->msg), fmt, args); + _upb_vsnprintf(status->msg, sizeof(status->msg), fmt, args); nullz(status); } @@ -4935,9 +4979,9 @@ void upb_status_copy(upb_status *to, const upb_status *from) { if (!to) return; *to = *from; } -// This file was generated by upbc (the upb compiler). -// Do not edit -- your changes will be discarded when the file is -// regenerated. +/* This file was generated by upbc (the upb compiler). + * Do not edit -- your changes will be discarded when the file is + * regenerated. */ static const upb_msgdef msgs[20]; @@ -4945,7 +4989,7 @@ static const upb_fielddef fields[81]; static const upb_enumdef enums[4]; static const upb_tabent strentries[236]; static const upb_tabent intentries[14]; -static const _upb_value arrays[232]; +static const upb_tabval arrays[232]; #ifdef UPB_DEBUG_REFS static upb_inttable reftables[212]; @@ -4978,21 +5022,21 @@ static const upb_fielddef fields[81] = { UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "aggregate_value", 8, &msgs[18], NULL, 15, 6, {0},&reftables[40], &reftables[41]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "allow_alias", 2, &msgs[3], NULL, 6, 1, {0},&reftables[42], &reftables[43]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "cc_generic_services", 16, &msgs[10], NULL, 17, 6, {0},&reftables[44], &reftables[45]), - UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_ENUM, 0, false, false, false, false, "ctype", 1, &msgs[7], UPB_UPCAST(&enums[2]), 6, 1, {0},&reftables[46], &reftables[47]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_ENUM, 0, false, false, false, false, "ctype", 1, &msgs[7], (const upb_def*)(&enums[2]), 6, 1, {0},&reftables[46], &reftables[47]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "default_value", 7, &msgs[6], NULL, 16, 7, {0},&reftables[48], &reftables[49]), UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_STRING, 0, false, false, false, false, "dependency", 3, &msgs[8], NULL, 30, 8, {0},&reftables[50], &reftables[51]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "deprecated", 3, &msgs[7], NULL, 8, 3, {0},&reftables[52], &reftables[53]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_DOUBLE, 0, false, false, false, false, "double_value", 6, &msgs[18], NULL, 11, 4, {0},&reftables[54], &reftables[55]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, false, "end", 2, &msgs[1], NULL, 3, 1, {0},&reftables[56], &reftables[57]), - UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "enum_type", 4, &msgs[0], UPB_UPCAST(&msgs[2]), 16, 2, {0},&reftables[58], &reftables[59]), - UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "enum_type", 5, &msgs[8], UPB_UPCAST(&msgs[2]), 13, 1, {0},&reftables[60], &reftables[61]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "enum_type", 4, &msgs[0], (const upb_def*)(&msgs[2]), 16, 2, {0},&reftables[58], &reftables[59]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "enum_type", 5, &msgs[8], (const upb_def*)(&msgs[2]), 13, 1, {0},&reftables[60], &reftables[61]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "experimental_map_key", 9, &msgs[7], NULL, 10, 5, {0},&reftables[62], &reftables[63]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "extendee", 2, &msgs[6], NULL, 7, 2, {0},&reftables[64], &reftables[65]), - UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "extension", 7, &msgs[8], UPB_UPCAST(&msgs[6]), 19, 3, {0},&reftables[66], &reftables[67]), - UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "extension", 6, &msgs[0], UPB_UPCAST(&msgs[6]), 22, 4, {0},&reftables[68], &reftables[69]), - UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "extension_range", 5, &msgs[0], UPB_UPCAST(&msgs[1]), 19, 3, {0},&reftables[70], &reftables[71]), - UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "field", 2, &msgs[0], UPB_UPCAST(&msgs[6]), 10, 0, {0},&reftables[72], &reftables[73]), - UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "file", 1, &msgs[9], UPB_UPCAST(&msgs[8]), 5, 0, {0},&reftables[74], &reftables[75]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "extension", 7, &msgs[8], (const upb_def*)(&msgs[6]), 19, 3, {0},&reftables[66], &reftables[67]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "extension", 6, &msgs[0], (const upb_def*)(&msgs[6]), 22, 4, {0},&reftables[68], &reftables[69]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "extension_range", 5, &msgs[0], (const upb_def*)(&msgs[1]), 19, 3, {0},&reftables[70], &reftables[71]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "field", 2, &msgs[0], (const upb_def*)(&msgs[6]), 10, 0, {0},&reftables[72], &reftables[73]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "file", 1, &msgs[9], (const upb_def*)(&msgs[8]), 5, 0, {0},&reftables[74], &reftables[75]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "go_package", 11, &msgs[10], NULL, 14, 5, {0},&reftables[76], &reftables[77]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "identifier_value", 3, &msgs[18], NULL, 6, 1, {0},&reftables[78], &reftables[79]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "input_type", 2, &msgs[12], NULL, 7, 2, {0},&reftables[80], &reftables[81]), @@ -5002,16 +5046,16 @@ static const upb_fielddef fields[81] = { UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "java_multiple_files", 10, &msgs[10], NULL, 13, 4, {0},&reftables[88], &reftables[89]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "java_outer_classname", 8, &msgs[10], NULL, 9, 2, {0},&reftables[90], &reftables[91]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "java_package", 1, &msgs[10], NULL, 6, 1, {0},&reftables[92], &reftables[93]), - UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_ENUM, 0, false, false, false, false, "label", 4, &msgs[6], UPB_UPCAST(&enums[0]), 11, 4, {0},&reftables[94], &reftables[95]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_ENUM, 0, false, false, false, false, "label", 4, &msgs[6], (const upb_def*)(&enums[0]), 11, 4, {0},&reftables[94], &reftables[95]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "lazy", 5, &msgs[7], NULL, 9, 4, {0},&reftables[96], &reftables[97]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "leading_comments", 3, &msgs[17], NULL, 8, 2, {0},&reftables[98], &reftables[99]), - UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "location", 1, &msgs[16], UPB_UPCAST(&msgs[17]), 5, 0, {0},&reftables[100], &reftables[101]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "location", 1, &msgs[16], (const upb_def*)(&msgs[17]), 5, 0, {0},&reftables[100], &reftables[101]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "message_set_wire_format", 1, &msgs[11], NULL, 6, 1, {0},&reftables[102], &reftables[103]), - UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "message_type", 4, &msgs[8], UPB_UPCAST(&msgs[0]), 10, 0, {0},&reftables[104], &reftables[105]), - UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "method", 2, &msgs[14], UPB_UPCAST(&msgs[12]), 6, 0, {0},&reftables[106], &reftables[107]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "message_type", 4, &msgs[8], (const upb_def*)(&msgs[0]), 10, 0, {0},&reftables[104], &reftables[105]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "method", 2, &msgs[14], (const upb_def*)(&msgs[12]), 6, 0, {0},&reftables[106], &reftables[107]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[8], NULL, 22, 6, {0},&reftables[108], &reftables[109]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[14], NULL, 8, 2, {0},&reftables[110], &reftables[111]), - UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "name", 2, &msgs[18], UPB_UPCAST(&msgs[19]), 5, 0, {0},&reftables[112], &reftables[113]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "name", 2, &msgs[18], (const upb_def*)(&msgs[19]), 5, 0, {0},&reftables[112], &reftables[113]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[4], NULL, 4, 1, {0},&reftables[114], &reftables[115]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[0], NULL, 24, 6, {0},&reftables[116], &reftables[117]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[12], NULL, 4, 1, {0},&reftables[118], &reftables[119]), @@ -5019,18 +5063,18 @@ static const upb_fielddef fields[81] = { UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "name", 1, &msgs[6], NULL, 4, 1, {0},&reftables[122], &reftables[123]), UPB_FIELDDEF_INIT(UPB_LABEL_REQUIRED, UPB_TYPE_STRING, 0, false, false, false, false, "name_part", 1, &msgs[19], NULL, 2, 0, {0},&reftables[124], &reftables[125]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_INT64, UPB_INTFMT_VARIABLE, false, false, false, false, "negative_int_value", 5, &msgs[18], NULL, 10, 3, {0},&reftables[126], &reftables[127]), - UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "nested_type", 3, &msgs[0], UPB_UPCAST(&msgs[0]), 13, 1, {0},&reftables[128], &reftables[129]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "nested_type", 3, &msgs[0], (const upb_def*)(&msgs[0]), 13, 1, {0},&reftables[128], &reftables[129]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "no_standard_descriptor_accessor", 2, &msgs[11], NULL, 7, 2, {0},&reftables[130], &reftables[131]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, false, "number", 3, &msgs[6], NULL, 10, 3, {0},&reftables[132], &reftables[133]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, false, "number", 2, &msgs[4], NULL, 7, 2, {0},&reftables[134], &reftables[135]), - UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_ENUM, 0, false, false, false, false, "optimize_for", 9, &msgs[10], UPB_UPCAST(&enums[3]), 12, 3, {0},&reftables[136], &reftables[137]), - UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 7, &msgs[0], UPB_UPCAST(&msgs[11]), 23, 5, {0},&reftables[138], &reftables[139]), - UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 3, &msgs[2], UPB_UPCAST(&msgs[3]), 7, 1, {0},&reftables[140], &reftables[141]), - UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 8, &msgs[6], UPB_UPCAST(&msgs[7]), 3, 0, {0},&reftables[142], &reftables[143]), - UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 3, &msgs[4], UPB_UPCAST(&msgs[5]), 3, 0, {0},&reftables[144], &reftables[145]), - UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 8, &msgs[8], UPB_UPCAST(&msgs[10]), 20, 4, {0},&reftables[146], &reftables[147]), - UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 3, &msgs[14], UPB_UPCAST(&msgs[15]), 7, 1, {0},&reftables[148], &reftables[149]), - UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 4, &msgs[12], UPB_UPCAST(&msgs[13]), 3, 0, {0},&reftables[150], &reftables[151]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_ENUM, 0, false, false, false, false, "optimize_for", 9, &msgs[10], (const upb_def*)(&enums[3]), 12, 3, {0},&reftables[136], &reftables[137]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 7, &msgs[0], (const upb_def*)(&msgs[11]), 23, 5, {0},&reftables[138], &reftables[139]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 3, &msgs[2], (const upb_def*)(&msgs[3]), 7, 1, {0},&reftables[140], &reftables[141]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 8, &msgs[6], (const upb_def*)(&msgs[7]), 3, 0, {0},&reftables[142], &reftables[143]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 3, &msgs[4], (const upb_def*)(&msgs[5]), 3, 0, {0},&reftables[144], &reftables[145]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 8, &msgs[8], (const upb_def*)(&msgs[10]), 20, 4, {0},&reftables[146], &reftables[147]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 3, &msgs[14], (const upb_def*)(&msgs[15]), 7, 1, {0},&reftables[148], &reftables[149]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "options", 4, &msgs[12], (const upb_def*)(&msgs[13]), 3, 0, {0},&reftables[150], &reftables[151]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "output_type", 3, &msgs[12], NULL, 10, 3, {0},&reftables[152], &reftables[153]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "package", 2, &msgs[8], NULL, 25, 7, {0},&reftables[154], &reftables[155]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "packed", 2, &msgs[7], NULL, 7, 2, {0},&reftables[156], &reftables[157]), @@ -5038,22 +5082,22 @@ static const upb_fielddef fields[81] = { UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_UINT64, UPB_INTFMT_VARIABLE, false, false, false, false, "positive_int_value", 4, &msgs[18], NULL, 9, 2, {0},&reftables[160], &reftables[161]), UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, false, "public_dependency", 10, &msgs[8], NULL, 35, 9, {0},&reftables[162], &reftables[163]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "py_generic_services", 18, &msgs[10], NULL, 19, 8, {0},&reftables[164], &reftables[165]), - UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "service", 6, &msgs[8], UPB_UPCAST(&msgs[14]), 16, 2, {0},&reftables[166], &reftables[167]), - UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "source_code_info", 9, &msgs[8], UPB_UPCAST(&msgs[16]), 21, 5, {0},&reftables[168], &reftables[169]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "service", 6, &msgs[8], (const upb_def*)(&msgs[14]), 16, 2, {0},&reftables[166], &reftables[167]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_MESSAGE, 0, false, false, false, false, "source_code_info", 9, &msgs[8], (const upb_def*)(&msgs[16]), 21, 5, {0},&reftables[168], &reftables[169]), UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, true, "span", 2, &msgs[17], NULL, 7, 1, {0},&reftables[170], &reftables[171]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, false, "start", 1, &msgs[1], NULL, 2, 0, {0},&reftables[172], &reftables[173]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BYTES, 0, false, false, false, false, "string_value", 7, &msgs[18], NULL, 12, 5, {0},&reftables[174], &reftables[175]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "trailing_comments", 4, &msgs[17], NULL, 11, 3, {0},&reftables[176], &reftables[177]), - UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_ENUM, 0, false, false, false, false, "type", 5, &msgs[6], UPB_UPCAST(&enums[1]), 12, 5, {0},&reftables[178], &reftables[179]), + UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_ENUM, 0, false, false, false, false, "type", 5, &msgs[6], (const upb_def*)(&enums[1]), 12, 5, {0},&reftables[178], &reftables[179]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_STRING, 0, false, false, false, false, "type_name", 6, &msgs[6], NULL, 13, 6, {0},&reftables[180], &reftables[181]), - UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[5], UPB_UPCAST(&msgs[18]), 5, 0, {0},&reftables[182], &reftables[183]), - UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[15], UPB_UPCAST(&msgs[18]), 5, 0, {0},&reftables[184], &reftables[185]), - UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[3], UPB_UPCAST(&msgs[18]), 5, 0, {0},&reftables[186], &reftables[187]), - UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[13], UPB_UPCAST(&msgs[18]), 5, 0, {0},&reftables[188], &reftables[189]), - UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[10], UPB_UPCAST(&msgs[18]), 5, 0, {0},&reftables[190], &reftables[191]), - UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[11], UPB_UPCAST(&msgs[18]), 5, 0, {0},&reftables[192], &reftables[193]), - UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[7], UPB_UPCAST(&msgs[18]), 5, 0, {0},&reftables[194], &reftables[195]), - UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "value", 2, &msgs[2], UPB_UPCAST(&msgs[4]), 6, 0, {0},&reftables[196], &reftables[197]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[5], (const upb_def*)(&msgs[18]), 5, 0, {0},&reftables[182], &reftables[183]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[15], (const upb_def*)(&msgs[18]), 5, 0, {0},&reftables[184], &reftables[185]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[3], (const upb_def*)(&msgs[18]), 5, 0, {0},&reftables[186], &reftables[187]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[13], (const upb_def*)(&msgs[18]), 5, 0, {0},&reftables[188], &reftables[189]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[10], (const upb_def*)(&msgs[18]), 5, 0, {0},&reftables[190], &reftables[191]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[11], (const upb_def*)(&msgs[18]), 5, 0, {0},&reftables[192], &reftables[193]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "uninterpreted_option", 999, &msgs[7], (const upb_def*)(&msgs[18]), 5, 0, {0},&reftables[194], &reftables[195]), + UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_MESSAGE, 0, false, false, false, false, "value", 2, &msgs[2], (const upb_def*)(&msgs[4]), 6, 0, {0},&reftables[196], &reftables[197]), UPB_FIELDDEF_INIT(UPB_LABEL_OPTIONAL, UPB_TYPE_BOOL, 0, false, false, false, false, "weak", 10, &msgs[7], NULL, 13, 6, {0},&reftables[198], &reftables[199]), UPB_FIELDDEF_INIT(UPB_LABEL_REPEATED, UPB_TYPE_INT32, UPB_INTFMT_VARIABLE, false, false, false, false, "weak_dependency", 11, &msgs[8], NULL, 38, 10, {0},&reftables[200], &reftables[201]), }; @@ -5066,494 +5110,494 @@ static const upb_enumdef enums[4] = { }; static const upb_tabent strentries[236] = { - {UPB_TABKEY_STR("extension"), UPB_VALUE_INIT_CONSTPTR(&fields[14]), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("name"), UPB_VALUE_INIT_CONSTPTR(&fields[38]), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("field"), UPB_VALUE_INIT_CONSTPTR(&fields[16]), NULL}, - {UPB_TABKEY_STR("extension_range"), UPB_VALUE_INIT_CONSTPTR(&fields[15]), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("nested_type"), UPB_VALUE_INIT_CONSTPTR(&fields[44]), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("options"), UPB_VALUE_INIT_CONSTPTR(&fields[49]), NULL}, - {UPB_TABKEY_STR("enum_type"), UPB_VALUE_INIT_CONSTPTR(&fields[9]), &strentries[14]}, - {UPB_TABKEY_STR("start"), UPB_VALUE_INIT_CONSTPTR(&fields[66]), NULL}, - {UPB_TABKEY_STR("end"), UPB_VALUE_INIT_CONSTPTR(&fields[8]), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("value"), UPB_VALUE_INIT_CONSTPTR(&fields[78]), NULL}, - {UPB_TABKEY_STR("options"), UPB_VALUE_INIT_CONSTPTR(&fields[50]), NULL}, - {UPB_TABKEY_STR("name"), UPB_VALUE_INIT_CONSTPTR(&fields[40]), &strentries[22]}, - {UPB_TABKEY_STR("uninterpreted_option"), UPB_VALUE_INIT_CONSTPTR(&fields[73]), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("allow_alias"), UPB_VALUE_INIT_CONSTPTR(&fields[1]), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("number"), UPB_VALUE_INIT_CONSTPTR(&fields[47]), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("options"), UPB_VALUE_INIT_CONSTPTR(&fields[52]), NULL}, - {UPB_TABKEY_STR("name"), UPB_VALUE_INIT_CONSTPTR(&fields[37]), &strentries[30]}, - {UPB_TABKEY_STR("uninterpreted_option"), UPB_VALUE_INIT_CONSTPTR(&fields[71]), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("label"), UPB_VALUE_INIT_CONSTPTR(&fields[27]), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("name"), UPB_VALUE_INIT_CONSTPTR(&fields[41]), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("number"), UPB_VALUE_INIT_CONSTPTR(&fields[46]), &strentries[49]}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("type_name"), UPB_VALUE_INIT_CONSTPTR(&fields[70]), NULL}, - {UPB_TABKEY_STR("extendee"), UPB_VALUE_INIT_CONSTPTR(&fields[12]), NULL}, - {UPB_TABKEY_STR("type"), UPB_VALUE_INIT_CONSTPTR(&fields[69]), &strentries[48]}, - {UPB_TABKEY_STR("default_value"), UPB_VALUE_INIT_CONSTPTR(&fields[4]), NULL}, - {UPB_TABKEY_STR("options"), UPB_VALUE_INIT_CONSTPTR(&fields[51]), NULL}, - {UPB_TABKEY_STR("experimental_map_key"), UPB_VALUE_INIT_CONSTPTR(&fields[11]), &strentries[67]}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("weak"), UPB_VALUE_INIT_CONSTPTR(&fields[79]), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("packed"), UPB_VALUE_INIT_CONSTPTR(&fields[58]), NULL}, - {UPB_TABKEY_STR("lazy"), UPB_VALUE_INIT_CONSTPTR(&fields[28]), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("ctype"), UPB_VALUE_INIT_CONSTPTR(&fields[3]), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("deprecated"), UPB_VALUE_INIT_CONSTPTR(&fields[6]), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("uninterpreted_option"), UPB_VALUE_INIT_CONSTPTR(&fields[77]), NULL}, - {UPB_TABKEY_STR("extension"), UPB_VALUE_INIT_CONSTPTR(&fields[13]), NULL}, - {UPB_TABKEY_STR("weak_dependency"), UPB_VALUE_INIT_CONSTPTR(&fields[80]), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("name"), UPB_VALUE_INIT_CONSTPTR(&fields[34]), NULL}, - {UPB_TABKEY_STR("service"), UPB_VALUE_INIT_CONSTPTR(&fields[63]), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("source_code_info"), UPB_VALUE_INIT_CONSTPTR(&fields[64]), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("dependency"), UPB_VALUE_INIT_CONSTPTR(&fields[5]), NULL}, - {UPB_TABKEY_STR("message_type"), UPB_VALUE_INIT_CONSTPTR(&fields[32]), NULL}, - {UPB_TABKEY_STR("package"), UPB_VALUE_INIT_CONSTPTR(&fields[57]), NULL}, - {UPB_TABKEY_STR("options"), UPB_VALUE_INIT_CONSTPTR(&fields[53]), &strentries[82]}, - {UPB_TABKEY_STR("enum_type"), UPB_VALUE_INIT_CONSTPTR(&fields[10]), NULL}, - {UPB_TABKEY_STR("public_dependency"), UPB_VALUE_INIT_CONSTPTR(&fields[61]), &strentries[81]}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("file"), UPB_VALUE_INIT_CONSTPTR(&fields[17]), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("uninterpreted_option"), UPB_VALUE_INIT_CONSTPTR(&fields[75]), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("cc_generic_services"), UPB_VALUE_INIT_CONSTPTR(&fields[2]), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("java_multiple_files"), UPB_VALUE_INIT_CONSTPTR(&fields[24]), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("java_generic_services"), UPB_VALUE_INIT_CONSTPTR(&fields[23]), &strentries[102]}, - {UPB_TABKEY_STR("java_generate_equals_and_hash"), UPB_VALUE_INIT_CONSTPTR(&fields[22]), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("go_package"), UPB_VALUE_INIT_CONSTPTR(&fields[18]), NULL}, - {UPB_TABKEY_STR("java_package"), UPB_VALUE_INIT_CONSTPTR(&fields[26]), NULL}, - {UPB_TABKEY_STR("optimize_for"), UPB_VALUE_INIT_CONSTPTR(&fields[48]), NULL}, - {UPB_TABKEY_STR("py_generic_services"), UPB_VALUE_INIT_CONSTPTR(&fields[62]), NULL}, - {UPB_TABKEY_STR("java_outer_classname"), UPB_VALUE_INIT_CONSTPTR(&fields[25]), NULL}, - {UPB_TABKEY_STR("message_set_wire_format"), UPB_VALUE_INIT_CONSTPTR(&fields[31]), &strentries[106]}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("uninterpreted_option"), UPB_VALUE_INIT_CONSTPTR(&fields[76]), NULL}, - {UPB_TABKEY_STR("no_standard_descriptor_accessor"), UPB_VALUE_INIT_CONSTPTR(&fields[45]), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("name"), UPB_VALUE_INIT_CONSTPTR(&fields[39]), NULL}, - {UPB_TABKEY_STR("input_type"), UPB_VALUE_INIT_CONSTPTR(&fields[20]), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("output_type"), UPB_VALUE_INIT_CONSTPTR(&fields[56]), NULL}, - {UPB_TABKEY_STR("options"), UPB_VALUE_INIT_CONSTPTR(&fields[55]), NULL}, - {UPB_TABKEY_STR("uninterpreted_option"), UPB_VALUE_INIT_CONSTPTR(&fields[74]), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("options"), UPB_VALUE_INIT_CONSTPTR(&fields[54]), &strentries[122]}, - {UPB_TABKEY_STR("method"), UPB_VALUE_INIT_CONSTPTR(&fields[33]), NULL}, - {UPB_TABKEY_STR("name"), UPB_VALUE_INIT_CONSTPTR(&fields[35]), &strentries[121]}, - {UPB_TABKEY_STR("uninterpreted_option"), UPB_VALUE_INIT_CONSTPTR(&fields[72]), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("location"), UPB_VALUE_INIT_CONSTPTR(&fields[30]), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("span"), UPB_VALUE_INIT_CONSTPTR(&fields[65]), &strentries[139]}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("trailing_comments"), UPB_VALUE_INIT_CONSTPTR(&fields[68]), NULL}, - {UPB_TABKEY_STR("leading_comments"), UPB_VALUE_INIT_CONSTPTR(&fields[29]), &strentries[137]}, - {UPB_TABKEY_STR("path"), UPB_VALUE_INIT_CONSTPTR(&fields[59]), NULL}, - {UPB_TABKEY_STR("double_value"), UPB_VALUE_INIT_CONSTPTR(&fields[7]), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("name"), UPB_VALUE_INIT_CONSTPTR(&fields[36]), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("negative_int_value"), UPB_VALUE_INIT_CONSTPTR(&fields[43]), NULL}, - {UPB_TABKEY_STR("aggregate_value"), UPB_VALUE_INIT_CONSTPTR(&fields[0]), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("positive_int_value"), UPB_VALUE_INIT_CONSTPTR(&fields[60]), NULL}, - {UPB_TABKEY_STR("identifier_value"), UPB_VALUE_INIT_CONSTPTR(&fields[19]), NULL}, - {UPB_TABKEY_STR("string_value"), UPB_VALUE_INIT_CONSTPTR(&fields[67]), &strentries[154]}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("is_extension"), UPB_VALUE_INIT_CONSTPTR(&fields[21]), NULL}, - {UPB_TABKEY_STR("name_part"), UPB_VALUE_INIT_CONSTPTR(&fields[42]), NULL}, - {UPB_TABKEY_STR("LABEL_REQUIRED"), UPB_VALUE_INIT_INT32(2), &strentries[162]}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("LABEL_REPEATED"), UPB_VALUE_INIT_INT32(3), NULL}, - {UPB_TABKEY_STR("LABEL_OPTIONAL"), UPB_VALUE_INIT_INT32(1), NULL}, - {UPB_TABKEY_STR("TYPE_FIXED64"), UPB_VALUE_INIT_INT32(6), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("TYPE_STRING"), UPB_VALUE_INIT_INT32(9), NULL}, - {UPB_TABKEY_STR("TYPE_FLOAT"), UPB_VALUE_INIT_INT32(2), &strentries[193]}, - {UPB_TABKEY_STR("TYPE_DOUBLE"), UPB_VALUE_INIT_INT32(1), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("TYPE_INT32"), UPB_VALUE_INIT_INT32(5), NULL}, - {UPB_TABKEY_STR("TYPE_SFIXED32"), UPB_VALUE_INIT_INT32(15), NULL}, - {UPB_TABKEY_STR("TYPE_FIXED32"), UPB_VALUE_INIT_INT32(7), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("TYPE_MESSAGE"), UPB_VALUE_INIT_INT32(11), &strentries[194]}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("TYPE_INT64"), UPB_VALUE_INIT_INT32(3), &strentries[191]}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("TYPE_ENUM"), UPB_VALUE_INIT_INT32(14), NULL}, - {UPB_TABKEY_STR("TYPE_UINT32"), UPB_VALUE_INIT_INT32(13), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("TYPE_UINT64"), UPB_VALUE_INIT_INT32(4), &strentries[190]}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("TYPE_SFIXED64"), UPB_VALUE_INIT_INT32(16), NULL}, - {UPB_TABKEY_STR("TYPE_BYTES"), UPB_VALUE_INIT_INT32(12), NULL}, - {UPB_TABKEY_STR("TYPE_SINT64"), UPB_VALUE_INIT_INT32(18), NULL}, - {UPB_TABKEY_STR("TYPE_BOOL"), UPB_VALUE_INIT_INT32(8), NULL}, - {UPB_TABKEY_STR("TYPE_GROUP"), UPB_VALUE_INIT_INT32(10), NULL}, - {UPB_TABKEY_STR("TYPE_SINT32"), UPB_VALUE_INIT_INT32(17), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("CORD"), UPB_VALUE_INIT_INT32(1), NULL}, - {UPB_TABKEY_STR("STRING"), UPB_VALUE_INIT_INT32(0), &strentries[197]}, - {UPB_TABKEY_STR("STRING_PIECE"), UPB_VALUE_INIT_INT32(2), NULL}, - {UPB_TABKEY_STR("CODE_SIZE"), UPB_VALUE_INIT_INT32(2), NULL}, - {UPB_TABKEY_STR("SPEED"), UPB_VALUE_INIT_INT32(1), &strentries[203]}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("LITE_RUNTIME"), UPB_VALUE_INIT_INT32(3), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("google.protobuf.SourceCodeInfo.Location"), UPB_VALUE_INIT_CONSTPTR(&msgs[17]), NULL}, - {UPB_TABKEY_STR("google.protobuf.UninterpretedOption"), UPB_VALUE_INIT_CONSTPTR(&msgs[18]), NULL}, - {UPB_TABKEY_STR("google.protobuf.FileDescriptorProto"), UPB_VALUE_INIT_CONSTPTR(&msgs[8]), NULL}, - {UPB_TABKEY_STR("google.protobuf.MethodDescriptorProto"), UPB_VALUE_INIT_CONSTPTR(&msgs[12]), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("google.protobuf.EnumValueOptions"), UPB_VALUE_INIT_CONSTPTR(&msgs[5]), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("google.protobuf.DescriptorProto"), UPB_VALUE_INIT_CONSTPTR(&msgs[0]), &strentries[228]}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("google.protobuf.SourceCodeInfo"), UPB_VALUE_INIT_CONSTPTR(&msgs[16]), NULL}, - {UPB_TABKEY_STR("google.protobuf.FieldDescriptorProto.Type"), UPB_VALUE_INIT_CONSTPTR(&enums[1]), NULL}, - {UPB_TABKEY_STR("google.protobuf.DescriptorProto.ExtensionRange"), UPB_VALUE_INIT_CONSTPTR(&msgs[1]), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_STR("google.protobuf.EnumValueDescriptorProto"), UPB_VALUE_INIT_CONSTPTR(&msgs[4]), NULL}, - {UPB_TABKEY_STR("google.protobuf.FieldOptions"), UPB_VALUE_INIT_CONSTPTR(&msgs[7]), NULL}, - {UPB_TABKEY_STR("google.protobuf.FileOptions"), UPB_VALUE_INIT_CONSTPTR(&msgs[10]), NULL}, - {UPB_TABKEY_STR("google.protobuf.EnumDescriptorProto"), UPB_VALUE_INIT_CONSTPTR(&msgs[2]), &strentries[233]}, - {UPB_TABKEY_STR("google.protobuf.FieldDescriptorProto.Label"), UPB_VALUE_INIT_CONSTPTR(&enums[0]), NULL}, - {UPB_TABKEY_STR("google.protobuf.ServiceDescriptorProto"), UPB_VALUE_INIT_CONSTPTR(&msgs[14]), NULL}, - {UPB_TABKEY_STR("google.protobuf.FieldOptions.CType"), UPB_VALUE_INIT_CONSTPTR(&enums[2]), &strentries[229]}, - {UPB_TABKEY_STR("google.protobuf.FileDescriptorSet"), UPB_VALUE_INIT_CONSTPTR(&msgs[9]), &strentries[235]}, - {UPB_TABKEY_STR("google.protobuf.EnumOptions"), UPB_VALUE_INIT_CONSTPTR(&msgs[3]), NULL}, - {UPB_TABKEY_STR("google.protobuf.FieldDescriptorProto"), UPB_VALUE_INIT_CONSTPTR(&msgs[6]), NULL}, - {UPB_TABKEY_STR("google.protobuf.FileOptions.OptimizeMode"), UPB_VALUE_INIT_CONSTPTR(&enums[3]), &strentries[221]}, - {UPB_TABKEY_STR("google.protobuf.ServiceOptions"), UPB_VALUE_INIT_CONSTPTR(&msgs[15]), NULL}, - {UPB_TABKEY_STR("google.protobuf.MessageOptions"), UPB_VALUE_INIT_CONSTPTR(&msgs[11]), NULL}, - {UPB_TABKEY_STR("google.protobuf.MethodOptions"), UPB_VALUE_INIT_CONSTPTR(&msgs[13]), &strentries[226]}, - {UPB_TABKEY_STR("google.protobuf.UninterpretedOption.NamePart"), UPB_VALUE_INIT_CONSTPTR(&msgs[19]), NULL}, + {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "extension"), UPB_TABVALUE_PTR_INIT(&fields[14]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[38]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\005", "\000", "\000", "\000", "field"), UPB_TABVALUE_PTR_INIT(&fields[16]), NULL}, + {UPB_TABKEY_STR("\017", "\000", "\000", "\000", "extension_range"), UPB_TABVALUE_PTR_INIT(&fields[15]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\013", "\000", "\000", "\000", "nested_type"), UPB_TABVALUE_PTR_INIT(&fields[44]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[49]), NULL}, + {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "enum_type"), UPB_TABVALUE_PTR_INIT(&fields[9]), &strentries[14]}, + {UPB_TABKEY_STR("\005", "\000", "\000", "\000", "start"), UPB_TABVALUE_PTR_INIT(&fields[66]), NULL}, + {UPB_TABKEY_STR("\003", "\000", "\000", "\000", "end"), UPB_TABVALUE_PTR_INIT(&fields[8]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\005", "\000", "\000", "\000", "value"), UPB_TABVALUE_PTR_INIT(&fields[78]), NULL}, + {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[50]), NULL}, + {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[40]), &strentries[22]}, + {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "uninterpreted_option"), UPB_TABVALUE_PTR_INIT(&fields[73]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\013", "\000", "\000", "\000", "allow_alias"), UPB_TABVALUE_PTR_INIT(&fields[1]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\006", "\000", "\000", "\000", "number"), UPB_TABVALUE_PTR_INIT(&fields[47]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[52]), NULL}, + {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[37]), &strentries[30]}, + {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "uninterpreted_option"), UPB_TABVALUE_PTR_INIT(&fields[71]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\005", "\000", "\000", "\000", "label"), UPB_TABVALUE_PTR_INIT(&fields[27]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[41]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\006", "\000", "\000", "\000", "number"), UPB_TABVALUE_PTR_INIT(&fields[46]), &strentries[49]}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "type_name"), UPB_TABVALUE_PTR_INIT(&fields[70]), NULL}, + {UPB_TABKEY_STR("\010", "\000", "\000", "\000", "extendee"), UPB_TABVALUE_PTR_INIT(&fields[12]), NULL}, + {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "type"), UPB_TABVALUE_PTR_INIT(&fields[69]), &strentries[48]}, + {UPB_TABKEY_STR("\015", "\000", "\000", "\000", "default_value"), UPB_TABVALUE_PTR_INIT(&fields[4]), NULL}, + {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[51]), NULL}, + {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "experimental_map_key"), UPB_TABVALUE_PTR_INIT(&fields[11]), &strentries[67]}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "weak"), UPB_TABVALUE_PTR_INIT(&fields[79]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\006", "\000", "\000", "\000", "packed"), UPB_TABVALUE_PTR_INIT(&fields[58]), NULL}, + {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "lazy"), UPB_TABVALUE_PTR_INIT(&fields[28]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\005", "\000", "\000", "\000", "ctype"), UPB_TABVALUE_PTR_INIT(&fields[3]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "deprecated"), UPB_TABVALUE_PTR_INIT(&fields[6]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "uninterpreted_option"), UPB_TABVALUE_PTR_INIT(&fields[77]), NULL}, + {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "extension"), UPB_TABVALUE_PTR_INIT(&fields[13]), NULL}, + {UPB_TABKEY_STR("\017", "\000", "\000", "\000", "weak_dependency"), UPB_TABVALUE_PTR_INIT(&fields[80]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[34]), NULL}, + {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "service"), UPB_TABVALUE_PTR_INIT(&fields[63]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\020", "\000", "\000", "\000", "source_code_info"), UPB_TABVALUE_PTR_INIT(&fields[64]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "dependency"), UPB_TABVALUE_PTR_INIT(&fields[5]), NULL}, + {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "message_type"), UPB_TABVALUE_PTR_INIT(&fields[32]), NULL}, + {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "package"), UPB_TABVALUE_PTR_INIT(&fields[57]), NULL}, + {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[53]), &strentries[82]}, + {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "enum_type"), UPB_TABVALUE_PTR_INIT(&fields[10]), NULL}, + {UPB_TABKEY_STR("\021", "\000", "\000", "\000", "public_dependency"), UPB_TABVALUE_PTR_INIT(&fields[61]), &strentries[81]}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "file"), UPB_TABVALUE_PTR_INIT(&fields[17]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "uninterpreted_option"), UPB_TABVALUE_PTR_INIT(&fields[75]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\023", "\000", "\000", "\000", "cc_generic_services"), UPB_TABVALUE_PTR_INIT(&fields[2]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\023", "\000", "\000", "\000", "java_multiple_files"), UPB_TABVALUE_PTR_INIT(&fields[24]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\025", "\000", "\000", "\000", "java_generic_services"), UPB_TABVALUE_PTR_INIT(&fields[23]), &strentries[102]}, + {UPB_TABKEY_STR("\035", "\000", "\000", "\000", "java_generate_equals_and_hash"), UPB_TABVALUE_PTR_INIT(&fields[22]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "go_package"), UPB_TABVALUE_PTR_INIT(&fields[18]), NULL}, + {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "java_package"), UPB_TABVALUE_PTR_INIT(&fields[26]), NULL}, + {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "optimize_for"), UPB_TABVALUE_PTR_INIT(&fields[48]), NULL}, + {UPB_TABKEY_STR("\023", "\000", "\000", "\000", "py_generic_services"), UPB_TABVALUE_PTR_INIT(&fields[62]), NULL}, + {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "java_outer_classname"), UPB_TABVALUE_PTR_INIT(&fields[25]), NULL}, + {UPB_TABKEY_STR("\027", "\000", "\000", "\000", "message_set_wire_format"), UPB_TABVALUE_PTR_INIT(&fields[31]), &strentries[106]}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "uninterpreted_option"), UPB_TABVALUE_PTR_INIT(&fields[76]), NULL}, + {UPB_TABKEY_STR("\037", "\000", "\000", "\000", "no_standard_descriptor_accessor"), UPB_TABVALUE_PTR_INIT(&fields[45]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[39]), NULL}, + {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "input_type"), UPB_TABVALUE_PTR_INIT(&fields[20]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\013", "\000", "\000", "\000", "output_type"), UPB_TABVALUE_PTR_INIT(&fields[56]), NULL}, + {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[55]), NULL}, + {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "uninterpreted_option"), UPB_TABVALUE_PTR_INIT(&fields[74]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\007", "\000", "\000", "\000", "options"), UPB_TABVALUE_PTR_INIT(&fields[54]), &strentries[122]}, + {UPB_TABKEY_STR("\006", "\000", "\000", "\000", "method"), UPB_TABVALUE_PTR_INIT(&fields[33]), NULL}, + {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[35]), &strentries[121]}, + {UPB_TABKEY_STR("\024", "\000", "\000", "\000", "uninterpreted_option"), UPB_TABVALUE_PTR_INIT(&fields[72]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\010", "\000", "\000", "\000", "location"), UPB_TABVALUE_PTR_INIT(&fields[30]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "span"), UPB_TABVALUE_PTR_INIT(&fields[65]), &strentries[139]}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\021", "\000", "\000", "\000", "trailing_comments"), UPB_TABVALUE_PTR_INIT(&fields[68]), NULL}, + {UPB_TABKEY_STR("\020", "\000", "\000", "\000", "leading_comments"), UPB_TABVALUE_PTR_INIT(&fields[29]), &strentries[137]}, + {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "path"), UPB_TABVALUE_PTR_INIT(&fields[59]), NULL}, + {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "double_value"), UPB_TABVALUE_PTR_INIT(&fields[7]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "name"), UPB_TABVALUE_PTR_INIT(&fields[36]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\022", "\000", "\000", "\000", "negative_int_value"), UPB_TABVALUE_PTR_INIT(&fields[43]), NULL}, + {UPB_TABKEY_STR("\017", "\000", "\000", "\000", "aggregate_value"), UPB_TABVALUE_PTR_INIT(&fields[0]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\022", "\000", "\000", "\000", "positive_int_value"), UPB_TABVALUE_PTR_INIT(&fields[60]), NULL}, + {UPB_TABKEY_STR("\020", "\000", "\000", "\000", "identifier_value"), UPB_TABVALUE_PTR_INIT(&fields[19]), NULL}, + {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "string_value"), UPB_TABVALUE_PTR_INIT(&fields[67]), &strentries[154]}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "is_extension"), UPB_TABVALUE_PTR_INIT(&fields[21]), NULL}, + {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "name_part"), UPB_TABVALUE_PTR_INIT(&fields[42]), NULL}, + {UPB_TABKEY_STR("\016", "\000", "\000", "\000", "LABEL_REQUIRED"), UPB_TABVALUE_INT_INIT(2), &strentries[162]}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\016", "\000", "\000", "\000", "LABEL_REPEATED"), UPB_TABVALUE_INT_INIT(3), NULL}, + {UPB_TABKEY_STR("\016", "\000", "\000", "\000", "LABEL_OPTIONAL"), UPB_TABVALUE_INT_INIT(1), NULL}, + {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "TYPE_FIXED64"), UPB_TABVALUE_INT_INIT(6), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\013", "\000", "\000", "\000", "TYPE_STRING"), UPB_TABVALUE_INT_INIT(9), NULL}, + {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "TYPE_FLOAT"), UPB_TABVALUE_INT_INIT(2), &strentries[193]}, + {UPB_TABKEY_STR("\013", "\000", "\000", "\000", "TYPE_DOUBLE"), UPB_TABVALUE_INT_INIT(1), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "TYPE_INT32"), UPB_TABVALUE_INT_INIT(5), NULL}, + {UPB_TABKEY_STR("\015", "\000", "\000", "\000", "TYPE_SFIXED32"), UPB_TABVALUE_INT_INIT(15), NULL}, + {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "TYPE_FIXED32"), UPB_TABVALUE_INT_INIT(7), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "TYPE_MESSAGE"), UPB_TABVALUE_INT_INIT(11), &strentries[194]}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "TYPE_INT64"), UPB_TABVALUE_INT_INIT(3), &strentries[191]}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "TYPE_ENUM"), UPB_TABVALUE_INT_INIT(14), NULL}, + {UPB_TABKEY_STR("\013", "\000", "\000", "\000", "TYPE_UINT32"), UPB_TABVALUE_INT_INIT(13), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\013", "\000", "\000", "\000", "TYPE_UINT64"), UPB_TABVALUE_INT_INIT(4), &strentries[190]}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\015", "\000", "\000", "\000", "TYPE_SFIXED64"), UPB_TABVALUE_INT_INIT(16), NULL}, + {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "TYPE_BYTES"), UPB_TABVALUE_INT_INIT(12), NULL}, + {UPB_TABKEY_STR("\013", "\000", "\000", "\000", "TYPE_SINT64"), UPB_TABVALUE_INT_INIT(18), NULL}, + {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "TYPE_BOOL"), UPB_TABVALUE_INT_INIT(8), NULL}, + {UPB_TABKEY_STR("\012", "\000", "\000", "\000", "TYPE_GROUP"), UPB_TABVALUE_INT_INIT(10), NULL}, + {UPB_TABKEY_STR("\013", "\000", "\000", "\000", "TYPE_SINT32"), UPB_TABVALUE_INT_INIT(17), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\004", "\000", "\000", "\000", "CORD"), UPB_TABVALUE_INT_INIT(1), NULL}, + {UPB_TABKEY_STR("\006", "\000", "\000", "\000", "STRING"), UPB_TABVALUE_INT_INIT(0), &strentries[197]}, + {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "STRING_PIECE"), UPB_TABVALUE_INT_INIT(2), NULL}, + {UPB_TABKEY_STR("\011", "\000", "\000", "\000", "CODE_SIZE"), UPB_TABVALUE_INT_INIT(2), NULL}, + {UPB_TABKEY_STR("\005", "\000", "\000", "\000", "SPEED"), UPB_TABVALUE_INT_INIT(1), &strentries[203]}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\014", "\000", "\000", "\000", "LITE_RUNTIME"), UPB_TABVALUE_INT_INIT(3), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\047", "\000", "\000", "\000", "google.protobuf.SourceCodeInfo.Location"), UPB_TABVALUE_PTR_INIT(&msgs[17]), NULL}, + {UPB_TABKEY_STR("\043", "\000", "\000", "\000", "google.protobuf.UninterpretedOption"), UPB_TABVALUE_PTR_INIT(&msgs[18]), NULL}, + {UPB_TABKEY_STR("\043", "\000", "\000", "\000", "google.protobuf.FileDescriptorProto"), UPB_TABVALUE_PTR_INIT(&msgs[8]), NULL}, + {UPB_TABKEY_STR("\045", "\000", "\000", "\000", "google.protobuf.MethodDescriptorProto"), UPB_TABVALUE_PTR_INIT(&msgs[12]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\040", "\000", "\000", "\000", "google.protobuf.EnumValueOptions"), UPB_TABVALUE_PTR_INIT(&msgs[5]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\037", "\000", "\000", "\000", "google.protobuf.DescriptorProto"), UPB_TABVALUE_PTR_INIT(&msgs[0]), &strentries[228]}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\036", "\000", "\000", "\000", "google.protobuf.SourceCodeInfo"), UPB_TABVALUE_PTR_INIT(&msgs[16]), NULL}, + {UPB_TABKEY_STR("\051", "\000", "\000", "\000", "google.protobuf.FieldDescriptorProto.Type"), UPB_TABVALUE_PTR_INIT(&enums[1]), NULL}, + {UPB_TABKEY_STR("\056", "\000", "\000", "\000", "google.protobuf.DescriptorProto.ExtensionRange"), UPB_TABVALUE_PTR_INIT(&msgs[1]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_STR("\050", "\000", "\000", "\000", "google.protobuf.EnumValueDescriptorProto"), UPB_TABVALUE_PTR_INIT(&msgs[4]), NULL}, + {UPB_TABKEY_STR("\034", "\000", "\000", "\000", "google.protobuf.FieldOptions"), UPB_TABVALUE_PTR_INIT(&msgs[7]), NULL}, + {UPB_TABKEY_STR("\033", "\000", "\000", "\000", "google.protobuf.FileOptions"), UPB_TABVALUE_PTR_INIT(&msgs[10]), NULL}, + {UPB_TABKEY_STR("\043", "\000", "\000", "\000", "google.protobuf.EnumDescriptorProto"), UPB_TABVALUE_PTR_INIT(&msgs[2]), &strentries[233]}, + {UPB_TABKEY_STR("\052", "\000", "\000", "\000", "google.protobuf.FieldDescriptorProto.Label"), UPB_TABVALUE_PTR_INIT(&enums[0]), NULL}, + {UPB_TABKEY_STR("\046", "\000", "\000", "\000", "google.protobuf.ServiceDescriptorProto"), UPB_TABVALUE_PTR_INIT(&msgs[14]), NULL}, + {UPB_TABKEY_STR("\042", "\000", "\000", "\000", "google.protobuf.FieldOptions.CType"), UPB_TABVALUE_PTR_INIT(&enums[2]), &strentries[229]}, + {UPB_TABKEY_STR("\041", "\000", "\000", "\000", "google.protobuf.FileDescriptorSet"), UPB_TABVALUE_PTR_INIT(&msgs[9]), &strentries[235]}, + {UPB_TABKEY_STR("\033", "\000", "\000", "\000", "google.protobuf.EnumOptions"), UPB_TABVALUE_PTR_INIT(&msgs[3]), NULL}, + {UPB_TABKEY_STR("\044", "\000", "\000", "\000", "google.protobuf.FieldDescriptorProto"), UPB_TABVALUE_PTR_INIT(&msgs[6]), NULL}, + {UPB_TABKEY_STR("\050", "\000", "\000", "\000", "google.protobuf.FileOptions.OptimizeMode"), UPB_TABVALUE_PTR_INIT(&enums[3]), &strentries[221]}, + {UPB_TABKEY_STR("\036", "\000", "\000", "\000", "google.protobuf.ServiceOptions"), UPB_TABVALUE_PTR_INIT(&msgs[15]), NULL}, + {UPB_TABKEY_STR("\036", "\000", "\000", "\000", "google.protobuf.MessageOptions"), UPB_TABVALUE_PTR_INIT(&msgs[11]), NULL}, + {UPB_TABKEY_STR("\035", "\000", "\000", "\000", "google.protobuf.MethodOptions"), UPB_TABVALUE_PTR_INIT(&msgs[13]), &strentries[226]}, + {UPB_TABKEY_STR("\054", "\000", "\000", "\000", "google.protobuf.UninterpretedOption.NamePart"), UPB_TABVALUE_PTR_INIT(&msgs[19]), NULL}, }; static const upb_tabent intentries[14] = { - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NUM(999), UPB_VALUE_INIT_CONSTPTR(&fields[73]), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NUM(999), UPB_VALUE_INIT_CONSTPTR(&fields[71]), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NUM(999), UPB_VALUE_INIT_CONSTPTR(&fields[77]), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NUM(999), UPB_VALUE_INIT_CONSTPTR(&fields[75]), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NUM(999), UPB_VALUE_INIT_CONSTPTR(&fields[76]), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NUM(999), UPB_VALUE_INIT_CONSTPTR(&fields[74]), NULL}, - {UPB_TABKEY_NONE, UPB__VALUE_INIT_NONE, NULL}, - {UPB_TABKEY_NUM(999), UPB_VALUE_INIT_CONSTPTR(&fields[72]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NUM(999), UPB_TABVALUE_PTR_INIT(&fields[73]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NUM(999), UPB_TABVALUE_PTR_INIT(&fields[71]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NUM(999), UPB_TABVALUE_PTR_INIT(&fields[77]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NUM(999), UPB_TABVALUE_PTR_INIT(&fields[75]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NUM(999), UPB_TABVALUE_PTR_INIT(&fields[76]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NUM(999), UPB_TABVALUE_PTR_INIT(&fields[74]), NULL}, + {UPB_TABKEY_NONE, UPB_TABVALUE_EMPTY_INIT, NULL}, + {UPB_TABKEY_NUM(999), UPB_TABVALUE_PTR_INIT(&fields[72]), NULL}, }; -static const _upb_value arrays[232] = { - UPB_ARRAY_EMPTYENT, - UPB_VALUE_INIT_CONSTPTR(&fields[38]), - UPB_VALUE_INIT_CONSTPTR(&fields[16]), - UPB_VALUE_INIT_CONSTPTR(&fields[44]), - UPB_VALUE_INIT_CONSTPTR(&fields[9]), - UPB_VALUE_INIT_CONSTPTR(&fields[15]), - UPB_VALUE_INIT_CONSTPTR(&fields[14]), - UPB_VALUE_INIT_CONSTPTR(&fields[49]), - UPB_ARRAY_EMPTYENT, - UPB_VALUE_INIT_CONSTPTR(&fields[66]), - UPB_VALUE_INIT_CONSTPTR(&fields[8]), - UPB_ARRAY_EMPTYENT, - UPB_VALUE_INIT_CONSTPTR(&fields[40]), - UPB_VALUE_INIT_CONSTPTR(&fields[78]), - UPB_VALUE_INIT_CONSTPTR(&fields[50]), - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_VALUE_INIT_CONSTPTR(&fields[1]), - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_VALUE_INIT_CONSTPTR(&fields[37]), - UPB_VALUE_INIT_CONSTPTR(&fields[47]), - UPB_VALUE_INIT_CONSTPTR(&fields[52]), - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_VALUE_INIT_CONSTPTR(&fields[41]), - UPB_VALUE_INIT_CONSTPTR(&fields[12]), - UPB_VALUE_INIT_CONSTPTR(&fields[46]), - UPB_VALUE_INIT_CONSTPTR(&fields[27]), - UPB_VALUE_INIT_CONSTPTR(&fields[69]), - UPB_VALUE_INIT_CONSTPTR(&fields[70]), - UPB_VALUE_INIT_CONSTPTR(&fields[4]), - UPB_VALUE_INIT_CONSTPTR(&fields[51]), - UPB_ARRAY_EMPTYENT, - UPB_VALUE_INIT_CONSTPTR(&fields[3]), - UPB_VALUE_INIT_CONSTPTR(&fields[58]), - UPB_VALUE_INIT_CONSTPTR(&fields[6]), - UPB_ARRAY_EMPTYENT, - UPB_VALUE_INIT_CONSTPTR(&fields[28]), - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_VALUE_INIT_CONSTPTR(&fields[11]), - UPB_VALUE_INIT_CONSTPTR(&fields[79]), - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_VALUE_INIT_CONSTPTR(&fields[34]), - UPB_VALUE_INIT_CONSTPTR(&fields[57]), - UPB_VALUE_INIT_CONSTPTR(&fields[5]), - UPB_VALUE_INIT_CONSTPTR(&fields[32]), - UPB_VALUE_INIT_CONSTPTR(&fields[10]), - UPB_VALUE_INIT_CONSTPTR(&fields[63]), - UPB_VALUE_INIT_CONSTPTR(&fields[13]), - UPB_VALUE_INIT_CONSTPTR(&fields[53]), - UPB_VALUE_INIT_CONSTPTR(&fields[64]), - UPB_VALUE_INIT_CONSTPTR(&fields[61]), - UPB_VALUE_INIT_CONSTPTR(&fields[80]), - UPB_ARRAY_EMPTYENT, - UPB_VALUE_INIT_CONSTPTR(&fields[17]), - UPB_ARRAY_EMPTYENT, - UPB_VALUE_INIT_CONSTPTR(&fields[26]), - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_VALUE_INIT_CONSTPTR(&fields[25]), - UPB_VALUE_INIT_CONSTPTR(&fields[48]), - UPB_VALUE_INIT_CONSTPTR(&fields[24]), - UPB_VALUE_INIT_CONSTPTR(&fields[18]), - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_VALUE_INIT_CONSTPTR(&fields[2]), - UPB_VALUE_INIT_CONSTPTR(&fields[23]), - UPB_VALUE_INIT_CONSTPTR(&fields[62]), - UPB_ARRAY_EMPTYENT, - UPB_VALUE_INIT_CONSTPTR(&fields[22]), - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_VALUE_INIT_CONSTPTR(&fields[31]), - UPB_VALUE_INIT_CONSTPTR(&fields[45]), - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_VALUE_INIT_CONSTPTR(&fields[39]), - UPB_VALUE_INIT_CONSTPTR(&fields[20]), - UPB_VALUE_INIT_CONSTPTR(&fields[56]), - UPB_VALUE_INIT_CONSTPTR(&fields[55]), - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_VALUE_INIT_CONSTPTR(&fields[35]), - UPB_VALUE_INIT_CONSTPTR(&fields[33]), - UPB_VALUE_INIT_CONSTPTR(&fields[54]), - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_VALUE_INIT_CONSTPTR(&fields[30]), - UPB_ARRAY_EMPTYENT, - UPB_VALUE_INIT_CONSTPTR(&fields[59]), - UPB_VALUE_INIT_CONSTPTR(&fields[65]), - UPB_VALUE_INIT_CONSTPTR(&fields[29]), - UPB_VALUE_INIT_CONSTPTR(&fields[68]), - UPB_ARRAY_EMPTYENT, - UPB_ARRAY_EMPTYENT, - UPB_VALUE_INIT_CONSTPTR(&fields[36]), - UPB_VALUE_INIT_CONSTPTR(&fields[19]), - UPB_VALUE_INIT_CONSTPTR(&fields[60]), - UPB_VALUE_INIT_CONSTPTR(&fields[43]), - UPB_VALUE_INIT_CONSTPTR(&fields[7]), - UPB_VALUE_INIT_CONSTPTR(&fields[67]), - UPB_VALUE_INIT_CONSTPTR(&fields[0]), - UPB_ARRAY_EMPTYENT, - UPB_VALUE_INIT_CONSTPTR(&fields[42]), - UPB_VALUE_INIT_CONSTPTR(&fields[21]), - UPB_ARRAY_EMPTYENT, - UPB_VALUE_INIT_CONSTPTR("LABEL_OPTIONAL"), - UPB_VALUE_INIT_CONSTPTR("LABEL_REQUIRED"), - UPB_VALUE_INIT_CONSTPTR("LABEL_REPEATED"), - UPB_ARRAY_EMPTYENT, - UPB_VALUE_INIT_CONSTPTR("TYPE_DOUBLE"), - UPB_VALUE_INIT_CONSTPTR("TYPE_FLOAT"), - UPB_VALUE_INIT_CONSTPTR("TYPE_INT64"), - UPB_VALUE_INIT_CONSTPTR("TYPE_UINT64"), - UPB_VALUE_INIT_CONSTPTR("TYPE_INT32"), - UPB_VALUE_INIT_CONSTPTR("TYPE_FIXED64"), - UPB_VALUE_INIT_CONSTPTR("TYPE_FIXED32"), - UPB_VALUE_INIT_CONSTPTR("TYPE_BOOL"), - UPB_VALUE_INIT_CONSTPTR("TYPE_STRING"), - UPB_VALUE_INIT_CONSTPTR("TYPE_GROUP"), - UPB_VALUE_INIT_CONSTPTR("TYPE_MESSAGE"), - UPB_VALUE_INIT_CONSTPTR("TYPE_BYTES"), - UPB_VALUE_INIT_CONSTPTR("TYPE_UINT32"), - UPB_VALUE_INIT_CONSTPTR("TYPE_ENUM"), - UPB_VALUE_INIT_CONSTPTR("TYPE_SFIXED32"), - UPB_VALUE_INIT_CONSTPTR("TYPE_SFIXED64"), - UPB_VALUE_INIT_CONSTPTR("TYPE_SINT32"), - UPB_VALUE_INIT_CONSTPTR("TYPE_SINT64"), - UPB_VALUE_INIT_CONSTPTR("STRING"), - UPB_VALUE_INIT_CONSTPTR("CORD"), - UPB_VALUE_INIT_CONSTPTR("STRING_PIECE"), - UPB_ARRAY_EMPTYENT, - UPB_VALUE_INIT_CONSTPTR("SPEED"), - UPB_VALUE_INIT_CONSTPTR("CODE_SIZE"), - UPB_VALUE_INIT_CONSTPTR("LITE_RUNTIME"), +static const upb_tabval arrays[232] = { + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[38]), + UPB_TABVALUE_PTR_INIT(&fields[16]), + UPB_TABVALUE_PTR_INIT(&fields[44]), + UPB_TABVALUE_PTR_INIT(&fields[9]), + UPB_TABVALUE_PTR_INIT(&fields[15]), + UPB_TABVALUE_PTR_INIT(&fields[14]), + UPB_TABVALUE_PTR_INIT(&fields[49]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[66]), + UPB_TABVALUE_PTR_INIT(&fields[8]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[40]), + UPB_TABVALUE_PTR_INIT(&fields[78]), + UPB_TABVALUE_PTR_INIT(&fields[50]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[1]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[37]), + UPB_TABVALUE_PTR_INIT(&fields[47]), + UPB_TABVALUE_PTR_INIT(&fields[52]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[41]), + UPB_TABVALUE_PTR_INIT(&fields[12]), + UPB_TABVALUE_PTR_INIT(&fields[46]), + UPB_TABVALUE_PTR_INIT(&fields[27]), + UPB_TABVALUE_PTR_INIT(&fields[69]), + UPB_TABVALUE_PTR_INIT(&fields[70]), + UPB_TABVALUE_PTR_INIT(&fields[4]), + UPB_TABVALUE_PTR_INIT(&fields[51]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[3]), + UPB_TABVALUE_PTR_INIT(&fields[58]), + UPB_TABVALUE_PTR_INIT(&fields[6]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[28]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[11]), + UPB_TABVALUE_PTR_INIT(&fields[79]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[34]), + UPB_TABVALUE_PTR_INIT(&fields[57]), + UPB_TABVALUE_PTR_INIT(&fields[5]), + UPB_TABVALUE_PTR_INIT(&fields[32]), + UPB_TABVALUE_PTR_INIT(&fields[10]), + UPB_TABVALUE_PTR_INIT(&fields[63]), + UPB_TABVALUE_PTR_INIT(&fields[13]), + UPB_TABVALUE_PTR_INIT(&fields[53]), + UPB_TABVALUE_PTR_INIT(&fields[64]), + UPB_TABVALUE_PTR_INIT(&fields[61]), + UPB_TABVALUE_PTR_INIT(&fields[80]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[17]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[26]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[25]), + UPB_TABVALUE_PTR_INIT(&fields[48]), + UPB_TABVALUE_PTR_INIT(&fields[24]), + UPB_TABVALUE_PTR_INIT(&fields[18]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[2]), + UPB_TABVALUE_PTR_INIT(&fields[23]), + UPB_TABVALUE_PTR_INIT(&fields[62]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[22]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[31]), + UPB_TABVALUE_PTR_INIT(&fields[45]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[39]), + UPB_TABVALUE_PTR_INIT(&fields[20]), + UPB_TABVALUE_PTR_INIT(&fields[56]), + UPB_TABVALUE_PTR_INIT(&fields[55]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[35]), + UPB_TABVALUE_PTR_INIT(&fields[33]), + UPB_TABVALUE_PTR_INIT(&fields[54]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[30]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[59]), + UPB_TABVALUE_PTR_INIT(&fields[65]), + UPB_TABVALUE_PTR_INIT(&fields[29]), + UPB_TABVALUE_PTR_INIT(&fields[68]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[36]), + UPB_TABVALUE_PTR_INIT(&fields[19]), + UPB_TABVALUE_PTR_INIT(&fields[60]), + UPB_TABVALUE_PTR_INIT(&fields[43]), + UPB_TABVALUE_PTR_INIT(&fields[7]), + UPB_TABVALUE_PTR_INIT(&fields[67]), + UPB_TABVALUE_PTR_INIT(&fields[0]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT(&fields[42]), + UPB_TABVALUE_PTR_INIT(&fields[21]), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT("LABEL_OPTIONAL"), + UPB_TABVALUE_PTR_INIT("LABEL_REQUIRED"), + UPB_TABVALUE_PTR_INIT("LABEL_REPEATED"), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT("TYPE_DOUBLE"), + UPB_TABVALUE_PTR_INIT("TYPE_FLOAT"), + UPB_TABVALUE_PTR_INIT("TYPE_INT64"), + UPB_TABVALUE_PTR_INIT("TYPE_UINT64"), + UPB_TABVALUE_PTR_INIT("TYPE_INT32"), + UPB_TABVALUE_PTR_INIT("TYPE_FIXED64"), + UPB_TABVALUE_PTR_INIT("TYPE_FIXED32"), + UPB_TABVALUE_PTR_INIT("TYPE_BOOL"), + UPB_TABVALUE_PTR_INIT("TYPE_STRING"), + UPB_TABVALUE_PTR_INIT("TYPE_GROUP"), + UPB_TABVALUE_PTR_INIT("TYPE_MESSAGE"), + UPB_TABVALUE_PTR_INIT("TYPE_BYTES"), + UPB_TABVALUE_PTR_INIT("TYPE_UINT32"), + UPB_TABVALUE_PTR_INIT("TYPE_ENUM"), + UPB_TABVALUE_PTR_INIT("TYPE_SFIXED32"), + UPB_TABVALUE_PTR_INIT("TYPE_SFIXED64"), + UPB_TABVALUE_PTR_INIT("TYPE_SINT32"), + UPB_TABVALUE_PTR_INIT("TYPE_SINT64"), + UPB_TABVALUE_PTR_INIT("STRING"), + UPB_TABVALUE_PTR_INIT("CORD"), + UPB_TABVALUE_PTR_INIT("STRING_PIECE"), + UPB_TABVALUE_EMPTY_INIT, + UPB_TABVALUE_PTR_INIT("SPEED"), + UPB_TABVALUE_PTR_INIT("CODE_SIZE"), + UPB_TABVALUE_PTR_INIT("LITE_RUNTIME"), }; static const upb_symtab symtab = UPB_SYMTAB_INIT(UPB_STRTABLE_INIT(24, 31, UPB_CTYPE_PTR, 5, &strentries[204]), &reftables[210], &reftables[211]); @@ -5798,8 +5842,8 @@ static upb_inttable reftables[212] = { #include #include -// upb_deflist is an internal-only dynamic array for storing a growing list of -// upb_defs. +/* upb_deflist is an internal-only dynamic array for storing a growing list of + * upb_defs. */ typedef struct { upb_def **defs; size_t len; @@ -5807,27 +5851,27 @@ typedef struct { bool owned; } upb_deflist; -// We keep a stack of all the messages scopes we are currently in, as well as -// the top-level file scope. This is necessary to correctly qualify the -// definitions that are contained inside. "name" tracks the name of the -// message or package (a bare name -- not qualified by any enclosing scopes). +/* We keep a stack of all the messages scopes we are currently in, as well as + * the top-level file scope. This is necessary to correctly qualify the + * definitions that are contained inside. "name" tracks the name of the + * message or package (a bare name -- not qualified by any enclosing scopes). */ typedef struct { char *name; - // Index of the first def that is under this scope. For msgdefs, the - // msgdef itself is at start-1. + /* Index of the first def that is under this scope. For msgdefs, the + * msgdef itself is at start-1. */ int start; } upb_descreader_frame; -// The maximum number of nested declarations that are allowed, ie. -// message Foo { -// message Bar { -// message Baz { -// } -// } -// } -// -// This is a resource limit that affects how big our runtime stack can grow. -// TODO: make this a runtime-settable property of the Reader instance. +/* The maximum number of nested declarations that are allowed, ie. + * message Foo { + * message Bar { + * message Baz { + * } + * } + * } + * + * This is a resource limit that affects how big our runtime stack can grow. + * TODO: make this a runtime-settable property of the Reader instance. */ #define UPB_MAX_MESSAGE_NESTING 64 struct upb_descreader { @@ -5854,11 +5898,11 @@ static char *upb_strndup(const char *buf, size_t n) { return ret; } -// Returns a newly allocated string that joins input strings together, for -// example: -// join("Foo.Bar", "Baz") -> "Foo.Bar.Baz" -// join("", "Baz") -> "Baz" -// Caller owns a ref on the returned string. +/* Returns a newly allocated string that joins input strings together, for + * example: + * join("Foo.Bar", "Baz") -> "Foo.Bar.Baz" + * join("", "Baz") -> "Baz" + * Caller owns a ref on the returned string. */ static char *upb_join(const char *base, const char *name) { if (!base || strlen(base) == 0) { return upb_strdup(name); @@ -5883,8 +5927,9 @@ void upb_deflist_init(upb_deflist *l) { } void upb_deflist_uninit(upb_deflist *l) { + size_t i; if (l->owned) - for(size_t i = 0; i < l->len; i++) + for(i = 0; i < l->len; i++) upb_def_unref(l->defs[i], l); free(l->defs); } @@ -5902,8 +5947,9 @@ bool upb_deflist_push(upb_deflist *l, upb_def *d) { } void upb_deflist_donaterefs(upb_deflist *l, void *owner) { + size_t i; assert(l->owned); - for (size_t i = 0; i < l->len; i++) + for (i = 0; i < l->len; i++) upb_def_donateref(l->defs[i], l, owner); l->owned = false; } @@ -5912,9 +5958,10 @@ static upb_def *upb_deflist_last(upb_deflist *l) { return l->defs[l->len-1]; } -// Qualify the defname for all defs starting with offset "start" with "str". +/* Qualify the defname for all defs starting with offset "start" with "str". */ static void upb_deflist_qualify(upb_deflist *l, char *str, int32_t start) { - for (uint32_t i = start; i < l->len; i++) { + uint32_t i; + for (i = start; i < l->len; i++) { upb_def *def = l->defs[i]; char *name = upb_join(str, upb_def_fullname(def)); upb_def_setfullname(def, name, NULL); @@ -5926,8 +5973,9 @@ static void upb_deflist_qualify(upb_deflist *l, char *str, int32_t start) { /* upb_descreader ************************************************************/ static upb_msgdef *upb_descreader_top(upb_descreader *r) { + int index; assert(r->stack_len > 1); - int index = r->stack[r->stack_len-1].start - 1; + index = r->stack[r->stack_len-1].start - 1; assert(index >= 0); return upb_downcast_msgdef_mutable(r->defs.defs[index]); } @@ -5936,8 +5984,8 @@ static upb_def *upb_descreader_last(upb_descreader *r) { return upb_deflist_last(&r->defs); } -// Start/end handlers for FileDescriptorProto and DescriptorProto (the two -// entities that have names and can contain sub-definitions. +/* Start/end handlers for FileDescriptorProto and DescriptorProto (the two + * entities that have names and can contain sub-definitions. */ void upb_descreader_startcontainer(upb_descreader *r) { upb_descreader_frame *f = &r->stack[r->stack_len++]; f->start = r->defs.len; @@ -5957,7 +6005,7 @@ void upb_descreader_setscopename(upb_descreader *r, char *str) { f->name = str; } -// Handlers for google.protobuf.FileDescriptorProto. +/* Handlers for google.protobuf.FileDescriptorProto. */ static bool file_startmsg(void *r, const void *hd) { UPB_UNUSED(hd); upb_descreader_startcontainer(r); @@ -5965,27 +6013,27 @@ static bool file_startmsg(void *r, const void *hd) { } static bool file_endmsg(void *closure, const void *hd, upb_status *status) { + upb_descreader *r = closure; UPB_UNUSED(hd); UPB_UNUSED(status); - upb_descreader *r = closure; upb_descreader_endcontainer(r); return true; } static size_t file_onpackage(void *closure, const void *hd, const char *buf, size_t n, const upb_bufhandle *handle) { + upb_descreader *r = closure; UPB_UNUSED(hd); UPB_UNUSED(handle); - upb_descreader *r = closure; - // XXX: see comment at the top of the file. + /* XXX: see comment at the top of the file. */ upb_descreader_setscopename(r, upb_strndup(buf, n)); return n; } -// Handlers for google.protobuf.EnumValueDescriptorProto. +/* Handlers for google.protobuf.EnumValueDescriptorProto. */ static bool enumval_startmsg(void *closure, const void *hd) { - UPB_UNUSED(hd); upb_descreader *r = closure; + UPB_UNUSED(hd); r->saw_number = false; r->saw_name = false; return true; @@ -5993,10 +6041,10 @@ static bool enumval_startmsg(void *closure, const void *hd) { static size_t enumval_onname(void *closure, const void *hd, const char *buf, size_t n, const upb_bufhandle *handle) { + upb_descreader *r = closure; UPB_UNUSED(hd); UPB_UNUSED(handle); - upb_descreader *r = closure; - // XXX: see comment at the top of the file. + /* XXX: see comment at the top of the file. */ free(r->name); r->name = upb_strndup(buf, n); r->saw_name = true; @@ -6004,21 +6052,23 @@ static size_t enumval_onname(void *closure, const void *hd, const char *buf, } static bool enumval_onnumber(void *closure, const void *hd, int32_t val) { - UPB_UNUSED(hd); upb_descreader *r = closure; + UPB_UNUSED(hd); r->number = val; r->saw_number = true; return true; } static bool enumval_endmsg(void *closure, const void *hd, upb_status *status) { - UPB_UNUSED(hd); upb_descreader *r = closure; + upb_enumdef *e; + UPB_UNUSED(hd); + if(!r->saw_number || !r->saw_name) { upb_status_seterrmsg(status, "Enum value missing name or number."); return false; } - upb_enumdef *e = upb_downcast_enumdef_mutable(upb_descreader_last(r)); + e = upb_downcast_enumdef_mutable(upb_descreader_last(r)); upb_enumdef_addval(e, r->name, r->number, status); free(r->name); r->name = NULL; @@ -6026,18 +6076,21 @@ static bool enumval_endmsg(void *closure, const void *hd, upb_status *status) { } -// Handlers for google.protobuf.EnumDescriptorProto. +/* Handlers for google.protobuf.EnumDescriptorProto. */ static bool enum_startmsg(void *closure, const void *hd) { - UPB_UNUSED(hd); upb_descreader *r = closure; - upb_deflist_push(&r->defs, UPB_UPCAST(upb_enumdef_new(&r->defs))); + UPB_UNUSED(hd); + upb_deflist_push(&r->defs, + upb_enumdef_upcast_mutable(upb_enumdef_new(&r->defs))); return true; } static bool enum_endmsg(void *closure, const void *hd, upb_status *status) { - UPB_UNUSED(hd); upb_descreader *r = closure; - upb_enumdef *e = upb_downcast_enumdef_mutable(upb_descreader_last(r)); + upb_enumdef *e; + UPB_UNUSED(hd); + + e = upb_downcast_enumdef_mutable(upb_descreader_last(r)); if (upb_def_fullname(upb_descreader_last(r)) == NULL) { upb_status_seterrmsg(status, "Enum had no name."); return false; @@ -6051,31 +6104,31 @@ static bool enum_endmsg(void *closure, const void *hd, upb_status *status) { static size_t enum_onname(void *closure, const void *hd, const char *buf, size_t n, const upb_bufhandle *handle) { - UPB_UNUSED(hd); - UPB_UNUSED(handle); upb_descreader *r = closure; - // XXX: see comment at the top of the file. char *fullname = upb_strndup(buf, n); + UPB_UNUSED(hd); + UPB_UNUSED(handle); + /* XXX: see comment at the top of the file. */ upb_def_setfullname(upb_descreader_last(r), fullname, NULL); free(fullname); return n; } -// Handlers for google.protobuf.FieldDescriptorProto +/* Handlers for google.protobuf.FieldDescriptorProto */ static bool field_startmsg(void *closure, const void *hd) { - UPB_UNUSED(hd); upb_descreader *r = closure; + UPB_UNUSED(hd); r->f = upb_fielddef_new(&r->defs); free(r->default_string); r->default_string = NULL; - // fielddefs default to packed, but descriptors default to non-packed. + /* fielddefs default to packed, but descriptors default to non-packed. */ upb_fielddef_setpacked(r->f, false); return true; } -// Converts the default value in string "str" into "d". Passes a ref on str. -// Returns true on success. +/* Converts the default value in string "str" into "d". Passes a ref on str. + * Returns true on success. */ static bool parse_default(char *str, upb_fielddef *f) { bool success = true; char *end; @@ -6089,7 +6142,8 @@ static bool parse_default(char *str, upb_fielddef *f) { break; } case UPB_TYPE_INT64: { - long long val = strtoll(str, &end, 0); + /* XXX: Need to write our own strtoll, since it's not available in c89. */ + long long val = strtol(str, &end, 0); if (val > INT64_MAX || val < INT64_MIN || errno == ERANGE || *end) success = false; else @@ -6105,7 +6159,8 @@ static bool parse_default(char *str, upb_fielddef *f) { break; } case UPB_TYPE_UINT64: { - unsigned long long val = strtoull(str, &end, 0); + /* XXX: Need to write our own strtoull, since it's not available in c89. */ + unsigned long long val = strtoul(str, &end, 0); if (val > UINT64_MAX || errno == ERANGE || *end) success = false; else @@ -6121,7 +6176,8 @@ static bool parse_default(char *str, upb_fielddef *f) { break; } case UPB_TYPE_FLOAT: { - float val = strtof(str, &end); + /* XXX: Need to write our own strtof, since it's not available in c89. */ + float val = strtod(str, &end); if (errno == ERANGE || *end) success = false; else @@ -6143,10 +6199,11 @@ static bool parse_default(char *str, upb_fielddef *f) { } static bool field_endmsg(void *closure, const void *hd, upb_status *status) { - UPB_UNUSED(hd); upb_descreader *r = closure; upb_fielddef *f = r->f; - // TODO: verify that all required fields were present. + UPB_UNUSED(hd); + + /* TODO: verify that all required fields were present. */ assert(upb_fielddef_number(f) != 0); assert(upb_fielddef_name(f) != NULL); assert((upb_fielddef_subdefname(f) != NULL) == upb_fielddef_hassubdef(f)); @@ -6160,8 +6217,8 @@ static bool field_endmsg(void *closure, const void *hd, upb_status *status) { upb_fielddef_setdefaultcstr(f, r->default_string, NULL); } else { if (r->default_string && !parse_default(r->default_string, f)) { - // We don't worry too much about giving a great error message since the - // compiler should have ensured this was correct. + /* We don't worry too much about giving a great error message since the + * compiler should have ensured this was correct. */ upb_status_seterrmsg(status, "Error converting default value."); return false; } @@ -6171,48 +6228,54 @@ static bool field_endmsg(void *closure, const void *hd, upb_status *status) { } static bool field_onlazy(void *closure, const void *hd, bool val) { - UPB_UNUSED(hd); upb_descreader *r = closure; + UPB_UNUSED(hd); + upb_fielddef_setlazy(r->f, val); return true; } static bool field_onpacked(void *closure, const void *hd, bool val) { - UPB_UNUSED(hd); upb_descreader *r = closure; + UPB_UNUSED(hd); + upb_fielddef_setpacked(r->f, val); return true; } static bool field_ontype(void *closure, const void *hd, int32_t val) { - UPB_UNUSED(hd); upb_descreader *r = closure; + UPB_UNUSED(hd); + upb_fielddef_setdescriptortype(r->f, val); return true; } static bool field_onlabel(void *closure, const void *hd, int32_t val) { - UPB_UNUSED(hd); upb_descreader *r = closure; + UPB_UNUSED(hd); + upb_fielddef_setlabel(r->f, val); return true; } static bool field_onnumber(void *closure, const void *hd, int32_t val) { - UPB_UNUSED(hd); upb_descreader *r = closure; bool ok = upb_fielddef_setnumber(r->f, val, NULL); + UPB_UNUSED(hd); + UPB_ASSERT_VAR(ok, ok); return true; } static size_t field_onname(void *closure, const void *hd, const char *buf, size_t n, const upb_bufhandle *handle) { - UPB_UNUSED(hd); - UPB_UNUSED(handle); upb_descreader *r = closure; - // XXX: see comment at the top of the file. char *name = upb_strndup(buf, n); + UPB_UNUSED(hd); + UPB_UNUSED(handle); + + /* XXX: see comment at the top of the file. */ upb_fielddef_setname(r->f, name, NULL); free(name); return n; @@ -6220,11 +6283,12 @@ static size_t field_onname(void *closure, const void *hd, const char *buf, static size_t field_ontypename(void *closure, const void *hd, const char *buf, size_t n, const upb_bufhandle *handle) { - UPB_UNUSED(hd); - UPB_UNUSED(handle); upb_descreader *r = closure; - // XXX: see comment at the top of the file. char *name = upb_strndup(buf, n); + UPB_UNUSED(hd); + UPB_UNUSED(handle); + + /* XXX: see comment at the top of the file. */ upb_fielddef_setsubdefname(r->f, name, NULL); free(name); return n; @@ -6232,11 +6296,12 @@ static size_t field_ontypename(void *closure, const void *hd, const char *buf, static size_t field_onextendee(void *closure, const void *hd, const char *buf, size_t n, const upb_bufhandle *handle) { - UPB_UNUSED(hd); - UPB_UNUSED(handle); upb_descreader *r = closure; - // XXX: see comment at the top of the file. char *name = upb_strndup(buf, n); + UPB_UNUSED(hd); + UPB_UNUSED(handle); + + /* XXX: see comment at the top of the file. */ upb_fielddef_setcontainingtypename(r->f, name, NULL); free(name); return n; @@ -6244,31 +6309,35 @@ static size_t field_onextendee(void *closure, const void *hd, const char *buf, static size_t field_ondefaultval(void *closure, const void *hd, const char *buf, size_t n, const upb_bufhandle *handle) { + upb_descreader *r = closure; UPB_UNUSED(hd); UPB_UNUSED(handle); - upb_descreader *r = closure; - // Have to convert from string to the correct type, but we might not know the - // type yet, so we save it as a string until the end of the field. - // XXX: see comment at the top of the file. + + /* Have to convert from string to the correct type, but we might not know the + * type yet, so we save it as a string until the end of the field. + * XXX: see comment at the top of the file. */ free(r->default_string); r->default_string = upb_strndup(buf, n); return n; } -// Handlers for google.protobuf.DescriptorProto (representing a message). +/* Handlers for google.protobuf.DescriptorProto (representing a message). */ static bool msg_startmsg(void *closure, const void *hd) { - UPB_UNUSED(hd); upb_descreader *r = closure; - upb_deflist_push(&r->defs, UPB_UPCAST(upb_msgdef_new(&r->defs))); + UPB_UNUSED(hd); + + upb_deflist_push(&r->defs, + upb_msgdef_upcast_mutable(upb_msgdef_new(&r->defs))); upb_descreader_startcontainer(r); return true; } static bool msg_endmsg(void *closure, const void *hd, upb_status *status) { - UPB_UNUSED(hd); upb_descreader *r = closure; upb_msgdef *m = upb_descreader_top(r); - if(!upb_def_fullname(UPB_UPCAST(m))) { + UPB_UNUSED(hd); + + if(!upb_def_fullname(upb_msgdef_upcast_mutable(m))) { upb_status_seterrmsg(status, "Encountered message with no name."); return false; } @@ -6278,32 +6347,35 @@ static bool msg_endmsg(void *closure, const void *hd, upb_status *status) { static size_t msg_onname(void *closure, const void *hd, const char *buf, size_t n, const upb_bufhandle *handle) { - UPB_UNUSED(hd); - UPB_UNUSED(handle); upb_descreader *r = closure; upb_msgdef *m = upb_descreader_top(r); - // XXX: see comment at the top of the file. + /* XXX: see comment at the top of the file. */ char *name = upb_strndup(buf, n); - upb_def_setfullname(UPB_UPCAST(m), name, NULL); - upb_descreader_setscopename(r, name); // Passes ownership of name. + UPB_UNUSED(hd); + UPB_UNUSED(handle); + + upb_def_setfullname(upb_msgdef_upcast_mutable(m), name, NULL); + upb_descreader_setscopename(r, name); /* Passes ownership of name. */ return n; } static bool msg_onendfield(void *closure, const void *hd) { - UPB_UNUSED(hd); upb_descreader *r = closure; upb_msgdef *m = upb_descreader_top(r); + UPB_UNUSED(hd); + upb_msgdef_addfield(m, r->f, &r->defs, NULL); r->f = NULL; return true; } static bool pushextension(void *closure, const void *hd) { - UPB_UNUSED(hd); upb_descreader *r = closure; + UPB_UNUSED(hd); + assert(upb_fielddef_containingtypename(r->f)); upb_fielddef_setisextension(r->f, true); - upb_deflist_push(&r->defs, UPB_UPCAST(r->f)); + upb_deflist_push(&r->defs, upb_fielddef_upcast_mutable(r->f)); r->f = NULL; return true; } @@ -6455,14 +6527,14 @@ static void visitgroup(const upb_refcounted *r, upb_refcounted_visit *visit, upb_inttable_begin(&i, &g->methods); for(; !upb_inttable_done(&i); upb_inttable_next(&i)) { upb_pbdecodermethod *method = upb_value_getptr(upb_inttable_iter_value(&i)); - visit(r, UPB_UPCAST(method), closure); + visit(r, upb_pbdecodermethod_upcast(method), closure); } } mgroup *newgroup(const void *owner) { mgroup *g = malloc(sizeof(*g)); static const struct upb_refcounted_vtbl vtbl = {visitgroup, freegroup}; - upb_refcounted_init(UPB_UPCAST(g), &vtbl, owner); + upb_refcounted_init(mgroup_upcast_mutable(g), &vtbl, owner); upb_inttable_init(&g->methods, UPB_CTYPE_PTR); g->bytecode = NULL; g->bytecode_end = NULL; @@ -6493,18 +6565,18 @@ static upb_pbdecodermethod *newmethod(const upb_handlers *dest_handlers, mgroup *group) { static const struct upb_refcounted_vtbl vtbl = {visitmethod, freemethod}; upb_pbdecodermethod *ret = malloc(sizeof(*ret)); - upb_refcounted_init(UPB_UPCAST(ret), &vtbl, &ret); + upb_refcounted_init(upb_pbdecodermethod_upcast_mutable(ret), &vtbl, &ret); upb_byteshandler_init(&ret->input_handler_); - // The method references the group and vice-versa, in a circular reference. + /* The method references the group and vice-versa, in a circular reference. */ upb_ref2(ret, group); upb_ref2(group, ret); upb_inttable_insertptr(&group->methods, dest_handlers, upb_value_ptr(ret)); - upb_refcounted_unref(UPB_UPCAST(ret), &ret); + upb_pbdecodermethod_unref(ret, &ret); - ret->group = UPB_UPCAST(group); + ret->group = mgroup_upcast_mutable(group); ret->dest_handlers_ = dest_handlers; - ret->is_native_ = false; // If we JIT, it will update this later. + ret->is_native_ = false; /* If we JIT, it will update this later. */ upb_inttable_init(&ret->dispatch, UPB_CTYPE_UINT64); if (ret->dest_handlers_) { @@ -6513,25 +6585,6 @@ static upb_pbdecodermethod *newmethod(const upb_handlers *dest_handlers, return ret; } -void upb_pbdecodermethod_ref(const upb_pbdecodermethod *m, const void *owner) { - upb_refcounted_ref(UPB_UPCAST(m), owner); -} - -void upb_pbdecodermethod_unref(const upb_pbdecodermethod *m, - const void *owner) { - upb_refcounted_unref(UPB_UPCAST(m), owner); -} - -void upb_pbdecodermethod_donateref(const upb_pbdecodermethod *m, - const void *from, const void *to) { - upb_refcounted_donateref(UPB_UPCAST(m), from, to); -} - -void upb_pbdecodermethod_checkref(const upb_pbdecodermethod *m, - const void *owner) { - upb_refcounted_checkref(UPB_UPCAST(m), owner); -} - const upb_handlers *upb_pbdecodermethod_desthandlers( const upb_pbdecodermethod *m) { return m->dest_handlers_; @@ -6548,10 +6601,11 @@ bool upb_pbdecodermethod_isnative(const upb_pbdecodermethod *m) { const upb_pbdecodermethod *upb_pbdecodermethod_new( const upb_pbdecodermethodopts *opts, const void *owner) { + const upb_pbdecodermethod *ret; upb_pbcodecache cache; + upb_pbcodecache_init(&cache); - const upb_pbdecodermethod *ret = - upb_pbcodecache_getdecodermethod(&cache, opts); + ret = upb_pbcodecache_getdecodermethod(&cache, opts); upb_pbdecodermethod_ref(ret, owner); upb_pbcodecache_uninit(&cache); return ret; @@ -6560,7 +6614,7 @@ const upb_pbdecodermethod *upb_pbdecodermethod_new( /* bytecode compiler **********************************************************/ -// Data used only at compilation time. +/* Data used only at compilation time. */ typedef struct { mgroup *group; @@ -6568,15 +6622,17 @@ typedef struct { int fwd_labels[MAXLABEL]; int back_labels[MAXLABEL]; - // For fields marked "lazy", parse them lazily or eagerly? + /* For fields marked "lazy", parse them lazily or eagerly? */ bool lazy; } compiler; static compiler *newcompiler(mgroup *group, bool lazy) { compiler *ret = malloc(sizeof(*ret)); + int i; + ret->group = group; ret->lazy = lazy; - for (int i = 0; i < MAXLABEL; i++) { + for (i = 0; i < MAXLABEL; i++) { ret->fwd_labels[i] = EMPTYLABEL; ret->back_labels[i] = EMPTYLABEL; } @@ -6589,7 +6645,7 @@ static void freecompiler(compiler *c) { const size_t ptr_words = sizeof(void*) / sizeof(uint32_t); -// How many words an instruction is. +/* How many words an instruction is. */ static int instruction_len(uint32_t instr) { switch (getop(instr)) { case OP_SETDISPATCH: return 1 + ptr_words; @@ -6605,8 +6661,8 @@ bool op_has_longofs(int32_t instruction) { case OP_BRANCH: case OP_CHECKDELIM: return true; - // The "tag" instructions only have 8 bytes available for the jump target, - // but that is ok because these opcodes only require short jumps. + /* The "tag" instructions only have 8 bytes available for the jump target, + * but that is ok because these opcodes only require short jumps. */ case OP_TAG1: case OP_TAG2: case OP_TAGN: @@ -6631,18 +6687,21 @@ static void setofs(uint32_t *instruction, int32_t ofs) { } else { *instruction = (*instruction & ~0xff00) | ((ofs & 0xff) << 8); } - assert(getofs(*instruction) == ofs); // Would fail in cases of overflow. + assert(getofs(*instruction) == ofs); /* Would fail in cases of overflow. */ } static uint32_t pcofs(compiler *c) { return c->pc - c->group->bytecode; } -// Defines a local label at the current PC location. All previous forward -// references are updated to point to this location. The location is noted -// for any future backward references. +/* Defines a local label at the current PC location. All previous forward + * references are updated to point to this location. The location is noted + * for any future backward references. */ static void label(compiler *c, unsigned int label) { + int val; + uint32_t *codep; + assert(label < MAXLABEL); - int val = c->fwd_labels[label]; - uint32_t *codep = (val == EMPTYLABEL) ? NULL : c->group->bytecode + val; + val = c->fwd_labels[label]; + codep = (val == EMPTYLABEL) ? NULL : c->group->bytecode + val; while (codep) { int ofs = getofs(*codep); setofs(codep, c->pc - codep - instruction_len(*codep)); @@ -6652,24 +6711,25 @@ static void label(compiler *c, unsigned int label) { c->back_labels[label] = pcofs(c); } -// Creates a reference to a numbered label; either a forward reference -// (positive arg) or backward reference (negative arg). For forward references -// the value returned now is actually a "next" pointer into a linked list of all -// instructions that use this label and will be patched later when the label is -// defined with label(). -// -// The returned value is the offset that should be written into the instruction. +/* Creates a reference to a numbered label; either a forward reference + * (positive arg) or backward reference (negative arg). For forward references + * the value returned now is actually a "next" pointer into a linked list of all + * instructions that use this label and will be patched later when the label is + * defined with label(). + * + * The returned value is the offset that should be written into the instruction. + */ static int32_t labelref(compiler *c, int label) { assert(label < MAXLABEL); if (label == LABEL_DISPATCH) { - // No resolving required. + /* No resolving required. */ return 0; } else if (label < 0) { - // Backward local label. Relative to the next instruction. + /* Backward local label. Relative to the next instruction. */ uint32_t from = (c->pc + 1) - c->group->bytecode; return c->back_labels[-label] - from; } else { - // Forward local label: prepend to (possibly-empty) linked list. + /* Forward local label: prepend to (possibly-empty) linked list. */ int *lptr = &c->fwd_labels[label]; int32_t ret = (*lptr == EMPTYLABEL) ? 0 : *lptr - pcofs(c); *lptr = pcofs(c); @@ -6683,7 +6743,7 @@ static void put32(compiler *c, uint32_t v) { int ofs = pcofs(c); size_t oldsize = g->bytecode_end - g->bytecode; size_t newsize = UPB_MAX(oldsize * 2, 64); - // TODO(haberman): handle OOM. + /* TODO(haberman): handle OOM. */ g->bytecode = realloc(g->bytecode, newsize * sizeof(uint32_t)); g->bytecode_end = g->bytecode + newsize; c->pc = g->bytecode + ofs; @@ -6782,19 +6842,22 @@ static void putop(compiler *c, opcode op, ...) { #if defined(UPB_USE_JIT_X64) || defined(UPB_DUMP_BYTECODE) const char *upb_pbdecoder_getopname(unsigned int op) { -#define OP(op) [OP_ ## op] = "OP_" #op -#define T(op) OP(PARSE_##op) - static const char *names[] = { - "", - T(DOUBLE), T(FLOAT), T(INT64), T(UINT64), T(INT32), T(FIXED64), T(FIXED32), - T(BOOL), T(UINT32), T(SFIXED32), T(SFIXED64), T(SINT32), T(SINT64), - OP(STARTMSG), OP(ENDMSG), OP(STARTSEQ), OP(ENDSEQ), OP(STARTSUBMSG), - OP(ENDSUBMSG), OP(STARTSTR), OP(STRING), OP(ENDSTR), OP(CALL), OP(RET), - OP(PUSHLENDELIM), OP(PUSHTAGDELIM), OP(SETDELIM), OP(CHECKDELIM), - OP(BRANCH), OP(TAG1), OP(TAG2), OP(TAGN), OP(SETDISPATCH), OP(POP), - OP(SETBIGGROUPNUM), OP(DISPATCH), OP(HALT), - }; - return op > OP_HALT ? names[0] : names[op]; +#define QUOTE(x) #x +#define EXPAND_AND_QUOTE(x) QUOTE(x) +#define OPNAME(x) OP_##x +#define OP(x) case OPNAME(x): return EXPAND_AND_QUOTE(OPNAME(x)); +#define T(x) OP(PARSE_##x) + /* Keep in sync with list in decoder.int.h. */ + switch ((opcode)op) { + T(DOUBLE) T(FLOAT) T(INT64) T(UINT64) T(INT32) T(FIXED64) T(FIXED32) + T(BOOL) T(UINT32) T(SFIXED32) T(SFIXED64) T(SINT32) T(SINT64) + OP(STARTMSG) OP(ENDMSG) OP(STARTSEQ) OP(ENDSEQ) OP(STARTSUBMSG) + OP(ENDSUBMSG) OP(STARTSTR) OP(STRING) OP(ENDSTR) OP(CALL) OP(RET) + OP(PUSHLENDELIM) OP(PUSHTAGDELIM) OP(SETDELIM) OP(CHECKDELIM) + OP(BRANCH) OP(TAG1) OP(TAG2) OP(TAGN) OP(SETDISPATCH) OP(POP) + OP(SETBIGGROUPNUM) OP(DISPATCH) OP(HALT) + } + return ""; #undef OP #undef T } @@ -6892,7 +6955,7 @@ static void dumpbc(uint32_t *p, uint32_t *end, FILE *f) { static uint64_t get_encoded_tag(const upb_fielddef *f, int wire_type) { uint32_t tag = (upb_fielddef_number(f) << 3) | wire_type; uint64_t encoded_tag = upb_vencode32(tag); - // No tag should be greater than 5 bytes. + /* No tag should be greater than 5 bytes. */ assert(encoded_tag <= 0xffffffffff); return encoded_tag; } @@ -6920,29 +6983,29 @@ static upb_selector_t getsel(const upb_fielddef *f, upb_handlertype_t type) { return selector; } -// Takes an existing, primary dispatch table entry and repacks it with a -// different alternate wire type. Called when we are inserting a secondary -// dispatch table entry for an alternate wire type. +/* Takes an existing, primary dispatch table entry and repacks it with a + * different alternate wire type. Called when we are inserting a secondary + * dispatch table entry for an alternate wire type. */ static uint64_t repack(uint64_t dispatch, int new_wt2) { uint64_t ofs; uint8_t wt1; uint8_t old_wt2; upb_pbdecoder_unpackdispatch(dispatch, &ofs, &wt1, &old_wt2); - assert(old_wt2 == NO_WIRE_TYPE); // wt2 should not be set yet. + assert(old_wt2 == NO_WIRE_TYPE); /* wt2 should not be set yet. */ return upb_pbdecoder_packdispatch(ofs, wt1, new_wt2); } -// Marks the current bytecode position as the dispatch target for this message, -// field, and wire type. +/* Marks the current bytecode position as the dispatch target for this message, + * field, and wire type. */ static void dispatchtarget(compiler *c, upb_pbdecodermethod *method, const upb_fielddef *f, int wire_type) { - // Offset is relative to msg base. + /* Offset is relative to msg base. */ uint64_t ofs = pcofs(c) - method->code_base.ofs; uint32_t fn = upb_fielddef_number(f); upb_inttable *d = &method->dispatch; upb_value v; if (upb_inttable_remove(d, fn, &v)) { - // TODO: prioritize based on packed setting in .proto file. + /* TODO: prioritize based on packed setting in .proto file. */ uint64_t repacked = repack(upb_value_getuint64(v), wire_type); upb_inttable_insert(d, fn, upb_value_uint64(repacked)); upb_inttable_insert(d, fn + UPB_MAX_FIELDNUMBER, upb_value_uint64(ofs)); @@ -6984,8 +7047,8 @@ static void putsel(compiler *c, opcode op, upb_selector_t sel, } } -// Puts an opcode to call a callback, but only if a callback actually exists for -// this field and handler type. +/* Puts an opcode to call a callback, but only if a callback actually exists for + * this field and handler type. */ static void maybeput(compiler *c, opcode op, const upb_handlers *h, const upb_fielddef *f, upb_handlertype_t type) { putsel(c, op, getsel(f, type), h); @@ -7003,27 +7066,28 @@ static bool haslazyhandlers(const upb_handlers *h, const upb_fielddef *f) { /* bytecode compiler code generation ******************************************/ -// Symbolic names for our local labels. -#define LABEL_LOOPSTART 1 // Top of a repeated field loop. -#define LABEL_LOOPBREAK 2 // To jump out of a repeated loop -#define LABEL_FIELD 3 // Jump backward to find the most recent field. -#define LABEL_ENDMSG 4 // To reach the OP_ENDMSG instr for this msg. +/* Symbolic names for our local labels. */ +#define LABEL_LOOPSTART 1 /* Top of a repeated field loop. */ +#define LABEL_LOOPBREAK 2 /* To jump out of a repeated loop */ +#define LABEL_FIELD 3 /* Jump backward to find the most recent field. */ +#define LABEL_ENDMSG 4 /* To reach the OP_ENDMSG instr for this msg. */ -// Generates bytecode to parse a single non-lazy message field. +/* Generates bytecode to parse a single non-lazy message field. */ static void generate_msgfield(compiler *c, const upb_fielddef *f, upb_pbdecodermethod *method) { const upb_handlers *h = upb_pbdecodermethod_desthandlers(method); const upb_pbdecodermethod *sub_m = find_submethod(c, method, f); + int wire_type; if (!sub_m) { - // Don't emit any code for this field at all; it will be parsed as an - // unknown field. + /* Don't emit any code for this field at all; it will be parsed as an + * unknown field. */ return; } label(c, LABEL_FIELD); - int wire_type = + wire_type = (upb_fielddef_descriptortype(f) == UPB_DESCRIPTOR_TYPE_MESSAGE) ? UPB_WIRE_TYPE_DELIMITED : UPB_WIRE_TYPE_START_GROUP; @@ -7064,7 +7128,7 @@ static void generate_msgfield(compiler *c, const upb_fielddef *f, } } -// Generates bytecode to parse a single string or lazy submessage field. +/* Generates bytecode to parse a single string or lazy submessage field. */ static void generate_delimfield(compiler *c, const upb_fielddef *f, upb_pbdecodermethod *method) { const upb_handlers *h = upb_pbdecodermethod_desthandlers(method); @@ -7079,7 +7143,7 @@ static void generate_delimfield(compiler *c, const upb_fielddef *f, label(c, LABEL_LOOPSTART); putop(c, OP_PUSHLENDELIM); putop(c, OP_STARTSTR, getsel(f, UPB_HANDLER_STARTSTR)); - // Need to emit even if no handler to skip past the string. + /* Need to emit even if no handler to skip past the string. */ putop(c, OP_STRING, getsel(f, UPB_HANDLER_STRING)); putop(c, OP_POP); maybeput(c, OP_ENDSTR, h, f, UPB_HANDLER_ENDSTR); @@ -7103,49 +7167,52 @@ static void generate_delimfield(compiler *c, const upb_fielddef *f, } } -// Generates bytecode to parse a single primitive field. +/* Generates bytecode to parse a single primitive field. */ static void generate_primitivefield(compiler *c, const upb_fielddef *f, upb_pbdecodermethod *method) { - label(c, LABEL_FIELD); - const upb_handlers *h = upb_pbdecodermethod_desthandlers(method); upb_descriptortype_t descriptor_type = upb_fielddef_descriptortype(f); + opcode parse_type; + upb_selector_t sel; + int wire_type; + + label(c, LABEL_FIELD); - // From a decoding perspective, ENUM is the same as INT32. + /* From a decoding perspective, ENUM is the same as INT32. */ if (descriptor_type == UPB_DESCRIPTOR_TYPE_ENUM) descriptor_type = UPB_DESCRIPTOR_TYPE_INT32; - opcode parse_type = (opcode)descriptor_type; + parse_type = (opcode)descriptor_type; - // TODO(haberman): generate packed or non-packed first depending on "packed" - // setting in the fielddef. This will favor (in speed) whichever was - // specified. + /* TODO(haberman): generate packed or non-packed first depending on "packed" + * setting in the fielddef. This will favor (in speed) whichever was + * specified. */ assert((int)parse_type >= 0 && parse_type <= OP_MAX); - upb_selector_t sel = getsel(f, upb_handlers_getprimitivehandlertype(f)); - int wire_type = upb_pb_native_wire_types[upb_fielddef_descriptortype(f)]; + sel = getsel(f, upb_handlers_getprimitivehandlertype(f)); + wire_type = upb_pb_native_wire_types[upb_fielddef_descriptortype(f)]; if (upb_fielddef_isseq(f)) { putop(c, OP_CHECKDELIM, LABEL_ENDMSG); putchecktag(c, f, UPB_WIRE_TYPE_DELIMITED, LABEL_DISPATCH); dispatchtarget(c, method, f, UPB_WIRE_TYPE_DELIMITED); putop(c, OP_PUSHLENDELIM); - putop(c, OP_STARTSEQ, getsel(f, UPB_HANDLER_STARTSEQ)); // Packed + putop(c, OP_STARTSEQ, getsel(f, UPB_HANDLER_STARTSEQ)); /* Packed */ label(c, LABEL_LOOPSTART); putop(c, parse_type, sel); putop(c, OP_CHECKDELIM, LABEL_LOOPBREAK); putop(c, OP_BRANCH, -LABEL_LOOPSTART); dispatchtarget(c, method, f, wire_type); putop(c, OP_PUSHTAGDELIM, 0); - putop(c, OP_STARTSEQ, getsel(f, UPB_HANDLER_STARTSEQ)); // Non-packed + putop(c, OP_STARTSEQ, getsel(f, UPB_HANDLER_STARTSEQ)); /* Non-packed */ label(c, LABEL_LOOPSTART); putop(c, parse_type, sel); putop(c, OP_CHECKDELIM, LABEL_LOOPBREAK); putchecktag(c, f, wire_type, LABEL_LOOPBREAK); putop(c, OP_BRANCH, -LABEL_LOOPSTART); label(c, LABEL_LOOPBREAK); - putop(c, OP_POP); // Packed and non-packed join. + putop(c, OP_POP); /* Packed and non-packed join. */ maybeput(c, OP_ENDSEQ, h, f, UPB_HANDLER_ENDSEQ); - putop(c, OP_SETDELIM); // Could remove for non-packed by dup ENDSEQ. + putop(c, OP_SETDELIM); /* Could remove for non-packed by dup ENDSEQ. */ } else { putop(c, OP_CHECKDELIM, LABEL_ENDMSG); putchecktag(c, f, wire_type, LABEL_DISPATCH); @@ -7154,24 +7221,29 @@ static void generate_primitivefield(compiler *c, const upb_fielddef *f, } } -// Adds bytecode for parsing the given message to the given decoderplan, -// while adding all dispatch targets to this message's dispatch table. +/* Adds bytecode for parsing the given message to the given decoderplan, + * while adding all dispatch targets to this message's dispatch table. */ static void compile_method(compiler *c, upb_pbdecodermethod *method) { + const upb_handlers *h; + const upb_msgdef *md; + uint32_t* start_pc; + upb_msg_field_iter i; + upb_value val; + assert(method); - // Clear all entries in the dispatch table. + /* Clear all entries in the dispatch table. */ upb_inttable_uninit(&method->dispatch); upb_inttable_init(&method->dispatch, UPB_CTYPE_UINT64); - const upb_handlers *h = upb_pbdecodermethod_desthandlers(method); - const upb_msgdef *md = upb_handlers_msgdef(h); + h = upb_pbdecodermethod_desthandlers(method); + md = upb_handlers_msgdef(h); method->code_base.ofs = pcofs(c); putop(c, OP_SETDISPATCH, &method->dispatch); putsel(c, OP_STARTMSG, UPB_STARTMSG_SELECTOR, h); label(c, LABEL_FIELD); - uint32_t* start_pc = c->pc; - upb_msg_field_iter i; + start_pc = c->pc; for(upb_msg_field_begin(&i, md); !upb_msg_field_done(&i); upb_msg_field_next(&i)) { @@ -7188,23 +7260,23 @@ static void compile_method(compiler *c, upb_pbdecodermethod *method) { } } - // If there were no fields, or if no handlers were defined, we need to - // generate a non-empty loop body so that we can at least dispatch for unknown - // fields and check for the end of the message. + /* If there were no fields, or if no handlers were defined, we need to + * generate a non-empty loop body so that we can at least dispatch for unknown + * fields and check for the end of the message. */ if (c->pc == start_pc) { - // Check for end-of-message. + /* Check for end-of-message. */ putop(c, OP_CHECKDELIM, LABEL_ENDMSG); - // Unconditionally dispatch. + /* Unconditionally dispatch. */ putop(c, OP_DISPATCH, 0); } - // For now we just loop back to the last field of the message (or if none, - // the DISPATCH opcode for the message). + /* For now we just loop back to the last field of the message (or if none, + * the DISPATCH opcode for the message). */ putop(c, OP_BRANCH, -LABEL_FIELD); - // Insert both a label and a dispatch table entry for this end-of-msg. + /* Insert both a label and a dispatch table entry for this end-of-msg. */ label(c, LABEL_ENDMSG); - upb_value val = upb_value_uint64(pcofs(c) - method->code_base.ofs); + val = upb_value_uint64(pcofs(c) - method->code_base.ofs); upb_inttable_insert(&method->dispatch, DISPATCH_ENDMSG, val); putsel(c, OP_ENDMSG, UPB_ENDMSG_SELECTOR, h); @@ -7213,19 +7285,21 @@ static void compile_method(compiler *c, upb_pbdecodermethod *method) { upb_inttable_compact(&method->dispatch); } -// Populate "methods" with new upb_pbdecodermethod objects reachable from "h". -// Returns the method for these handlers. -// -// Generates a new method for every destination handlers reachable from "h". +/* Populate "methods" with new upb_pbdecodermethod objects reachable from "h". + * Returns the method for these handlers. + * + * Generates a new method for every destination handlers reachable from "h". */ static void find_methods(compiler *c, const upb_handlers *h) { upb_value v; + upb_msg_field_iter i; + const upb_msgdef *md; + if (upb_inttable_lookupptr(&c->group->methods, h, &v)) return; newmethod(h, c->group); - // Find submethods. - upb_msg_field_iter i; - const upb_msgdef *md = upb_handlers_msgdef(h); + /* Find submethods. */ + md = upb_handlers_msgdef(h); for(upb_msg_field_begin(&i, md); !upb_msg_field_done(&i); upb_msg_field_next(&i)) { @@ -7233,20 +7307,21 @@ static void find_methods(compiler *c, const upb_handlers *h) { const upb_handlers *sub_h; if (upb_fielddef_type(f) == UPB_TYPE_MESSAGE && (sub_h = upb_handlers_getsubhandlers(h, f)) != NULL) { - // We only generate a decoder method for submessages with handlers. - // Others will be parsed as unknown fields. + /* We only generate a decoder method for submessages with handlers. + * Others will be parsed as unknown fields. */ find_methods(c, sub_h); } } } -// (Re-)compile bytecode for all messages in "msgs." -// Overwrites any existing bytecode in "c". +/* (Re-)compile bytecode for all messages in "msgs." + * Overwrites any existing bytecode in "c". */ static void compile_methods(compiler *c) { - // Start over at the beginning of the bytecode. + upb_inttable_iter i; + + /* Start over at the beginning of the bytecode. */ c->pc = c->group->bytecode; - upb_inttable_iter i; upb_inttable_begin(&i, &c->group->methods); for(; !upb_inttable_done(&i); upb_inttable_next(&i)) { upb_pbdecodermethod *method = upb_value_getptr(upb_inttable_iter_value(&i)); @@ -7259,10 +7334,10 @@ static void set_bytecode_handlers(mgroup *g) { upb_inttable_begin(&i, &g->methods); for(; !upb_inttable_done(&i); upb_inttable_next(&i)) { upb_pbdecodermethod *m = upb_value_getptr(upb_inttable_iter_value(&i)); + upb_byteshandler *h = &m->input_handler_; m->code_base.ptr = g->bytecode + m->code_base.ofs; - upb_byteshandler *h = &m->input_handler_; upb_byteshandler_setstartstr(h, upb_pbdecoder_startbc, m->code_base.ptr); upb_byteshandler_setstring(h, upb_pbdecoder_decode, g); upb_byteshandler_setendstr(h, upb_pbdecoder_end, m); @@ -7277,53 +7352,58 @@ static void set_bytecode_handlers(mgroup *g) { static void sethandlers(mgroup *g, bool allowjit) { g->jit_code = NULL; if (allowjit) { - // Compile byte-code into machine code, create handlers. + /* Compile byte-code into machine code, create handlers. */ upb_pbdecoder_jit(g); } else { set_bytecode_handlers(g); } } -#else // UPB_USE_JIT_X64 +#else /* UPB_USE_JIT_X64 */ static void sethandlers(mgroup *g, bool allowjit) { - // No JIT compiled in; use bytecode handlers unconditionally. + /* No JIT compiled in; use bytecode handlers unconditionally. */ UPB_UNUSED(allowjit); set_bytecode_handlers(g); } -#endif // UPB_USE_JIT_X64 +#endif /* UPB_USE_JIT_X64 */ -// TODO(haberman): allow this to be constructed for an arbitrary set of dest -// handlers and other mgroups (but verify we have a transitive closure). +/* TODO(haberman): allow this to be constructed for an arbitrary set of dest + * handlers and other mgroups (but verify we have a transitive closure). */ const mgroup *mgroup_new(const upb_handlers *dest, bool allowjit, bool lazy, const void *owner) { + mgroup *g; + compiler *c; + UPB_UNUSED(allowjit); assert(upb_handlers_isfrozen(dest)); - mgroup *g = newgroup(owner); - compiler *c = newcompiler(g, lazy); + g = newgroup(owner); + c = newcompiler(g, lazy); find_methods(c, dest); - // We compile in two passes: - // 1. all messages are assigned relative offsets from the beginning of the - // bytecode (saved in method->code_base). - // 2. forwards OP_CALL instructions can be correctly linked since message - // offsets have been previously assigned. - // - // Could avoid the second pass by linking OP_CALL instructions somehow. + /* We compile in two passes: + * 1. all messages are assigned relative offsets from the beginning of the + * bytecode (saved in method->code_base). + * 2. forwards OP_CALL instructions can be correctly linked since message + * offsets have been previously assigned. + * + * Could avoid the second pass by linking OP_CALL instructions somehow. */ compile_methods(c); compile_methods(c); g->bytecode_end = c->pc; freecompiler(c); #ifdef UPB_DUMP_BYTECODE - FILE *f = fopen("/tmp/upb-bytecode", "wb"); - assert(f); - dumpbc(g->bytecode, g->bytecode_end, stderr); - dumpbc(g->bytecode, g->bytecode_end, f); - fclose(f); + { + FILE *f = fopen("/tmp/upb-bytecode", "wb"); + assert(f); + dumpbc(g->bytecode, g->bytecode_end, stderr); + dumpbc(g->bytecode, g->bytecode_end, f); + fclose(f); + } #endif sethandlers(g, allowjit); @@ -7343,7 +7423,7 @@ void upb_pbcodecache_uninit(upb_pbcodecache *c) { upb_inttable_begin(&i, &c->groups); for(; !upb_inttable_done(&i); upb_inttable_next(&i)) { const mgroup *group = upb_value_getconstptr(upb_inttable_iter_value(&i)); - upb_refcounted_unref(UPB_UPCAST(group), c); + mgroup_unref(group, c); } upb_inttable_uninit(&c->groups); } @@ -7361,13 +7441,15 @@ bool upb_pbcodecache_setallowjit(upb_pbcodecache *c, bool allow) { const upb_pbdecodermethod *upb_pbcodecache_getdecodermethod( upb_pbcodecache *c, const upb_pbdecodermethodopts *opts) { - // Right now we build a new DecoderMethod every time. - // TODO(haberman): properly cache methods by their true key. + upb_value v; + bool ok; + + /* Right now we build a new DecoderMethod every time. + * TODO(haberman): properly cache methods by their true key. */ const mgroup *g = mgroup_new(opts->handlers, c->allow_jit_, opts->lazy, c); upb_inttable_push(&c->groups, upb_value_constptr(g)); - upb_value v; - bool ok = upb_inttable_lookupptr(&g->methods, opts->handlers, &v); + ok = upb_inttable_lookupptr(&g->methods, opts->handlers, &v); UPB_ASSERT_VAR(ok, ok); return upb_value_getptr(v); } @@ -7413,17 +7495,17 @@ void upb_pbdecodermethodopts_setlazy(upb_pbdecodermethodopts *opts, bool lazy) { #define CHECK_SUSPEND(x) if (!(x)) return upb_pbdecoder_suspend(d); -// Error messages that are shared between the bytecode and JIT decoders. +/* Error messages that are shared between the bytecode and JIT decoders. */ const char *kPbDecoderStackOverflow = "Nesting too deep."; -// Error messages shared within this file. +/* Error messages shared within this file. */ static const char *kUnterminatedVarint = "Unterminated varint."; /* upb_pbdecoder **************************************************************/ static opcode halt = OP_HALT; -// Whether an op consumes any of the input buffer. +/* Whether an op consumes any of the input buffer. */ static bool consumes_input(opcode op) { switch (op) { case OP_SETDISPATCH: @@ -7451,12 +7533,12 @@ static bool consumes_input(opcode op) { static bool in_residual_buf(const upb_pbdecoder *d, const char *p); -// It's unfortunate that we have to micro-manage the compiler with -// UPB_FORCEINLINE and UPB_NOINLINE, especially since this tuning is necessarily -// specific to one hardware configuration. But empirically on a Core i7, -// performance increases 30-50% with these annotations. Every instance where -// these appear, gcc 4.2.1 made the wrong decision and degraded performance in -// benchmarks. +/* It's unfortunate that we have to micro-manage the compiler with + * UPB_FORCEINLINE and UPB_NOINLINE, especially since this tuning is necessarily + * specific to one hardware configuration. But empirically on a Core i7, + * performance increases 30-50% with these annotations. Every instance where + * these appear, gcc 4.2.1 made the wrong decision and degraded performance in + * benchmarks. */ static void seterr(upb_pbdecoder *d, const char *msg) { upb_status status = UPB_STATUS_INIT; @@ -7471,22 +7553,22 @@ void upb_pbdecoder_seterr(upb_pbdecoder *d, const char *msg) { /* Buffering ******************************************************************/ -// We operate on one buffer at a time, which is either the user's buffer passed -// to our "decode" callback or some residual bytes from the previous buffer. +/* We operate on one buffer at a time, which is either the user's buffer passed + * to our "decode" callback or some residual bytes from the previous buffer. */ -// How many bytes can be safely read from d->ptr without reading past end-of-buf -// or past the current delimited end. +/* How many bytes can be safely read from d->ptr without reading past end-of-buf + * or past the current delimited end. */ static size_t curbufleft(const upb_pbdecoder *d) { assert(d->data_end >= d->ptr); return d->data_end - d->ptr; } -// Overall stream offset of d->ptr. +/* Overall stream offset of d->ptr. */ uint64_t offset(const upb_pbdecoder *d) { return d->bufstart_ofs + (d->ptr - d->buf); } -// Advances d->ptr. +/* Advances d->ptr. */ static void advance(upb_pbdecoder *d, size_t len) { assert(curbufleft(d) >= len); d->ptr += len; @@ -7500,8 +7582,8 @@ static bool in_residual_buf(const upb_pbdecoder *d, const char *p) { return in_buf(p, d->residual, d->residual_end); } -// Calculates the delim_end value, which is affected by both the current buffer -// and the parsing stack, so must be called whenever either is updated. +/* Calculates the delim_end value, which is affected by both the current buffer + * and the parsing stack, so must be called whenever either is updated. */ static void set_delim_end(upb_pbdecoder *d) { size_t delim_ofs = d->top->end_ofs - d->bufstart_ofs; if (delim_ofs <= (size_t)(d->end - d->buf)) { @@ -7527,22 +7609,22 @@ static void advancetobuf(upb_pbdecoder *d, const char *buf, size_t len) { } static void checkpoint(upb_pbdecoder *d) { - // The assertion here is in the interests of efficiency, not correctness. - // We are trying to ensure that we don't checkpoint() more often than - // necessary. + /* The assertion here is in the interests of efficiency, not correctness. + * We are trying to ensure that we don't checkpoint() more often than + * necessary. */ assert(d->checkpoint != d->ptr); d->checkpoint = d->ptr; } -// Resumes the decoder from an initial state or from a previous suspend. +/* Resumes the decoder from an initial state or from a previous suspend. */ int32_t upb_pbdecoder_resume(upb_pbdecoder *d, void *p, const char *buf, size_t size, const upb_bufhandle *handle) { - UPB_UNUSED(p); // Useless; just for the benefit of the JIT. + UPB_UNUSED(p); /* Useless; just for the benefit of the JIT. */ d->buf_param = buf; d->size_param = size; d->handle = handle; if (d->residual_end > d->residual) { - // We have residual bytes from the last buffer. + /* We have residual bytes from the last buffer. */ assert(d->ptr == d->residual); } else { switchtobuf(d, buf, buf + size); @@ -7555,18 +7637,20 @@ int32_t upb_pbdecoder_resume(upb_pbdecoder *d, void *p, const char *buf, return DECODE_OK; } -// Suspends the decoder at the last checkpoint, without saving any residual -// bytes. If there are any unconsumed bytes, returns a short byte count. +/* Suspends the decoder at the last checkpoint, without saving any residual + * bytes. If there are any unconsumed bytes, returns a short byte count. */ size_t upb_pbdecoder_suspend(upb_pbdecoder *d) { d->pc = d->last; if (d->checkpoint == d->residual) { - // Checkpoint was in residual buf; no user bytes were consumed. + /* Checkpoint was in residual buf; no user bytes were consumed. */ d->ptr = d->residual; return 0; } else { + size_t consumed; assert(!in_residual_buf(d, d->checkpoint)); assert(d->buf == d->buf_param); - size_t consumed = d->checkpoint - d->buf; + + consumed = d->checkpoint - d->buf; d->bufstart_ofs += consumed; d->residual_end = d->residual; switchtobuf(d, d->residual, d->residual_end); @@ -7574,17 +7658,17 @@ size_t upb_pbdecoder_suspend(upb_pbdecoder *d) { } } -// Suspends the decoder at the last checkpoint, and saves any unconsumed -// bytes in our residual buffer. This is necessary if we need more user -// bytes to form a complete value, which might not be contiguous in the -// user's buffers. Always consumes all user bytes. +/* Suspends the decoder at the last checkpoint, and saves any unconsumed + * bytes in our residual buffer. This is necessary if we need more user + * bytes to form a complete value, which might not be contiguous in the + * user's buffers. Always consumes all user bytes. */ static size_t suspend_save(upb_pbdecoder *d) { - // We hit end-of-buffer before we could parse a full value. - // Save any unconsumed bytes (if any) to the residual buffer. + /* We hit end-of-buffer before we could parse a full value. + * Save any unconsumed bytes (if any) to the residual buffer. */ d->pc = d->last; if (d->checkpoint == d->residual) { - // Checkpoint was in residual buf; append user byte(s) to residual buf. + /* Checkpoint was in residual buf; append user byte(s) to residual buf. */ assert((d->residual_end - d->residual) + d->size_param <= sizeof(d->residual)); if (!in_residual_buf(d, d->ptr)) { @@ -7593,10 +7677,12 @@ static size_t suspend_save(upb_pbdecoder *d) { memcpy(d->residual_end, d->buf_param, d->size_param); d->residual_end += d->size_param; } else { - // Checkpoint was in user buf; old residual bytes not needed. + /* Checkpoint was in user buf; old residual bytes not needed. */ + size_t save; assert(!in_residual_buf(d, d->checkpoint)); + d->ptr = d->checkpoint; - size_t save = curbufleft(d); + save = curbufleft(d); assert(save <= sizeof(d->residual)); memcpy(d->residual, d->ptr, save); d->residual_end = d->residual + save; @@ -7607,19 +7693,21 @@ static size_t suspend_save(upb_pbdecoder *d) { return d->size_param; } -// Skips "bytes" bytes in the stream, which may be more than available. If we -// skip more bytes than are available, we return a long read count to the caller -// indicating how many bytes the caller should skip before passing a new buffer. +/* Skips "bytes" bytes in the stream, which may be more than available. If we + * skip more bytes than are available, we return a long read count to the caller + * indicating how many bytes the caller should skip before passing a new buffer. + */ static int32_t skip(upb_pbdecoder *d, size_t bytes) { assert(!in_residual_buf(d, d->ptr) || d->size_param == 0); if (curbufleft(d) >= bytes) { - // Skipped data is all in current buffer. + /* Skipped data is all in current buffer. */ advance(d, bytes); return DECODE_OK; } else { - // Skipped data extends beyond currently available buffers. + /* Skipped data extends beyond currently available buffers. */ + size_t skip; d->pc = d->last; - size_t skip = bytes - curbufleft(d); + skip = bytes - curbufleft(d); d->bufstart_ofs += (d->end - d->buf) + skip; d->residual_end = d->residual; switchtobuf(d, d->residual, d->residual_end); @@ -7627,8 +7715,8 @@ static int32_t skip(upb_pbdecoder *d, size_t bytes) { } } -// Copies the next "bytes" bytes into "buf" and advances the stream. -// Requires that this many bytes are available in the current buffer. +/* Copies the next "bytes" bytes into "buf" and advances the stream. + * Requires that this many bytes are available in the current buffer. */ UPB_FORCEINLINE static void consumebytes(upb_pbdecoder *d, void *buf, size_t bytes) { assert(bytes <= curbufleft(d)); @@ -7636,9 +7724,9 @@ UPB_FORCEINLINE static void consumebytes(upb_pbdecoder *d, void *buf, advance(d, bytes); } -// Slow path for getting the next "bytes" bytes, regardless of whether they are -// available in the current buffer or not. Returns a status code as described -// in decoder.int.h. +/* Slow path for getting the next "bytes" bytes, regardless of whether they are + * available in the current buffer or not. Returns a status code as described + * in decoder.int.h. */ UPB_NOINLINE static int32_t getbytes_slow(upb_pbdecoder *d, void *buf, size_t bytes) { const size_t avail = curbufleft(d); @@ -7659,12 +7747,13 @@ UPB_NOINLINE static int32_t getbytes_slow(upb_pbdecoder *d, void *buf, } } -// Gets the next "bytes" bytes, regardless of whether they are available in the -// current buffer or not. Returns a status code as described in decoder.int.h. +/* Gets the next "bytes" bytes, regardless of whether they are available in the + * current buffer or not. Returns a status code as described in decoder.int.h. + */ UPB_FORCEINLINE static int32_t getbytes(upb_pbdecoder *d, void *buf, size_t bytes) { if (curbufleft(d) >= bytes) { - // Buffer has enough data to satisfy. + /* Buffer has enough data to satisfy. */ consumebytes(d, buf, bytes); return DECODE_OK; } else { @@ -7697,13 +7786,13 @@ UPB_FORCEINLINE static size_t peekbytes(upb_pbdecoder *d, void *buf, /* Decoding of wire types *****************************************************/ -// Slow path for decoding a varint from the current buffer position. -// Returns a status code as described in decoder.int.h. +/* Slow path for decoding a varint from the current buffer position. + * Returns a status code as described in decoder.int.h. */ UPB_NOINLINE int32_t upb_pbdecoder_decode_varint_slow(upb_pbdecoder *d, uint64_t *u64) { - *u64 = 0; uint8_t byte = 0x80; int bitpos; + *u64 = 0; for(bitpos = 0; bitpos < 70 && (byte & 0x80); bitpos += 7) { int32_t ret = getbytes(d, &byte, 1); if (ret >= 0) return ret; @@ -7716,15 +7805,15 @@ UPB_NOINLINE int32_t upb_pbdecoder_decode_varint_slow(upb_pbdecoder *d, return DECODE_OK; } -// Decodes a varint from the current buffer position. -// Returns a status code as described in decoder.int.h. +/* Decodes a varint from the current buffer position. + * Returns a status code as described in decoder.int.h. */ UPB_FORCEINLINE static int32_t decode_varint(upb_pbdecoder *d, uint64_t *u64) { if (curbufleft(d) > 0 && !(*d->ptr & 0x80)) { *u64 = *d->ptr; advance(d, 1); return DECODE_OK; } else if (curbufleft(d) >= 10) { - // Fast case. + /* Fast case. */ upb_decoderet r = upb_vdecode_fast(d->ptr); if (r.p == NULL) { seterr(d, kUnterminatedVarint); @@ -7734,22 +7823,23 @@ UPB_FORCEINLINE static int32_t decode_varint(upb_pbdecoder *d, uint64_t *u64) { *u64 = r.val; return DECODE_OK; } else { - // Slow case -- varint spans buffer seam. + /* Slow case -- varint spans buffer seam. */ return upb_pbdecoder_decode_varint_slow(d, u64); } } -// Decodes a 32-bit varint from the current buffer position. -// Returns a status code as described in decoder.int.h. +/* Decodes a 32-bit varint from the current buffer position. + * Returns a status code as described in decoder.int.h. */ UPB_FORCEINLINE static int32_t decode_v32(upb_pbdecoder *d, uint32_t *u32) { uint64_t u64; int32_t ret = decode_varint(d, &u64); if (ret >= 0) return ret; if (u64 > UINT32_MAX) { seterr(d, "Unterminated 32-bit varint"); - // TODO(haberman) guarantee that this function return is >= 0 somehow, - // so we know this path will always be treated as error by our caller. - // Right now the size_t -> int32_t can overflow and produce negative values. + /* TODO(haberman) guarantee that this function return is >= 0 somehow, + * so we know this path will always be treated as error by our caller. + * Right now the size_t -> int32_t can overflow and produce negative values. + */ *u32 = 0; return upb_pbdecoder_suspend(d); } @@ -7757,22 +7847,22 @@ UPB_FORCEINLINE static int32_t decode_v32(upb_pbdecoder *d, uint32_t *u32) { return DECODE_OK; } -// Decodes a fixed32 from the current buffer position. -// Returns a status code as described in decoder.int.h. -// TODO: proper byte swapping for big-endian machines. +/* Decodes a fixed32 from the current buffer position. + * Returns a status code as described in decoder.int.h. + * TODO: proper byte swapping for big-endian machines. */ UPB_FORCEINLINE static int32_t decode_fixed32(upb_pbdecoder *d, uint32_t *u32) { return getbytes(d, u32, 4); } -// Decodes a fixed64 from the current buffer position. -// Returns a status code as described in decoder.int.h. -// TODO: proper byte swapping for big-endian machines. +/* Decodes a fixed64 from the current buffer position. + * Returns a status code as described in decoder.int.h. + * TODO: proper byte swapping for big-endian machines. */ UPB_FORCEINLINE static int32_t decode_fixed64(upb_pbdecoder *d, uint64_t *u64) { return getbytes(d, u64, 8); } -// Non-static versions of the above functions. -// These are called by the JIT for fallback paths. +/* Non-static versions of the above functions. + * These are called by the JIT for fallback paths. */ int32_t upb_pbdecoder_decode_f32(upb_pbdecoder *d, uint32_t *u32) { return decode_fixed32(d, u32); } @@ -7784,7 +7874,7 @@ int32_t upb_pbdecoder_decode_f64(upb_pbdecoder *d, uint64_t *u64) { static double as_double(uint64_t n) { double d; memcpy(&d, &n, 8); return d; } static float as_float(uint32_t n) { float f; memcpy(&f, &n, 4); return f; } -// Pushes a frame onto the decoder stack. +/* Pushes a frame onto the decoder stack. */ static bool decoder_push(upb_pbdecoder *d, uint64_t end) { upb_pbdecoder_frame *fr = d->top; @@ -7805,17 +7895,17 @@ static bool decoder_push(upb_pbdecoder *d, uint64_t end) { } static bool pushtagdelim(upb_pbdecoder *d, uint32_t arg) { - // While we expect to see an "end" tag (either ENDGROUP or a non-sequence - // field number) prior to hitting any enclosing submessage end, pushing our - // existing delim end prevents us from continuing to parse values from a - // corrupt proto that doesn't give us an END tag in time. + /* While we expect to see an "end" tag (either ENDGROUP or a non-sequence + * field number) prior to hitting any enclosing submessage end, pushing our + * existing delim end prevents us from continuing to parse values from a + * corrupt proto that doesn't give us an END tag in time. */ if (!decoder_push(d, d->top->end_ofs)) return false; d->top->groupnum = arg; return true; } -// Pops a frame from the decoder stack. +/* Pops a frame from the decoder stack. */ static void decoder_pop(upb_pbdecoder *d) { d->top--; } UPB_NOINLINE int32_t upb_pbdecoder_checktag_slow(upb_pbdecoder *d, @@ -7824,7 +7914,7 @@ UPB_NOINLINE int32_t upb_pbdecoder_checktag_slow(upb_pbdecoder *d, size_t bytes = upb_value_size(expected); size_t read = peekbytes(d, &data, bytes); if (read == bytes && data == expected) { - // Advance past matched bytes. + /* Advance past matched bytes. */ int32_t ok = getbytes(d, &data, read); UPB_ASSERT_VAR(ok, ok < 0); return DECODE_OK; @@ -7852,7 +7942,7 @@ have_tag: return upb_pbdecoder_suspend(d); } - // TODO: deliver to unknown field callback. + /* TODO: deliver to unknown field callback. */ switch (wire_type) { case UPB_WIRE_TYPE_32BIT: CHECK_RETURN(skip(d, 4)); @@ -7895,29 +7985,29 @@ have_tag: if (d->ptr == d->delim_end) { seterr(d, "Enclosing submessage ended in the middle of value or group"); - // Unlike most errors we notice during parsing, right now we have consumed - // all of the user's input. - // - // There are three different options for how to handle this case: - // - // 1. decode() = short count, error = set - // 2. decode() = full count, error = set - // 3. decode() = full count, error NOT set, short count and error will - // be reported on next call to decode() (or end()) - // - // (1) and (3) have the advantage that they preserve the invariant that an - // error occurs iff decode() returns a short count. - // - // (2) and (3) have the advantage of reflecting the fact that all of the - // bytes were in fact parsed (and possibly delivered to the unknown field - // handler, in the future when that is supported). - // - // (3) requires extra state in the decode (a place to store the "permanent - // error" that we should return for all subsequent attempts to decode). - // But we likely want this anyway. - // - // Right now we do (1), thanks to the fact that we checkpoint *after* this - // check. (3) may be a better choice long term; unclear at the moment. + /* Unlike most errors we notice during parsing, right now we have consumed + * all of the user's input. + * + * There are three different options for how to handle this case: + * + * 1. decode() = short count, error = set + * 2. decode() = full count, error = set + * 3. decode() = full count, error NOT set, short count and error will + * be reported on next call to decode() (or end()) + * + * (1) and (3) have the advantage that they preserve the invariant that an + * error occurs iff decode() returns a short count. + * + * (2) and (3) have the advantage of reflecting the fact that all of the + * bytes were in fact parsed (and possibly delivered to the unknown field + * handler, in the future when that is supported). + * + * (3) requires extra state in the decode (a place to store the "permanent + * error" that we should return for all subsequent attempts to decode). + * But we likely want this anyway. + * + * Right now we do (1), thanks to the fact that we checkpoint *after* this + * check. (3) may be a better choice long term; unclear at the moment. */ return upb_pbdecoder_suspend(d); } @@ -7932,24 +8022,27 @@ static void goto_endmsg(upb_pbdecoder *d) { d->pc = d->top->base + upb_value_getuint64(v); } -// Parses a tag and jumps to the corresponding bytecode instruction for this -// field. -// -// If the tag is unknown (or the wire type doesn't match), parses the field as -// unknown. If the tag is a valid ENDGROUP tag, jumps to the bytecode -// instruction for the end of message. +/* Parses a tag and jumps to the corresponding bytecode instruction for this + * field. + * + * If the tag is unknown (or the wire type doesn't match), parses the field as + * unknown. If the tag is a valid ENDGROUP tag, jumps to the bytecode + * instruction for the end of message. */ static int32_t dispatch(upb_pbdecoder *d) { upb_inttable *dispatch = d->top->dispatch; - - // Decode tag. uint32_t tag; + uint8_t wire_type; + uint32_t fieldnum; + upb_value val; + int32_t ret; + + /* Decode tag. */ CHECK_RETURN(decode_v32(d, &tag)); - uint8_t wire_type = tag & 0x7; - uint32_t fieldnum = tag >> 3; + wire_type = tag & 0x7; + fieldnum = tag >> 3; - // Lookup tag. Because of packed/non-packed compatibility, we have to - // check the wire type against two possibilities. - upb_value val; + /* Lookup tag. Because of packed/non-packed compatibility, we have to + * check the wire type against two possibilities. */ if (fieldnum != DISPATCH_ENDMSG && upb_inttable_lookup32(dispatch, fieldnum, &val)) { uint64_t v = upb_value_getuint64(val); @@ -7965,17 +8058,17 @@ static int32_t dispatch(upb_pbdecoder *d) { } } - // Unknown field or ENDGROUP. - int32_t ret = upb_pbdecoder_skipunknown(d, fieldnum, wire_type); + /* Unknown field or ENDGROUP. */ + ret = upb_pbdecoder_skipunknown(d, fieldnum, wire_type); if (ret == DECODE_ENDGROUP) { goto_endmsg(d); return DECODE_OK; } else if (ret == DECODE_OK) { - // We just consumed some input, so we might now have consumed all the data - // in the delmited region. Since every opcode that can trigger dispatch is - // directly preceded by OP_CHECKDELIM, rewind to it now to re-check the - // delimited end. + /* We just consumed some input, so we might now have consumed all the data + * in the delmited region. Since every opcode that can trigger dispatch is + * directly preceded by OP_CHECKDELIM, rewind to it now to re-check the + * delimited end. */ d->pc = d->last - 1; assert(getop(*d->pc) == OP_CHECKDELIM); return DECODE_OK; @@ -7984,8 +8077,8 @@ static int32_t dispatch(upb_pbdecoder *d) { return ret; } -// Callers know that the stack is more than one deep because the opcodes that -// call this only occur after PUSH operations. +/* Callers know that the stack is more than one deep because the opcodes that + * call this only occur after PUSH operations. */ upb_pbdecoder_frame *outer_frame(upb_pbdecoder *d) { assert(d->top != d->stack); return d->top - 1; @@ -7994,14 +8087,15 @@ upb_pbdecoder_frame *outer_frame(upb_pbdecoder *d) { /* The main decoding loop *****************************************************/ -// The main decoder VM function. Uses traditional bytecode dispatch loop with a -// switch() statement. +/* The main decoder VM function. Uses traditional bytecode dispatch loop with a + * switch() statement. */ size_t upb_pbdecoder_decode(void *closure, const void *hd, const char *buf, size_t size, const upb_bufhandle *handle) { upb_pbdecoder *d = closure; const mgroup *group = hd; + int32_t result; assert(buf); - int32_t result = upb_pbdecoder_resume(d, NULL, buf, size, handle); + result = upb_pbdecoder_resume(d, NULL, buf, size, handle); if (result == DECODE_ENDGROUP) { goto_endmsg(d); } @@ -8018,11 +8112,16 @@ size_t upb_pbdecoder_decode(void *closure, const void *hd, const char *buf, }) while(1) { + int32_t instruction; + opcode op; + uint32_t arg; + int32_t longofs; + d->last = d->pc; - int32_t instruction = *d->pc++; - opcode op = getop(instruction); - uint32_t arg = instruction >> 8; - int32_t longofs = arg; + instruction = *d->pc++; + op = getop(instruction); + arg = instruction >> 8; + longofs = arg; assert(d->ptr != d->residual_end); #ifdef UPB_DUMP_BYTECODE fprintf(stderr, "s_ofs=%d buf_ofs=%d data_rem=%d buf_rem=%d delim_rem=%d " @@ -8037,9 +8136,9 @@ size_t upb_pbdecoder_decode(void *closure, const void *hd, const char *buf, arg); #endif switch (op) { - // Technically, we are losing data if we see a 32-bit varint that is not - // properly sign-extended. We could detect this and error about the data - // loss, but proto2 does not do this, so we pass. + /* Technically, we are losing data if we see a 32-bit varint that is not + * properly sign-extended. We could detect this and error about the data + * loss, but proto2 does not do this, so we pass. */ PRIMITIVE_OP(INT32, varint, int32, int32_t, uint64_t) PRIMITIVE_OP(INT64, varint, int64, int64_t, uint64_t) PRIMITIVE_OP(UINT32, varint, uint32, uint32_t, uint64_t) @@ -8084,7 +8183,7 @@ size_t upb_pbdecoder_decode(void *closure, const void *hd, const char *buf, upb_pbdecoder_frame *outer = outer_frame(d); CHECK_SUSPEND(upb_sink_startstr(&outer->sink, arg, len, &d->top->sink)); if (len == 0) { - d->pc++; // Skip OP_STRING. + d->pc++; /* Skip OP_STRING. */ } ) VMCASE(OP_STRING, @@ -8096,15 +8195,15 @@ size_t upb_pbdecoder_decode(void *closure, const void *hd, const char *buf, return upb_pbdecoder_suspend(d); } else { int32_t ret = skip(d, n); - // This shouldn't return DECODE_OK, because n > len. + /* This shouldn't return DECODE_OK, because n > len. */ assert(ret >= 0); return ret; } } advance(d, n); if (n < len || d->delim_end == NULL) { - // We aren't finished with this string yet. - d->pc--; // Repeat OP_STRING. + /* We aren't finished with this string yet. */ + d->pc--; /* Repeat OP_STRING. */ if (n > 0) checkpoint(d); return upb_pbdecoder_suspend(d); } @@ -8132,8 +8231,9 @@ size_t upb_pbdecoder_decode(void *closure, const void *hd, const char *buf, set_delim_end(d); ) VMCASE(OP_CHECKDELIM, - // We are guaranteed of this assert because we never allow ourselves to - // consume bytes beyond data_end, which covers delim_end when non-NULL. + /* We are guaranteed of this assert because we never allow ourselves to + * consume bytes beyond data_end, which covers delim_end when non-NULL. + */ assert(!(d->delim_end && d->ptr > d->delim_end)); if (d->ptr == d->delim_end) d->pc += longofs; @@ -8150,8 +8250,9 @@ size_t upb_pbdecoder_decode(void *closure, const void *hd, const char *buf, d->pc += longofs; ) VMCASE(OP_TAG1, + uint8_t expected; CHECK_SUSPEND(curbufleft(d) > 0); - uint8_t expected = (arg >> 8) & 0xff; + expected = (arg >> 8) & 0xff; if (*d->ptr == expected) { advance(d, 1); } else { @@ -8162,13 +8263,14 @@ size_t upb_pbdecoder_decode(void *closure, const void *hd, const char *buf, CHECK_RETURN(dispatch(d)); } else { d->pc += shortofs; - break; // Avoid checkpoint(). + break; /* Avoid checkpoint(). */ } } ) VMCASE(OP_TAG2, + uint16_t expected; CHECK_SUSPEND(curbufleft(d) > 0); - uint16_t expected = (arg >> 8) & 0xffff; + expected = (arg >> 8) & 0xffff; if (curbufleft(d) >= 2) { uint16_t actual; memcpy(&actual, d->ptr, 2); @@ -8185,9 +8287,10 @@ size_t upb_pbdecoder_decode(void *closure, const void *hd, const char *buf, ) VMCASE(OP_TAGN, { uint64_t expected; + int32_t result; memcpy(&expected, d->pc, 8); d->pc += 2; - int32_t result = upb_pbdecoder_checktag_slow(d, expected); + result = upb_pbdecoder_checktag_slow(d, expected); if (result == DECODE_MISMATCH) goto badtag; if (result >= 0) return result; }) @@ -8213,9 +8316,9 @@ void *upb_pbdecoder_startbc(void *closure, const void *pc, size_t size_hint) { } void *upb_pbdecoder_startjit(void *closure, const void *hd, size_t size_hint) { + upb_pbdecoder *d = closure; UPB_UNUSED(hd); UPB_UNUSED(size_hint); - upb_pbdecoder *d = closure; d->top->end_ofs = UINT64_MAX; d->bufstart_ofs = 0; d->call_len = 0; @@ -8225,6 +8328,11 @@ void *upb_pbdecoder_startjit(void *closure, const void *hd, size_t size_hint) { bool upb_pbdecoder_end(void *closure, const void *handler_data) { upb_pbdecoder *d = closure; const upb_pbdecodermethod *method = handler_data; + uint64_t end; + char dummy; +#ifdef UPB_USE_JIT_X64 + const mgroup *group = (const mgroup*)method->group; +#endif if (d->residual_end > d->residual) { seterr(d, "Unexpected EOF"); @@ -8236,35 +8344,32 @@ bool upb_pbdecoder_end(void *closure, const void *handler_data) { return false; } - // Message ends here. - uint64_t end = offset(d); + /* Message ends here. */ + end = offset(d); d->top->end_ofs = end; - char dummy; #ifdef UPB_USE_JIT_X64 - const mgroup *group = (const mgroup*)method->group; if (group->jit_code) { if (d->top != d->stack) d->stack->end_ofs = 0; group->jit_code(closure, method->code_base.ptr, &dummy, 0, NULL); - } else { + } else #endif - d->stack->end_ofs = end; + { const uint32_t *p = d->pc; - // Check the previous bytecode, but guard against beginning. + d->stack->end_ofs = end; + /* Check the previous bytecode, but guard against beginning. */ if (p != method->code_base.ptr) p--; if (getop(*p) == OP_CHECKDELIM) { - // Rewind from OP_TAG* to OP_CHECKDELIM. + /* Rewind from OP_TAG* to OP_CHECKDELIM. */ assert(getop(*d->pc) == OP_TAG1 || getop(*d->pc) == OP_TAG2 || getop(*d->pc) == OP_TAGN || - getop(*d->pc == OP_DISPATCH)); + getop(*d->pc) == OP_DISPATCH); d->pc = p; } upb_pbdecoder_decode(closure, handler_data, &dummy, 0, NULL); -#ifdef UPB_USE_JIT_X64 } -#endif if (d->call_len != 0) { seterr(d, "Unexpected EOF"); @@ -8293,8 +8398,8 @@ static size_t callstacksize(upb_pbdecoder *d, size_t entries) { #ifdef UPB_USE_JIT_X64 if (d->method_->is_native_) { - // Each native stack frame needs two pointers, plus we need a few frames for - // the enter/exit trampolines. + /* Each native stack frame needs two pointers, plus we need a few frames for + * the enter/exit trampolines. */ size_t ret = entries * sizeof(void*) * 2; ret += sizeof(void*) * 10; return ret; @@ -8335,7 +8440,7 @@ upb_pbdecoder *upb_pbdecoder_create(upb_env *e, const upb_pbdecodermethod *m, } upb_sink_reset(&d->top->sink, sink->handlers, sink->closure); - // If this fails, increase the value in decoder.h. + /* If this fails, increase the value in decoder.h. */ assert(upb_env_bytesallocated(e) - size_before <= UPB_PB_DECODER_SIZE); return d; } @@ -8360,12 +8465,12 @@ bool upb_pbdecoder_setmaxnesting(upb_pbdecoder *d, size_t max) { assert(d->top >= d->stack); if (max < (size_t)(d->top - d->stack)) { - // Can't set a limit smaller than what we are currently at. + /* Can't set a limit smaller than what we are currently at. */ return false; } if (max > d->stack_size) { - // Need to reallocate stack and callstack to accommodate. + /* Need to reallocate stack and callstack to accommodate. */ size_t old_size = stacksize(d, d->stack_size); size_t new_size = stacksize(d, max); void *p = upb_env_realloc(d->env, d->stack, old_size, new_size); @@ -8450,73 +8555,74 @@ bool upb_pbdecoder_setmaxnesting(upb_pbdecoder *d, size_t max) { #include -// The output buffer is divided into segments; a segment is a string of data -// that is "ready to go" -- it does not need any varint lengths inserted into -// the middle. The seams between segments are where varints will be inserted -// once they are known. -// -// We also use the concept of a "run", which is a range of encoded bytes that -// occur at a single submessage level. Every segment contains one or more runs. -// -// A segment can span messages. Consider: -// -// .--Submessage lengths---------. -// | | | -// | V V -// V | |--------------- | |----------------- -// Submessages: | |----------------------------------------------- -// Top-level msg: ------------------------------------------------------------ -// -// Segments: ----- ------------------- ----------------- -// Runs: *---- *--------------*--- *---------------- -// (* marks the start) -// -// Note that the top-level menssage is not in any segment because it does not -// have any length preceding it. -// -// A segment is only interrupted when another length needs to be inserted. So -// observe how the second segment spans both the inner submessage and part of -// the next enclosing message. +/* The output buffer is divided into segments; a segment is a string of data + * that is "ready to go" -- it does not need any varint lengths inserted into + * the middle. The seams between segments are where varints will be inserted + * once they are known. + * + * We also use the concept of a "run", which is a range of encoded bytes that + * occur at a single submessage level. Every segment contains one or more runs. + * + * A segment can span messages. Consider: + * + * .--Submessage lengths---------. + * | | | + * | V V + * V | |--------------- | |----------------- + * Submessages: | |----------------------------------------------- + * Top-level msg: ------------------------------------------------------------ + * + * Segments: ----- ------------------- ----------------- + * Runs: *---- *--------------*--- *---------------- + * (* marks the start) + * + * Note that the top-level menssage is not in any segment because it does not + * have any length preceding it. + * + * A segment is only interrupted when another length needs to be inserted. So + * observe how the second segment spans both the inner submessage and part of + * the next enclosing message. */ typedef struct { - uint32_t msglen; // The length to varint-encode before this segment. - uint32_t seglen; // Length of the segment. + uint32_t msglen; /* The length to varint-encode before this segment. */ + uint32_t seglen; /* Length of the segment. */ } upb_pb_encoder_segment; struct upb_pb_encoder { upb_env *env; - // Our input and output. + /* Our input and output. */ upb_sink input_; upb_bytessink *output_; - // The "subclosure" -- used as the inner closure as part of the bytessink - // protocol. + /* The "subclosure" -- used as the inner closure as part of the bytessink + * protocol. */ void *subc; - // The output buffer and limit, and our current write position. "buf" - // initially points to "initbuf", but is dynamically allocated if we need to - // grow beyond the initial size. + /* The output buffer and limit, and our current write position. "buf" + * initially points to "initbuf", but is dynamically allocated if we need to + * grow beyond the initial size. */ char *buf, *ptr, *limit; - // The beginning of the current run, or undefined if we are at the top level. + /* The beginning of the current run, or undefined if we are at the top + * level. */ char *runbegin; - // The list of segments we are accumulating. + /* The list of segments we are accumulating. */ upb_pb_encoder_segment *segbuf, *segptr, *seglimit; - // The stack of enclosing submessages. Each entry in the stack points to the - // segment where this submessage's length is being accumulated. + /* The stack of enclosing submessages. Each entry in the stack points to the + * segment where this submessage's length is being accumulated. */ int *stack, *top, *stacklimit; - // Depth of startmsg/endmsg calls. + /* Depth of startmsg/endmsg calls. */ int depth; }; /* low-level buffering ********************************************************/ -// Low-level functions for interacting with the output buffer. +/* Low-level functions for interacting with the output buffer. */ -// TODO(haberman): handle pushback +/* TODO(haberman): handle pushback */ static void putbuf(upb_pb_encoder *e, const char *buf, size_t len) { size_t n = upb_bytessink_putbuf(e->output_, e->subc, buf, len, NULL); UPB_ASSERT_VAR(n, n == len); @@ -8526,11 +8632,12 @@ static upb_pb_encoder_segment *top(upb_pb_encoder *e) { return &e->segbuf[*e->top]; } -// Call to ensure that at least "bytes" bytes are available for writing at -// e->ptr. Returns false if the bytes could not be allocated. +/* Call to ensure that at least "bytes" bytes are available for writing at + * e->ptr. Returns false if the bytes could not be allocated. */ static bool reserve(upb_pb_encoder *e, size_t bytes) { if ((size_t)(e->limit - e->ptr) < bytes) { - // Grow buffer. + /* Grow buffer. */ + char *new_buf; size_t needed = bytes + (e->ptr - e->buf); size_t old_size = e->limit - e->buf; @@ -8540,7 +8647,7 @@ static bool reserve(upb_pb_encoder *e, size_t bytes) { new_size *= 2; } - char *new_buf = upb_env_realloc(e->env, e->buf, old_size, new_size); + new_buf = upb_env_realloc(e->env, e->buf, old_size, new_size); if (new_buf == NULL) { return false; @@ -8555,22 +8662,22 @@ static bool reserve(upb_pb_encoder *e, size_t bytes) { return true; } -// Call when "bytes" bytes have been writte at e->ptr. The caller *must* have -// previously called reserve() with at least this many bytes. +/* Call when "bytes" bytes have been writte at e->ptr. The caller *must* have + * previously called reserve() with at least this many bytes. */ static void encoder_advance(upb_pb_encoder *e, size_t bytes) { assert((size_t)(e->limit - e->ptr) >= bytes); e->ptr += bytes; } -// Call when all of the bytes for a handler have been written. Flushes the -// bytes if possible and necessary, returning false if this failed. +/* Call when all of the bytes for a handler have been written. Flushes the + * bytes if possible and necessary, returning false if this failed. */ static bool commit(upb_pb_encoder *e) { if (!e->top) { - // We aren't inside a delimited region. Flush our accumulated bytes to - // the output. - // - // TODO(haberman): in the future we may want to delay flushing for - // efficiency reasons. + /* We aren't inside a delimited region. Flush our accumulated bytes to + * the output. + * + * TODO(haberman): in the future we may want to delay flushing for + * efficiency reasons. */ putbuf(e, e->buf, e->ptr - e->buf); e->ptr = e->buf; } @@ -8578,7 +8685,7 @@ static bool commit(upb_pb_encoder *e) { return true; } -// Writes the given bytes to the buffer, handling reserve/advance. +/* Writes the given bytes to the buffer, handling reserve/advance. */ static bool encode_bytes(upb_pb_encoder *e, const void *data, size_t len) { if (!reserve(e, len)) { return false; @@ -8589,32 +8696,33 @@ static bool encode_bytes(upb_pb_encoder *e, const void *data, size_t len) { return true; } -// Finish the current run by adding the run totals to the segment and message -// length. +/* Finish the current run by adding the run totals to the segment and message + * length. */ static void accumulate(upb_pb_encoder *e) { + size_t run_len; assert(e->ptr >= e->runbegin); - size_t run_len = e->ptr - e->runbegin; + run_len = e->ptr - e->runbegin; e->segptr->seglen += run_len; top(e)->msglen += run_len; e->runbegin = e->ptr; } -// Call to indicate the start of delimited region for which the full length is -// not yet known. All data will be buffered until the length is known. -// Delimited regions may be nested; their lengths will all be tracked properly. +/* Call to indicate the start of delimited region for which the full length is + * not yet known. All data will be buffered until the length is known. + * Delimited regions may be nested; their lengths will all be tracked properly. */ static bool start_delim(upb_pb_encoder *e) { if (e->top) { - // We are already buffering, advance to the next segment and push it on the - // stack. + /* We are already buffering, advance to the next segment and push it on the + * stack. */ accumulate(e); if (++e->top == e->stacklimit) { - // TODO(haberman): grow stack? + /* TODO(haberman): grow stack? */ return false; } if (++e->segptr == e->seglimit) { - // Grow segment buffer. + /* Grow segment buffer. */ size_t old_size = (e->seglimit - e->segbuf) * sizeof(upb_pb_encoder_segment); size_t new_size = old_size * 2; @@ -8630,7 +8738,7 @@ static bool start_delim(upb_pb_encoder *e) { e->segbuf = new_buf; } } else { - // We were previously at the top level, start buffering. + /* We were previously at the top level, start buffering. */ e->segptr = e->segbuf; e->top = e->stack; e->runbegin = e->ptr; @@ -8643,15 +8751,16 @@ static bool start_delim(upb_pb_encoder *e) { return true; } -// Call to indicate the end of a delimited region. We now know the length of -// the delimited region. If we are not nested inside any other delimited -// regions, we can now emit all of the buffered data we accumulated. +/* Call to indicate the end of a delimited region. We now know the length of + * the delimited region. If we are not nested inside any other delimited + * regions, we can now emit all of the buffered data we accumulated. */ static bool end_delim(upb_pb_encoder *e) { + size_t msglen; accumulate(e); - size_t msglen = top(e)->msglen; + msglen = top(e)->msglen; if (e->top == e->stack) { - // All lengths are now available, emit all buffered data. + /* All lengths are now available, emit all buffered data. */ char buf[UPB_PB_VARINT_MAX_LEN]; upb_pb_encoder_segment *s; const char *ptr = e->buf; @@ -8665,7 +8774,8 @@ static bool end_delim(upb_pb_encoder *e) { e->ptr = e->buf; e->top = NULL; } else { - // Need to keep buffering; propagate length info into enclosing submessages. + /* Need to keep buffering; propagate length info into enclosing + * submessages. */ --e->top; top(e)->msglen += msglen + upb_varint_size(msglen); } @@ -8676,14 +8786,14 @@ static bool end_delim(upb_pb_encoder *e) { /* tag_t **********************************************************************/ -// A precomputed (pre-encoded) tag and length. +/* A precomputed (pre-encoded) tag and length. */ typedef struct { uint8_t bytes; char tag[7]; } tag_t; -// Allocates a new tag for this field, and sets it in these handlerattr. +/* Allocates a new tag for this field, and sets it in these handlerattr. */ static void new_tag(upb_handlers *h, const upb_fielddef *f, upb_wiretype_t wt, upb_handlerattr *attr) { uint32_t n = upb_fielddef_number(f); @@ -8704,12 +8814,12 @@ static bool encode_tag(upb_pb_encoder *e, const tag_t *tag) { /* encoding of wire types *****************************************************/ static bool encode_fixed64(upb_pb_encoder *e, uint64_t val) { - // TODO(haberman): byte-swap for big endian. + /* TODO(haberman): byte-swap for big endian. */ return encode_bytes(e, &val, sizeof(uint64_t)); } static bool encode_fixed32(upb_pb_encoder *e, uint32_t val) { - // TODO(haberman): byte-swap for big endian. + /* TODO(haberman): byte-swap for big endian. */ return encode_bytes(e, &val, sizeof(uint32_t)); } @@ -8796,19 +8906,19 @@ static size_t encode_strbuf(void *c, const void *hd, const char *buf, } T(double, double, dbl2uint64, encode_fixed64) -T(float, float, flt2uint32, encode_fixed32); -T(int64, int64_t, uint64_t, encode_varint); -T(int32, int32_t, uint32_t, encode_varint); -T(fixed64, uint64_t, uint64_t, encode_fixed64); -T(fixed32, uint32_t, uint32_t, encode_fixed32); -T(bool, bool, bool, encode_varint); -T(uint32, uint32_t, uint32_t, encode_varint); -T(uint64, uint64_t, uint64_t, encode_varint); -T(enum, int32_t, uint32_t, encode_varint); -T(sfixed32, int32_t, uint32_t, encode_fixed32); -T(sfixed64, int64_t, uint64_t, encode_fixed64); -T(sint32, int32_t, upb_zzenc_32, encode_varint); -T(sint64, int64_t, upb_zzenc_64, encode_varint); +T(float, float, flt2uint32, encode_fixed32) +T(int64, int64_t, uint64_t, encode_varint) +T(int32, int32_t, uint32_t, encode_varint) +T(fixed64, uint64_t, uint64_t, encode_fixed64) +T(fixed32, uint32_t, uint32_t, encode_fixed32) +T(bool, bool, bool, encode_varint) +T(uint32, uint32_t, uint32_t, encode_varint) +T(uint64, uint64_t, uint64_t, encode_varint) +T(enum, int32_t, uint32_t, encode_varint) +T(sfixed32, int32_t, uint32_t, encode_fixed32) +T(sfixed64, int64_t, uint64_t, encode_fixed64) +T(sint32, int32_t, upb_zzenc_32, encode_varint) +T(sint64, int64_t, upb_zzenc_64, encode_varint) #undef T @@ -8816,13 +8926,15 @@ T(sint64, int64_t, upb_zzenc_64, encode_varint); /* code to build the handlers *************************************************/ static void newhandlers_callback(const void *closure, upb_handlers *h) { + const upb_msgdef *m; + upb_msg_field_iter i; + UPB_UNUSED(closure); upb_handlers_setstartmsg(h, startmsg, NULL); upb_handlers_setendmsg(h, endmsg, NULL); - const upb_msgdef *m = upb_handlers_msgdef(h); - upb_msg_field_iter i; + m = upb_handlers_msgdef(h); for(upb_msg_field_begin(&i, m); !upb_msg_field_done(&i); upb_msg_field_next(&i)) { @@ -8834,7 +8946,7 @@ static void newhandlers_callback(const void *closure, upb_handlers *h) { packed ? UPB_WIRE_TYPE_DELIMITED : upb_pb_native_wire_types[upb_fielddef_descriptortype(f)]; - // Pre-encode the tag for this field. + /* Pre-encode the tag for this field. */ new_tag(h, f, wt, &attr); if (packed) { @@ -8877,7 +8989,7 @@ static void newhandlers_callback(const void *closure, upb_handlers *h) { upb_handlers_setendsubmsg(h, f, encode_enddelimfield, &attr); break; case UPB_DESCRIPTOR_TYPE_GROUP: { - // Endgroup takes a different tag (wire_type = END_GROUP). + /* Endgroup takes a different tag (wire_type = END_GROUP). */ upb_handlerattr attr2; new_tag(h, f, UPB_WIRE_TYPE_END_GROUP, &attr2); @@ -8913,7 +9025,7 @@ upb_pb_encoder *upb_pb_encoder_create(upb_env *env, const upb_handlers *h, upb_bytessink *output) { const size_t initial_bufsize = 256; const size_t initial_segbufsize = 16; - // TODO(haberman): make this configurable. + /* TODO(haberman): make this configurable. */ const size_t stack_size = 64; #ifndef NDEBUG const size_t size_before = upb_env_bytesallocated(env); @@ -8942,7 +9054,7 @@ upb_pb_encoder *upb_pb_encoder_create(upb_env *env, const upb_handlers *h, e->subc = output->closure; e->ptr = e->buf; - // If this fails, increase the value in encoder.h. + /* If this fails, increase the value in encoder.h. */ assert(upb_env_bytesallocated(env) - size_before <= UPB_PB_ENCODER_SIZE); return e; } @@ -8962,28 +9074,31 @@ upb_sink *upb_pb_encoder_input(upb_pb_encoder *e) { return &e->input_; } upb_def **upb_load_defs_from_descriptor(const char *str, size_t len, int *n, void *owner, upb_status *status) { - // Create handlers. + /* Create handlers. */ + const upb_pbdecodermethod *decoder_m; const upb_handlers *reader_h = upb_descreader_newhandlers(&reader_h); + upb_env env; upb_pbdecodermethodopts opts; + upb_pbdecoder *decoder; + upb_descreader *reader; + bool ok; + upb_def **ret = NULL; + upb_def **defs; + upb_pbdecodermethodopts_init(&opts, reader_h); - const upb_pbdecodermethod *decoder_m = - upb_pbdecodermethod_new(&opts, &decoder_m); + decoder_m = upb_pbdecodermethod_new(&opts, &decoder_m); - upb_env env; upb_env_init(&env); upb_env_reporterrorsto(&env, status); - upb_descreader *reader = upb_descreader_create(&env, reader_h); - upb_pbdecoder *decoder = - upb_pbdecoder_create(&env, decoder_m, upb_descreader_input(reader)); + reader = upb_descreader_create(&env, reader_h); + decoder = upb_pbdecoder_create(&env, decoder_m, upb_descreader_input(reader)); - // Push input data. - bool ok = upb_bufsrc_putbuf(str, len, upb_pbdecoder_input(decoder)); - - upb_def **ret = NULL; + /* Push input data. */ + ok = upb_bufsrc_putbuf(str, len, upb_pbdecoder_input(decoder)); if (!ok) goto cleanup; - upb_def **defs = upb_descreader_getdefs(reader, owner, n); + defs = upb_descreader_getdefs(reader, owner, n); ret = malloc(sizeof(upb_def*) * (*n)); memcpy(ret, defs, sizeof(upb_def*) * (*n)); @@ -8997,21 +9112,24 @@ cleanup: bool upb_load_descriptor_into_symtab(upb_symtab *s, const char *str, size_t len, upb_status *status) { int n; + bool success; upb_def **defs = upb_load_defs_from_descriptor(str, len, &n, &defs, status); if (!defs) return false; - bool success = upb_symtab_add(s, defs, n, &defs, status); + success = upb_symtab_add(s, defs, n, &defs, status); free(defs); return success; } char *upb_readfile(const char *filename, size_t *len) { + long size; + char *buf; FILE *f = fopen(filename, "rb"); if(!f) return NULL; if(fseek(f, 0, SEEK_END) != 0) goto error; - long size = ftell(f); + size = ftell(f); if(size < 0) goto error; if(fseek(f, 0, SEEK_SET) != 0) goto error; - char *buf = malloc(size + 1); + buf = malloc(size + 1); if(size && fread(buf, size, 1, f) != 1) goto error; fclose(f); if (len) *len = size; @@ -9025,12 +9143,13 @@ error: bool upb_load_descriptor_file_into_symtab(upb_symtab *symtab, const char *fname, upb_status *status) { size_t len; + bool success; char *data = upb_readfile(fname, &len); if (!data) { if (status) upb_status_seterrf(status, "Couldn't read file: %s", fname); return false; } - bool success = upb_load_descriptor_into_symtab(symtab, data, len, status); + success = upb_load_descriptor_into_symtab(symtab, data, len, status); free(data); return success; } @@ -9048,6 +9167,7 @@ bool upb_load_descriptor_file_into_symtab(upb_symtab *symtab, const char *fname, #include #include #include +#include #include #include #include @@ -9084,22 +9204,24 @@ static int endfield(upb_textprinter *p) { static int putescaped(upb_textprinter *p, const char *buf, size_t len, bool preserve_utf8) { - // Based on CEscapeInternal() from Google's protobuf release. + /* Based on CEscapeInternal() from Google's protobuf release. */ char dstbuf[4096], *dst = dstbuf, *dstend = dstbuf + sizeof(dstbuf); const char *end = buf + len; - // I think hex is prettier and more useful, but proto2 uses octal; should - // investigate whether it can parse hex also. + /* I think hex is prettier and more useful, but proto2 uses octal; should + * investigate whether it can parse hex also. */ const bool use_hex = false; - bool last_hex_escape = false; // true if last output char was \xNN + bool last_hex_escape = false; /* true if last output char was \xNN */ for (; buf < end; buf++) { + bool is_hex_escape; + if (dstend - dst < 4) { upb_bytessink_putbuf(p->output_, p->subc, dstbuf, dst - dstbuf, NULL); dst = dstbuf; } - bool is_hex_escape = false; + is_hex_escape = false; switch (*buf) { case '\n': *(dst++) = '\\'; *(dst++) = 'n'; break; case '\r': *(dst++) = '\\'; *(dst++) = 'r'; break; @@ -9108,9 +9230,9 @@ static int putescaped(upb_textprinter *p, const char *buf, size_t len, case '\'': *(dst++) = '\\'; *(dst++) = '\''; break; case '\\': *(dst++) = '\\'; *(dst++) = '\\'; break; default: - // Note that if we emit \xNN and the buf character after that is a hex - // digit then that digit must be escaped too to prevent it being - // interpreted as part of the character code by C. + /* Note that if we emit \xNN and the buf character after that is a hex + * digit then that digit must be escaped too to prevent it being + * interpreted as part of the character code by C. */ if ((!preserve_utf8 || (uint8_t)*buf < 0x80) && (!isprint(*buf) || (last_hex_escape && isxdigit(*buf)))) { sprintf(dst, (use_hex ? "\\x%02x" : "\\%03o"), (uint8_t)*buf); @@ -9122,29 +9244,34 @@ static int putescaped(upb_textprinter *p, const char *buf, size_t len, } last_hex_escape = is_hex_escape; } - // Flush remaining data. + /* Flush remaining data. */ upb_bytessink_putbuf(p->output_, p->subc, dstbuf, dst - dstbuf, NULL); return 0; } bool putf(upb_textprinter *p, const char *fmt, ...) { va_list args; + va_list args_copy; + char *str; + int written; + int len; + bool ok; + va_start(args, fmt); - // Run once to get the length of the string. - va_list args_copy; - va_copy(args_copy, args); - int len = vsnprintf(NULL, 0, fmt, args_copy); + /* Run once to get the length of the string. */ + _upb_va_copy(args_copy, args); + len = _upb_vsnprintf(NULL, 0, fmt, args_copy); va_end(args_copy); - // + 1 for NULL terminator (vsnprintf() requires it even if we don't). - char *str = malloc(len + 1); + /* + 1 for NULL terminator (vsprintf() requires it even if we don't). */ + str = malloc(len + 1); if (!str) return false; - int written = vsnprintf(str, len + 1, fmt, args); + written = vsprintf(str, fmt, args); va_end(args); UPB_ASSERT_VAR(written, written == len); - bool ok = upb_bytessink_putbuf(p->output_, p->subc, str, len, NULL); + ok = upb_bytessink_putbuf(p->output_, p->subc, str, len, NULL); free(str); return ok; } @@ -9153,8 +9280,8 @@ bool putf(upb_textprinter *p, const char *fmt, ...) { /* handlers *******************************************************************/ static bool textprinter_startmsg(void *c, const void *hd) { - UPB_UNUSED(hd); upb_textprinter *p = c; + UPB_UNUSED(hd); if (p->indent_depth_ == 0) { upb_bytessink_start(p->output_, 0, &p->subc); } @@ -9162,9 +9289,9 @@ static bool textprinter_startmsg(void *c, const void *hd) { } static bool textprinter_endmsg(void *c, const void *hd, upb_status *s) { + upb_textprinter *p = c; UPB_UNUSED(hd); UPB_UNUSED(s); - upb_textprinter *p = c; if (p->indent_depth_ == 0) { upb_bytessink_end(p->output_); } @@ -9201,14 +9328,14 @@ err: TYPE(int32, int32_t, "%" PRId32) TYPE(int64, int64_t, "%" PRId64) -TYPE(uint32, uint32_t, "%" PRIu32); +TYPE(uint32, uint32_t, "%" PRIu32) TYPE(uint64, uint64_t, "%" PRIu64) TYPE(float, float, "%." STRINGIFY_MACROVAL(FLT_DIG) "g") TYPE(double, double, "%." STRINGIFY_MACROVAL(DBL_DIG) "g") #undef TYPE -// Output a symbolic value from the enum if found, else just print as int32. +/* Output a symbolic value from the enum if found, else just print as int32. */ static bool textprinter_putenum(void *closure, const void *handler_data, int32_t val) { upb_textprinter *p = closure; @@ -9228,17 +9355,17 @@ static bool textprinter_putenum(void *closure, const void *handler_data, static void *textprinter_startstr(void *closure, const void *handler_data, size_t size_hint) { + upb_textprinter *p = closure; const upb_fielddef *f = handler_data; UPB_UNUSED(size_hint); - upb_textprinter *p = closure; indent(p); putf(p, "%s: \"", upb_fielddef_name(f)); return p; } static bool textprinter_endstr(void *closure, const void *handler_data) { - UPB_UNUSED(handler_data); upb_textprinter *p = closure; + UPB_UNUSED(handler_data); putf(p, "\""); endfield(p); return true; @@ -9246,9 +9373,9 @@ static bool textprinter_endstr(void *closure, const void *handler_data) { static size_t textprinter_putstr(void *closure, const void *hd, const char *buf, size_t len, const upb_bufhandle *handle) { - UPB_UNUSED(handle); upb_textprinter *p = closure; const upb_fielddef *f = hd; + UPB_UNUSED(handle); CHECK(putescaped(p, buf, len, upb_fielddef_type(f) == UPB_TYPE_STRING)); return len; err: @@ -9267,8 +9394,8 @@ err: } static bool textprinter_endsubmsg(void *closure, const void *handler_data) { - UPB_UNUSED(handler_data); upb_textprinter *p = closure; + UPB_UNUSED(handler_data); p->indent_depth_--; CHECK(indent(p)); upb_bytessink_putbuf(p->output_, p->subc, "}", 1, NULL); @@ -9279,13 +9406,13 @@ err: } static void onmreg(const void *c, upb_handlers *h) { - UPB_UNUSED(c); const upb_msgdef *m = upb_handlers_msgdef(h); + upb_msg_field_iter i; + UPB_UNUSED(c); upb_handlers_setstartmsg(h, textprinter_startmsg, NULL); upb_handlers_setendmsg(h, textprinter_endmsg, NULL); - upb_msg_field_iter i; for(upb_msg_field_begin(&i, m); !upb_msg_field_done(&i); upb_msg_field_next(&i)) { @@ -9375,32 +9502,33 @@ void upb_textprinter_setsingleline(upb_textprinter *p, bool single_line) { */ -// Index is descriptor type. +/* Index is descriptor type. */ const uint8_t upb_pb_native_wire_types[] = { - UPB_WIRE_TYPE_END_GROUP, // ENDGROUP - UPB_WIRE_TYPE_64BIT, // DOUBLE - UPB_WIRE_TYPE_32BIT, // FLOAT - UPB_WIRE_TYPE_VARINT, // INT64 - UPB_WIRE_TYPE_VARINT, // UINT64 - UPB_WIRE_TYPE_VARINT, // INT32 - UPB_WIRE_TYPE_64BIT, // FIXED64 - UPB_WIRE_TYPE_32BIT, // FIXED32 - UPB_WIRE_TYPE_VARINT, // BOOL - UPB_WIRE_TYPE_DELIMITED, // STRING - UPB_WIRE_TYPE_START_GROUP, // GROUP - UPB_WIRE_TYPE_DELIMITED, // MESSAGE - UPB_WIRE_TYPE_DELIMITED, // BYTES - UPB_WIRE_TYPE_VARINT, // UINT32 - UPB_WIRE_TYPE_VARINT, // ENUM - UPB_WIRE_TYPE_32BIT, // SFIXED32 - UPB_WIRE_TYPE_64BIT, // SFIXED64 - UPB_WIRE_TYPE_VARINT, // SINT32 - UPB_WIRE_TYPE_VARINT, // SINT64 + UPB_WIRE_TYPE_END_GROUP, /* ENDGROUP */ + UPB_WIRE_TYPE_64BIT, /* DOUBLE */ + UPB_WIRE_TYPE_32BIT, /* FLOAT */ + UPB_WIRE_TYPE_VARINT, /* INT64 */ + UPB_WIRE_TYPE_VARINT, /* UINT64 */ + UPB_WIRE_TYPE_VARINT, /* INT32 */ + UPB_WIRE_TYPE_64BIT, /* FIXED64 */ + UPB_WIRE_TYPE_32BIT, /* FIXED32 */ + UPB_WIRE_TYPE_VARINT, /* BOOL */ + UPB_WIRE_TYPE_DELIMITED, /* STRING */ + UPB_WIRE_TYPE_START_GROUP, /* GROUP */ + UPB_WIRE_TYPE_DELIMITED, /* MESSAGE */ + UPB_WIRE_TYPE_DELIMITED, /* BYTES */ + UPB_WIRE_TYPE_VARINT, /* UINT32 */ + UPB_WIRE_TYPE_VARINT, /* ENUM */ + UPB_WIRE_TYPE_32BIT, /* SFIXED32 */ + UPB_WIRE_TYPE_64BIT, /* SFIXED64 */ + UPB_WIRE_TYPE_VARINT, /* SINT32 */ + UPB_WIRE_TYPE_VARINT, /* SINT64 */ }; -// A basic branch-based decoder, uses 32-bit values to get good performance -// on 32-bit architectures (but performs well on 64-bits also). -// This scheme comes from the original Google Protobuf implementation (proto2). +/* A basic branch-based decoder, uses 32-bit values to get good performance + * on 32-bit architectures (but performs well on 64-bits also). + * This scheme comes from the original Google Protobuf implementation + * (proto2). */ upb_decoderet upb_vdecode_max8_branch32(upb_decoderet r) { upb_decoderet err = {NULL, 0}; const char *p = r.p; @@ -9424,7 +9552,7 @@ done: return r; } -// Like the previous, but uses 64-bit values. +/* Like the previous, but uses 64-bit values. */ upb_decoderet upb_vdecode_max8_branch64(upb_decoderet r) { const char *p = r.p; uint64_t val = r.val; @@ -9446,50 +9574,54 @@ done: return r; } -// Given an encoded varint v, returns an integer with a single bit set that -// indicates the end of the varint. Subtracting one from this value will -// yield a mask that leaves only bits that are part of the varint. Returns -// 0 if the varint is unterminated. +/* Given an encoded varint v, returns an integer with a single bit set that + * indicates the end of the varint. Subtracting one from this value will + * yield a mask that leaves only bits that are part of the varint. Returns + * 0 if the varint is unterminated. */ static uint64_t upb_get_vstopbit(uint64_t v) { uint64_t cbits = v | 0x7f7f7f7f7f7f7f7fULL; return ~cbits & (cbits+1); } -// A branchless decoder. Credit to Pascal Massimino for the bit-twiddling. +/* A branchless decoder. Credit to Pascal Massimino for the bit-twiddling. */ upb_decoderet upb_vdecode_max8_massimino(upb_decoderet r) { uint64_t b; + uint64_t stop_bit; + upb_decoderet my_r; memcpy(&b, r.p, sizeof(b)); - uint64_t stop_bit = upb_get_vstopbit(b); + stop_bit = upb_get_vstopbit(b); b = (b & 0x7f7f7f7f7f7f7f7fULL) & (stop_bit - 1); b += b & 0x007f007f007f007fULL; b += 3 * (b & 0x0000ffff0000ffffULL); b += 15 * (b & 0x00000000ffffffffULL); if (stop_bit == 0) { - // Error: unterminated varint. + /* Error: unterminated varint. */ upb_decoderet err_r = {(void*)0, 0}; return err_r; } - upb_decoderet my_r = {r.p + ((__builtin_ctzll(stop_bit) + 1) / 8), - r.val | (b << 7)}; + my_r = upb_decoderet_make(r.p + ((__builtin_ctzll(stop_bit) + 1) / 8), + r.val | (b << 7)); return my_r; } -// A branchless decoder. Credit to Daniel Wright for the bit-twiddling. +/* A branchless decoder. Credit to Daniel Wright for the bit-twiddling. */ upb_decoderet upb_vdecode_max8_wright(upb_decoderet r) { uint64_t b; + uint64_t stop_bit; + upb_decoderet my_r; memcpy(&b, r.p, sizeof(b)); - uint64_t stop_bit = upb_get_vstopbit(b); + stop_bit = upb_get_vstopbit(b); b &= (stop_bit - 1); b = ((b & 0x7f007f007f007f00ULL) >> 1) | (b & 0x007f007f007f007fULL); b = ((b & 0xffff0000ffff0000ULL) >> 2) | (b & 0x0000ffff0000ffffULL); b = ((b & 0xffffffff00000000ULL) >> 4) | (b & 0x00000000ffffffffULL); if (stop_bit == 0) { - // Error: unterminated varint. + /* Error: unterminated varint. */ upb_decoderet err_r = {(void*)0, 0}; return err_r; } - upb_decoderet my_r = {r.p + ((__builtin_ctzll(stop_bit) + 1) / 8), - r.val | (b << 14)}; + my_r = upb_decoderet_make(r.p + ((__builtin_ctzll(stop_bit) + 1) / 8), + r.val | (b << 14)); return my_r; } @@ -9531,26 +9663,26 @@ upb_decoderet upb_vdecode_max8_wright(upb_decoderet r) { typedef struct { upb_sink sink; - // The current message in which we're parsing, and the field whose value we're - // expecting next. + /* 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. + /* 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. + /* 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. + /* 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; @@ -9559,41 +9691,41 @@ struct upb_json_parser { upb_byteshandler input_handler_; upb_bytessink input_; - // Stack to track the JSON scopes we are in. + /* Stack to track the JSON scopes we are in. */ upb_jsonparser_frame stack[UPB_JSON_MAX_DEPTH]; upb_jsonparser_frame *top; upb_jsonparser_frame *limit; upb_status *status; - // Ragel's internal parsing stack for the parsing state machine. + /* Ragel's internal parsing stack for the parsing state machine. */ int current_state; int parser_stack[UPB_JSON_MAX_DEPTH]; int parser_top; - // The handle for the current buffer. + /* The handle for the current buffer. */ const upb_bufhandle *handle; - // Accumulate buffer. See details in parser.rl. + /* Accumulate buffer. See details in parser.rl. */ const char *accumulated; size_t accumulated_len; char *accumulate_buf; size_t accumulate_buf_size; - // Multi-part text data. See details in parser.rl. + /* Multi-part text data. See details in parser.rl. */ int multipart_state; upb_selector_t string_selector; - // Input capture. See details in parser.rl. + /* Input capture. See details in parser.rl. */ const char *capture; - // Intermediate result of parsing a unicode escape sequence. + /* Intermediate result of parsing a unicode escape sequence. */ uint32_t digit; }; #define PARSER_CHECK_RETURN(x) if (!(x)) return false -// Used to signal that a capture has been suspended. +/* Used to signal that a capture has been suspended. */ static char suspend_capture; static upb_selector_t getsel_for_handlertype(upb_json_parser *p, @@ -9618,8 +9750,8 @@ static bool check_stack(upb_json_parser *p) { return true; } -// There are GCC/Clang built-ins for overflow checking which we could start -// using if there was any performance benefit to it. +/* There are GCC/Clang built-ins for overflow checking which we could start + * using if there was any performance benefit to it. */ static bool checked_add(size_t a, size_t b, size_t *c) { if (SIZE_MAX - a < b) return false; @@ -9628,7 +9760,7 @@ static bool checked_add(size_t a, size_t b, size_t *c) { } static size_t saturating_multiply(size_t a, size_t b) { - // size_t is unsigned, so this is defined behavior even on overflow. + /* size_t is unsigned, so this is defined behavior even on overflow. */ size_t ret = a * b; if (b != 0 && ret / b != a) { ret = SIZE_MAX; @@ -9639,7 +9771,7 @@ static size_t saturating_multiply(size_t a, size_t b) { /* Base64 decoding ************************************************************/ -// TODO(haberman): make this streaming. +/* TODO(haberman): make this streaming. */ static const signed char b64table[] = { -1, -1, -1, -1, -1, -1, -1, -1, @@ -9676,19 +9808,22 @@ static const signed char b64table[] = { -1, -1, -1, -1, -1, -1, -1, -1 }; -// Returns the table value sign-extended to 32 bits. Knowing that the upper -// bits will be 1 for unrecognized characters makes it easier to check for -// this error condition later (see below). +/* Returns the table value sign-extended to 32 bits. Knowing that the upper + * bits will be 1 for unrecognized characters makes it easier to check for + * this error condition later (see below). */ int32_t b64lookup(unsigned char ch) { return b64table[ch]; } -// Returns true if the given character is not a valid base64 character or -// padding. +/* Returns true if the given character is not a valid base64 character or + * padding. */ bool nonbase64(unsigned char ch) { return b64lookup(ch) == -1 && ch != '='; } static bool base64_push(upb_json_parser *p, upb_selector_t sel, const char *ptr, size_t len) { const char *limit = ptr + len; for (; ptr < limit; ptr += 4) { + uint32_t val; + char output[3]; + if (limit - ptr < 4) { upb_status_seterrf(p->status, "Base64 input for bytes field not a multiple of 4: %s", @@ -9696,17 +9831,16 @@ static bool base64_push(upb_json_parser *p, upb_selector_t sel, const char *ptr, return false; } - uint32_t val = b64lookup(ptr[0]) << 18 | - b64lookup(ptr[1]) << 12 | - b64lookup(ptr[2]) << 6 | - b64lookup(ptr[3]); + val = b64lookup(ptr[0]) << 18 | + b64lookup(ptr[1]) << 12 | + b64lookup(ptr[2]) << 6 | + b64lookup(ptr[3]); - // Test the upper bit; returns true if any of the characters returned -1. + /* Test the upper bit; returns true if any of the characters returned -1. */ if (val & 0x80000000) { goto otherchar; } - char output[3]; output[0] = val >> 16; output[1] = (val >> 8) & 0xff; output[2] = val & 0xff; @@ -9722,29 +9856,34 @@ otherchar: upb_fielddef_name(p->top->f)); return false; } if (ptr[2] == '=') { - // Last group contains only two input bytes, one output byte. + uint32_t val; + char output; + + /* Last group contains only two input bytes, one output byte. */ if (ptr[0] == '=' || ptr[1] == '=' || ptr[3] != '=') { goto badpadding; } - uint32_t val = b64lookup(ptr[0]) << 18 | - b64lookup(ptr[1]) << 12; + val = b64lookup(ptr[0]) << 18 | + b64lookup(ptr[1]) << 12; assert(!(val & 0x80000000)); - char output = val >> 16; + output = val >> 16; upb_sink_putstring(&p->top->sink, sel, &output, 1, NULL); return true; } else { - // Last group contains only three input bytes, two output bytes. + uint32_t val; + char output[2]; + + /* Last group contains only three input bytes, two output bytes. */ if (ptr[0] == '=' || ptr[1] == '=' || ptr[2] == '=') { goto badpadding; } - uint32_t val = b64lookup(ptr[0]) << 18 | - b64lookup(ptr[1]) << 12 | - b64lookup(ptr[2]) << 6; + val = b64lookup(ptr[0]) << 18 | + b64lookup(ptr[1]) << 12 | + b64lookup(ptr[2]) << 6; - char output[2]; output[0] = val >> 16; output[1] = (val >> 8) & 0xff; upb_sink_putstring(&p->top->sink, sel, output, 2, NULL); @@ -9762,23 +9901,23 @@ badpadding: /* Accumulate buffer **********************************************************/ -// Functionality for accumulating a buffer. -// -// Some parts of the parser need an entire value as a contiguous string. For -// example, to look up a member name in a hash table, or to turn a string into -// a number, the relevant library routines need the input string to be in -// contiguous memory, even if the value spanned two or more buffers in the -// input. These routines handle that. -// -// In the common case we can just point to the input buffer to get this -// contiguous string and avoid any actual copy. So we optimistically begin -// this way. But there are a few cases where we must instead copy into a -// separate buffer: -// -// 1. The string was not contiguous in the input (it spanned buffers). -// -// 2. The string included escape sequences that need to be interpreted to get -// the true value in a contiguous buffer. +/* Functionality for accumulating a buffer. + * + * Some parts of the parser need an entire value as a contiguous string. For + * example, to look up a member name in a hash table, or to turn a string into + * a number, the relevant library routines need the input string to be in + * contiguous memory, even if the value spanned two or more buffers in the + * input. These routines handle that. + * + * In the common case we can just point to the input buffer to get this + * contiguous string and avoid any actual copy. So we optimistically begin + * this way. But there are a few cases where we must instead copy into a + * separate buffer: + * + * 1. The string was not contiguous in the input (it spanned buffers). + * + * 2. The string included escape sequences that need to be interpreted to get + * the true value in a contiguous buffer. */ static void assert_accumulate_empty(upb_json_parser *p) { UPB_UNUSED(p); @@ -9791,15 +9930,16 @@ static void accumulate_clear(upb_json_parser *p) { p->accumulated_len = 0; } -// Used internally by accumulate_append(). +/* Used internally by accumulate_append(). */ static bool accumulate_realloc(upb_json_parser *p, size_t need) { + void *mem; size_t old_size = p->accumulate_buf_size; size_t new_size = UPB_MAX(old_size, 128); while (new_size < need) { new_size = saturating_multiply(new_size, 2); } - void *mem = upb_env_realloc(p->env, p->accumulate_buf, old_size, new_size); + mem = upb_env_realloc(p->env, p->accumulate_buf, old_size, new_size); if (!mem) { upb_status_seterrmsg(p->status, "Out of memory allocating buffer."); return false; @@ -9810,18 +9950,19 @@ static bool accumulate_realloc(upb_json_parser *p, size_t need) { return true; } -// Logically appends the given data to the append buffer. -// If "can_alias" is true, we will try to avoid actually copying, but the buffer -// must be valid until the next accumulate_append() call (if any). +/* Logically appends the given data to the append buffer. + * If "can_alias" is true, we will try to avoid actually copying, but the buffer + * must be valid until the next accumulate_append() call (if any). */ static bool accumulate_append(upb_json_parser *p, const char *buf, size_t len, bool can_alias) { + size_t need; + if (!p->accumulated && can_alias) { p->accumulated = buf; p->accumulated_len = len; return true; } - size_t need; if (!checked_add(p->accumulated_len, len, &need)) { upb_status_seterrmsg(p->status, "Integer overflow."); return false; @@ -9841,9 +9982,9 @@ static bool accumulate_append(upb_json_parser *p, const char *buf, size_t len, return true; } -// Returns a pointer to the data accumulated since the last accumulate_clear() -// call, and writes the length to *len. This with point either to the input -// buffer or a temporary accumulate buffer. +/* Returns a pointer to the data accumulated since the last accumulate_clear() + * call, and writes the length to *len. This with point either to the input + * buffer or a temporary accumulate buffer. */ static const char *accumulate_getptr(upb_json_parser *p, size_t *len) { assert(p->accumulated); *len = p->accumulated_len; @@ -9853,42 +9994,42 @@ static const char *accumulate_getptr(upb_json_parser *p, size_t *len) { /* Mult-part text data ********************************************************/ -// When we have text data in the input, it can often come in multiple segments. -// For example, there may be some raw string data followed by an escape -// sequence. The two segments are processed with different logic. Also buffer -// seams in the input can cause multiple segments. -// -// As we see segments, there are two main cases for how we want to process them: -// -// 1. we want to push the captured input directly to string handlers. -// -// 2. we need to accumulate all the parts into a contiguous buffer for further -// processing (field name lookup, string->number conversion, etc). - -// This is the set of states for p->multipart_state. +/* When we have text data in the input, it can often come in multiple segments. + * For example, there may be some raw string data followed by an escape + * sequence. The two segments are processed with different logic. Also buffer + * seams in the input can cause multiple segments. + * + * As we see segments, there are two main cases for how we want to process them: + * + * 1. we want to push the captured input directly to string handlers. + * + * 2. we need to accumulate all the parts into a contiguous buffer for further + * processing (field name lookup, string->number conversion, etc). */ + +/* This is the set of states for p->multipart_state. */ enum { - // We are not currently processing multipart data. + /* We are not currently processing multipart data. */ MULTIPART_INACTIVE = 0, - // We are processing multipart data by accumulating it into a contiguous - // buffer. + /* We are processing multipart data by accumulating it into a contiguous + * buffer. */ MULTIPART_ACCUMULATE = 1, - // We are processing multipart data by pushing each part directly to the - // current string handlers. + /* We are processing multipart data by pushing each part directly to the + * current string handlers. */ MULTIPART_PUSHEAGERLY = 2 }; -// Start a multi-part text value where we accumulate the data for processing at -// the end. +/* Start a multi-part text value where we accumulate the data for processing at + * the end. */ static void multipart_startaccum(upb_json_parser *p) { assert_accumulate_empty(p); assert(p->multipart_state == MULTIPART_INACTIVE); p->multipart_state = MULTIPART_ACCUMULATE; } -// Start a multi-part text value where we immediately push text data to a string -// value with the given selector. +/* Start a multi-part text value where we immediately push text data to a string + * value with the given selector. */ static void multipart_start(upb_json_parser *p, upb_selector_t sel) { assert_accumulate_empty(p); assert(p->multipart_state == MULTIPART_INACTIVE); @@ -9920,8 +10061,8 @@ static bool multipart_text(upb_json_parser *p, const char *buf, size_t len, return true; } -// Note: this invalidates the accumulate buffer! Call only after reading its -// contents. +/* Note: this invalidates the accumulate buffer! Call only after reading its + * contents. */ static void multipart_end(upb_json_parser *p) { assert(p->multipart_state != MULTIPART_INACTIVE); p->multipart_state = MULTIPART_INACTIVE; @@ -9931,9 +10072,9 @@ static void multipart_end(upb_json_parser *p) { /* Input capture **************************************************************/ -// Functionality for capturing a region of the input as text. Gracefully -// handles the case where a buffer seam occurs in the middle of the captured -// region. +/* Functionality for capturing a region of the input as text. Gracefully + * handles the case where a buffer seam occurs in the middle of the captured + * region. */ static void capture_begin(upb_json_parser *p, const char *ptr) { assert(p->multipart_state != MULTIPART_INACTIVE); @@ -9951,24 +10092,24 @@ static bool capture_end(upb_json_parser *p, const char *ptr) { } } -// This is called at the end of each input buffer (ie. when we have hit a -// buffer seam). If we are in the middle of capturing the input, this -// processes the unprocessed capture region. +/* This is called at the end of each input buffer (ie. when we have hit a + * buffer seam). If we are in the middle of capturing the input, this + * processes the unprocessed capture region. */ static void capture_suspend(upb_json_parser *p, const char **ptr) { if (!p->capture) return; if (multipart_text(p, p->capture, *ptr - p->capture, false)) { - // We use this as a signal that we were in the middle of capturing, and - // that capturing should resume at the beginning of the next buffer. - // - // We can't use *ptr here, because we have no guarantee that this pointer - // will be valid when we resume (if the underlying memory is freed, then - // using the pointer at all, even to compare to NULL, is likely undefined - // behavior). + /* We use this as a signal that we were in the middle of capturing, and + * that capturing should resume at the beginning of the next buffer. + * + * We can't use *ptr here, because we have no guarantee that this pointer + * will be valid when we resume (if the underlying memory is freed, then + * using the pointer at all, even to compare to NULL, is likely undefined + * behavior). */ p->capture = &suspend_capture; } else { - // Need to back up the pointer to the beginning of the capture, since - // we were not able to actually preserve it. + /* Need to back up the pointer to the beginning of the capture, since + * we were not able to actually preserve it. */ *ptr = p->capture; } } @@ -9983,8 +10124,8 @@ static void capture_resume(upb_json_parser *p, const char *ptr) { /* Callbacks from the parser **************************************************/ -// These are the functions called directly from the parser itself. -// We define these in the same order as their declarations in the parser. +/* These are the functions called directly from the parser itself. + * We define these in the same order as their declarations in the parser. */ static char escape_char(char in) { switch (in) { @@ -10029,8 +10170,8 @@ static void hexdigit(upb_json_parser *p, const char *ptr) { static bool end_hex(upb_json_parser *p) { uint32_t codepoint = p->digit; - // emit the codepoint as UTF-8. - char utf8[3]; // support \u0000 -- \uFFFF -- need only three bytes. + /* emit the codepoint as UTF-8. */ + char utf8[3]; /* support \u0000 -- \uFFFF -- need only three bytes. */ int length = 0; if (codepoint <= 0x7F) { utf8[0] = codepoint; @@ -10048,8 +10189,8 @@ static bool end_hex(upb_json_parser *p) { utf8[0] = (codepoint & 0x0F) | 0xE0; length = 3; } - // TODO(haberman): Handle high surrogates: if codepoint is a high surrogate - // we have to wait for the next escape to get the full code point). + /* TODO(haberman): Handle high surrogates: if codepoint is a high surrogate + * we have to wait for the next escape to get the full code point). */ return multipart_text(p, utf8, length, false); } @@ -10078,17 +10219,29 @@ static bool end_number(upb_json_parser *p, const char *ptr) { } 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. + size_t len; + const char *buf; + const char *myend; + char *end; + + /* 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)) { return false; } - size_t len; - const char *buf = accumulate_getptr(p, &len); - const char *myend = buf + len - 1; // One for NULL. + buf = accumulate_getptr(p, &len); + myend = buf + len - 1; /* One for NULL. */ - char *end; + /* XXX: We are using strtol to parse integers, but this is wrong as even + * integers can be represented as 1e6 (for example), which strtol can't + * handle correctly. + * + * XXX: Also, we can't handle large integers properly because strto[u]ll + * isn't in C89. + * + * XXX: Also, we don't properly check floats for overflow, since strtof + * isn't in C89. */ switch (upb_fielddef_type(p->top->f)) { case UPB_TYPE_ENUM: case UPB_TYPE_INT32: { @@ -10100,7 +10253,7 @@ static bool parse_number(upb_json_parser *p) { break; } case UPB_TYPE_INT64: { - long long val = strtoll(p->accumulated, &end, 0); + long long val = strtol(p->accumulated, &end, 0); if (val > INT64_MAX || val < INT64_MIN || errno == ERANGE || end != myend) goto err; else @@ -10116,7 +10269,7 @@ static bool parse_number(upb_json_parser *p) { break; } case UPB_TYPE_UINT64: { - unsigned long long val = strtoull(p->accumulated, &end, 0); + unsigned long long val = strtoul(p->accumulated, &end, 0); if (val > UINT64_MAX || errno == ERANGE || end != myend) goto err; else @@ -10132,7 +10285,7 @@ static bool parse_number(upb_json_parser *p) { break; } case UPB_TYPE_FLOAT: { - float val = strtof(p->accumulated, &end); + float val = strtod(p->accumulated, &end); if (errno == ERANGE || end != myend) goto err; else @@ -10154,6 +10307,8 @@ err: } static bool parser_putbool(upb_json_parser *p, bool val) { + bool ok; + if (upb_fielddef_type(p->top->f) != UPB_TYPE_BOOL) { upb_status_seterrf(p->status, "Boolean value specified for non-bool field: %s", @@ -10161,7 +10316,7 @@ static bool parser_putbool(upb_json_parser *p, bool val) { return false; } - bool ok = upb_sink_putbool(&p->top->sink, parser_getsel(p), val); + ok = upb_sink_putbool(&p->top->sink, parser_getsel(p), val); UPB_ASSERT_VAR(ok, ok); return true; @@ -10171,12 +10326,15 @@ static bool start_stringval(upb_json_parser *p) { assert(p->top->f); if (upb_fielddef_isstring(p->top->f)) { + upb_jsonparser_frame *inner; + upb_selector_t sel; + if (!check_stack(p)) return false; - // Start a new parser frame: parser frames correspond one-to-one with - // handler frames, and string events occur in a sub-frame. - upb_jsonparser_frame *inner = p->top + 1; - upb_selector_t sel = getsel_for_handlertype(p, UPB_HANDLER_STARTSTR); + /* Start a new parser frame: parser frames correspond one-to-one with + * handler frames, and string events occur in a sub-frame. */ + inner = p->top + 1; + sel = getsel_for_handlertype(p, UPB_HANDLER_STARTSTR); upb_sink_startstr(&p->top->sink, sel, 0, &inner->sink); inner->m = p->top->m; inner->f = p->top->f; @@ -10185,11 +10343,11 @@ static bool start_stringval(upb_json_parser *p) { p->top = inner; if (upb_fielddef_type(p->top->f) == UPB_TYPE_STRING) { - // For STRING fields we push data directly to the handlers as it is - // parsed. We don't do this yet for BYTES fields, because our base64 - // decoder is not streaming. - // - // TODO(haberman): make base64 decoding streaming also. + /* For STRING fields we push data directly to the handlers as it is + * parsed. We don't do this yet for BYTES fields, because our base64 + * decoder is not streaming. + * + * TODO(haberman): make base64 decoding streaming also. */ multipart_start(p, getsel_for_handlertype(p, UPB_HANDLER_STRING)); return true; } else { @@ -10197,11 +10355,11 @@ static bool start_stringval(upb_json_parser *p) { return true; } } else if (upb_fielddef_type(p->top->f) == UPB_TYPE_ENUM) { - // No need to push a frame -- symbolic enum names in quotes remain in the - // current parser frame. - // - // Enum string values must accumulate so we can look up the value in a table - // once it is complete. + /* No need to push a frame -- symbolic enum names in quotes remain in the + * current parser frame. + * + * Enum string values must accumulate so we can look up the value in a table + * once it is complete. */ multipart_startaccum(p); return true; } else { @@ -10221,7 +10379,7 @@ static bool end_stringval(upb_json_parser *p) { p->accumulated, p->accumulated_len)) { return false; } - // Fall through. + /* Fall through. */ case UPB_TYPE_STRING: { upb_selector_t sel = getsel_for_handlertype(p, UPB_HANDLER_ENDSTR); @@ -10231,7 +10389,7 @@ static bool end_stringval(upb_json_parser *p) { } case UPB_TYPE_ENUM: { - // Resolve enum symbolic name to integer value. + /* Resolve enum symbolic name to integer value. */ const upb_enumdef *enumdef = (const upb_enumdef*)upb_fielddef_subdef(p->top->f); @@ -10268,18 +10426,18 @@ static void start_member(upb_json_parser *p) { multipart_startaccum(p); } -// Helper: invoked during parse_mapentry() to emit the mapentry message's key -// field based on the current contents of the accumulate buffer. +/* 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); - // 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. + /* 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. */ p->top->f = upb_msgdef_itof(p->top->m, UPB_MAPENTRY_KEY); if (p->top->f == NULL) { @@ -10291,7 +10449,7 @@ static bool parse_mapentry_key(upb_json_parser *p) { case UPB_TYPE_INT64: case UPB_TYPE_UINT32: case UPB_TYPE_UINT64: - // Invoke end_number. The accum buffer has the number's text already. + /* Invoke end_number. The accum buffer has the number's text already. */ if (!parse_number(p)) { return false; } @@ -10332,47 +10490,52 @@ static bool parse_mapentry_key(upb_json_parser *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. +/* 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. + const upb_fielddef *mapfield; + const upb_msgdef *mapentrymsg; + upb_jsonparser_frame *inner; + upb_selector_t sel; + + /* 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); + mapfield = p->top->mapfield; + mapentrymsg = upb_fielddef_msgsubdef(mapfield); - upb_jsonparser_frame *inner = p->top + 1; + inner = p->top + 1; p->top->f = mapfield; - upb_selector_t sel = getsel_for_handlertype(p, UPB_HANDLER_STARTSUBMSG); + 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). + /* 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. + /* 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. + /* 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->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"); @@ -10393,7 +10556,8 @@ static bool end_membername(upb_json_parser *p) { 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. + /* 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; } @@ -10406,19 +10570,21 @@ static bool end_membername(upb_json_parser *p) { } static void end_member(upb_json_parser *p) { - // If we just parsed a map-entry value, end that frame too. + /* 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_selector_t sel; + bool ok; + const upb_fielddef *mapfield; + + assert(p->top > p->stack); + /* send ENDMSG on submsg. */ upb_sink_endmsg(&p->top->sink, &s); - const upb_fielddef* mapfield = p->top->mapfield; + mapfield = p->top->mapfield; - // send ENDSUBMSG in repeated-field-of-mapentries frame. + /* send ENDSUBMSG in repeated-field-of-mapentries frame. */ p->top--; - upb_selector_t sel; - bool ok = upb_handlers_getselector(mapfield, - UPB_HANDLER_ENDSUBMSG, &sel); + ok = upb_handlers_getselector(mapfield, UPB_HANDLER_ENDSUBMSG, &sel); UPB_ASSERT_VAR(ok, ok); upb_sink_endsubmsg(&p->top->sink, sel); } @@ -10430,12 +10596,15 @@ static bool start_subobject(upb_json_parser *p) { assert(p->top->f); if (upb_fielddef_ismap(p->top->f)) { - // Beginning of a map. Start a new parser frame in a repeated-field - // context. + upb_jsonparser_frame *inner; + upb_selector_t sel; + + /* 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); + inner = p->top + 1; + 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; @@ -10446,13 +10615,16 @@ static bool start_subobject(upb_json_parser *p) { return true; } else if (upb_fielddef_issubmsg(p->top->f)) { - // Beginning of a subobject. Start a new parser frame in the submsg - // context. + upb_jsonparser_frame *inner; + upb_selector_t sel; + + /* 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; + inner = p->top + 1; - upb_selector_t sel = getsel_for_handlertype(p, UPB_HANDLER_STARTSUBMSG); + 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; @@ -10471,17 +10643,22 @@ static bool start_subobject(upb_json_parser *p) { static void end_subobject(upb_json_parser *p) { if (p->top->is_map) { + upb_selector_t sel; p->top--; - upb_selector_t sel = getsel_for_handlertype(p, UPB_HANDLER_ENDSEQ); + sel = getsel_for_handlertype(p, UPB_HANDLER_ENDSEQ); upb_sink_endseq(&p->top->sink, sel); } else { + upb_selector_t sel; p->top--; - upb_selector_t sel = getsel_for_handlertype(p, UPB_HANDLER_ENDSUBMSG); + sel = getsel_for_handlertype(p, UPB_HANDLER_ENDSUBMSG); upb_sink_endsubmsg(&p->top->sink, sel); } } static bool start_array(upb_json_parser *p) { + upb_jsonparser_frame *inner; + upb_selector_t sel; + assert(p->top->f); if (!upb_fielddef_isseq(p->top->f)) { @@ -10493,8 +10670,8 @@ static bool start_array(upb_json_parser *p) { if (!check_stack(p)) return false; - upb_jsonparser_frame *inner = p->top + 1; - upb_selector_t sel = getsel_for_handlertype(p, UPB_HANDLER_STARTSEQ); + inner = p->top + 1; + sel = getsel_for_handlertype(p, UPB_HANDLER_STARTSEQ); upb_sink_startseq(&p->top->sink, sel, &inner->sink); inner->m = p->top->m; inner->f = p->top->f; @@ -10506,10 +10683,12 @@ static bool start_array(upb_json_parser *p) { } static void end_array(upb_json_parser *p) { + upb_selector_t sel; + assert(p->top > p->stack); p->top--; - upb_selector_t sel = getsel_for_handlertype(p, UPB_HANDLER_ENDSEQ); + sel = getsel_for_handlertype(p, UPB_HANDLER_ENDSEQ); upb_sink_endseq(&p->top->sink, sel); } @@ -10532,27 +10711,27 @@ static void end_object(upb_json_parser *p) { /* The actual parser **********************************************************/ -// What follows is the Ragel parser itself. The language is specified in Ragel -// and the actions call our C functions above. -// -// Ragel has an extensive set of functionality, and we use only a small part of -// it. There are many action types but we only use a few: -// -// ">" -- transition into a machine -// "%" -- transition out of a machine -// "@" -- transition into a final state of a machine. -// -// "@" transitions are tricky because a machine can transition into a final -// state repeatedly. But in some cases we know this can't happen, for example -// a string which is delimited by a final '"' can only transition into its -// final state once, when the closing '"' is seen. +/* What follows is the Ragel parser itself. The language is specified in Ragel + * and the actions call our C functions above. + * + * Ragel has an extensive set of functionality, and we use only a small part of + * it. There are many action types but we only use a few: + * + * ">" -- transition into a machine + * "%" -- transition out of a machine + * "@" -- transition into a final state of a machine. + * + * "@" transitions are tricky because a machine can transition into a final + * state repeatedly. But in some cases we know this can't happen, for example + * a string which is delimited by a final '"' can only transition into its + * final state once, when the closing '"' is seen. */ -#line 1151 "upb/json/parser.rl" +#line 1198 "upb/json/parser.rl" -#line 1063 "upb/json/parser.c" +#line 1110 "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, @@ -10701,16 +10880,13 @@ static const int json_en_value_machine = 27; static const int json_en_main = 1; -#line 1154 "upb/json/parser.rl" +#line 1201 "upb/json/parser.rl" size_t parse(void *closure, const void *hd, const char *buf, size_t size, const upb_bufhandle *handle) { - UPB_UNUSED(hd); - UPB_UNUSED(handle); upb_json_parser *parser = closure; - parser->handle = handle; - // Variables used by Ragel's generated code. + /* Variables used by Ragel's generated code. */ int cs = parser->current_state; int *stack = parser->parser_stack; int top = parser->parser_top; @@ -10718,10 +10894,15 @@ size_t parse(void *closure, const void *hd, const char *buf, size_t size, const char *p = buf; const char *pe = buf + size; + parser->handle = handle; + + UPB_UNUSED(hd); + UPB_UNUSED(handle); + capture_resume(parser, buf); -#line 1232 "upb/json/parser.c" +#line 1281 "upb/json/parser.c" { int _klen; unsigned int _trans; @@ -10796,118 +10977,118 @@ _match: switch ( *_acts++ ) { case 0: -#line 1066 "upb/json/parser.rl" +#line 1113 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; case 1: -#line 1067 "upb/json/parser.rl" +#line 1114 "upb/json/parser.rl" { p--; {stack[top++] = cs; cs = 10; goto _again;} } break; case 2: -#line 1071 "upb/json/parser.rl" +#line 1118 "upb/json/parser.rl" { start_text(parser, p); } break; case 3: -#line 1072 "upb/json/parser.rl" +#line 1119 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_text(parser, p)); } break; case 4: -#line 1078 "upb/json/parser.rl" +#line 1125 "upb/json/parser.rl" { start_hex(parser); } break; case 5: -#line 1079 "upb/json/parser.rl" +#line 1126 "upb/json/parser.rl" { hexdigit(parser, p); } break; case 6: -#line 1080 "upb/json/parser.rl" +#line 1127 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_hex(parser)); } break; case 7: -#line 1086 "upb/json/parser.rl" +#line 1133 "upb/json/parser.rl" { CHECK_RETURN_TOP(escape(parser, p)); } break; case 8: -#line 1092 "upb/json/parser.rl" +#line 1139 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; case 9: -#line 1095 "upb/json/parser.rl" +#line 1142 "upb/json/parser.rl" { {stack[top++] = cs; cs = 19; goto _again;} } break; case 10: -#line 1097 "upb/json/parser.rl" +#line 1144 "upb/json/parser.rl" { p--; {stack[top++] = cs; cs = 27; goto _again;} } break; case 11: -#line 1102 "upb/json/parser.rl" +#line 1149 "upb/json/parser.rl" { start_member(parser); } break; case 12: -#line 1103 "upb/json/parser.rl" +#line 1150 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_membername(parser)); } break; case 13: -#line 1106 "upb/json/parser.rl" +#line 1153 "upb/json/parser.rl" { end_member(parser); } break; case 14: -#line 1112 "upb/json/parser.rl" +#line 1159 "upb/json/parser.rl" { start_object(parser); } break; case 15: -#line 1115 "upb/json/parser.rl" +#line 1162 "upb/json/parser.rl" { end_object(parser); } break; case 16: -#line 1121 "upb/json/parser.rl" +#line 1168 "upb/json/parser.rl" { CHECK_RETURN_TOP(start_array(parser)); } break; case 17: -#line 1125 "upb/json/parser.rl" +#line 1172 "upb/json/parser.rl" { end_array(parser); } break; case 18: -#line 1130 "upb/json/parser.rl" +#line 1177 "upb/json/parser.rl" { start_number(parser, p); } break; case 19: -#line 1131 "upb/json/parser.rl" +#line 1178 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_number(parser, p)); } break; case 20: -#line 1133 "upb/json/parser.rl" +#line 1180 "upb/json/parser.rl" { CHECK_RETURN_TOP(start_stringval(parser)); } break; case 21: -#line 1134 "upb/json/parser.rl" +#line 1181 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_stringval(parser)); } break; case 22: -#line 1136 "upb/json/parser.rl" +#line 1183 "upb/json/parser.rl" { CHECK_RETURN_TOP(parser_putbool(parser, true)); } break; case 23: -#line 1138 "upb/json/parser.rl" +#line 1185 "upb/json/parser.rl" { CHECK_RETURN_TOP(parser_putbool(parser, false)); } break; case 24: -#line 1140 "upb/json/parser.rl" +#line 1187 "upb/json/parser.rl" { /* null value */ } break; case 25: -#line 1142 "upb/json/parser.rl" +#line 1189 "upb/json/parser.rl" { CHECK_RETURN_TOP(start_subobject(parser)); } break; case 26: -#line 1143 "upb/json/parser.rl" +#line 1190 "upb/json/parser.rl" { end_subobject(parser); } break; case 27: -#line 1148 "upb/json/parser.rl" +#line 1195 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; -#line 1418 "upb/json/parser.c" +#line 1467 "upb/json/parser.c" } } @@ -10920,7 +11101,7 @@ _again: _out: {} } -#line 1173 "upb/json/parser.rl" +#line 1222 "upb/json/parser.rl" if (p != pe) { upb_status_seterrf(parser->status, "Parse error at %s\n", p); @@ -10929,7 +11110,7 @@ _again: } error: - // Save parsing state back to parser. + /* Save parsing state back to parser. */ parser->current_state = cs; parser->parser_top = top; @@ -10940,7 +11121,7 @@ bool end(void *closure, const void *hd) { UPB_UNUSED(closure); UPB_UNUSED(hd); - // Prevent compile warning on unused static constants. + /* Prevent compile warning on unused static constants. */ UPB_UNUSED(json_start); UPB_UNUSED(json_en_number_machine); UPB_UNUSED(json_en_string_machine); @@ -10950,22 +11131,23 @@ bool end(void *closure, const void *hd) { } static void json_parser_reset(upb_json_parser *p) { + int cs; + int top; + 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. + /* Emit Ragel initialization of the parser. */ -#line 1470 "upb/json/parser.c" +#line 1520 "upb/json/parser.c" { cs = json_start; top = 0; } -#line 1211 "upb/json/parser.rl" +#line 1261 "upb/json/parser.rl" p->current_state = cs; p->parser_top = top; accumulate_clear(p); @@ -10997,8 +11179,8 @@ upb_json_parser *upb_json_parser_create(upb_env *env, upb_sink *output) { upb_sink_reset(&p->top->sink, output->handlers, output->closure); p->top->m = upb_handlers_msgdef(output->handlers); - // If this fails, uncomment and increase the value in parser.h. - // fprintf(stderr, "%zd\n", upb_env_bytesallocated(env) - size_before); + /* If this fails, uncomment and increase the value in parser.h. + * fprintf(stderr, "%zd\n", upb_env_bytesallocated(env) - size_before); */ assert(upb_env_bytesallocated(env) - size_before <= UPB_JSON_PARSER_SIZE); return p; } @@ -11024,26 +11206,26 @@ upb_bytessink *upb_json_parser_input(upb_json_parser *p) { struct upb_json_printer { upb_sink input_; - // BytesSink closure. + /* BytesSink closure. */ void *subc_; upb_bytessink *output_; - // We track the depth so that we know when to emit startstr/endstr on the - // output. + /* We track the depth so that we know when to emit startstr/endstr on the + * output. */ int depth_; - // Have we emitted the first element? This state is necessary to emit commas - // without leaving a trailing comma in arrays/maps. We keep this state per - // frame depth. - // - // Why max_depth * 2? UPB_MAX_HANDLER_DEPTH counts depth as nested messages. - // We count frames (contexts in which we separate elements by commas) as both - // repeated fields and messages (maps), and the worst case is a - // message->repeated field->submessage->repeated field->... nesting. + /* Have we emitted the first element? This state is necessary to emit commas + * without leaving a trailing comma in arrays/maps. We keep this state per + * frame depth. + * + * Why max_depth * 2? UPB_MAX_HANDLER_DEPTH counts depth as nested messages. + * We count frames (contexts in which we separate elements by commas) as both + * repeated fields and messages (maps), and the worst case is a + * message->repeated field->submessage->repeated field->... nesting. */ bool first_elem_[UPB_MAX_HANDLER_DEPTH * 2]; }; -// StringPiece; a pointer plus a length. +/* StringPiece; a pointer plus a length. */ typedef struct { const char *ptr; size_t len; @@ -11057,11 +11239,11 @@ strpc *newstrpc(upb_handlers *h, const upb_fielddef *f) { return ret; } -// ------------ JSON string printing: values, maps, arrays -------------------- +/* ------------ JSON string printing: values, maps, arrays ------------------ */ static void print_data( upb_json_printer *p, const char *buf, unsigned int len) { - // TODO: Will need to change if we support pushback from the sink. + /* TODO: Will need to change if we support pushback from the sink. */ size_t n = upb_bytessink_putbuf(p->output_, p->subc_, buf, len, NULL); UPB_ASSERT_VAR(n, n == len); } @@ -11073,18 +11255,18 @@ static void print_comma(upb_json_printer *p) { p->first_elem_[p->depth_] = false; } -// Helpers that print properly formatted elements to the JSON output stream. +/* Helpers that print properly formatted elements to the JSON output stream. */ -// Used for escaping control chars in strings. +/* Used for escaping control chars in strings. */ static const char kControlCharLimit = 0x20; -static inline bool is_json_escaped(char c) { - // See RFC 4627. +UPB_INLINE bool is_json_escaped(char c) { + /* See RFC 4627. */ unsigned char uc = (unsigned char)c; return uc < kControlCharLimit || uc == '"' || uc == '\\'; } -static inline char* json_nice_escape(char c) { +UPB_INLINE char* json_nice_escape(char c) { switch (c) { case '"': return "\\\""; case '\\': return "\\\\"; @@ -11097,46 +11279,47 @@ static inline char* json_nice_escape(char c) { } } -// Write a properly escaped string chunk. The surrounding quotes are *not* -// printed; this is so that the caller has the option of emitting the string -// content in chunks. +/* Write a properly escaped string chunk. The surrounding quotes are *not* + * printed; this is so that the caller has the option of emitting the string + * content in chunks. */ static void putstring(upb_json_printer *p, const char *buf, unsigned int len) { const char* unescaped_run = NULL; - for (unsigned int i = 0; i < len; i++) { + unsigned int i; + for (i = 0; i < len; i++) { char c = buf[i]; - // Handle escaping. + /* Handle escaping. */ if (is_json_escaped(c)) { - // Use a "nice" escape, like \n, if one exists for this character. + /* Use a "nice" escape, like \n, if one exists for this character. */ const char* escape = json_nice_escape(c); - // If we don't have a specific 'nice' escape code, use a \uXXXX-style - // escape. + /* If we don't have a specific 'nice' escape code, use a \uXXXX-style + * escape. */ char escape_buf[8]; if (!escape) { unsigned char byte = (unsigned char)c; - snprintf(escape_buf, sizeof(escape_buf), "\\u%04x", (int)byte); + _upb_snprintf(escape_buf, sizeof(escape_buf), "\\u%04x", (int)byte); escape = escape_buf; } - // N.B. that we assume that the input encoding is equal to the output - // encoding (both UTF-8 for now), so for chars >= 0x20 and != \, ", we - // can simply pass the bytes through. + /* N.B. that we assume that the input encoding is equal to the output + * encoding (both UTF-8 for now), so for chars >= 0x20 and != \, ", we + * can simply pass the bytes through. */ - // If there's a current run of unescaped chars, print that run first. + /* If there's a current run of unescaped chars, print that run first. */ if (unescaped_run) { print_data(p, unescaped_run, &buf[i] - unescaped_run); unescaped_run = NULL; } - // Then print the escape code. + /* Then print the escape code. */ print_data(p, escape, strlen(escape)); } else { - // Add to the current unescaped run of characters. + /* Add to the current unescaped run of characters. */ if (unescaped_run == NULL) { unescaped_run = &buf[i]; } } } - // If the string ended in a run of unescaped characters, print that last run. + /* If the string ended in a run of unescaped characters, print that last run. */ if (unescaped_run) { print_data(p, unescaped_run, &buf[len] - unescaped_run); } @@ -11144,42 +11327,42 @@ static void putstring(upb_json_printer *p, const char *buf, unsigned int len) { #define CHKLENGTH(x) if (!(x)) return -1; -// Helpers that format floating point values according to our custom formats. -// Right now we use %.8g and %.17g for float/double, respectively, to match -// proto2::util::JsonFormat's defaults. May want to change this later. +/* Helpers that format floating point values according to our custom formats. + * Right now we use %.8g and %.17g for float/double, respectively, to match + * proto2::util::JsonFormat's defaults. May want to change this later. */ static size_t fmt_double(double val, char* buf, size_t length) { - size_t n = snprintf(buf, length, "%.17g", val); + size_t n = _upb_snprintf(buf, length, "%.17g", val); CHKLENGTH(n > 0 && n < length); return n; } static size_t fmt_float(float val, char* buf, size_t length) { - size_t n = snprintf(buf, length, "%.8g", val); + size_t n = _upb_snprintf(buf, length, "%.8g", val); CHKLENGTH(n > 0 && n < length); return n; } static size_t fmt_bool(bool val, char* buf, size_t length) { - size_t n = snprintf(buf, length, "%s", (val ? "true" : "false")); + size_t n = _upb_snprintf(buf, length, "%s", (val ? "true" : "false")); CHKLENGTH(n > 0 && n < length); return n; } static size_t fmt_int64(long val, char* buf, size_t length) { - size_t n = snprintf(buf, length, "%ld", val); + size_t n = _upb_snprintf(buf, length, "%ld", val); CHKLENGTH(n > 0 && n < length); return n; } static size_t fmt_uint64(unsigned long long val, char* buf, size_t length) { - size_t n = snprintf(buf, length, "%llu", val); + size_t n = _upb_snprintf(buf, length, "%llu", val); CHKLENGTH(n > 0 && n < length); return n; } -// Print a map key given a field name. Called by scalar field handlers and by -// startseq for repeated fields. +/* Print a map key given a field name. Called by scalar field handlers and by + * startseq for repeated fields. */ static bool putkey(void *closure, const void *handler_data) { upb_json_printer *p = closure; const strpc *key = handler_data; @@ -11196,9 +11379,9 @@ static bool putkey(void *closure, const void *handler_data) { #define TYPE_HANDLERS(type, fmt_func) \ static bool put##type(void *closure, const void *handler_data, type val) { \ upb_json_printer *p = closure; \ - UPB_UNUSED(handler_data); \ char data[64]; \ size_t length = fmt_func(val, data, sizeof(data)); \ + UPB_UNUSED(handler_data); \ CHKFMT(length); \ print_data(p, data, length); \ return true; \ @@ -11227,20 +11410,20 @@ static bool putkey(void *closure, const void *handler_data) { return true; \ } -TYPE_HANDLERS(double, fmt_double); -TYPE_HANDLERS(float, fmt_float); -TYPE_HANDLERS(bool, fmt_bool); -TYPE_HANDLERS(int32_t, fmt_int64); -TYPE_HANDLERS(uint32_t, fmt_int64); -TYPE_HANDLERS(int64_t, fmt_int64); -TYPE_HANDLERS(uint64_t, fmt_uint64); +TYPE_HANDLERS(double, fmt_double) +TYPE_HANDLERS(float, fmt_float) +TYPE_HANDLERS(bool, fmt_bool) +TYPE_HANDLERS(int32_t, fmt_int64) +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); +/* 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 @@ -11254,9 +11437,11 @@ static bool scalar_enum(void *closure, const void *handler_data, int32_t val) { const EnumHandlerData *hd = handler_data; upb_json_printer *p = closure; + const char *symbolic_name; + CHK(putkey(closure, hd->keyname)); - const char *symbolic_name = upb_enumdef_iton(hd->enumdef, val); + symbolic_name = upb_enumdef_iton(hd->enumdef, val); if (symbolic_name) { print_data(p, "\"", 1); putstring(p, symbolic_name, strlen(symbolic_name)); @@ -11307,8 +11492,8 @@ static void *scalar_startsubmsg(void *closure, const void *handler_data) { } static void *repeated_startsubmsg(void *closure, const void *handler_data) { - UPB_UNUSED(handler_data); upb_json_printer *p = closure; + UPB_UNUSED(handler_data); print_comma(p); return closure; } @@ -11325,8 +11510,8 @@ static void end_frame(upb_json_printer *p) { } static bool printer_startmsg(void *closure, const void *handler_data) { - UPB_UNUSED(handler_data); upb_json_printer *p = closure; + UPB_UNUSED(handler_data); if (p->depth_ == 0) { upb_bytessink_start(p->output_, 0, &p->subc_); } @@ -11335,9 +11520,9 @@ static bool printer_startmsg(void *closure, const void *handler_data) { } static bool printer_endmsg(void *closure, const void *handler_data, upb_status *s) { + upb_json_printer *p = closure; UPB_UNUSED(handler_data); UPB_UNUSED(s); - upb_json_printer *p = closure; end_frame(p); if (p->depth_ == 0) { upb_bytessink_end(p->output_); @@ -11355,8 +11540,8 @@ static void *startseq(void *closure, const void *handler_data) { } static bool endseq(void *closure, const void *handler_data) { - UPB_UNUSED(handler_data); upb_json_printer *p = closure; + UPB_UNUSED(handler_data); print_data(p, "]", 1); p->depth_--; return true; @@ -11372,8 +11557,8 @@ static void *startmap(void *closure, const void *handler_data) { } static bool endmap(void *closure, const void *handler_data) { - UPB_UNUSED(handler_data); upb_json_printer *p = closure; + UPB_UNUSED(handler_data); print_data(p, "}", 1); p->depth_--; return true; @@ -11381,32 +11566,35 @@ static bool endmap(void *closure, const void *handler_data) { static size_t putstr(void *closure, const void *handler_data, const char *str, size_t len, const upb_bufhandle *handle) { + upb_json_printer *p = closure; UPB_UNUSED(handler_data); UPB_UNUSED(handle); - upb_json_printer *p = closure; putstring(p, str, len); return len; } -// This has to Base64 encode the bytes, because JSON has no "bytes" type. +/* This has to Base64 encode the bytes, because JSON has no "bytes" type. */ static size_t putbytes(void *closure, const void *handler_data, const char *str, size_t len, const upb_bufhandle *handle) { - UPB_UNUSED(handler_data); - UPB_UNUSED(handle); upb_json_printer *p = closure; - // This is the regular base64, not the "web-safe" version. + /* This is the regular base64, not the "web-safe" version. */ static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - // Base64-encode. + /* Base64-encode. */ char data[16000]; const char *limit = data + sizeof(data); const unsigned char *from = (const unsigned char*)str; char *to = data; size_t remaining = len; + size_t bytes; + + UPB_UNUSED(handler_data); + UPB_UNUSED(handle); + while (remaining > 2) { - // TODO(haberman): handle encoded lengths > sizeof(data) + /* TODO(haberman): handle encoded lengths > sizeof(data) */ UPB_ASSERT_VAR(limit, (limit - to) >= 4); to[0] = base64[from[0] >> 2]; @@ -11438,7 +11626,7 @@ static size_t putbytes(void *closure, const void *handler_data, const char *str, break; } - size_t bytes = to - data; + bytes = to - data; print_data(p, "\"", 1); putstring(p, data, bytes); print_data(p, "\"", 1); @@ -11447,9 +11635,9 @@ static size_t putbytes(void *closure, const void *handler_data, const char *str, static void *scalar_startstr(void *closure, const void *handler_data, size_t size_hint) { + upb_json_printer *p = closure; UPB_UNUSED(handler_data); UPB_UNUSED(size_hint); - upb_json_printer *p = closure; CHK(putkey(closure, handler_data)); print_data(p, "\"", 1); return p; @@ -11463,17 +11651,17 @@ static size_t scalar_str(void *closure, const void *handler_data, } static bool scalar_endstr(void *closure, const void *handler_data) { - UPB_UNUSED(handler_data); upb_json_printer *p = closure; + UPB_UNUSED(handler_data); print_data(p, "\"", 1); return true; } static void *repeated_startstr(void *closure, const void *handler_data, size_t size_hint) { + upb_json_printer *p = closure; UPB_UNUSED(handler_data); UPB_UNUSED(size_hint); - upb_json_printer *p = closure; print_comma(p); print_data(p, "\"", 1); return p; @@ -11487,17 +11675,17 @@ static size_t repeated_str(void *closure, const void *handler_data, } static bool repeated_endstr(void *closure, const void *handler_data) { - UPB_UNUSED(handler_data); upb_json_printer *p = closure; + UPB_UNUSED(handler_data); print_data(p, "\"", 1); return true; } static void *mapkeyval_startstr(void *closure, const void *handler_data, size_t size_hint) { + upb_json_printer *p = closure; UPB_UNUSED(handler_data); UPB_UNUSED(size_hint); - upb_json_printer *p = closure; print_data(p, "\"", 1); return p; } @@ -11510,15 +11698,15 @@ static size_t mapkey_str(void *closure, const void *handler_data, } static bool mapkey_endstr(void *closure, const void *handler_data) { - UPB_UNUSED(handler_data); upb_json_printer *p = closure; + UPB_UNUSED(handler_data); 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; + UPB_UNUSED(handler_data); print_data(p, "\"", 1); return true; } @@ -11559,30 +11747,31 @@ static void set_enum_hd(upb_handlers *h, 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. +/* 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. + /* 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; + UPB_UNUSED(closure); + switch (upb_fielddef_type(key_field)) { case UPB_TYPE_INT32: upb_handlers_setint32(h, key_field, putmapkey_int32_t, &empty_attr); @@ -11650,8 +11839,8 @@ void printer_sethandlers_mapentry(const void *closure, upb_handlers *h) { break; } case UPB_TYPE_MESSAGE: - // No handler necessary -- the submsg handlers will print the message - // as appropriate. + /* No handler necessary -- the submsg handlers will print the message + * as appropriate. */ break; } @@ -11659,14 +11848,16 @@ void printer_sethandlers_mapentry(const void *closure, upb_handlers *h) { } 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_msg_field_iter i; + + UPB_UNUSED(closure); if (is_mapentry) { - // mapentry messages are sufficiently different that we handle them - // separately. + /* mapentry messages are sufficiently different that we handle them + * separately. */ printer_sethandlers_mapentry(closure, h); return; } @@ -11683,7 +11874,6 @@ void printer_sethandlers(const void *closure, upb_handlers *h) { } \ break; - 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); @@ -11708,9 +11898,9 @@ void printer_sethandlers(const void *closure, upb_handlers *h) { TYPE(UPB_TYPE_INT64, int64, int64_t); TYPE(UPB_TYPE_UINT64, uint64, uint64_t); case UPB_TYPE_ENUM: { - // 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. + /* 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. */ upb_handlerattr enum_attr = UPB_HANDLERATTR_INITIALIZER; set_enum_hd(h, f, &enum_attr); @@ -11735,8 +11925,8 @@ void printer_sethandlers(const void *closure, upb_handlers *h) { } break; case UPB_TYPE_BYTES: - // XXX: this doesn't support strings that span buffers yet. The base64 - // encoder will need to be made resumable for this to work properly. + /* XXX: this doesn't support strings that span buffers yet. The base64 + * encoder will need to be made resumable for this to work properly. */ if (upb_fielddef_isseq(f)) { upb_handlers_setstring(h, f, repeated_bytes, &empty_attr); } else { @@ -11779,7 +11969,7 @@ upb_json_printer *upb_json_printer_create(upb_env *e, const upb_handlers *h, json_printer_reset(p); upb_sink_reset(&p->input_, h, p); - // If this fails, increase the value in printer.h. + /* If this fails, increase the value in printer.h. */ assert(upb_env_bytesallocated(e) - size_before <= UPB_JSON_PRINTER_SIZE); return p; } diff --git a/ruby/ext/google/protobuf_c/upb.h b/ruby/ext/google/protobuf_c/upb.h index 97df943a..4855e754 100644 --- a/ruby/ext/google/protobuf_c/upb.h +++ b/ruby/ext/google/protobuf_c/upb.h @@ -92,31 +92,61 @@ #include #include -// inline if possible, emit standalone code if required. +/* UPB_INLINE: inline if possible, emit standalone code if required. */ #ifdef __cplusplus #define UPB_INLINE inline +#elif defined (__GNUC__) +#define UPB_INLINE static __inline__ #else -#define UPB_INLINE static inline +#define UPB_INLINE static #endif -// For use in C/C++ source files (not headers), forces inlining within the file. +/* Define UPB_BIG_ENDIAN manually if you're on big endian and your compiler + * doesn't provide these preprocessor symbols. */ +#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +#define UPB_BIG_ENDIAN +#endif + +/* Macros for function attributes on compilers that support them. */ #ifdef __GNUC__ -#define UPB_FORCEINLINE inline __attribute__((always_inline)) +#define UPB_FORCEINLINE __inline__ __attribute__((always_inline)) #define UPB_NOINLINE __attribute__((noinline)) -#else +#define UPB_NORETURN __attribute__((__noreturn__)) +#else /* !defined(__GNUC__) */ #define UPB_FORCEINLINE #define UPB_NOINLINE +#define UPB_NORETURN #endif -#if __STDC_VERSION__ >= 199901L -#define UPB_C99 +/* A few hacky workarounds for functions not in C89. + * For internal use only! + * TODO(haberman): fix these by including our own implementations, or finding + * another workaround. + */ +#ifdef __GNUC__ +#define _upb_snprintf __builtin_snprintf +#define _upb_vsnprintf __builtin_vsnprintf +#define _upb_va_copy(a, b) __va_copy(a, b) +#elif __STDC_VERSION__ >= 199901L +/* C99 versions. */ +#define _upb_snprintf snprintf +#define _upb_vsnprintf vsnprintf +#define _upb_va_copy(a, b) va_copy(a, b) +#else +#error Need implementations of [v]snprintf and va_copy #endif + #if ((defined(__cplusplus) && __cplusplus >= 201103L) || \ defined(__GXX_EXPERIMENTAL_CXX0X__)) && !defined(UPB_NO_CXX11) #define UPB_CXX11 #endif +/* UPB_DISALLOW_COPY_AND_ASSIGN() + * UPB_DISALLOW_POD_OPS() + * + * Declare these in the "private" section of a C++ class to forbid copy/assign + * or all POD ops (construct, destruct, copy, assign) on that class. */ #ifdef UPB_CXX11 #include #define UPB_DISALLOW_COPY_AND_ASSIGN(class_name) \ @@ -125,51 +155,53 @@ #define UPB_DISALLOW_POD_OPS(class_name, full_class_name) \ class_name() = delete; \ ~class_name() = delete; \ - /* Friend Pointer so it can access base class. */ \ - friend class Pointer; \ - friend class Pointer; \ UPB_DISALLOW_COPY_AND_ASSIGN(class_name) #define UPB_ASSERT_STDLAYOUT(type) \ static_assert(std::is_standard_layout::value, \ #type " must be standard layout"); -#else // !defined(UPB_CXX11) +#else /* !defined(UPB_CXX11) */ #define UPB_DISALLOW_COPY_AND_ASSIGN(class_name) \ class_name(const class_name&); \ void operator=(const class_name&); #define UPB_DISALLOW_POD_OPS(class_name, full_class_name) \ class_name(); \ ~class_name(); \ - /* Friend Pointer so it can access base class. */ \ - friend class Pointer; \ - friend class Pointer; \ UPB_DISALLOW_COPY_AND_ASSIGN(class_name) #define UPB_ASSERT_STDLAYOUT(type) #endif +/* UPB_DECLARE_TYPE() + * UPB_DECLARE_DERIVED_TYPE() + * UPB_DECLARE_DERIVED_TYPE2() + * + * Macros for declaring C and C++ types both, including inheritance. + * The inheritance doesn't use real C++ inheritance, to stay compatible with C. + * + * These macros also provide upcasts: + * - in C: types-specific functions (ie. upb_foo_upcast(foo)) + * - in C++: upb::upcast(foo) along with implicit conversions + * + * Downcasts are not provided, but upb/def.h defines downcasts for upb::Def. */ + +#define UPB_C_UPCASTS(ty, base) \ + UPB_INLINE base *ty ## _upcast_mutable(ty *p) { return (base*)p; } \ + UPB_INLINE const base *ty ## _upcast(const ty *p) { return (const base*)p; } + +#define UPB_C_UPCASTS2(ty, base, base2) \ + UPB_C_UPCASTS(ty, base) \ + UPB_INLINE base2 *ty ## _upcast2_mutable(ty *p) { return (base2*)p; } \ + UPB_INLINE const base2 *ty ## _upcast2(const ty *p) { return (const base2*)p; } #ifdef __cplusplus -#define UPB_PRIVATE_FOR_CPP private: -#define UPB_DECLARE_TYPE(cppname, cname) typedef cppname cname; #define UPB_BEGIN_EXTERN_C extern "C" { #define UPB_END_EXTERN_C } -#define UPB_DEFINE_STRUCT0(cname, members) members; -#define UPB_DEFINE_STRUCT(cname, cbase, members) \ - public: \ - cbase* base() { return &base_; } \ - const cbase* base() const { return &base_; } \ - \ - private: \ - cbase base_; \ - members; -#define UPB_DEFINE_CLASS0(cppname, cppmethods, members) \ - class cppname { \ - cppmethods \ - members \ - }; \ - UPB_ASSERT_STDLAYOUT(cppname); -#define UPB_DEFINE_CLASS1(cppname, cppbase, cppmethods, members) \ - UPB_DEFINE_CLASS0(cppname, cppmethods, members) \ +#define UPB_PRIVATE_FOR_CPP private: +#define UPB_DECLARE_TYPE(cppname, cname) typedef cppname cname; + +#define UPB_DECLARE_DERIVED_TYPE(cppname, cppbase, cname, cbase) \ + UPB_DECLARE_TYPE(cppname, cname) \ + UPB_C_UPCASTS(cname, cbase) \ namespace upb { \ template <> \ class Pointer : public PointerBase { \ @@ -183,8 +215,11 @@ explicit Pointer(const cppname* ptr) : PointerBase(ptr) {} \ }; \ } -#define UPB_DEFINE_CLASS2(cppname, cppbase, cppbase2, cppmethods, members) \ - UPB_DEFINE_CLASS0(cppname, UPB_QUOTE(cppmethods), members) \ + +#define UPB_DECLARE_DERIVED_TYPE2(cppname, cppbase, cppbase2, cname, cbase, \ + cbase2) \ + UPB_DECLARE_TYPE(cppname, cname) \ + UPB_C_UPCASTS2(cname, cbase, cbase2) \ namespace upb { \ template <> \ class Pointer : public PointerBase2 { \ @@ -199,96 +234,97 @@ }; \ } -#else // !defined(__cplusplus) +#else /* !defined(__cplusplus) */ +#define UPB_BEGIN_EXTERN_C +#define UPB_END_EXTERN_C #define UPB_PRIVATE_FOR_CPP #define UPB_DECLARE_TYPE(cppname, cname) \ struct cname; \ typedef struct cname cname; -#define UPB_BEGIN_EXTERN_C -#define UPB_END_EXTERN_C -#define UPB_DEFINE_STRUCT0(cname, members) \ - struct cname { \ - members; \ - }; -#define UPB_DEFINE_STRUCT(cname, cbase, members) \ - struct cname { \ - cbase base; \ - members; \ - }; -#define UPB_DEFINE_CLASS0(cppname, cppmethods, members) members -#define UPB_DEFINE_CLASS1(cppname, cppbase, cppmethods, members) members -#define UPB_DEFINE_CLASS2(cppname, cppbase, cppbase2, cppmethods, members) \ - members - -#endif // defined(__cplusplus) +#define UPB_DECLARE_DERIVED_TYPE(cppname, cppbase, cname, cbase) \ + UPB_DECLARE_TYPE(cppname, cname) \ + UPB_C_UPCASTS(cname, cbase) +#define UPB_DECLARE_DERIVED_TYPE2(cppname, cppbase, cppbase2, \ + cname, cbase, cbase2) \ + UPB_DECLARE_TYPE(cppname, cname) \ + UPB_C_UPCASTS2(cname, cbase, cbase2) -#ifdef __GNUC__ -#define UPB_NORETURN __attribute__((__noreturn__)) -#else -#define UPB_NORETURN -#endif +#endif /* defined(__cplusplus) */ #define UPB_MAX(x, y) ((x) > (y) ? (x) : (y)) #define UPB_MIN(x, y) ((x) < (y) ? (x) : (y)) #define UPB_UNUSED(var) (void)var -// Code with commas confuses the preprocessor when passed as arguments, whether -// C++ type names with commas (eg. Foo) or code blocks that declare -// variables (ie. int foo, bar). -#define UPB_QUOTE(...) __VA_ARGS__ - -// For asserting something about a variable when the variable is not used for -// anything else. This prevents "unused variable" warnings when compiling in -// debug mode. +/* For asserting something about a variable when the variable is not used for + * anything else. This prevents "unused variable" warnings when compiling in + * debug mode. */ #define UPB_ASSERT_VAR(var, predicate) UPB_UNUSED(var); assert(predicate) -// Generic function type. +/* Generic function type. */ typedef void upb_func(); -/* Casts **********************************************************************/ - -// Upcasts for C. For downcasts see the definitions of the subtypes. -#define UPB_UPCAST(obj) (&(obj)->base) -#define UPB_UPCAST2(obj) UPB_UPCAST(UPB_UPCAST(obj)) +/* C++ Casts ******************************************************************/ #ifdef __cplusplus -// Downcasts for C++. We can't use C++ inheritance directly and maintain -// compatibility with C. So our inheritance is undeclared in C++. -// Specializations of these casting functions are defined for appropriate type -// pairs, and perform the necessary checks. -// -// Example: -// upb::Def* def = <...>; -// upb::MessageDef* = upb::dyn_cast(def); - namespace upb { -// Casts to a direct subclass. The caller must know that cast is correct; an -// incorrect cast will throw an assertion failure in debug mode. +template class Pointer; + +/* Casts to a subclass. The caller must know that cast is correct; an + * incorrect cast will throw an assertion failure in debug mode. + * + * Example: + * upb::Def* def = GetDef(); + * // Assert-fails if this was not actually a MessageDef. + * upb::MessgeDef* md = upb::down_cast(def); + * + * Note that downcasts are only defined for some types (at the moment you can + * only downcast from a upb::Def to a specific Def type). */ template To down_cast(From* f); -// Casts to a direct subclass. If the class does not actually match the given -// subtype, returns NULL. +/* Casts to a subclass. If the class does not actually match the given To type, + * returns NULL. + * + * Example: + * upb::Def* def = GetDef(); + * // md will be NULL if this was not actually a MessageDef. + * upb::MessgeDef* md = upb::down_cast(def); + * + * Note that dynamic casts are only defined for some types (at the moment you + * can only downcast from a upb::Def to a specific Def type).. */ template To dyn_cast(From* f); -// Pointer is a simple wrapper around a T*. It is only constructed for -// upcast() below, and its sole purpose is to be implicitly convertable to T* or -// pointers to base classes, just as a pointer would be in regular C++ if the -// inheritance were directly expressed as C++ inheritance. -template class Pointer; - -// Casts to any base class, or the type itself (ie. can be a no-op). +/* Casts to any base class, or the type itself (ie. can be a no-op). + * + * Example: + * upb::MessageDef* md = GetDef(); + * // This will fail to compile if this wasn't actually a base class. + * upb::Def* def = upb::upcast(md); + */ template inline Pointer upcast(T *f) { return Pointer(f); } +/* Attempt upcast to specific base class. + * + * Example: + * upb::MessageDef* md = GetDef(); + * upb::upcast_to(md)->MethodOnDef(); + */ +template inline T* upcast_to(F *f) { + return static_cast(upcast(f)); +} + +/* PointerBase: implementation detail of upb::upcast(). + * It is implicitly convertable to pointers to the Base class(es). + */ template class PointerBase { public: explicit PointerBase(T* ptr) : ptr_(ptr) {} operator T*() { return ptr_; } - operator Base*() { return ptr_->base(); } + operator Base*() { return (Base*)ptr_; } private: T* ptr_; @@ -310,17 +346,17 @@ class PointerBase2 : public PointerBase { #ifdef __cplusplus -#include // For std::swap(). +#include /* For std::swap(). */ namespace upb { -// Provides RAII semantics for upb refcounted objects. Each reffed_ptr owns a -// ref on whatever object it points to (if any). +/* Provides RAII semantics for upb refcounted objects. Each reffed_ptr owns a + * ref on whatever object it points to (if any). */ template class reffed_ptr { public: reffed_ptr() : ptr_(NULL) {} - // If ref_donor is NULL, takes a new ref, otherwise adopts from ref_donor. + /* If ref_donor is NULL, takes a new ref, otherwise adopts from ref_donor. */ template reffed_ptr(U* val, const void* ref_donor = NULL) : ptr_(upb::upcast(val)) { @@ -351,8 +387,8 @@ template class reffed_ptr { return *this; } - // TODO(haberman): add C++11 move construction/assignment for greater - // efficiency. + /* TODO(haberman): add C++11 move construction/assignment for greater + * efficiency. */ void swap(reffed_ptr& other) { if (ptr_ == other.ptr_) { @@ -376,7 +412,7 @@ template class reffed_ptr { T* get() const { return ptr_; } - // If ref_donor is NULL, takes a new ref, otherwise adopts from ref_donor. + /* If ref_donor is NULL, takes a new ref, otherwise adopts from ref_donor. */ template void reset(U* ptr = NULL, const void* ref_donor = NULL) { reffed_ptr(ptr, ref_donor).swap(*this); @@ -392,8 +428,8 @@ template class reffed_ptr { return reffed_ptr(upb::dyn_cast(get())); } - // Plain release() is unsafe; if we were the only owner, it would leak the - // object. Instead we provide this: + /* Plain release() is unsafe; if we were the only owner, it would leak the + * object. Instead we provide this: */ T* ReleaseTo(const void* new_owner) { T* ret = NULL; ptr_->DonateRef(this, new_owner); @@ -405,9 +441,9 @@ template class reffed_ptr { T* ptr_; }; -} // namespace upb +} /* namespace upb */ -#endif // __cplusplus +#endif /* __cplusplus */ /* upb::Status ****************************************************************/ @@ -419,70 +455,76 @@ class Status; } #endif -UPB_DECLARE_TYPE(upb::ErrorSpace, upb_errorspace); -UPB_DECLARE_TYPE(upb::Status, upb_status); +UPB_DECLARE_TYPE(upb::ErrorSpace, upb_errorspace) +UPB_DECLARE_TYPE(upb::Status, upb_status) -// The maximum length of an error message before it will get truncated. +/* The maximum length of an error message before it will get truncated. */ #define UPB_STATUS_MAX_MESSAGE 128 -// An error callback function is used to report errors from some component. -// The function can return "true" to indicate that the component should try -// to recover and proceed, but this is not always possible. +/* An error callback function is used to report errors from some component. + * The function can return "true" to indicate that the component should try + * to recover and proceed, but this is not always possible. */ typedef bool upb_errcb_t(void *closure, const upb_status* status); -UPB_DEFINE_CLASS0(upb::ErrorSpace, -, -UPB_DEFINE_STRUCT0(upb_errorspace, +#ifdef __cplusplus +class upb::ErrorSpace { +#else +struct upb_errorspace { +#endif const char *name; - // Should the error message in the status object according to this code. + /* Should the error message in the status object according to this code. */ void (*set_message)(upb_status* status, int code); -)); +}; + +#ifdef __cplusplus + +/* Object representing a success or failure status. + * It owns no resources and allocates no memory, so it should work + * even in OOM situations. */ -// Object representing a success or failure status. -// It owns no resources and allocates no memory, so it should work -// even in OOM situations. -UPB_DEFINE_CLASS0(upb::Status, +class upb::Status { public: Status(); - // Returns true if there is no error. + /* Returns true if there is no error. */ bool ok() const; - // Optional error space and code, useful if the caller wants to - // programmatically check the specific kind of error. + /* Optional error space and code, useful if the caller wants to + * programmatically check the specific kind of error. */ ErrorSpace* error_space(); int code() const; const char *error_message() const; - // The error message will be truncated if it is longer than - // UPB_STATUS_MAX_MESSAGE-4. + /* The error message will be truncated if it is longer than + * UPB_STATUS_MAX_MESSAGE-4. */ void SetErrorMessage(const char* msg); void SetFormattedErrorMessage(const char* fmt, ...); - // If there is no error message already, this will use the ErrorSpace to - // populate the error message for this code. The caller can still call - // SetErrorMessage() to give a more specific message. + /* If there is no error message already, this will use the ErrorSpace to + * populate the error message for this code. The caller can still call + * SetErrorMessage() to give a more specific message. */ void SetErrorCode(ErrorSpace* space, int code); - // Resets the status to a successful state with no message. + /* Resets the status to a successful state with no message. */ void Clear(); void CopyFrom(const Status& other); private: - UPB_DISALLOW_COPY_AND_ASSIGN(Status); -, -UPB_DEFINE_STRUCT0(upb_status, + UPB_DISALLOW_COPY_AND_ASSIGN(Status) +#else +struct upb_status { +#endif bool ok_; - // Specific status code defined by some error space (optional). + /* Specific status code defined by some error space (optional). */ int code_; upb_errorspace *error_space_; - // Error message; NULL-terminated. + /* Error message; NULL-terminated. */ char msg[UPB_STATUS_MAX_MESSAGE]; -)); +}; #define UPB_STATUS_INIT {true, 0, NULL, {0}} @@ -490,15 +532,15 @@ UPB_DEFINE_STRUCT0(upb_status, extern "C" { #endif -// The returned string is invalidated by any other call into the status. +/* The returned string is invalidated by any other call into the status. */ const char *upb_status_errmsg(const upb_status *status); bool upb_ok(const upb_status *status); upb_errorspace *upb_status_errspace(const upb_status *status); int upb_status_errcode(const upb_status *status); -// Any of the functions that write to a status object allow status to be NULL, -// to support use cases where the function's caller does not care about the -// status message. +/* Any of the functions that write to a status object allow status to be NULL, + * to support use cases where the function's caller does not care about the + * status message. */ void upb_status_clear(upb_status *status); void upb_status_seterrmsg(upb_status *status, const char *msg); void upb_status_seterrf(upb_status *status, const char *fmt, ...); @@ -507,11 +549,11 @@ void upb_status_seterrcode(upb_status *status, upb_errorspace *space, int code); void upb_status_copy(upb_status *to, const upb_status *from); #ifdef __cplusplus -} // extern "C" +} /* extern "C" */ namespace upb { -// C++ Wrappers +/* C++ Wrappers */ inline Status::Status() { Clear(); } inline bool Status::ok() const { return upb_ok(this); } inline const char* Status::error_message() const { @@ -534,7 +576,7 @@ inline void Status::CopyFrom(const Status& other) { upb_status_copy(this, &other); } -} // namespace upb +} /* namespace upb */ #endif @@ -547,9 +589,9 @@ extern "C" { /* upb_value ******************************************************************/ -// A tagged union (stored untagged inside the table) so that we can check that -// clients calling table accessors are correctly typed without having to have -// an explosion of accessors. +/* A tagged union (stored untagged inside the table) so that we can check that + * clients calling table accessors are correctly typed without having to have + * an explosion of accessors. */ typedef enum { UPB_CTYPE_INT32 = 1, UPB_CTYPE_INT64 = 2, @@ -559,85 +601,54 @@ typedef enum { UPB_CTYPE_CSTR = 6, UPB_CTYPE_PTR = 7, UPB_CTYPE_CONSTPTR = 8, - UPB_CTYPE_FPTR = 9, + UPB_CTYPE_FPTR = 9 } upb_ctype_t; -typedef union { - int32_t int32; - int64_t int64; - uint64_t uint64; - uint32_t uint32; - bool _bool; - char *cstr; - void *ptr; - const void *constptr; - upb_func *fptr; -} _upb_value; - typedef struct { - _upb_value val; + uint64_t val; #ifndef NDEBUG - // In debug mode we carry the value type around also so we can check accesses - // to be sure the right member is being read. + /* In debug mode we carry the value type around also so we can check accesses + * to be sure the right member is being read. */ upb_ctype_t ctype; #endif } upb_value; -#ifdef UPB_C99 -#define UPB_VALUE_INIT(v, member) {.member = v} -#endif -#define UPB__VALUE_INIT_NONE UPB_VALUE_INIT(NULL, ptr) - #ifdef NDEBUG #define SET_TYPE(dest, val) UPB_UNUSED(val) -#define UPB_VALUE_INIT_NONE {UPB__VALUE_INIT_NONE} #else #define SET_TYPE(dest, val) dest = val -// Non-existent type, all reads will fail. -#define UPB_VALUE_INIT_NONE {UPB__VALUE_INIT_NONE, -1} #endif -#define UPB_VALUE_INIT_INT32(v) UPB_VALUE_INIT(v, int32) -#define UPB_VALUE_INIT_INT64(v) UPB_VALUE_INIT(v, int64) -#define UPB_VALUE_INIT_UINT32(v) UPB_VALUE_INIT(v, uint32) -#define UPB_VALUE_INIT_UINT64(v) UPB_VALUE_INIT(v, uint64) -#define UPB_VALUE_INIT_BOOL(v) UPB_VALUE_INIT(v, _bool) -#define UPB_VALUE_INIT_CSTR(v) UPB_VALUE_INIT(v, cstr) -#define UPB_VALUE_INIT_PTR(v) UPB_VALUE_INIT(v, ptr) -#define UPB_VALUE_INIT_CONSTPTR(v) UPB_VALUE_INIT(v, constptr) -#define UPB_VALUE_INIT_FPTR(v) UPB_VALUE_INIT(v, fptr) - -// Like strdup(), which isn't always available since it's not ANSI C. +/* Like strdup(), which isn't always available since it's not ANSI C. */ char *upb_strdup(const char *s); -// Variant that works with a length-delimited rather than NULL-delimited string, -// as supported by strtable. +/* Variant that works with a length-delimited rather than NULL-delimited string, + * as supported by strtable. */ char *upb_strdup2(const char *s, size_t len); -UPB_INLINE void _upb_value_setval(upb_value *v, _upb_value val, +UPB_INLINE void _upb_value_setval(upb_value *v, uint64_t val, upb_ctype_t ctype) { v->val = val; SET_TYPE(v->ctype, ctype); } -UPB_INLINE upb_value _upb_value_val(_upb_value val, upb_ctype_t ctype) { +UPB_INLINE upb_value _upb_value_val(uint64_t val, upb_ctype_t ctype) { upb_value ret; _upb_value_setval(&ret, val, ctype); return ret; } -// For each value ctype, define the following set of functions: -// -// // Get/set an int32 from a upb_value. -// int32_t upb_value_getint32(upb_value val); -// void upb_value_setint32(upb_value *val, int32_t cval); -// -// // Construct a new upb_value from an int32. -// upb_value upb_value_int32(int32_t val); -#define FUNCS(name, membername, type_t, proto_type) \ +/* For each value ctype, define the following set of functions: + * + * // Get/set an int32 from a upb_value. + * int32_t upb_value_getint32(upb_value val); + * void upb_value_setint32(upb_value *val, int32_t cval); + * + * // Construct a new upb_value from an int32. + * upb_value upb_value_int32(int32_t val); */ +#define FUNCS(name, membername, type_t, converter, proto_type) \ UPB_INLINE void upb_value_set ## name(upb_value *val, type_t cval) { \ - val->val.uint64 = 0; \ + val->val = (converter)cval; \ SET_TYPE(val->ctype, proto_type); \ - val->val.membername = cval; \ } \ UPB_INLINE upb_value upb_value_ ## name(type_t val) { \ upb_value ret; \ @@ -646,69 +657,148 @@ UPB_INLINE upb_value _upb_value_val(_upb_value val, upb_ctype_t ctype) { } \ UPB_INLINE type_t upb_value_get ## name(upb_value val) { \ assert(val.ctype == proto_type); \ - return val.val.membername; \ + return (type_t)(converter)val.val; \ } -FUNCS(int32, int32, int32_t, UPB_CTYPE_INT32); -FUNCS(int64, int64, int64_t, UPB_CTYPE_INT64); -FUNCS(uint32, uint32, uint32_t, UPB_CTYPE_UINT32); -FUNCS(uint64, uint64, uint64_t, UPB_CTYPE_UINT64); -FUNCS(bool, _bool, bool, UPB_CTYPE_BOOL); -FUNCS(cstr, cstr, char*, UPB_CTYPE_CSTR); -FUNCS(ptr, ptr, void*, UPB_CTYPE_PTR); -FUNCS(constptr, constptr, const void*, UPB_CTYPE_CONSTPTR); -FUNCS(fptr, fptr, upb_func*, UPB_CTYPE_FPTR); +FUNCS(int32, int32, int32_t, int32_t, UPB_CTYPE_INT32) +FUNCS(int64, int64, int64_t, int64_t, UPB_CTYPE_INT64) +FUNCS(uint32, uint32, uint32_t, uint32_t, UPB_CTYPE_UINT32) +FUNCS(uint64, uint64, uint64_t, uint64_t, UPB_CTYPE_UINT64) +FUNCS(bool, _bool, bool, bool, UPB_CTYPE_BOOL) +FUNCS(cstr, cstr, char*, uintptr_t, UPB_CTYPE_CSTR) +FUNCS(ptr, ptr, void*, uintptr_t, UPB_CTYPE_PTR) +FUNCS(constptr, constptr, const void*, uintptr_t, UPB_CTYPE_CONSTPTR) +FUNCS(fptr, fptr, upb_func*, uintptr_t, UPB_CTYPE_FPTR) #undef FUNCS +#undef SET_TYPE -/* upb_table ******************************************************************/ +/* upb_tabkey *****************************************************************/ + +/* Either: + * 1. an actual integer key, or + * 2. a pointer to a string prefixed by its uint32_t length, owned by us. + * + * ...depending on whether this is a string table or an int table. We would + * make this a union of those two types, but C89 doesn't support statically + * initializing a non-first union member. */ +typedef uintptr_t upb_tabkey; + +#define UPB_TABKEY_NUM(n) n +#define UPB_TABKEY_NONE 0 +/* The preprocessor isn't quite powerful enough to turn the compile-time string + * length into a byte-wise string representation, so code generation needs to + * help it along. + * + * "len1" is the low byte and len4 is the high byte. */ +#ifdef UPB_BIG_ENDIAN +#define UPB_TABKEY_STR(len1, len2, len3, len4, strval) \ + (uintptr_t)(len4 len3 len2 len1 strval) +#else +#define UPB_TABKEY_STR(len1, len2, len3, len4, strval) \ + (uintptr_t)(len1 len2 len3 len4 strval) +#endif + +UPB_INLINE char *upb_tabstr(upb_tabkey key, uint32_t *len) { + char* mem = (char*)key; + if (len) memcpy(len, mem, sizeof(*len)); + return mem + sizeof(*len); +} + + +/* upb_tabval *****************************************************************/ + +#ifdef __cplusplus + +/* Status initialization not supported. + * + * This separate definition is necessary because in C++, UINTPTR_MAX isn't + * reliably available. */ +typedef struct { + uint64_t val; +} upb_tabval; + +#else + +/* C -- supports static initialization, but to support static initialization of + * both integers and points for both 32 and 64 bit targets, it takes a little + * bit of doing. */ + +#if UINTPTR_MAX == 0xffffffffffffffffULL +#define UPB_PTR_IS_64BITS +#elif UINTPTR_MAX != 0xffffffff +#error Could not determine how many bits pointers are. +#endif typedef union { - uintptr_t num; + /* For static initialization. + * + * Unfortunately this ugliness is necessary -- it is the only way that we can, + * with -std=c89 -pedantic, statically initialize this to either a pointer or + * an integer on 32-bit platforms. */ struct { - // We own this. NULL-terminated but may also contain binary data; see - // explicit length below. - // TODO: move the length to the start of the string in order to reduce - // tabkey's size (to one machine word) in a way that supports static - // initialization. - const char *str; - size_t length; - } s; -} upb_tabkey; - -#define UPB_TABKEY_NUM(n) {n} -#ifdef UPB_C99 -// Given that |s| is a string literal, sizeof(s) gives us a -// compile-time-constant strlen(). We must ensure that this works for static -// data initializers. -#define UPB_TABKEY_STR(strval) { .s = { .str = strval, \ - .length = sizeof(strval) - 1 } } +#ifdef UPB_PTR_IS_64BITS + uintptr_t val; +#else + uintptr_t val1; + uintptr_t val2; +#endif + } staticinit; + + /* The normal accessor that we use for everything at runtime. */ + uint64_t val; +} upb_tabval; + +#ifdef UPB_PTR_IS_64BITS +#define UPB_TABVALUE_INT_INIT(v) {{v}} +#define UPB_TABVALUE_EMPTY_INIT {{-1}} +#else + +/* 32-bit pointers */ + +#ifdef UPB_BIG_ENDIAN +#define UPB_TABVALUE_INT_INIT(v) {{0, v}} +#define UPB_TABVALUE_EMPTY_INIT {{-1, -1}} +#else +#define UPB_TABVALUE_INT_INIT(v) {{v, 0}} +#define UPB_TABVALUE_EMPTY_INIT {{-1, -1}} +#endif + #endif -// TODO(haberman): C++ -#define UPB_TABKEY_NONE {0} + +#define UPB_TABVALUE_PTR_INIT(v) UPB_TABVALUE_INT_INIT((uintptr_t)v) + +#undef UPB_PTR_IS_64BITS + +#endif /* __cplusplus */ + + +/* upb_table ******************************************************************/ typedef struct _upb_tabent { upb_tabkey key; - _upb_value val; - // Internal chaining. This is const so we can create static initializers for - // tables. We cast away const sometimes, but *only* when the containing - // upb_table is known to be non-const. This requires a bit of care, but - // the subtlety is confined to table.c. + upb_tabval val; + + /* Internal chaining. This is const so we can create static initializers for + * tables. We cast away const sometimes, but *only* when the containing + * upb_table is known to be non-const. This requires a bit of care, but + * the subtlety is confined to table.c. */ const struct _upb_tabent *next; } upb_tabent; typedef struct { - size_t count; // Number of entries in the hash part. - size_t mask; // Mask to turn hash value -> bucket. - upb_ctype_t ctype; // Type of all values. - uint8_t size_lg2; // Size of the hash table part is 2^size_lg2 entries. - - // Hash table entries. - // Making this const isn't entirely accurate; what we really want is for it to - // have the same const-ness as the table it's inside. But there's no way to - // declare that in C. So we have to make it const so that we can statically - // initialize const hash tables. Then we cast away const when we have to. + size_t count; /* Number of entries in the hash part. */ + size_t mask; /* Mask to turn hash value -> bucket. */ + upb_ctype_t ctype; /* Type of all values. */ + uint8_t size_lg2; /* Size of the hashtable part is 2^size_lg2 entries. */ + + /* Hash table entries. + * Making this const isn't entirely accurate; what we really want is for it to + * have the same const-ness as the table it's inside. But there's no way to + * declare that in C. So we have to make it const so that we can statically + * initialize const hash tables. Then we cast away const when we have to. + */ const upb_tabent *entries; } upb_table; @@ -723,10 +813,10 @@ typedef struct { 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. - size_t array_size; // Array part size. - size_t array_count; // Array part number of elements. + upb_table t; /* For entries that don't fit in the array part. */ + const upb_tabval *array; /* Array part of the table. See const note above. */ + size_t array_size; /* Array part size. */ + size_t array_count; /* Array part number of elements. */ } upb_inttable; #define UPB_INTTABLE_INIT(count, mask, ctype, size_lg2, ent, a, asize, acount) \ @@ -735,8 +825,7 @@ typedef struct { #define UPB_EMPTY_INTTABLE_INIT(ctype) \ UPB_INTTABLE_INIT(0, 0, ctype, 0, NULL, NULL, 0, 0) -#define UPB_ARRAY_EMPTYVAL -1 -#define UPB_ARRAY_EMPTYENT UPB_VALUE_INIT_INT64(UPB_ARRAY_EMPTYVAL) +#define UPB_ARRAY_EMPTYENT -1 UPB_INLINE size_t upb_table_size(const upb_table *t) { if (t->size_lg2 == 0) @@ -745,18 +834,16 @@ UPB_INLINE size_t upb_table_size(const upb_table *t) { return 1 << t->size_lg2; } -// Internal-only functions, in .h file only out of necessity. +/* Internal-only functions, in .h file only out of necessity. */ UPB_INLINE bool upb_tabent_isempty(const upb_tabent *e) { - return e->key.num == 0; + return e->key == 0; } -// Used by some of the unit tests for generic hashing functionality. +/* Used by some of the unit tests for generic hashing functionality. */ uint32_t MurmurHash2(const void * key, size_t len, uint32_t seed); -UPB_INLINE upb_tabkey upb_intkey(uintptr_t key) { - upb_tabkey k; - k.num = key; - return k; +UPB_INLINE uintptr_t upb_intkey(uintptr_t key) { + return key; } UPB_INLINE uint32_t upb_inthash(uintptr_t key) { @@ -767,93 +854,94 @@ static const upb_tabent *upb_getentry(const upb_table *t, uint32_t hash) { return t->entries + (hash & t->mask); } -UPB_INLINE bool upb_arrhas(_upb_value v) { - return v.uint64 != (uint64_t)UPB_ARRAY_EMPTYVAL; +UPB_INLINE bool upb_arrhas(upb_tabval key) { + return key.val != (uint64_t)-1; } -// Initialize and uninitialize a table, respectively. If memory allocation -// failed, false is returned that the table is uninitialized. +/* Initialize and uninitialize a table, respectively. If memory allocation + * failed, false is returned that the table is uninitialized. */ bool upb_inttable_init(upb_inttable *table, upb_ctype_t ctype); bool upb_strtable_init(upb_strtable *table, upb_ctype_t ctype); void upb_inttable_uninit(upb_inttable *table); void upb_strtable_uninit(upb_strtable *table); -// Returns the number of values in the table. +/* Returns the number of values in the table. */ size_t upb_inttable_count(const upb_inttable *t); UPB_INLINE size_t upb_strtable_count(const upb_strtable *t) { return t->t.count; } -// Inserts the given key into the hashtable with the given value. The key must -// not already exist in the hash table. For string tables, the key must be -// NULL-terminated, and the table will make an internal copy of the key. -// Inttables must not insert a value of UINTPTR_MAX. -// -// If a table resize was required but memory allocation failed, false is -// returned and the table is unchanged. +/* Inserts the given key into the hashtable with the given value. The key must + * not already exist in the hash table. For string tables, the key must be + * NULL-terminated, and the table will make an internal copy of the key. + * Inttables must not insert a value of UINTPTR_MAX. + * + * If a table resize was required but memory allocation failed, false is + * returned and the table is unchanged. */ bool upb_inttable_insert(upb_inttable *t, uintptr_t key, upb_value val); bool upb_strtable_insert2(upb_strtable *t, const char *key, size_t len, upb_value val); -// For NULL-terminated strings. +/* For NULL-terminated strings. */ UPB_INLINE bool upb_strtable_insert(upb_strtable *t, const char *key, upb_value val) { return upb_strtable_insert2(t, key, strlen(key), val); } -// Looks up key in this table, returning "true" if the key was found. -// If v is non-NULL, copies the value for this key into *v. +/* Looks up key in this table, returning "true" if the key was found. + * If v is non-NULL, copies the value for this key into *v. */ bool upb_inttable_lookup(const upb_inttable *t, uintptr_t key, upb_value *v); bool upb_strtable_lookup2(const upb_strtable *t, const char *key, size_t len, upb_value *v); -// For NULL-terminated strings. +/* For NULL-terminated strings. */ UPB_INLINE bool upb_strtable_lookup(const upb_strtable *t, const char *key, upb_value *v) { return upb_strtable_lookup2(t, key, strlen(key), v); } -// Removes an item from the table. Returns true if the remove was successful, -// and stores the removed item in *val if non-NULL. +/* Removes an item from the table. Returns true if the remove was successful, + * and stores the removed item in *val if non-NULL. */ bool upb_inttable_remove(upb_inttable *t, uintptr_t key, upb_value *val); bool upb_strtable_remove2(upb_strtable *t, const char *key, size_t len, upb_value *val); -// For NULL-terminated strings. +/* For NULL-terminated strings. */ UPB_INLINE bool upb_strtable_remove(upb_strtable *t, const char *key, upb_value *v) { return upb_strtable_remove2(t, key, strlen(key), v); } -// Updates an existing entry in an inttable. If the entry does not exist, -// returns false and does nothing. Unlike insert/remove, this does not -// invalidate iterators. +/* Updates an existing entry in an inttable. If the entry does not exist, + * returns false and does nothing. Unlike insert/remove, this does not + * invalidate iterators. */ bool upb_inttable_replace(upb_inttable *t, uintptr_t key, upb_value val); -// Handy routines for treating an inttable like a stack. May not be mixed with -// other insert/remove calls. +/* Handy routines for treating an inttable like a stack. May not be mixed with + * other insert/remove calls. */ bool upb_inttable_push(upb_inttable *t, upb_value val); upb_value upb_inttable_pop(upb_inttable *t); -// Convenience routines for inttables with pointer keys. +/* Convenience routines for inttables with pointer keys. */ bool upb_inttable_insertptr(upb_inttable *t, const void *key, upb_value val); bool upb_inttable_removeptr(upb_inttable *t, const void *key, upb_value *val); bool upb_inttable_lookupptr( const upb_inttable *t, const void *key, upb_value *val); -// Optimizes the table for the current set of entries, for both memory use and -// lookup time. Client should call this after all entries have been inserted; -// inserting more entries is legal, but will likely require a table resize. +/* Optimizes the table for the current set of entries, for both memory use and + * lookup time. Client should call this after all entries have been inserted; + * inserting more entries is legal, but will likely require a table resize. */ void upb_inttable_compact(upb_inttable *t); -// A special-case inlinable version of the lookup routine for 32-bit integers. +/* A special-case inlinable version of the lookup routine for 32-bit + * integers. */ UPB_INLINE bool upb_inttable_lookup32(const upb_inttable *t, uint32_t key, upb_value *v) { - *v = upb_value_int32(0); // Silence compiler warnings. + *v = upb_value_int32(0); /* Silence compiler warnings. */ if (key < t->array_size) { - _upb_value arrval = t->array[key]; + upb_tabval arrval = t->array[key]; if (upb_arrhas(arrval)) { - _upb_value_setval(v, arrval, t->t.ctype); + _upb_value_setval(v, arrval.val, t->t.ctype); return true; } else { return false; @@ -862,8 +950,8 @@ UPB_INLINE bool upb_inttable_lookup32(const upb_inttable *t, uint32_t key, const upb_tabent *e; if (t->t.entries == NULL) return false; for (e = upb_getentry(&t->t, upb_inthash(key)); true; e = e->next) { - if ((uint32_t)e->key.num == key) { - _upb_value_setval(v, e->val, t->t.ctype); + if ((uint32_t)e->key == key) { + _upb_value_setval(v, e->val.val, t->t.ctype); return true; } if (e->next == NULL) return false; @@ -871,42 +959,43 @@ UPB_INLINE bool upb_inttable_lookup32(const upb_inttable *t, uint32_t key, } } -// Exposed for testing only. +/* Exposed for testing only. */ bool upb_strtable_resize(upb_strtable *t, size_t size_lg2); /* Iterators ******************************************************************/ -// Iterators for int and string tables. We are subject to some kind of unusual -// design constraints: -// -// For high-level languages: -// - we must be able to guarantee that we don't crash or corrupt memory even if -// the program accesses an invalidated iterator. -// -// For C++11 range-based for: -// - iterators must be copyable -// - iterators must be comparable -// - it must be possible to construct an "end" value. -// -// Iteration order is undefined. -// -// Modifying the table invalidates iterators. upb_{str,int}table_done() is -// guaranteed to work even on an invalidated iterator, as long as the table it -// is iterating over has not been freed. Calling next() or accessing data from -// an invalidated iterator yields unspecified elements from the table, but it is -// guaranteed not to crash and to return real table elements (except when done() -// is true). +/* Iterators for int and string tables. We are subject to some kind of unusual + * design constraints: + * + * For high-level languages: + * - we must be able to guarantee that we don't crash or corrupt memory even if + * the program accesses an invalidated iterator. + * + * For C++11 range-based for: + * - iterators must be copyable + * - iterators must be comparable + * - it must be possible to construct an "end" value. + * + * Iteration order is undefined. + * + * Modifying the table invalidates iterators. upb_{str,int}table_done() is + * guaranteed to work even on an invalidated iterator, as long as the table it + * is iterating over has not been freed. Calling next() or accessing data from + * an invalidated iterator yields unspecified elements from the table, but it is + * guaranteed not to crash and to return real table elements (except when done() + * is true). */ /* upb_strtable_iter **********************************************************/ -// upb_strtable_iter i; -// upb_strtable_begin(&i, t); -// for(; !upb_strtable_done(&i); upb_strtable_next(&i)) { -// const char *key = upb_strtable_iter_key(&i); -// const upb_value val = upb_strtable_iter_value(&i); -// // ... -// } +/* upb_strtable_iter i; + * upb_strtable_begin(&i, t); + * for(; !upb_strtable_done(&i); upb_strtable_next(&i)) { + * const char *key = upb_strtable_iter_key(&i); + * const upb_value val = upb_strtable_iter_value(&i); + * // ... + * } + */ typedef struct { const upb_strtable *t; @@ -926,13 +1015,14 @@ bool upb_strtable_iter_isequal(const upb_strtable_iter *i1, /* upb_inttable_iter **********************************************************/ -// upb_inttable_iter i; -// upb_inttable_begin(&i, t); -// for(; !upb_inttable_done(&i); upb_inttable_next(&i)) { -// uintptr_t key = upb_inttable_iter_key(&i); -// upb_value val = upb_inttable_iter_value(&i); -// // ... -// } +/* upb_inttable_iter i; + * upb_inttable_begin(&i, t); + * for(; !upb_inttable_done(&i); upb_inttable_next(&i)) { + * uintptr_t key = upb_inttable_iter_key(&i); + * upb_value val = upb_inttable_iter_value(&i); + * // ... + * } + */ typedef struct { const upb_inttable *t; @@ -956,84 +1046,102 @@ bool upb_inttable_iter_isequal(const upb_inttable_iter *i1, #endif /* UPB_TABLE_H_ */ -// Reference tracking will check ref()/unref() operations to make sure the -// ref ownership is correct. Where possible it will also make tools like -// Valgrind attribute ref leaks to the code that took the leaked ref, not -// the code that originally created the object. -// -// Enabling this requires the application to define upb_lock()/upb_unlock() -// functions that acquire/release a global mutex (or #define UPB_THREAD_UNSAFE). -#ifndef NDEBUG -#define UPB_DEBUG_REFS -#endif +/* Reference tracking will check ref()/unref() operations to make sure the + * ref ownership is correct. Where possible it will also make tools like + * Valgrind attribute ref leaks to the code that took the leaked ref, not + * the code that originally created the object. + * + * Enabling this requires the application to define upb_lock()/upb_unlock() + * functions that acquire/release a global mutex (or #define UPB_THREAD_UNSAFE). + * For this reason we don't enable it by default, even in debug builds. + */ + +/* #define UPB_DEBUG_REFS */ #ifdef __cplusplus namespace upb { class RefCounted; } #endif -UPB_DECLARE_TYPE(upb::RefCounted, upb_refcounted); +UPB_DECLARE_TYPE(upb::RefCounted, upb_refcounted) struct upb_refcounted_vtbl; -UPB_DEFINE_CLASS0(upb::RefCounted, +#ifdef __cplusplus + +class upb::RefCounted { public: - // Returns true if the given object is frozen. + /* Returns true if the given object is frozen. */ bool IsFrozen() const; - // Increases the ref count, the new ref is owned by "owner" which must not - // already own a ref (and should not itself be a refcounted object if the ref - // could possibly be circular; see below). - // Thread-safe iff "this" is frozen. + /* Increases the ref count, the new ref is owned by "owner" which must not + * already own a ref (and should not itself be a refcounted object if the ref + * could possibly be circular; see below). + * Thread-safe iff "this" is frozen. */ void Ref(const void *owner) const; - // Release a ref that was acquired from upb_refcounted_ref() and collects any - // objects it can. + /* Release a ref that was acquired from upb_refcounted_ref() and collects any + * objects it can. */ void Unref(const void *owner) const; - // Moves an existing ref from "from" to "to", without changing the overall - // ref count. DonateRef(foo, NULL, owner) is the same as Ref(foo, owner), - // but "to" may not be NULL. + /* Moves an existing ref from "from" to "to", without changing the overall + * ref count. DonateRef(foo, NULL, owner) is the same as Ref(foo, owner), + * but "to" may not be NULL. */ void DonateRef(const void *from, const void *to) const; - // Verifies that a ref to the given object is currently held by the given - // owner. Only effective in UPB_DEBUG_REFS builds. + /* Verifies that a ref to the given object is currently held by the given + * owner. Only effective in UPB_DEBUG_REFS builds. */ void CheckRef(const void *owner) const; private: - UPB_DISALLOW_POD_OPS(RefCounted, upb::RefCounted); -, -UPB_DEFINE_STRUCT0(upb_refcounted, - // A single reference count shared by all objects in the group. + UPB_DISALLOW_POD_OPS(RefCounted, upb::RefCounted) +#else +struct upb_refcounted { +#endif + /* TODO(haberman): move the actual structure definition to structdefs.int.h. + * The only reason they are here is because inline functions need to see the + * definition of upb_handlers, which needs to see this definition. But we + * can change the upb_handlers inline functions to deal in raw offsets + * instead. + */ + + /* A single reference count shared by all objects in the group. */ uint32_t *group; - // A singly-linked list of all objects in the group. + /* A singly-linked list of all objects in the group. */ upb_refcounted *next; - // Table of function pointers for this type. + /* Table of function pointers for this type. */ const struct upb_refcounted_vtbl *vtbl; - // Maintained only when mutable, this tracks the number of refs (but not - // ref2's) to this object. *group should be the sum of all individual_count - // in the group. + /* Maintained only when mutable, this tracks the number of refs (but not + * ref2's) to this object. *group should be the sum of all individual_count + * in the group. */ uint32_t individual_count; bool is_frozen; #ifdef UPB_DEBUG_REFS - upb_inttable *refs; // Maps owner -> trackedref for incoming refs. - upb_inttable *ref2s; // Set of targets for outgoing ref2s. + upb_inttable *refs; /* Maps owner -> trackedref for incoming refs. */ + upb_inttable *ref2s; /* Set of targets for outgoing ref2s. */ +#endif +}; + +#ifdef UPB_DEBUG_REFS +#define UPB_REFCOUNT_INIT(refs, ref2s) \ + {&static_refcount, NULL, NULL, 0, true, refs, ref2s} +#else +#define UPB_REFCOUNT_INIT(refs, ref2s) {&static_refcount, NULL, NULL, 0, true} #endif -)); -UPB_BEGIN_EXTERN_C // { +UPB_BEGIN_EXTERN_C -// It is better to use tracked refs when possible, for the extra debugging -// capability. But if this is not possible (because you don't have easy access -// to a stable pointer value that is associated with the ref), you can pass -// UPB_UNTRACKED_REF instead. +/* It is better to use tracked refs when possible, for the extra debugging + * capability. But if this is not possible (because you don't have easy access + * to a stable pointer value that is associated with the ref), you can pass + * UPB_UNTRACKED_REF instead. */ extern const void *UPB_UNTRACKED_REF; -// Native C API. +/* Native C API. */ bool upb_refcounted_isfrozen(const upb_refcounted *r); void upb_refcounted_ref(const upb_refcounted *r, const void *owner); void upb_refcounted_unref(const upb_refcounted *r, const void *owner); @@ -1041,37 +1149,70 @@ void upb_refcounted_donateref( const upb_refcounted *r, const void *from, const void *to); void upb_refcounted_checkref(const upb_refcounted *r, const void *owner); +#define UPB_REFCOUNTED_CMETHODS(type, upcastfunc) \ + UPB_INLINE bool type ## _isfrozen(const type *v) { \ + return upb_refcounted_isfrozen(upcastfunc(v)); \ + } \ + UPB_INLINE void type ## _ref(const type *v, const void *owner) { \ + upb_refcounted_ref(upcastfunc(v), owner); \ + } \ + UPB_INLINE void type ## _unref(const type *v, const void *owner) { \ + upb_refcounted_unref(upcastfunc(v), owner); \ + } \ + UPB_INLINE void type ## _donateref(const type *v, const void *from, const void *to) { \ + upb_refcounted_donateref(upcastfunc(v), from, to); \ + } \ + UPB_INLINE void type ## _checkref(const type *v, const void *owner) { \ + upb_refcounted_checkref(upcastfunc(v), owner); \ + } + +#define UPB_REFCOUNTED_CPPMETHODS \ + bool IsFrozen() const { \ + return upb::upcast_to(this)->IsFrozen(); \ + } \ + void Ref(const void *owner) const { \ + return upb::upcast_to(this)->Ref(owner); \ + } \ + void Unref(const void *owner) const { \ + return upb::upcast_to(this)->Unref(owner); \ + } \ + void DonateRef(const void *from, const void *to) const { \ + return upb::upcast_to(this)->DonateRef(from, to); \ + } \ + void CheckRef(const void *owner) const { \ + return upb::upcast_to(this)->CheckRef(owner); \ + } -// Internal-to-upb Interface /////////////////////////////////////////////////// +/* Internal-to-upb Interface **************************************************/ typedef void upb_refcounted_visit(const upb_refcounted *r, const upb_refcounted *subobj, void *closure); struct upb_refcounted_vtbl { - // Must visit all subobjects that are currently ref'd via upb_refcounted_ref2. - // Must be longjmp()-safe. + /* Must visit all subobjects that are currently ref'd via upb_refcounted_ref2. + * Must be longjmp()-safe. */ void (*visit)(const upb_refcounted *r, upb_refcounted_visit *visit, void *c); - // Must free the object and release all references to other objects. + /* Must free the object and release all references to other objects. */ void (*free)(upb_refcounted *r); }; -// Initializes the refcounted with a single ref for the given owner. Returns -// false if memory could not be allocated. +/* Initializes the refcounted with a single ref for the given owner. Returns + * false if memory could not be allocated. */ bool upb_refcounted_init(upb_refcounted *r, const struct upb_refcounted_vtbl *vtbl, const void *owner); -// Adds a ref from one refcounted object to another ("from" must not already -// own a ref). These refs may be circular; cycles will be collected correctly -// (if conservatively). These refs do not need to be freed in from's free() -// function. +/* Adds a ref from one refcounted object to another ("from" must not already + * own a ref). These refs may be circular; cycles will be collected correctly + * (if conservatively). These refs do not need to be freed in from's free() + * function. */ void upb_refcounted_ref2(const upb_refcounted *r, upb_refcounted *from); -// Removes a ref that was acquired from upb_refcounted_ref2(), and collects any -// object it can. This is only necessary when "from" no longer points to "r", -// and not from from's "free" function. +/* Removes a ref that was acquired from upb_refcounted_ref2(), and collects any + * object it can. This is only necessary when "from" no longer points to "r", + * and not from from's "free" function. */ void upb_refcounted_unref2(const upb_refcounted *r, upb_refcounted *from); #define upb_ref2(r, from) \ @@ -1079,37 +1220,30 @@ void upb_refcounted_unref2(const upb_refcounted *r, upb_refcounted *from); #define upb_unref2(r, from) \ upb_refcounted_unref2((const upb_refcounted*)r, (upb_refcounted*)from) -// Freezes all mutable object reachable by ref2() refs from the given roots. -// This will split refcounting groups into precise SCC groups, so that -// refcounting of frozen objects can be more aggressive. If memory allocation -// fails, or if more than 2**31 mutable objects are reachable from "roots", or -// if the maximum depth of the graph exceeds "maxdepth", false is returned and -// the objects are unchanged. -// -// After this operation succeeds, the objects are frozen/const, and may not be -// used through non-const pointers. In particular, they may not be passed as -// the second parameter of upb_refcounted_{ref,unref}2(). On the upside, all -// operations on frozen refcounteds are threadsafe, and objects will be freed -// at the precise moment that they become unreachable. -// -// Caller must own refs on each object in the "roots" list. +/* Freezes all mutable object reachable by ref2() refs from the given roots. + * This will split refcounting groups into precise SCC groups, so that + * refcounting of frozen objects can be more aggressive. If memory allocation + * fails, or if more than 2**31 mutable objects are reachable from "roots", or + * if the maximum depth of the graph exceeds "maxdepth", false is returned and + * the objects are unchanged. + * + * After this operation succeeds, the objects are frozen/const, and may not be + * used through non-const pointers. In particular, they may not be passed as + * the second parameter of upb_refcounted_{ref,unref}2(). On the upside, all + * operations on frozen refcounteds are threadsafe, and objects will be freed + * at the precise moment that they become unreachable. + * + * Caller must own refs on each object in the "roots" list. */ bool upb_refcounted_freeze(upb_refcounted *const*roots, int n, upb_status *s, int maxdepth); -// Shared by all compiled-in refcounted objects. +/* Shared by all compiled-in refcounted objects. */ extern uint32_t static_refcount; -UPB_END_EXTERN_C // } - -#ifdef UPB_DEBUG_REFS -#define UPB_REFCOUNT_INIT(refs, ref2s) \ - {&static_refcount, NULL, NULL, 0, true, refs, ref2s} -#else -#define UPB_REFCOUNT_INIT(refs, ref2s) {&static_refcount, NULL, NULL, 0, true} -#endif +UPB_END_EXTERN_C #ifdef __cplusplus -// C++ Wrappers. +/* C++ Wrappers. */ namespace upb { inline bool RefCounted::IsFrozen() const { return upb_refcounted_isfrozen(this); @@ -1126,10 +1260,10 @@ inline void RefCounted::DonateRef(const void *from, const void *to) const { inline void RefCounted::CheckRef(const void *owner) const { upb_refcounted_checkref(this, owner); } -} // namespace upb +} /* namespace upb */ #endif -#endif // UPB_REFCOUNT_H_ +#endif /* UPB_REFCOUNT_H_ */ #ifdef __cplusplus #include @@ -1145,114 +1279,90 @@ class OneofDef; } #endif -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. -#define UPB_MAX_FIELDNUMBER ((1 << 29) - 1) +UPB_DECLARE_DERIVED_TYPE(upb::Def, upb::RefCounted, upb_def, upb_refcounted) -// The maximum message depth that the type graph can have. This is a resource -// limit for the C stack since we sometimes need to recursively traverse the -// graph. Cycles are ok; the traversal will stop when it detects a cycle, but -// we must hit the cycle before the maximum depth is reached. -// -// If having a single static limit is too inflexible, we can add another variant -// of Def::Freeze that allows specifying this as a parameter. +/* The maximum message depth that the type graph can have. This is a resource + * limit for the C stack since we sometimes need to recursively traverse the + * graph. Cycles are ok; the traversal will stop when it detects a cycle, but + * we must hit the cycle before the maximum depth is reached. + * + * If having a single static limit is too inflexible, we can add another variant + * of Def::Freeze that allows specifying this as a parameter. */ #define UPB_MAX_MESSAGE_DEPTH 64 /* upb::Def: base class for defs *********************************************/ -// All the different kind of defs we support. These correspond 1:1 with -// declarations in a .proto file. +/* All the different kind of defs we support. These correspond 1:1 with + * declarations in a .proto file. */ 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_DEF_SERVICE, /* Not yet implemented. */ + UPB_DEF_ANY = -1 /* Wildcard for upb_symtab_get*() */ } upb_deftype_t; -// The base class of all defs. Its base is upb::RefCounted (use upb::upcast() -// to convert). -UPB_DEFINE_CLASS1(upb::Def, upb::RefCounted, +#ifdef __cplusplus + +/* The base class of all defs. Its base is upb::RefCounted (use upb::upcast() + * to convert). */ +class upb::Def { public: typedef upb_deftype_t Type; Def* Dup(const void *owner) const; - // 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; + /* upb::RefCounted methods like Ref()/Unref(). */ + UPB_REFCOUNTED_CPPMETHODS Type def_type() const; - // "fullname" is the def's fully-qualified name (eg. foo.bar.Message). + /* "fullname" is the def's fully-qualified name (eg. foo.bar.Message). */ const char *full_name() const; - // The def must be mutable. Caller retains ownership of fullname. Defs are - // not required to have a name; if a def has no name when it is frozen, it - // will remain an anonymous def. On failure, returns false and details in "s" - // if non-NULL. + /* The def must be mutable. Caller retains ownership of fullname. Defs are + * not required to have a name; if a def has no name when it is frozen, it + * will remain an anonymous def. On failure, returns false and details in "s" + * if non-NULL. */ bool set_full_name(const char* fullname, upb::Status* s); bool set_full_name(const std::string &fullname, upb::Status* s); - // Freezes the given defs; this validates all constraints and marks the defs - // as frozen (read-only). "defs" may not contain any fielddefs, but fields - // of any msgdefs will be frozen. - // - // Symbolic references to sub-types and enum defaults must have already been - // resolved. Any mutable defs reachable from any of "defs" must also be in - // the list; more formally, "defs" must be a transitive closure of mutable - // defs. - // - // After this operation succeeds, the finalized defs must only be accessed - // through a const pointer! + /* Freezes the given defs; this validates all constraints and marks the defs + * as frozen (read-only). "defs" may not contain any fielddefs, but fields + * of any msgdefs will be frozen. + * + * Symbolic references to sub-types and enum defaults must have already been + * resolved. Any mutable defs reachable from any of "defs" must also be in + * the list; more formally, "defs" must be a transitive closure of mutable + * defs. + * + * After this operation succeeds, the finalized defs must only be accessed + * through a const pointer! */ static bool Freeze(Def* const* defs, int n, Status* status); static bool Freeze(const std::vector& defs, Status* status); private: - UPB_DISALLOW_POD_OPS(Def, upb::Def); -, -UPB_DEFINE_STRUCT(upb_def, upb_refcounted, - const char *fullname; - upb_deftype_t type : 8; - // Used as a flag during the def's mutable stage. Must be false unless - // it is currently being used by a function on the stack. This allows - // us to easily determine which defs were passed into the function's - // current invocation. - bool came_from_user; -)); + UPB_DISALLOW_POD_OPS(Def, upb::Def) +}; -#define UPB_DEF_INIT(name, type, refs, ref2s) \ - { UPB_REFCOUNT_INIT(refs, ref2s), name, type, false } +#endif /* __cplusplus */ -UPB_BEGIN_EXTERN_C // { +UPB_BEGIN_EXTERN_C -// Native C API. +/* Native C API. */ upb_def *upb_def_dup(const upb_def *def, const void *owner); -// From upb_refcounted. -bool upb_def_isfrozen(const upb_def *def); -void upb_def_ref(const upb_def *def, const void *owner); -void upb_def_unref(const upb_def *def, const void *owner); -void upb_def_donateref(const upb_def *def, const void *from, const void *to); -void upb_def_checkref(const upb_def *def, const void *owner); +/* Include upb_refcounted methods like upb_def_ref()/upb_def_unref(). */ +UPB_REFCOUNTED_CMETHODS(upb_def, upb_def_upcast) upb_deftype_t upb_def_type(const upb_def *d); const char *upb_def_fullname(const upb_def *d); bool upb_def_setfullname(upb_def *def, const char *fullname, upb_status *s); bool upb_def_freeze(upb_def *const *defs, int n, upb_status *s); -UPB_END_EXTERN_C // } +UPB_END_EXTERN_C /* upb::Def casts *************************************************************/ @@ -1285,14 +1395,14 @@ UPB_END_EXTERN_C // } inline const cpptype *dyn_cast(Def * def) { \ return upb_dyncast_##cname(def); \ } \ - } // namespace upb + } /* namespace upb */ #else #define UPB_CPP_CASTS(cname, cpptype) -#endif +#endif /* __cplusplus */ -// Dynamic casts, for determining if a def is of a particular type at runtime. -// Downcasts, for when some wants to assert that a def is of a particular type. -// These are only checked if we are building debug. +/* Dynamic casts, for determining if a def is of a particular type at runtime. + * Downcasts, for when some wants to assert that a def is of a particular type. + * These are only checked if we are building debug. */ #define UPB_DEF_CASTS(lower, upper, cpptype) \ UPB_INLINE const upb_##lower *upb_dyncast_##lower(const upb_def *def) { \ if (upb_def_type(def) != UPB_DEF_##upper) return NULL; \ @@ -1311,16 +1421,30 @@ UPB_END_EXTERN_C // } UPB_CPP_CASTS(lower, cpptype) #define UPB_DEFINE_DEF(cppname, lower, upper, cppmethods, members) \ - UPB_DEFINE_CLASS2(cppname, upb::Def, upb::RefCounted, UPB_QUOTE(cppmethods), \ + UPB_DEFINE_CLASS2(cppname, upb::Def, upb::RefCounted, cppmethods, \ members) \ UPB_DEF_CASTS(lower, upper, cppname) +#define UPB_DECLARE_DEF_TYPE(cppname, lower, upper) \ + UPB_DECLARE_DERIVED_TYPE2(cppname, upb::Def, upb::RefCounted, \ + upb_ ## lower, upb_def, upb_refcounted) \ + UPB_DEF_CASTS(lower, upper, cppname) + +UPB_DECLARE_DEF_TYPE(upb::FieldDef, fielddef, FIELD) +UPB_DECLARE_DEF_TYPE(upb::MessageDef, msgdef, MSG) +UPB_DECLARE_DEF_TYPE(upb::EnumDef, enumdef, ENUM) +UPB_DECLARE_DEF_TYPE(upb::OneofDef, oneofdef, ONEOF) + +#undef UPB_DECLARE_DEF_TYPE +#undef UPB_DEF_CASTS +#undef UPB_CPP_CASTS + /* upb::FieldDef **************************************************************/ -// The types a field can have. Note that this list is not identical to the -// types defined in descriptor.proto, which gives INT32 and SINT32 separate -// types (we distinguish the two with the "integer encoding" enum below). +/* The types a field can have. Note that this list is not identical to the + * types defined in descriptor.proto, which gives INT32 and SINT32 separate + * types (we distinguish the two with the "integer encoding" enum below). */ typedef enum { UPB_TYPE_FLOAT = 1, UPB_TYPE_DOUBLE = 2, @@ -1328,29 +1452,29 @@ typedef enum { UPB_TYPE_STRING = 4, UPB_TYPE_BYTES = 5, UPB_TYPE_MESSAGE = 6, - UPB_TYPE_ENUM = 7, // Enum values are int32. + UPB_TYPE_ENUM = 7, /* Enum values are int32. */ UPB_TYPE_INT32 = 8, UPB_TYPE_UINT32 = 9, UPB_TYPE_INT64 = 10, - UPB_TYPE_UINT64 = 11, + UPB_TYPE_UINT64 = 11 } upb_fieldtype_t; -// The repeated-ness of each field; this matches descriptor.proto. +/* The repeated-ness of each field; this matches descriptor.proto. */ typedef enum { UPB_LABEL_OPTIONAL = 1, UPB_LABEL_REQUIRED = 2, - UPB_LABEL_REPEATED = 3, + UPB_LABEL_REPEATED = 3 } upb_label_t; -// How integers should be encoded in serializations that offer multiple -// integer encoding methods. +/* How integers should be encoded in serializations that offer multiple + * integer encoding methods. */ typedef enum { UPB_INTFMT_VARIABLE = 1, UPB_INTFMT_FIXED = 2, - UPB_INTFMT_ZIGZAG = 3, // Only for signed types (INT32/INT64). + UPB_INTFMT_ZIGZAG = 3 /* Only for signed types (INT32/INT64). */ } upb_intfmt_t; -// Descriptor types, as defined in descriptor.proto. +/* Descriptor types, as defined in descriptor.proto. */ typedef enum { UPB_DESCRIPTOR_TYPE_DOUBLE = 1, UPB_DESCRIPTOR_TYPE_FLOAT = 2, @@ -1369,128 +1493,129 @@ typedef enum { UPB_DESCRIPTOR_TYPE_SFIXED32 = 15, UPB_DESCRIPTOR_TYPE_SFIXED64 = 16, UPB_DESCRIPTOR_TYPE_SINT32 = 17, - UPB_DESCRIPTOR_TYPE_SINT64 = 18, + UPB_DESCRIPTOR_TYPE_SINT64 = 18 } upb_descriptortype_t; +/* Maximum field number allowed for FieldDefs. This is an inherent limit of the + * protobuf wire format. */ +#define UPB_MAX_FIELDNUMBER ((1 << 29) - 1) + +#ifdef __cplusplus -// A upb_fielddef describes a single field in a message. It is most often -// found as a part of a upb_msgdef, but can also stand alone to represent -// an extension. -// -// Its base class is upb::Def (use upb::upcast() to convert). -UPB_DEFINE_DEF(upb::FieldDef, fielddef, FIELD, +/* A upb_fielddef describes a single field in a message. It is most often + * found as a part of a upb_msgdef, but can also stand alone to represent + * an extension. + * + * Its base class is upb::Def (use upb::upcast() to convert). */ +class upb::FieldDef { public: typedef upb_fieldtype_t Type; typedef upb_label_t Label; typedef upb_intfmt_t IntegerFormat; typedef upb_descriptortype_t DescriptorType; - // These return true if the given value is a valid member of the enumeration. + /* These return true if the given value is a valid member of the enumeration. */ static bool CheckType(int32_t val); static bool CheckLabel(int32_t val); static bool CheckDescriptorType(int32_t val); static bool CheckIntegerFormat(int32_t val); - // These convert to the given enumeration; they require that the value is - // valid. + /* These convert to the given enumeration; they require that the value is + * valid. */ static Type ConvertType(int32_t val); static Label ConvertLabel(int32_t val); static DescriptorType ConvertDescriptorType(int32_t val); static IntegerFormat ConvertIntegerFormat(int32_t val); - // Returns NULL if memory allocation failed. + /* Returns NULL if memory allocation failed. */ static reffed_ptr New(); - // Duplicates the given field, returning NULL if memory allocation failed. - // When a fielddef is duplicated, the subdef (if any) is made symbolic if it - // wasn't already. If the subdef is set but has no name (which is possible - // since msgdefs are not required to have a name) the new fielddef's subdef - // will be unset. + /* Duplicates the given field, returning NULL if memory allocation failed. + * When a fielddef is duplicated, the subdef (if any) is made symbolic if it + * wasn't already. If the subdef is set but has no name (which is possible + * since msgdefs are not required to have a name) the new fielddef's subdef + * will be unset. */ FieldDef* Dup(const void* owner) const; - // 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; + /* upb::RefCounted methods like Ref()/Unref(). */ + UPB_REFCOUNTED_CPPMETHODS - // Functionality from upb::Def. + /* Functionality from upb::Def. */ const char* full_name() const; - bool type_is_set() const; // Whether set_[descriptor_]type() has been called. - Type type() const; // Requires that type_is_set() == true. - Label label() const; // Defaults to UPB_LABEL_OPTIONAL. - const char* name() const; // NULL if uninitialized. - uint32_t number() const; // Returns 0 if uninitialized. + bool type_is_set() const; /* set_[descriptor_]type() has been called? */ + Type type() const; /* Requires that type_is_set() == true. */ + Label label() const; /* Defaults to UPB_LABEL_OPTIONAL. */ + const char* name() const; /* NULL if uninitialized. */ + uint32_t number() const; /* Returns 0 if uninitialized. */ bool is_extension() const; - // For UPB_TYPE_MESSAGE fields only where is_tag_delimited() == false, - // indicates whether this field should have lazy parsing handlers that yield - // the unparsed string for the submessage. - // - // TODO(haberman): I think we want to move this into a FieldOptions container - // when we add support for custom options (the FieldOptions struct will - // contain both regular FieldOptions like "lazy" *and* custom options). + /* For UPB_TYPE_MESSAGE fields only where is_tag_delimited() == false, + * indicates whether this field should have lazy parsing handlers that yield + * the unparsed string for the submessage. + * + * TODO(haberman): I think we want to move this into a FieldOptions container + * when we add support for custom options (the FieldOptions struct will + * contain both regular FieldOptions like "lazy" *and* custom options). */ bool lazy() const; - // For non-string, non-submessage fields, this indicates whether binary - // protobufs are encoded in packed or non-packed format. - // - // TODO(haberman): see note above about putting options like this into a - // FieldOptions container. + /* For non-string, non-submessage fields, this indicates whether binary + * protobufs are encoded in packed or non-packed format. + * + * TODO(haberman): see note above about putting options like this into a + * FieldOptions container. */ bool packed() const; - // An integer that can be used as an index into an array of fields for - // whatever message this field belongs to. Guaranteed to be less than - // f->containing_type()->field_count(). May only be accessed once the def has - // been finalized. + /* An integer that can be used as an index into an array of fields for + * whatever message this field belongs to. Guaranteed to be less than + * f->containing_type()->field_count(). May only be accessed once the def has + * been finalized. */ int index() const; - // The MessageDef to which this field belongs. - // - // If this field has been added to a MessageDef, that message can be retrieved - // directly (this is always the case for frozen FieldDefs). - // - // If the field has not yet been added to a MessageDef, you can set the name - // of the containing type symbolically instead. This is mostly useful for - // extensions, where the extension is declared separately from the message. + /* The MessageDef to which this field belongs. + * + * If this field has been added to a MessageDef, that message can be retrieved + * directly (this is always the case for frozen FieldDefs). + * + * If the field has not yet been added to a MessageDef, you can set the name + * of the containing type symbolically instead. This is mostly useful for + * extensions, where the extension is declared separately from the message. */ 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. + /* 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 - // descriptor_type() is a function of type(), integer_format(), and - // is_tag_delimited(). Likewise set_descriptor_type() sets all three - // appropriately. + /* 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 + * descriptor_type() is a function of type(), integer_format(), and + * is_tag_delimited(). Likewise set_descriptor_type() sets all three + * appropriately. */ DescriptorType descriptor_type() const; - // Convenient field type tests. + /* Convenient field type tests. */ bool IsSubMessage() const; 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. + /* How integers are encoded. Only meaningful for integer types. + * Defaults to UPB_INTFMT_VARIABLE, and is reset when "type" changes. */ IntegerFormat integer_format() const; - // Whether a submessage field is tag-delimited or not (if false, then - // length-delimited). May only be set when type() == UPB_TYPE_MESSAGE. + /* Whether a submessage field is tag-delimited or not (if false, then + * length-delimited). May only be set when type() == UPB_TYPE_MESSAGE. */ bool is_tag_delimited() const; - // Returns the non-string default value for this fielddef, which may either - // be something the client set explicitly or the "default default" (0 for - // numbers, empty for strings). The field's type indicates the type of the - // returned value, except for enum fields that are still mutable. - // - // Requires that the given function matches the field's current type. + /* Returns the non-string default value for this fielddef, which may either + * be something the client set explicitly or the "default default" (0 for + * numbers, empty for strings). The field's type indicates the type of the + * returned value, except for enum fields that are still mutable. + * + * Requires that the given function matches the field's current type. */ int64_t default_int64() const; int32_t default_int32() const; uint64_t default_uint64() const; @@ -1499,83 +1624,81 @@ UPB_DEFINE_DEF(upb::FieldDef, fielddef, FIELD, float default_float() const; double default_double() const; - // The resulting string is always NULL-terminated. If non-NULL, the length - // will be stored in *len. + /* The resulting string is always NULL-terminated. If non-NULL, the length + * will be stored in *len. */ const char *default_string(size_t* len) const; - // For frozen UPB_TYPE_ENUM fields, enum defaults can always be read as either - // string or int32, and both of these methods will always return true. - // - // For mutable UPB_TYPE_ENUM fields, the story is a bit more complicated. - // Enum defaults are unusual. They can be specified either as string or int32, - // but to be valid the enum must have that value as a member. And if no - // default is specified, the "default default" comes from the EnumDef. - // - // We allow reading the default as either an int32 or a string, but only if - // we have a meaningful value to report. We have a meaningful value if it was - // set explicitly, or if we could get the "default default" from the EnumDef. - // Also if you explicitly set the name and we find the number in the EnumDef + /* For frozen UPB_TYPE_ENUM fields, enum defaults can always be read as either + * string or int32, and both of these methods will always return true. + * + * For mutable UPB_TYPE_ENUM fields, the story is a bit more complicated. + * Enum defaults are unusual. They can be specified either as string or int32, + * but to be valid the enum must have that value as a member. And if no + * default is specified, the "default default" comes from the EnumDef. + * + * We allow reading the default as either an int32 or a string, but only if + * we have a meaningful value to report. We have a meaningful value if it was + * set explicitly, or if we could get the "default default" from the EnumDef. + * Also if you explicitly set the name and we find the number in the EnumDef */ bool EnumHasStringDefault() const; bool EnumHasInt32Default() const; - // Submessage and enum fields must reference a "subdef", which is the - // upb::MessageDef or upb::EnumDef that defines their type. Note that when - // the FieldDef is mutable it may not have a subdef *yet*, but this function - // still returns true to indicate that the field's type requires a subdef. + /* Submessage and enum fields must reference a "subdef", which is the + * upb::MessageDef or upb::EnumDef that defines their type. Note that when + * the FieldDef is mutable it may not have a subdef *yet*, but this function + * still returns true to indicate that the field's type requires a subdef. */ bool HasSubDef() const; - // Returns the enum or submessage def for this field, if any. The field's - // type must match (ie. you may only call enum_subdef() for fields where - // type() == UPB_TYPE_ENUM). Returns NULL if the subdef has not been set or - // is currently set symbolically. + /* Returns the enum or submessage def for this field, if any. The field's + * type must match (ie. you may only call enum_subdef() for fields where + * type() == UPB_TYPE_ENUM). Returns NULL if the subdef has not been set or + * is currently set symbolically. */ const EnumDef* enum_subdef() const; const MessageDef* message_subdef() const; - // Returns the generic subdef for this field. Requires that HasSubDef() (ie. - // only works for UPB_TYPE_ENUM and UPB_TYPE_MESSAGE fields). + /* Returns the generic subdef for this field. Requires that HasSubDef() (ie. + * only works for UPB_TYPE_ENUM and UPB_TYPE_MESSAGE fields). */ const Def* subdef() const; - // Returns the symbolic name of the subdef. If the subdef is currently set - // unresolved (ie. set symbolically) returns the symbolic name. If it has - // been resolved to a specific subdef, returns the name from that subdef. + /* Returns the symbolic name of the subdef. If the subdef is currently set + * unresolved (ie. set symbolically) returns the symbolic name. If it has + * been resolved to a specific subdef, returns the name from that subdef. */ const char* subdef_name() const; - ////////////////////////////////////////////////////////////////////////////// - // Setters (non-const methods), only valid for mutable FieldDefs! - ////////////////////////////////////////////////////////////////////////////// + /* Setters (non-const methods), only valid for mutable FieldDefs! ***********/ bool set_full_name(const char* fullname, upb::Status* s); bool set_full_name(const std::string& fullname, upb::Status* s); - // This may only be called if containing_type() == NULL (ie. the field has not - // been added to a message yet). + /* This may only be called if containing_type() == NULL (ie. the field has not + * been added to a message yet). */ bool set_containing_type_name(const char *name, Status* status); bool set_containing_type_name(const std::string& name, Status* status); - // Defaults to false. When we freeze, we ensure that this can only be true - // for length-delimited message fields. Prior to freezing this can be true or - // false with no restrictions. + /* Defaults to false. When we freeze, we ensure that this can only be true + * for length-delimited message fields. Prior to freezing this can be true or + * false with no restrictions. */ void set_lazy(bool lazy); - // Defaults to true. Sets whether this field is encoded in packed format. + /* Defaults to true. Sets whether this field is encoded in packed format. */ void set_packed(bool packed); - // "type" or "descriptor_type" MUST be set explicitly before the fielddef is - // finalized. These setters require that the enum value is valid; if the - // value did not come directly from an enum constant, the caller should - // validate it first with the functions above (CheckFieldType(), etc). + /* "type" or "descriptor_type" MUST be set explicitly before the fielddef is + * finalized. These setters require that the enum value is valid; if the + * value did not come directly from an enum constant, the caller should + * validate it first with the functions above (CheckFieldType(), etc). */ void set_type(Type type); void set_label(Label label); void set_descriptor_type(DescriptorType type); void set_is_extension(bool is_extension); - // "number" and "name" must be set before the FieldDef is added to a - // MessageDef, and may not be set after that. - // - // "name" is the same as full_name()/set_full_name(), but since fielddefs - // most often use simple, non-qualified names, we provide this accessor - // also. Generally only extensions will want to think of this name as - // fully-qualified. + /* "number" and "name" must be set before the FieldDef is added to a + * MessageDef, and may not be set after that. + * + * "name" is the same as full_name()/set_full_name(), but since fielddefs + * most often use simple, non-qualified names, we provide this accessor + * also. Generally only extensions will want to think of this name as + * fully-qualified. */ bool set_number(uint32_t number, upb::Status* s); bool set_name(const char* name, upb::Status* s); bool set_name(const std::string& name, upb::Status* s); @@ -1583,12 +1706,12 @@ UPB_DEFINE_DEF(upb::FieldDef, fielddef, FIELD, void set_integer_format(IntegerFormat format); bool set_tag_delimited(bool tag_delimited, upb::Status* s); - // Sets default value for the field. The call must exactly match the type - // of the field. Enum fields may use either setint32 or setstring to set - // the default numerically or symbolically, respectively, but symbolic - // defaults must be resolved before finalizing (see ResolveEnumDefault()). - // - // Changing the type of a field will reset its default. + /* Sets default value for the field. The call must exactly match the type + * of the field. Enum fields may use either setint32 or setstring to set + * the default numerically or symbolically, respectively, but symbolic + * defaults must be resolved before finalizing (see ResolveEnumDefault()). + * + * Changing the type of a field will reset its default. */ void set_default_int64(int64_t val); void set_default_int32(int32_t val); void set_default_uint64(uint64_t val); @@ -1600,15 +1723,15 @@ UPB_DEFINE_DEF(upb::FieldDef, fielddef, FIELD, bool set_default_string(const std::string &str, Status *s); void set_default_cstr(const char *str, Status *s); - // Before a fielddef is frozen, its subdef may be set either directly (with a - // upb::Def*) or symbolically. Symbolic refs must be resolved before the - // containing msgdef can be frozen (see upb_resolve() above). upb always - // guarantees that any def reachable from a live def will also be kept alive. - // - // Both methods require that upb_hassubdef(f) (so the type must be set prior - // to calling these methods). Returns false if this is not the case, or if - // the given subdef is not of the correct type. The subdef is reset if the - // field's type is changed. The subdef can be set to NULL to clear it. + /* Before a fielddef is frozen, its subdef may be set either directly (with a + * upb::Def*) or symbolically. Symbolic refs must be resolved before the + * containing msgdef can be frozen (see upb_resolve() above). upb always + * guarantees that any def reachable from a live def will also be kept alive. + * + * Both methods require that upb_hassubdef(f) (so the type must be set prior + * to calling these methods). Returns false if this is not the case, or if + * the given subdef is not of the correct type. The subdef is reset if the + * field's type is changed. The subdef can be set to NULL to clear it. */ bool set_subdef(const Def* subdef, Status* s); bool set_enum_subdef(const EnumDef* subdef, Status* s); bool set_message_subdef(const MessageDef* subdef, Status* s); @@ -1616,66 +1739,21 @@ UPB_DEFINE_DEF(upb::FieldDef, fielddef, FIELD, bool set_subdef_name(const std::string &name, Status* s); private: - UPB_DISALLOW_POD_OPS(FieldDef, upb::FieldDef); -, -UPB_DEFINE_STRUCT(upb_fielddef, upb_def, - union { - int64_t sint; - uint64_t uint; - double dbl; - float flt; - void *bytes; - } defaultval; - union { - const upb_msgdef *def; // If !msg_is_symbolic. - char *name; // If msg_is_symbolic. - } msg; - union { - const upb_def *def; // If !subdef_is_symbolic. - char *name; // If subdef_is_symbolic. - } 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_; - bool lazy_; - bool packed_; - upb_intfmt_t intfmt; - bool tagdelim; - upb_fieldtype_t type_; - upb_label_t label_; - uint32_t number_; - uint32_t selector_base; // Used to index into a upb::Handlers table. - uint32_t index_; -)); + UPB_DISALLOW_POD_OPS(FieldDef, upb::FieldDef) +}; -#define UPB_FIELDDEF_INIT(label, type, intfmt, tagdelim, is_extension, lazy, \ - packed, name, num, msgdef, subdef, selector_base, \ - index, defaultval, refs, ref2s) \ - { \ - UPB_DEF_INIT(name, UPB_DEF_FIELD, refs, ref2s), defaultval, {msgdef}, \ - {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 \ - } +# endif /* defined(__cplusplus) */ -UPB_BEGIN_EXTERN_C // { +UPB_BEGIN_EXTERN_C -// Native C API. +/* Native C API. */ upb_fielddef *upb_fielddef_new(const void *owner); upb_fielddef *upb_fielddef_dup(const upb_fielddef *f, const void *owner); -// From upb_refcounted. -bool upb_fielddef_isfrozen(const upb_fielddef *f); -void upb_fielddef_ref(const upb_fielddef *f, const void *owner); -void upb_fielddef_unref(const upb_fielddef *f, const void *owner); -void upb_fielddef_donateref(const upb_fielddef *f, const void *from, - const void *to); -void upb_fielddef_checkref(const upb_fielddef *f, const void *owner); +/* Include upb_refcounted methods like upb_fielddef_ref(). */ +UPB_REFCOUNTED_CMETHODS(upb_fielddef, upb_fielddef_upcast2) -// From upb_def. +/* Methods from upb_def. */ const char *upb_fielddef_fullname(const upb_fielddef *f); bool upb_fielddef_setfullname(upb_fielddef *f, const char *fullname, upb_status *s); @@ -1754,7 +1832,7 @@ bool upb_fielddef_checktype(int32_t type); bool upb_fielddef_checkdescriptortype(int32_t type); bool upb_fielddef_checkintfmt(int32_t fmt); -UPB_END_EXTERN_C // } +UPB_END_EXTERN_C /* upb::MessageDef ************************************************************/ @@ -1762,66 +1840,64 @@ UPB_END_EXTERN_C // } typedef upb_inttable_iter upb_msg_field_iter; typedef upb_strtable_iter upb_msg_oneof_iter; -// Structure that describes a single .proto message type. -// -// Its base class is upb::Def (use upb::upcast() to convert). -UPB_DEFINE_DEF(upb::MessageDef, msgdef, MSG, UPB_QUOTE( +#ifdef __cplusplus + +/* Structure that describes a single .proto message type. + * + * Its base class is upb::Def (use upb::upcast() to convert). */ +class upb::MessageDef { public: - // Returns NULL if memory allocation failed. + /* Returns NULL if memory allocation failed. */ static reffed_ptr 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; + /* upb::RefCounted methods like Ref()/Unref(). */ + UPB_REFCOUNTED_CPPMETHODS - // Functionality from upb::Def. + /* Functionality from upb::Def. */ const char* full_name() const; bool set_full_name(const char* fullname, Status* s); bool set_full_name(const std::string& fullname, Status* s); - // Call to freeze this MessageDef. - // WARNING: this will fail if this message has any unfrozen submessages! - // Messages with cycles must be frozen as a batch using upb::Def::Freeze(). + /* Call to freeze this MessageDef. + * WARNING: this will fail if this message has any unfrozen submessages! + * Messages with cycles must be frozen as a batch using upb::Def::Freeze(). */ bool Freeze(Status* s); - // The number of fields that belong to the MessageDef. + /* The number of fields that belong to the MessageDef. */ int field_count() const; - // The number of oneofs that belong to the MessageDef. + /* 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. + /* 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& 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. + /* 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& o, Status* s); - // These return NULL if the field is not found. + /* These return NULL if the field is not found. */ FieldDef* FindFieldByNumber(uint32_t number); FieldDef* FindFieldByName(const char *name, size_t len); const FieldDef* FindFieldByNumber(uint32_t number) const; @@ -1863,21 +1939,21 @@ UPB_DEFINE_DEF(upb::MessageDef, msgdef, MSG, UPB_QUOTE( 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 - // failed. - // - // TODO(haberman): which is more useful, keeping fields resolved or - // unresolving them? If there's no obvious answer, Should this functionality - // just be moved into symtab.c? + /* 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 + * failed. + * + * TODO(haberman): which is more useful, keeping fields resolved or + * unresolving them? If there's no obvious answer, Should this functionality + * just be moved into symtab.c? */ MessageDef* Dup(const void* owner) const; - // Is this message a map entry? + /* Is this message a map entry? */ void setmapentry(bool map_entry); bool mapentry() const; - // Iteration over fields. The order is undefined. + /* Iteration over fields. The order is undefined. */ class field_iterator : public std::iterator { public: @@ -1908,7 +1984,7 @@ UPB_DEFINE_DEF(upb::MessageDef, msgdef, MSG, UPB_QUOTE( upb_msg_field_iter iter_; }; - // Iteration over oneofs. The order is undefined. + /* Iteration over oneofs. The order is undefined. */ class oneof_iterator : public std::iterator { public: @@ -1991,52 +2067,21 @@ UPB_DEFINE_DEF(upb::MessageDef, msgdef, MSG, UPB_QUOTE( ConstOneofAccessor oneofs() const { return ConstOneofAccessor(this); } private: - UPB_DISALLOW_POD_OPS(MessageDef, upb::MessageDef); -), -UPB_DEFINE_STRUCT(upb_msgdef, upb_def, - size_t selector_count; - uint32_t submsg_field_count; - - // Tables for looking up fields by number and name. - 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. - bool map_entry; - - // TODO(haberman): proper extension ranges (there can be multiple). -)); + UPB_DISALLOW_POD_OPS(MessageDef, upb::MessageDef) +}; -// 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, \ - UPB_EMPTY_STRTABLE_INIT(UPB_CTYPE_PTR), false \ - } +#endif /* __cplusplus */ -UPB_BEGIN_EXTERN_C // { +UPB_BEGIN_EXTERN_C -// Returns NULL if memory allocation failed. +/* Returns NULL if memory allocation failed. */ upb_msgdef *upb_msgdef_new(const void *owner); -// From upb_refcounted. -bool upb_msgdef_isfrozen(const upb_msgdef *m); -void upb_msgdef_ref(const upb_msgdef *m, const void *owner); -void upb_msgdef_unref(const upb_msgdef *m, const void *owner); -void upb_msgdef_donateref(const upb_msgdef *m, const void *from, - const void *to); -void upb_msgdef_checkref(const upb_msgdef *m, const void *owner); +/* Include upb_refcounted methods like upb_msgdef_ref(). */ +UPB_REFCOUNTED_CMETHODS(upb_msgdef, upb_msgdef_upcast2) + bool upb_msgdef_freeze(upb_msgdef *m, upb_status *status); -// From upb_def. const char *upb_msgdef_fullname(const upb_msgdef *m); bool upb_msgdef_setfullname(upb_msgdef *m, const char *fullname, upb_status *s); @@ -2046,10 +2091,10 @@ bool upb_msgdef_addfield(upb_msgdef *m, upb_fielddef *f, const void *ref_donor, 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 -// - ntof = name to field -// - ntofz = name to field, null-terminated string. +/* Field lookup in a couple of different variations: + * - itof = int to field + * - ntof = name to field + * - ntofz = name to field, null-terminated string. */ const upb_fielddef *upb_msgdef_itof(const upb_msgdef *m, uint32_t i); const upb_fielddef *upb_msgdef_ntof(const upb_msgdef *m, const char *name, size_t len); @@ -2069,9 +2114,9 @@ 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. +/* 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); @@ -2089,7 +2134,7 @@ UPB_INLINE upb_oneofdef *upb_msgdef_ntoo_mutable(upb_msgdef *m, void upb_msgdef_setmapentry(upb_msgdef *m, bool map_entry); bool upb_msgdef_mapentry(const upb_msgdef *m); -// Well-known field tag numbers for map-entry messages. +/* Well-known field tag numbers for map-entry messages. */ #define UPB_MAPENTRY_KEY 1 #define UPB_MAPENTRY_VALUE 2 @@ -2097,95 +2142,94 @@ 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); -// // ... -// } -// -// 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. +/* 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); + * // ... + * } + * + * 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_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. +/* 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 // } +UPB_END_EXTERN_C /* upb::EnumDef ***************************************************************/ typedef upb_strtable_iter upb_enum_iter; -// Class that represents an enum. Its base class is upb::Def (convert with -// upb::upcast()). -UPB_DEFINE_DEF(upb::EnumDef, enumdef, ENUM, +#ifdef __cplusplus + +/* Class that represents an enum. Its base class is upb::Def (convert with + * upb::upcast()). */ +class upb::EnumDef { public: - // Returns NULL if memory allocation failed. + /* Returns NULL if memory allocation failed. */ static reffed_ptr 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; + /* upb::RefCounted methods like Ref()/Unref(). */ + UPB_REFCOUNTED_CPPMETHODS - // Functionality from upb::Def. + /* Functionality from upb::Def. */ const char* full_name() const; bool set_full_name(const char* fullname, Status* s); bool set_full_name(const std::string& fullname, Status* s); - // Call to freeze this EnumDef. + /* Call to freeze this EnumDef. */ bool Freeze(Status* s); - // The value that is used as the default when no field default is specified. - // If not set explicitly, the first value that was added will be used. - // The default value must be a member of the enum. - // Requires that value_count() > 0. + /* The value that is used as the default when no field default is specified. + * If not set explicitly, the first value that was added will be used. + * The default value must be a member of the enum. + * Requires that value_count() > 0. */ int32_t default_value() const; - // Sets the default value. If this value is not valid, returns false and an - // error message in status. + /* Sets the default value. If this value is not valid, returns false and an + * error message in status. */ bool set_default_value(int32_t val, Status* status); - // Returns the number of values currently defined in the enum. Note that - // multiple names can refer to the same number, so this may be greater than - // the total number of unique numbers. + /* Returns the number of values currently defined in the enum. Note that + * multiple names can refer to the same number, so this may be greater than + * the total number of unique numbers. */ int value_count() const; - // Adds a single name/number pair to the enum. Fails if this name has - // already been used by another value. + /* Adds a single name/number pair to the enum. Fails if this name has + * already been used by another value. */ bool AddValue(const char* name, int32_t num, Status* status); bool AddValue(const std::string& name, int32_t num, Status* status); - // Lookups from name to integer, returning true if found. + /* Lookups from name to integer, returning true if found. */ bool FindValueByName(const char* name, int32_t* num) const; - // Finds the name corresponding to the given number, or NULL if none was - // found. If more than one name corresponds to this number, returns the - // first one that was added. + /* Finds the name corresponding to the given number, or NULL if none was + * found. If more than one name corresponds to this number, returns the + * first one that was added. */ const char* FindValueByNumber(int32_t num) const; - // Returns a new EnumDef with all the same values. The new EnumDef will be - // owned by the given owner. + /* Returns a new EnumDef with all the same values. The new EnumDef will be + * owned by the given owner. */ EnumDef* Dup(const void* owner) const; - // Iteration over name/value pairs. The order is undefined. - // Adding an enum val invalidates any iterators. - // - // TODO: make compatible with range-for, with elements as pairs? + /* Iteration over name/value pairs. The order is undefined. + * Adding an enum val invalidates any iterators. + * + * TODO: make compatible with range-for, with elements as pairs? */ class Iterator { public: explicit Iterator(const EnumDef*); @@ -2200,33 +2244,23 @@ UPB_DEFINE_DEF(upb::EnumDef, enumdef, ENUM, }; private: - UPB_DISALLOW_POD_OPS(EnumDef, upb::EnumDef); -, -UPB_DEFINE_STRUCT(upb_enumdef, upb_def, - upb_strtable ntoi; - upb_inttable iton; - int32_t defaultval; -)); + UPB_DISALLOW_POD_OPS(EnumDef, upb::EnumDef) +}; -#define UPB_ENUMDEF_INIT(name, ntoi, iton, defaultval, refs, ref2s) \ - { UPB_DEF_INIT(name, UPB_DEF_ENUM, refs, ref2s), ntoi, iton, defaultval } +#endif /* __cplusplus */ -UPB_BEGIN_EXTERN_C // { +UPB_BEGIN_EXTERN_C -// Native C API. +/* Native C API. */ upb_enumdef *upb_enumdef_new(const void *owner); upb_enumdef *upb_enumdef_dup(const upb_enumdef *e, const void *owner); -// From upb_refcounted. -void upb_enumdef_unref(const upb_enumdef *e, const void *owner); -bool upb_enumdef_isfrozen(const upb_enumdef *e); -void upb_enumdef_ref(const upb_enumdef *e, const void *owner); -void upb_enumdef_donateref(const upb_enumdef *m, const void *from, - const void *to); -void upb_enumdef_checkref(const upb_enumdef *e, const void *owner); +/* Include upb_refcounted methods like upb_enumdef_ref(). */ +UPB_REFCOUNTED_CMETHODS(upb_enumdef, upb_enumdef_upcast2) + bool upb_enumdef_freeze(upb_enumdef *e, upb_status *status); -// From upb_def. +/* From upb_def. */ const char *upb_enumdef_fullname(const upb_enumdef *e); bool upb_enumdef_setfullname(upb_enumdef *e, const char *fullname, upb_status *s); @@ -2237,10 +2271,11 @@ int upb_enumdef_numvals(const upb_enumdef *e); bool upb_enumdef_addval(upb_enumdef *e, const char *name, int32_t num, upb_status *status); -// Enum lookups: -// - ntoi: look up a name with specified length. -// - ntoiz: look up a name provided as a null-terminated string. -// - iton: look up an integer, returning the name as a null-terminated string. +/* Enum lookups: + * - ntoi: look up a name with specified length. + * - ntoiz: look up a name provided as a null-terminated string. + * - iton: look up an integer, returning the name as a null-terminated + * string. */ bool upb_enumdef_ntoi(const upb_enumdef *e, const char *name, size_t len, int32_t *num); UPB_INLINE bool upb_enumdef_ntoiz(const upb_enumdef *e, @@ -2249,66 +2284,65 @@ UPB_INLINE bool upb_enumdef_ntoiz(const upb_enumdef *e, } const char *upb_enumdef_iton(const upb_enumdef *e, int32_t num); -// upb_enum_iter i; -// for(upb_enum_begin(&i, e); !upb_enum_done(&i); upb_enum_next(&i)) { -// // ... -// } +/* upb_enum_iter i; + * for(upb_enum_begin(&i, e); !upb_enum_done(&i); upb_enum_next(&i)) { + * // ... + * } + */ void upb_enum_begin(upb_enum_iter *iter, const upb_enumdef *e); void upb_enum_next(upb_enum_iter *iter); bool upb_enum_done(upb_enum_iter *iter); const char *upb_enum_iter_name(upb_enum_iter *iter); int32_t upb_enum_iter_number(upb_enum_iter *iter); -UPB_END_EXTERN_C // } +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( +#ifdef __cplusplus + +/* Class that represents a oneof. Its base class is upb::Def (convert with + * upb::upcast()). */ +class upb::OneofDef { public: - // Returns NULL if memory allocation failed. + /* Returns NULL if memory allocation failed. */ static reffed_ptr 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; + /* upb::RefCounted methods like Ref()/Unref(). */ + UPB_REFCOUNTED_CPPMETHODS - // Functionality from upb::Def. + /* Functionality from upb::Def. */ const char* full_name() const; - // Returns the MessageDef that owns this OneofDef. + /* 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. + /* 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. + /* 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. + /* 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& field, Status* s); - // Looks up by name. + /* 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 { @@ -2327,14 +2361,14 @@ UPB_DEFINE_DEF(upb::OneofDef, oneofdef, ONEOF, UPB_QUOTE( return FindFieldByName(str.c_str(), str.size()); } - // Looks up by tag number. + /* 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. + /* 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. + /* Iteration over fields. The order is undefined. */ class iterator : public std::iterator { public: explicit iterator(OneofDef* md); @@ -2370,30 +2404,19 @@ UPB_DEFINE_DEF(upb::OneofDef, oneofdef, ONEOF, UPB_QUOTE( 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; -)); + UPB_DISALLOW_POD_OPS(OneofDef, upb::OneofDef) +}; -#define UPB_ONEOFDEF_INIT(name, ntof, itof, refs, ref2s) \ - { UPB_DEF_INIT(name, UPB_DEF_ENUM, refs, ref2s), ntof, itof } +#endif /* __cplusplus */ -UPB_BEGIN_EXTERN_C // { +UPB_BEGIN_EXTERN_C -// Native C API. +/* 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); +/* Include upb_refcounted methods like upb_oneofdef_ref(). */ +UPB_REFCOUNTED_CMETHODS(upb_oneofdef, upb_oneofdef_upcast2) const char *upb_oneofdef_name(const upb_oneofdef *o); bool upb_oneofdef_setname(upb_oneofdef *o, const char *name, upb_status *s); @@ -2404,10 +2427,10 @@ 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. +/* 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, @@ -2416,17 +2439,18 @@ UPB_INLINE const upb_fielddef *upb_oneofdef_ntofz(const upb_oneofdef *o, } 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)) { -// // ... -// } +/* 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 // } +UPB_END_EXTERN_C #ifdef __cplusplus @@ -2435,21 +2459,12 @@ UPB_INLINE const char* upb_safecstr(const std::string& str) { return str.c_str(); } -// Inline C++ wrappers. +/* Inline C++ wrappers. */ namespace upb { inline Def* Def::Dup(const void* owner) const { return upb_def_dup(this, owner); } -inline bool Def::IsFrozen() const { return upb_def_isfrozen(this); } -inline void Def::Ref(const void* owner) const { upb_def_ref(this, owner); } -inline void Def::Unref(const void* owner) const { upb_def_unref(this, owner); } -inline void Def::DonateRef(const void* from, const void* to) const { - upb_def_donateref(this, from, to); -} -inline void Def::CheckRef(const void* owner) const { - upb_def_checkref(this, owner); -} inline Def::Type Def::def_type() const { return upb_def_type(this); } inline const char* Def::full_name() const { return upb_def_fullname(this); } inline bool Def::set_full_name(const char* fullname, Status* s) { @@ -2501,19 +2516,6 @@ inline reffed_ptr FieldDef::New() { inline FieldDef* FieldDef::Dup(const void* owner) const { return upb_fielddef_dup(this, owner); } -inline bool FieldDef::IsFrozen() const { return upb_fielddef_isfrozen(this); } -inline void FieldDef::Ref(const void* owner) const { - upb_fielddef_ref(this, owner); -} -inline void FieldDef::Unref(const void* owner) const { - upb_fielddef_unref(this, owner); -} -inline void FieldDef::DonateRef(const void* from, const void* to) const { - upb_fielddef_donateref(this, from, to); -} -inline void FieldDef::CheckRef(const void* owner) const { - upb_fielddef_checkref(this, owner); -} inline const char* FieldDef::full_name() const { return upb_fielddef_fullname(this); } @@ -2679,19 +2681,6 @@ inline reffed_ptr MessageDef::New() { upb_msgdef *m = upb_msgdef_new(&m); return reffed_ptr(m, &m); } -inline bool MessageDef::IsFrozen() const { return upb_msgdef_isfrozen(this); } -inline void MessageDef::Ref(const void* owner) const { - return upb_msgdef_ref(this, owner); -} -inline void MessageDef::Unref(const void* owner) const { - return upb_msgdef_unref(this, owner); -} -inline void MessageDef::DonateRef(const void* from, const void* to) const { - return upb_msgdef_donateref(this, from, to); -} -inline void MessageDef::CheckRef(const void* owner) const { - return upb_msgdef_checkref(this, owner); -} inline const char *MessageDef::full_name() const { return upb_msgdef_fullname(this); } @@ -2879,19 +2868,6 @@ inline reffed_ptr EnumDef::New() { upb_enumdef *e = upb_enumdef_new(&e); return reffed_ptr(e, &e); } -inline bool EnumDef::IsFrozen() const { return upb_enumdef_isfrozen(this); } -inline void EnumDef::Ref(const void* owner) const { - return upb_enumdef_ref(this, owner); -} -inline void EnumDef::Unref(const void* owner) const { - return upb_enumdef_unref(this, owner); -} -inline void EnumDef::DonateRef(const void* from, const void* to) const { - return upb_enumdef_donateref(this, from, to); -} -inline void EnumDef::CheckRef(const void* owner) const { - return upb_enumdef_checkref(this, owner); -} inline const char* EnumDef::full_name() const { return upb_enumdef_fullname(this); } @@ -2944,19 +2920,6 @@ inline reffed_ptr OneofDef::New() { upb_oneofdef *o = upb_oneofdef_new(&o); return reffed_ptr(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); } @@ -3038,1375 +3001,543 @@ inline bool OneofDef::const_iterator::operator!=( return !(*this == other); } -} // namespace upb +} /* namespace upb */ #endif -#undef UPB_DEFINE_DEF -#undef UPB_DEF_CASTS -#undef UPB_CPP_CASTS - #endif /* UPB_DEF_H_ */ -// This file contains accessors for a set of compiled-in defs. -// Note that unlike Google's protobuf, it does *not* define -// generated classes or any other kind of data structure for -// actually storing protobufs. It only contains *defs* which -// let you reflect over a protobuf *schema*. -// -// This file was generated by upbc (the upb compiler). -// Do not edit -- your changes will be discarded when the file is -// regenerated. - -#ifndef GOOGLE_PROTOBUF_DESCRIPTOR_UPB_H_ -#define GOOGLE_PROTOBUF_DESCRIPTOR_UPB_H_ - /* * upb - a minimalist implementation of protocol buffers. * - * Copyright (c) 2009-2012 Google Inc. See LICENSE for details. + * Copyright (c) 2015 Google Inc. See LICENSE for details. * Author: Josh Haberman * - * A symtab (symbol table) stores a name->def map of upb_defs. Clients could - * always create such tables themselves, but upb_symtab has logic for resolving - * symbolic references, and in particular, for keeping a whole set of consistent - * defs when replacing some subset of those defs. This logic is nontrivial. + * This file contains definitions of structs that should be considered private + * and NOT stable across versions of upb. * - * This is a mixed C/C++ interface that offers a full API to both languages. - * See the top-level README for more information. + * The only reason they are declared here and not in .c files is to allow upb + * and the application (if desired) to embed statically-initialized instances + * of structures like defs. + * + * If you include this file, all guarantees of ABI compatibility go out the + * window! Any code that includes this file needs to recompile against the + * exact same version of upb that they are linking against. + * + * You also need to recompile if you change the value of the UPB_DEBUG_REFS + * flag. */ -#ifndef UPB_SYMTAB_H_ -#define UPB_SYMTAB_H_ +#ifndef UPB_STATICINIT_H_ +#define UPB_STATICINIT_H_ #ifdef __cplusplus -#include -namespace upb { class SymbolTable; } +/* Because of how we do our typedefs, this header can't be included from C++. */ +#error This file cannot be included from C++ #endif -UPB_DECLARE_TYPE(upb::SymbolTable, upb_symtab); +/* upb_refcounted *************************************************************/ -typedef struct { - UPB_PRIVATE_FOR_CPP - upb_strtable_iter iter; - upb_deftype_t type; -} upb_symtab_iter; -// Non-const methods in upb::SymbolTable are NOT thread-safe. -UPB_DEFINE_CLASS1(upb::SymbolTable, upb::RefCounted, - public: - // Returns a new symbol table with a single ref owned by "owner." - // Returns NULL if memory allocation failed. - static reffed_ptr New(); +/* upb_def ********************************************************************/ - // 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; +struct upb_def { + upb_refcounted base; - // For all lookup functions, the returned pointer is not owned by the - // caller; it may be invalidated by any non-const call or unref of the - // SymbolTable! To protect against this, take a ref if desired. - - // Freezes the symbol table: prevents further modification of it. - // After the Freeze() operation is successful, the SymbolTable must only be - // accessed via a const pointer. - // - // Unlike with upb::MessageDef/upb::EnumDef/etc, freezing a SymbolTable is not - // a necessary step in using a SymbolTable. If you have no need for it to be - // immutable, there is no need to freeze it ever. However sometimes it is - // useful, and SymbolTables that are statically compiled into the binary are - // always frozen by nature. - void Freeze(); + const char *fullname; + char type; /* A upb_deftype_t (char to save space) */ - // Resolves the given symbol using the rules described in descriptor.proto, - // namely: - // - // If the name starts with a '.', it is fully-qualified. Otherwise, - // C++-like scoping rules are used to find the type (i.e. first the nested - // types within this message are searched, then within the parent, on up - // to the root namespace). - // - // If not found, returns NULL. - const Def* Resolve(const char* base, const char* sym) const; + /* Used as a flag during the def's mutable stage. Must be false unless + * it is currently being used by a function on the stack. This allows + * us to easily determine which defs were passed into the function's + * current invocation. */ + bool came_from_user; +}; - // Finds an entry in the symbol table with this exact name. If not found, - // returns NULL. - const Def* Lookup(const char *sym) const; - const MessageDef* LookupMessage(const char *sym) const; - const EnumDef* LookupEnum(const char *sym) const; +#define UPB_DEF_INIT(name, type, refs, ref2s) \ + { UPB_REFCOUNT_INIT(refs, ref2s), name, type, false } - // TODO: introduce a C++ iterator, but make it nice and templated so that if - // you ask for an iterator of MessageDef the iterated elements are strongly - // typed as MessageDef*. - - // Adds the given mutable defs to the symtab, resolving all symbols - // (including enum default values) and finalizing the defs. Only one def per - // name may be in the list, but defs can replace existing defs in the symtab. - // All defs must have a name -- anonymous defs are not allowed. Anonymous - // defs can still be frozen by calling upb_def_freeze() directly. - // - // Any existing defs that can reach defs that are being replaced will - // themselves be replaced also, so that the resulting set of defs is fully - // consistent. - // - // This logic implemented in this method is a convenience; ultimately it - // calls some combination of upb_fielddef_setsubdef(), upb_def_dup(), and - // upb_freeze(), any of which the client could call themself. However, since - // the logic for doing so is nontrivial, we provide it here. - // - // The entire operation either succeeds or fails. If the operation fails, - // the symtab is unchanged, false is returned, and status indicates the - // error. The caller passes a ref on all defs to the symtab (even if the - // operation fails). - // - // TODO(haberman): currently failure will leave the symtab unchanged, but may - // leave the defs themselves partially resolved. Does this matter? If so we - // could do a prepass that ensures that all symbols are resolvable and bail - // if not, so we don't mutate anything until we know the operation will - // succeed. - // - // TODO(haberman): since the defs must be mutable, refining a frozen def - // requires making mutable copies of the entire tree. This is wasteful if - // only a few messages are changing. We may want to add a way of adding a - // tree of frozen defs to the symtab (perhaps an alternate constructor where - // you pass the root of the tree?) - bool Add(Def*const* defs, int n, void* ref_donor, upb_status* status); - bool Add(const std::vector& defs, void *owner, Status* status) { - return Add((Def*const*)&defs[0], defs.size(), owner, status); - } +/* upb_fielddef ***************************************************************/ - private: - UPB_DISALLOW_POD_OPS(SymbolTable, upb::SymbolTable); -, -UPB_DEFINE_STRUCT(upb_symtab, upb_refcounted, - upb_strtable symtab; -)); +struct upb_fielddef { + upb_def base; -#define UPB_SYMTAB_INIT(symtab, refs, ref2s) \ - { UPB_REFCOUNT_INIT(refs, ref2s), symtab } + union { + int64_t sint; + uint64_t uint; + double dbl; + float flt; + void *bytes; + } defaultval; + union { + const upb_msgdef *def; /* If !msg_is_symbolic. */ + char *name; /* If msg_is_symbolic. */ + } msg; + union { + const upb_def *def; /* If !subdef_is_symbolic. */ + char *name; /* If subdef_is_symbolic. */ + } 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_; + bool lazy_; + bool packed_; + upb_intfmt_t intfmt; + bool tagdelim; + upb_fieldtype_t type_; + upb_label_t label_; + uint32_t number_; + uint32_t selector_base; /* Used to index into a upb::Handlers table. */ + uint32_t index_; +}; -UPB_BEGIN_EXTERN_C // { +#define UPB_FIELDDEF_INIT(label, type, intfmt, tagdelim, is_extension, lazy, \ + packed, name, num, msgdef, subdef, selector_base, \ + index, defaultval, refs, ref2s) \ + { \ + UPB_DEF_INIT(name, UPB_DEF_FIELD, refs, ref2s), defaultval, {msgdef}, \ + {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 \ + } -// Native C API. -// From upb_refcounted. -bool upb_symtab_isfrozen(const upb_symtab *s); -void upb_symtab_ref(const upb_symtab *s, const void *owner); -void upb_symtab_unref(const upb_symtab *s, const void *owner); -void upb_symtab_donateref( - const upb_symtab *s, const void *from, const void *to); -void upb_symtab_checkref(const upb_symtab *s, const void *owner); -upb_symtab *upb_symtab_new(const void *owner); -void upb_symtab_freeze(upb_symtab *s); -const upb_def *upb_symtab_resolve(const upb_symtab *s, const char *base, - const char *sym); -const upb_def *upb_symtab_lookup(const upb_symtab *s, const char *sym); -const upb_msgdef *upb_symtab_lookupmsg(const upb_symtab *s, const char *sym); -const upb_enumdef *upb_symtab_lookupenum(const upb_symtab *s, const char *sym); -bool upb_symtab_add(upb_symtab *s, upb_def *const*defs, int n, void *ref_donor, - upb_status *status); +/* upb_msgdef *****************************************************************/ -// upb_symtab_iter i; -// for(upb_symtab_begin(&i, s, type); !upb_symtab_done(&i); -// upb_symtab_next(&i)) { -// const upb_def *def = upb_symtab_iter_def(&i); -// // ... -// } -// -// 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_symtab_begin(upb_symtab_iter *iter, const upb_symtab *s, - upb_deftype_t type); -void upb_symtab_next(upb_symtab_iter *iter); -bool upb_symtab_done(const upb_symtab_iter *iter); -const upb_def *upb_symtab_iter_def(const upb_symtab_iter *iter); +struct upb_msgdef { + upb_def base; -UPB_END_EXTERN_C // } + size_t selector_count; + uint32_t submsg_field_count; -#ifdef __cplusplus -// C++ inline wrappers. -namespace upb { -inline reffed_ptr SymbolTable::New() { - upb_symtab *s = upb_symtab_new(&s); - return reffed_ptr(s, &s); -} + /* Tables for looking up fields by number and name. */ + upb_inttable itof; /* int to field */ + upb_strtable ntof; /* name to field */ -inline bool SymbolTable::IsFrozen() const { - return upb_symtab_isfrozen(this); -} -inline void SymbolTable::Ref(const void *owner) const { - upb_symtab_ref(this, owner); -} -inline void SymbolTable::Unref(const void *owner) const { - upb_symtab_unref(this, owner); -} -inline void SymbolTable::DonateRef(const void *from, const void *to) const { - upb_symtab_donateref(this, from, to); -} -inline void SymbolTable::CheckRef(const void *owner) const { - upb_symtab_checkref(this, owner); -} + /* Tables for looking up oneofs by name. */ + upb_strtable ntoo; /* name to oneof */ -inline void SymbolTable::Freeze() { - return upb_symtab_freeze(this); -} -inline const Def *SymbolTable::Resolve(const char *base, - const char *sym) const { - return upb_symtab_resolve(this, base, sym); -} -inline const Def* SymbolTable::Lookup(const char *sym) const { - return upb_symtab_lookup(this, sym); -} -inline const MessageDef *SymbolTable::LookupMessage(const char *sym) const { - return upb_symtab_lookupmsg(this, sym); -} -inline bool SymbolTable::Add( - Def*const* defs, int n, void* ref_donor, upb_status* status) { - return upb_symtab_add(this, (upb_def*const*)defs, n, ref_donor, status); -} -} // namespace upb -#endif + /* Is this a map-entry message? + * TODO: set this flag properly for static descriptors; regenerate + * descriptor.upb.c. */ + bool map_entry; -#endif /* UPB_SYMTAB_H_ */ + /* TODO(haberman): proper extension ranges (there can be multiple). */ +}; -#ifdef __cplusplus -extern "C" { -#endif +/* 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, \ + UPB_EMPTY_STRTABLE_INIT(UPB_CTYPE_PTR), false \ + } -// Enums -typedef enum { - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_LABEL_OPTIONAL = 1, - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_LABEL_REQUIRED = 2, - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_LABEL_REPEATED = 3, -} google_protobuf_FieldDescriptorProto_Label; +/* upb_enumdef ****************************************************************/ -typedef enum { - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_DOUBLE = 1, - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_FLOAT = 2, - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_INT64 = 3, - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_UINT64 = 4, - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_INT32 = 5, - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_FIXED64 = 6, - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_FIXED32 = 7, - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_BOOL = 8, - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_STRING = 9, - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_GROUP = 10, - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_MESSAGE = 11, - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_BYTES = 12, - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_UINT32 = 13, - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_ENUM = 14, - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_SFIXED32 = 15, - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_SFIXED64 = 16, - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_SINT32 = 17, - GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_SINT64 = 18, -} google_protobuf_FieldDescriptorProto_Type; +struct upb_enumdef { + upb_def base; -typedef enum { - GOOGLE_PROTOBUF_FIELDOPTIONS_STRING = 0, - GOOGLE_PROTOBUF_FIELDOPTIONS_CORD = 1, - GOOGLE_PROTOBUF_FIELDOPTIONS_STRING_PIECE = 2, -} google_protobuf_FieldOptions_CType; + upb_strtable ntoi; + upb_inttable iton; + int32_t defaultval; +}; -typedef enum { - GOOGLE_PROTOBUF_FILEOPTIONS_SPEED = 1, - GOOGLE_PROTOBUF_FILEOPTIONS_CODE_SIZE = 2, - GOOGLE_PROTOBUF_FILEOPTIONS_LITE_RUNTIME = 3, -} google_protobuf_FileOptions_OptimizeMode; +#define UPB_ENUMDEF_INIT(name, ntoi, iton, defaultval, refs, ref2s) \ + { UPB_DEF_INIT(name, UPB_DEF_ENUM, refs, ref2s), ntoi, iton, defaultval } -// Selectors -// google.protobuf.DescriptorProto -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_FIELD_STARTSUBMSG 2 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_NESTED_TYPE_STARTSUBMSG 3 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_ENUM_TYPE_STARTSUBMSG 4 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSION_RANGE_STARTSUBMSG 5 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSION_STARTSUBMSG 6 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_OPTIONS_STARTSUBMSG 7 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_FIELD_STARTSEQ 8 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_FIELD_ENDSEQ 9 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_FIELD_ENDSUBMSG 10 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_NESTED_TYPE_STARTSEQ 11 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_NESTED_TYPE_ENDSEQ 12 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_NESTED_TYPE_ENDSUBMSG 13 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_ENUM_TYPE_STARTSEQ 14 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_ENUM_TYPE_ENDSEQ 15 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_ENUM_TYPE_ENDSUBMSG 16 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSION_RANGE_STARTSEQ 17 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSION_RANGE_ENDSEQ 18 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSION_RANGE_ENDSUBMSG 19 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSION_STARTSEQ 20 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSION_ENDSEQ 21 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSION_ENDSUBMSG 22 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_OPTIONS_ENDSUBMSG 23 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_NAME_STRING 24 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_NAME_STARTSTR 25 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_NAME_ENDSTR 26 +/* upb_oneofdef ***************************************************************/ -// google.protobuf.DescriptorProto.ExtensionRange -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSIONRANGE_START_INT32 2 -#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSIONRANGE_END_INT32 3 +struct upb_oneofdef { + upb_def base; -// google.protobuf.EnumDescriptorProto -#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_VALUE_STARTSUBMSG 2 -#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_OPTIONS_STARTSUBMSG 3 -#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_VALUE_STARTSEQ 4 -#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_VALUE_ENDSEQ 5 -#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_VALUE_ENDSUBMSG 6 -#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_OPTIONS_ENDSUBMSG 7 -#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_NAME_STRING 8 -#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_NAME_STARTSTR 9 -#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_NAME_ENDSTR 10 + upb_strtable ntof; + upb_inttable itof; + const upb_msgdef *parent; +}; -// google.protobuf.EnumOptions -#define SEL_GOOGLE_PROTOBUF_ENUMOPTIONS_UNINTERPRETED_OPTION_STARTSUBMSG 2 -#define SEL_GOOGLE_PROTOBUF_ENUMOPTIONS_UNINTERPRETED_OPTION_STARTSEQ 3 -#define SEL_GOOGLE_PROTOBUF_ENUMOPTIONS_UNINTERPRETED_OPTION_ENDSEQ 4 -#define SEL_GOOGLE_PROTOBUF_ENUMOPTIONS_UNINTERPRETED_OPTION_ENDSUBMSG 5 -#define SEL_GOOGLE_PROTOBUF_ENUMOPTIONS_ALLOW_ALIAS_BOOL 6 +#define UPB_ONEOFDEF_INIT(name, ntof, itof, refs, ref2s) \ + { UPB_DEF_INIT(name, UPB_DEF_ENUM, refs, ref2s), ntof, itof } -// google.protobuf.EnumValueDescriptorProto -#define SEL_GOOGLE_PROTOBUF_ENUMVALUEDESCRIPTORPROTO_OPTIONS_STARTSUBMSG 2 -#define SEL_GOOGLE_PROTOBUF_ENUMVALUEDESCRIPTORPROTO_OPTIONS_ENDSUBMSG 3 -#define SEL_GOOGLE_PROTOBUF_ENUMVALUEDESCRIPTORPROTO_NAME_STRING 4 -#define SEL_GOOGLE_PROTOBUF_ENUMVALUEDESCRIPTORPROTO_NAME_STARTSTR 5 -#define SEL_GOOGLE_PROTOBUF_ENUMVALUEDESCRIPTORPROTO_NAME_ENDSTR 6 -#define SEL_GOOGLE_PROTOBUF_ENUMVALUEDESCRIPTORPROTO_NUMBER_INT32 7 -// google.protobuf.EnumValueOptions -#define SEL_GOOGLE_PROTOBUF_ENUMVALUEOPTIONS_UNINTERPRETED_OPTION_STARTSUBMSG 2 -#define SEL_GOOGLE_PROTOBUF_ENUMVALUEOPTIONS_UNINTERPRETED_OPTION_STARTSEQ 3 -#define SEL_GOOGLE_PROTOBUF_ENUMVALUEOPTIONS_UNINTERPRETED_OPTION_ENDSEQ 4 -#define SEL_GOOGLE_PROTOBUF_ENUMVALUEOPTIONS_UNINTERPRETED_OPTION_ENDSUBMSG 5 +/* upb_symtab *****************************************************************/ -// google.protobuf.FieldDescriptorProto -#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_OPTIONS_STARTSUBMSG 2 -#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_OPTIONS_ENDSUBMSG 3 -#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_NAME_STRING 4 -#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_NAME_STARTSTR 5 -#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_NAME_ENDSTR 6 -#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_EXTENDEE_STRING 7 -#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_EXTENDEE_STARTSTR 8 -#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_EXTENDEE_ENDSTR 9 -#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_NUMBER_INT32 10 -#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_LABEL_INT32 11 -#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_INT32 12 -#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_NAME_STRING 13 -#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_NAME_STARTSTR 14 -#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_NAME_ENDSTR 15 -#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_DEFAULT_VALUE_STRING 16 -#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_DEFAULT_VALUE_STARTSTR 17 -#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_DEFAULT_VALUE_ENDSTR 18 +struct upb_symtab { + upb_refcounted base; -// google.protobuf.FieldOptions -#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_UNINTERPRETED_OPTION_STARTSUBMSG 2 -#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_UNINTERPRETED_OPTION_STARTSEQ 3 -#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_UNINTERPRETED_OPTION_ENDSEQ 4 -#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_UNINTERPRETED_OPTION_ENDSUBMSG 5 -#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_CTYPE_INT32 6 -#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_PACKED_BOOL 7 -#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_DEPRECATED_BOOL 8 -#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_LAZY_BOOL 9 -#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_EXPERIMENTAL_MAP_KEY_STRING 10 -#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_EXPERIMENTAL_MAP_KEY_STARTSTR 11 -#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_EXPERIMENTAL_MAP_KEY_ENDSTR 12 -#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_WEAK_BOOL 13 + upb_strtable symtab; +}; -// google.protobuf.FileDescriptorProto -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_MESSAGE_TYPE_STARTSUBMSG 2 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_ENUM_TYPE_STARTSUBMSG 3 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_SERVICE_STARTSUBMSG 4 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_EXTENSION_STARTSUBMSG 5 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_OPTIONS_STARTSUBMSG 6 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_SOURCE_CODE_INFO_STARTSUBMSG 7 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_MESSAGE_TYPE_STARTSEQ 8 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_MESSAGE_TYPE_ENDSEQ 9 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_MESSAGE_TYPE_ENDSUBMSG 10 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_ENUM_TYPE_STARTSEQ 11 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_ENUM_TYPE_ENDSEQ 12 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_ENUM_TYPE_ENDSUBMSG 13 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_SERVICE_STARTSEQ 14 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_SERVICE_ENDSEQ 15 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_SERVICE_ENDSUBMSG 16 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_EXTENSION_STARTSEQ 17 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_EXTENSION_ENDSEQ 18 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_EXTENSION_ENDSUBMSG 19 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_OPTIONS_ENDSUBMSG 20 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_SOURCE_CODE_INFO_ENDSUBMSG 21 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_NAME_STRING 22 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_NAME_STARTSTR 23 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_NAME_ENDSTR 24 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_PACKAGE_STRING 25 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_PACKAGE_STARTSTR 26 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_PACKAGE_ENDSTR 27 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_DEPENDENCY_STARTSEQ 28 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_DEPENDENCY_ENDSEQ 29 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_DEPENDENCY_STRING 30 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_DEPENDENCY_STARTSTR 31 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_DEPENDENCY_ENDSTR 32 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_PUBLIC_DEPENDENCY_STARTSEQ 33 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_PUBLIC_DEPENDENCY_ENDSEQ 34 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_PUBLIC_DEPENDENCY_INT32 35 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_WEAK_DEPENDENCY_STARTSEQ 36 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_WEAK_DEPENDENCY_ENDSEQ 37 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_WEAK_DEPENDENCY_INT32 38 +#define UPB_SYMTAB_INIT(symtab, refs, ref2s) \ + { UPB_REFCOUNT_INIT(refs, ref2s), symtab } -// google.protobuf.FileDescriptorSet -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORSET_FILE_STARTSUBMSG 2 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORSET_FILE_STARTSEQ 3 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORSET_FILE_ENDSEQ 4 -#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORSET_FILE_ENDSUBMSG 5 -// google.protobuf.FileOptions -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_UNINTERPRETED_OPTION_STARTSUBMSG 2 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_UNINTERPRETED_OPTION_STARTSEQ 3 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_UNINTERPRETED_OPTION_ENDSEQ 4 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_UNINTERPRETED_OPTION_ENDSUBMSG 5 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_PACKAGE_STRING 6 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_PACKAGE_STARTSTR 7 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_PACKAGE_ENDSTR 8 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_OUTER_CLASSNAME_STRING 9 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_OUTER_CLASSNAME_STARTSTR 10 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_OUTER_CLASSNAME_ENDSTR 11 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_OPTIMIZE_FOR_INT32 12 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_MULTIPLE_FILES_BOOL 13 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_GO_PACKAGE_STRING 14 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_GO_PACKAGE_STARTSTR 15 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_GO_PACKAGE_ENDSTR 16 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_CC_GENERIC_SERVICES_BOOL 17 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_GENERIC_SERVICES_BOOL 18 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_PY_GENERIC_SERVICES_BOOL 19 -#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_GENERATE_EQUALS_AND_HASH_BOOL 20 +#endif /* UPB_STATICINIT_H_ */ +/* + * upb - a minimalist implementation of protocol buffers. + * + * Copyright (c) 2010-2012 Google Inc. See LICENSE for details. + * Author: Josh Haberman + * + * A upb_handlers is like a virtual table for a upb_msgdef. Each field of the + * message can have associated functions that will be called when we are + * parsing or visiting a stream of data. This is similar to how handlers work + * in SAX (the Simple API for XML). + * + * The handlers have no idea where the data is coming from, so a single set of + * handlers could be used with two completely different data sources (for + * example, a parser and a visitor over in-memory objects). This decoupling is + * the most important feature of upb, because it allows parsers and serializers + * to be highly reusable. + * + * This is a mixed C/C++ interface that offers a full API to both languages. + * See the top-level README for more information. + */ -// google.protobuf.MessageOptions -#define SEL_GOOGLE_PROTOBUF_MESSAGEOPTIONS_UNINTERPRETED_OPTION_STARTSUBMSG 2 -#define SEL_GOOGLE_PROTOBUF_MESSAGEOPTIONS_UNINTERPRETED_OPTION_STARTSEQ 3 -#define SEL_GOOGLE_PROTOBUF_MESSAGEOPTIONS_UNINTERPRETED_OPTION_ENDSEQ 4 -#define SEL_GOOGLE_PROTOBUF_MESSAGEOPTIONS_UNINTERPRETED_OPTION_ENDSUBMSG 5 -#define SEL_GOOGLE_PROTOBUF_MESSAGEOPTIONS_MESSAGE_SET_WIRE_FORMAT_BOOL 6 -#define SEL_GOOGLE_PROTOBUF_MESSAGEOPTIONS_NO_STANDARD_DESCRIPTOR_ACCESSOR_BOOL 7 +#ifndef UPB_HANDLERS_H +#define UPB_HANDLERS_H -// google.protobuf.MethodDescriptorProto -#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_OPTIONS_STARTSUBMSG 2 -#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_OPTIONS_ENDSUBMSG 3 -#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_NAME_STRING 4 -#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_NAME_STARTSTR 5 -#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_NAME_ENDSTR 6 -#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_INPUT_TYPE_STRING 7 -#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_INPUT_TYPE_STARTSTR 8 -#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_INPUT_TYPE_ENDSTR 9 -#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_OUTPUT_TYPE_STRING 10 -#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_OUTPUT_TYPE_STARTSTR 11 -#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_OUTPUT_TYPE_ENDSTR 12 -// google.protobuf.MethodOptions -#define SEL_GOOGLE_PROTOBUF_METHODOPTIONS_UNINTERPRETED_OPTION_STARTSUBMSG 2 -#define SEL_GOOGLE_PROTOBUF_METHODOPTIONS_UNINTERPRETED_OPTION_STARTSEQ 3 -#define SEL_GOOGLE_PROTOBUF_METHODOPTIONS_UNINTERPRETED_OPTION_ENDSEQ 4 -#define SEL_GOOGLE_PROTOBUF_METHODOPTIONS_UNINTERPRETED_OPTION_ENDSUBMSG 5 +#ifdef __cplusplus +namespace upb { +class BufferHandle; +class BytesHandler; +class HandlerAttributes; +class Handlers; +template class Handler; +template struct CanonicalType; +} /* namespace upb */ +#endif -// google.protobuf.ServiceDescriptorProto -#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_METHOD_STARTSUBMSG 2 -#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_OPTIONS_STARTSUBMSG 3 -#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_METHOD_STARTSEQ 4 -#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_METHOD_ENDSEQ 5 -#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_METHOD_ENDSUBMSG 6 -#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_OPTIONS_ENDSUBMSG 7 -#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_NAME_STRING 8 -#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_NAME_STARTSTR 9 -#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_NAME_ENDSTR 10 +UPB_DECLARE_TYPE(upb::BufferHandle, upb_bufhandle) +UPB_DECLARE_TYPE(upb::BytesHandler, upb_byteshandler) +UPB_DECLARE_TYPE(upb::HandlerAttributes, upb_handlerattr) +UPB_DECLARE_DERIVED_TYPE(upb::Handlers, upb::RefCounted, + upb_handlers, upb_refcounted) -// google.protobuf.ServiceOptions -#define SEL_GOOGLE_PROTOBUF_SERVICEOPTIONS_UNINTERPRETED_OPTION_STARTSUBMSG 2 -#define SEL_GOOGLE_PROTOBUF_SERVICEOPTIONS_UNINTERPRETED_OPTION_STARTSEQ 3 -#define SEL_GOOGLE_PROTOBUF_SERVICEOPTIONS_UNINTERPRETED_OPTION_ENDSEQ 4 -#define SEL_GOOGLE_PROTOBUF_SERVICEOPTIONS_UNINTERPRETED_OPTION_ENDSUBMSG 5 +/* The maximum depth that the handler graph can have. This is a resource limit + * for the C stack since we sometimes need to recursively traverse the graph. + * Cycles are ok; the traversal will stop when it detects a cycle, but we must + * hit the cycle before the maximum depth is reached. + * + * If having a single static limit is too inflexible, we can add another variant + * of Handlers::Freeze that allows specifying this as a parameter. */ +#define UPB_MAX_HANDLER_DEPTH 64 -// google.protobuf.SourceCodeInfo -#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_STARTSUBMSG 2 -#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_STARTSEQ 3 -#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_ENDSEQ 4 -#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_ENDSUBMSG 5 +/* All the different types of handlers that can be registered. + * Only needed for the advanced functions in upb::Handlers. */ +typedef enum { + UPB_HANDLER_INT32, + UPB_HANDLER_INT64, + UPB_HANDLER_UINT32, + UPB_HANDLER_UINT64, + UPB_HANDLER_FLOAT, + UPB_HANDLER_DOUBLE, + UPB_HANDLER_BOOL, + UPB_HANDLER_STARTSTR, + UPB_HANDLER_STRING, + UPB_HANDLER_ENDSTR, + UPB_HANDLER_STARTSUBMSG, + UPB_HANDLER_ENDSUBMSG, + UPB_HANDLER_STARTSEQ, + UPB_HANDLER_ENDSEQ +} upb_handlertype_t; -// google.protobuf.SourceCodeInfo.Location -#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_PATH_STARTSEQ 2 -#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_PATH_ENDSEQ 3 -#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_PATH_INT32 4 -#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_SPAN_STARTSEQ 5 -#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_SPAN_ENDSEQ 6 -#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_SPAN_INT32 7 -#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_LEADING_COMMENTS_STRING 8 -#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_LEADING_COMMENTS_STARTSTR 9 -#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_LEADING_COMMENTS_ENDSTR 10 -#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_TRAILING_COMMENTS_STRING 11 -#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_TRAILING_COMMENTS_STARTSTR 12 -#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_TRAILING_COMMENTS_ENDSTR 13 +#define UPB_HANDLER_MAX (UPB_HANDLER_ENDSEQ+1) -// google.protobuf.UninterpretedOption -#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NAME_STARTSUBMSG 2 -#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NAME_STARTSEQ 3 -#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NAME_ENDSEQ 4 -#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NAME_ENDSUBMSG 5 -#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_IDENTIFIER_VALUE_STRING 6 -#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_IDENTIFIER_VALUE_STARTSTR 7 -#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_IDENTIFIER_VALUE_ENDSTR 8 -#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_POSITIVE_INT_VALUE_UINT64 9 -#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NEGATIVE_INT_VALUE_INT64 10 -#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_DOUBLE_VALUE_DOUBLE 11 -#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_STRING_VALUE_STRING 12 -#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_STRING_VALUE_STARTSTR 13 -#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_STRING_VALUE_ENDSTR 14 -#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_AGGREGATE_VALUE_STRING 15 -#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_AGGREGATE_VALUE_STARTSTR 16 -#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_AGGREGATE_VALUE_ENDSTR 17 +#define UPB_BREAK NULL -// google.protobuf.UninterpretedOption.NamePart -#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NAMEPART_NAME_PART_STRING 2 -#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NAMEPART_NAME_PART_STARTSTR 3 -#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NAMEPART_NAME_PART_ENDSTR 4 -#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NAMEPART_IS_EXTENSION_BOOL 5 +/* A convenient definition for when no closure is needed. */ +extern char _upb_noclosure; +#define UPB_NO_CLOSURE &_upb_noclosure -const upb_symtab *upbdefs_google_protobuf_descriptor(const void *owner); +/* A selector refers to a specific field handler in the Handlers object + * (for example: the STARTSUBMSG handler for field "field15"). */ +typedef int32_t upb_selector_t; -// MessageDefs -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_DescriptorProto(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.DescriptorProto"); - assert(m); - return m; -} -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_DescriptorProto_ExtensionRange(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.DescriptorProto.ExtensionRange"); - assert(m); - return m; -} -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_EnumDescriptorProto(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.EnumDescriptorProto"); - assert(m); - return m; -} -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_EnumOptions(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.EnumOptions"); - assert(m); - return m; -} -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_EnumValueDescriptorProto(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.EnumValueDescriptorProto"); - assert(m); - return m; -} -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_EnumValueOptions(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.EnumValueOptions"); - assert(m); - return m; -} -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_FieldDescriptorProto(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.FieldDescriptorProto"); - assert(m); - return m; -} -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_FieldOptions(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.FieldOptions"); - assert(m); - return m; -} -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_FileDescriptorProto(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.FileDescriptorProto"); - assert(m); - return m; -} -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_FileDescriptorSet(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.FileDescriptorSet"); - assert(m); - return m; -} -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_FileOptions(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.FileOptions"); - assert(m); - return m; -} -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_MessageOptions(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.MessageOptions"); - assert(m); - return m; -} -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_MethodDescriptorProto(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.MethodDescriptorProto"); - assert(m); - return m; -} -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_MethodOptions(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.MethodOptions"); - assert(m); - return m; -} -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_ServiceDescriptorProto(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.ServiceDescriptorProto"); - assert(m); - return m; -} -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_ServiceOptions(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.ServiceOptions"); - assert(m); - return m; -} -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_SourceCodeInfo(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.SourceCodeInfo"); - assert(m); - return m; -} -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_SourceCodeInfo_Location(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.SourceCodeInfo.Location"); - assert(m); - return m; -} -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_UninterpretedOption(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.UninterpretedOption"); - assert(m); - return m; -} -UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_UninterpretedOption_NamePart(const upb_symtab *s) { - const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.UninterpretedOption.NamePart"); - assert(m); - return m; -} +UPB_BEGIN_EXTERN_C +/* Forward-declares for C inline accessors. We need to declare these here + * so we can "friend" them in the class declarations in C++. */ +UPB_INLINE upb_func *upb_handlers_gethandler(const upb_handlers *h, + upb_selector_t s); +UPB_INLINE const void *upb_handlerattr_handlerdata(const upb_handlerattr *attr); +UPB_INLINE const void *upb_handlers_gethandlerdata(const upb_handlers *h, + upb_selector_t s); -// EnumDefs -UPB_INLINE const upb_enumdef *upbdefs_google_protobuf_FieldDescriptorProto_Label(const upb_symtab *s) { - const upb_enumdef *e = upb_symtab_lookupenum(s, "google.protobuf.FieldDescriptorProto.Label"); - assert(e); - return e; -} -UPB_INLINE const upb_enumdef *upbdefs_google_protobuf_FieldDescriptorProto_Type(const upb_symtab *s) { - const upb_enumdef *e = upb_symtab_lookupenum(s, "google.protobuf.FieldDescriptorProto.Type"); - assert(e); - return e; -} -UPB_INLINE const upb_enumdef *upbdefs_google_protobuf_FieldOptions_CType(const upb_symtab *s) { - const upb_enumdef *e = upb_symtab_lookupenum(s, "google.protobuf.FieldOptions.CType"); - assert(e); - return e; -} -UPB_INLINE const upb_enumdef *upbdefs_google_protobuf_FileOptions_OptimizeMode(const upb_symtab *s) { - const upb_enumdef *e = upb_symtab_lookupenum(s, "google.protobuf.FileOptions.OptimizeMode"); - assert(e); - return e; -} +UPB_INLINE void upb_bufhandle_init(upb_bufhandle *h); +UPB_INLINE void upb_bufhandle_setobj(upb_bufhandle *h, const void *obj, + const void *type); +UPB_INLINE void upb_bufhandle_setbuf(upb_bufhandle *h, const char *buf, + size_t ofs); +UPB_INLINE const void *upb_bufhandle_obj(const upb_bufhandle *h); +UPB_INLINE const void *upb_bufhandle_objtype(const upb_bufhandle *h); +UPB_INLINE const char *upb_bufhandle_buf(const upb_bufhandle *h); -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_ExtensionRange_end(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto_ExtensionRange(s), 2); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_ExtensionRange_start(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto_ExtensionRange(s), 1); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_enum_type(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto(s), 4); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_extension(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto(s), 6); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_extension_range(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto(s), 5); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_field(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto(s), 2); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto(s), 1); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_nested_type(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto(s), 3); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_options(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto(s), 7); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumDescriptorProto_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumDescriptorProto(s), 1); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumDescriptorProto_options(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumDescriptorProto(s), 3); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumDescriptorProto_value(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumDescriptorProto(s), 2); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumOptions_allow_alias(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumOptions(s), 2); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumOptions_uninterpreted_option(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumOptions(s), 999); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumValueDescriptorProto_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumValueDescriptorProto(s), 1); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumValueDescriptorProto_number(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumValueDescriptorProto(s), 2); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumValueDescriptorProto_options(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumValueDescriptorProto(s), 3); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumValueOptions_uninterpreted_option(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumValueOptions(s), 999); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_default_value(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 7); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_extendee(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 2); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_label(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 4); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 1); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_number(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 3); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_options(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 8); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_type(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 5); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_type_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 6); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_ctype(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldOptions(s), 1); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_deprecated(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldOptions(s), 3); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_experimental_map_key(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldOptions(s), 9); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_lazy(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldOptions(s), 5); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_packed(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldOptions(s), 2); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_uninterpreted_option(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldOptions(s), 999); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_weak(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldOptions(s), 10); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_dependency(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 3); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_enum_type(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 5); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_extension(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 7); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_message_type(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 4); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 1); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_options(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 8); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_package(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 2); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_public_dependency(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 10); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_service(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 6); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_source_code_info(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 9); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_weak_dependency(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 11); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorSet_file(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorSet(s), 1); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_cc_generic_services(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 16); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_go_package(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 11); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_java_generate_equals_and_hash(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 20); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_java_generic_services(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 17); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_java_multiple_files(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 10); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_java_outer_classname(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 8); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_java_package(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 1); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_optimize_for(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 9); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_py_generic_services(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 18); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_uninterpreted_option(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 999); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MessageOptions_message_set_wire_format(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MessageOptions(s), 1); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MessageOptions_no_standard_descriptor_accessor(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MessageOptions(s), 2); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MessageOptions_uninterpreted_option(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MessageOptions(s), 999); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodDescriptorProto_input_type(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MethodDescriptorProto(s), 2); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodDescriptorProto_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MethodDescriptorProto(s), 1); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodDescriptorProto_options(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MethodDescriptorProto(s), 4); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodDescriptorProto_output_type(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MethodDescriptorProto(s), 3); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodOptions_uninterpreted_option(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MethodOptions(s), 999); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_ServiceDescriptorProto_method(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_ServiceDescriptorProto(s), 2); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_ServiceDescriptorProto_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_ServiceDescriptorProto(s), 1); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_ServiceDescriptorProto_options(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_ServiceDescriptorProto(s), 3); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_ServiceOptions_uninterpreted_option(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_ServiceOptions(s), 999); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_SourceCodeInfo_Location_leading_comments(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_SourceCodeInfo_Location(s), 3); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_SourceCodeInfo_Location_path(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_SourceCodeInfo_Location(s), 1); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_SourceCodeInfo_Location_span(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_SourceCodeInfo_Location(s), 2); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_SourceCodeInfo_Location_trailing_comments(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_SourceCodeInfo_Location(s), 4); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_SourceCodeInfo_location(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_SourceCodeInfo(s), 1); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_NamePart_is_extension(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption_NamePart(s), 2); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_NamePart_name_part(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption_NamePart(s), 1); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_aggregate_value(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption(s), 8); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_double_value(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption(s), 6); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_identifier_value(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption(s), 3); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption(s), 2); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_negative_int_value(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption(s), 5); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_positive_int_value(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption(s), 4); } -UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_string_value(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption(s), 7); } +UPB_END_EXTERN_C -#ifdef __cplusplus -}; // extern "C" -#endif -#ifdef __cplusplus +/* Static selectors for upb::Handlers. */ +#define UPB_STARTMSG_SELECTOR 0 +#define UPB_ENDMSG_SELECTOR 1 +#define UPB_STATIC_SELECTOR_COUNT 2 -namespace upbdefs { -namespace google { -namespace protobuf { -namespace descriptor { -inline upb::reffed_ptr SymbolTable() { - const upb::SymbolTable* s = upbdefs_google_protobuf_descriptor(&s); - return upb::reffed_ptr(s, &s); -} -} // namespace descriptor -} // namespace protobuf -} // namespace google +/* Static selectors for upb::BytesHandler. */ +#define UPB_STARTSTR_SELECTOR 0 +#define UPB_STRING_SELECTOR 1 +#define UPB_ENDSTR_SELECTOR 2 -#define RETURN_REFFED(type, func) \ - const type* obj = func(upbdefs::google::protobuf::descriptor::SymbolTable().get()); \ - return upb::reffed_ptr(obj); +typedef void upb_handlerfree(void *d); -namespace google { -namespace protobuf { -namespace DescriptorProto { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_DescriptorProto) } -inline upb::reffed_ptr enum_type() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_enum_type) } -inline upb::reffed_ptr extension() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_extension) } -inline upb::reffed_ptr extension_range() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_extension_range) } -inline upb::reffed_ptr field() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_field) } -inline upb::reffed_ptr name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_name) } -inline upb::reffed_ptr nested_type() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_nested_type) } -inline upb::reffed_ptr options() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_options) } -} // namespace DescriptorProto -} // namespace protobuf -} // namespace google +#ifdef __cplusplus -namespace google { -namespace protobuf { -namespace DescriptorProto { -namespace ExtensionRange { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_DescriptorProto_ExtensionRange) } -inline upb::reffed_ptr end() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_ExtensionRange_end) } -inline upb::reffed_ptr start() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_ExtensionRange_start) } -} // namespace ExtensionRange -} // namespace DescriptorProto -} // namespace protobuf -} // namespace google +/* A set of attributes that accompanies a handler's function pointer. */ +class upb::HandlerAttributes { + public: + HandlerAttributes(); + ~HandlerAttributes(); -namespace google { -namespace protobuf { -namespace EnumDescriptorProto { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_EnumDescriptorProto) } -inline upb::reffed_ptr name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumDescriptorProto_name) } -inline upb::reffed_ptr options() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumDescriptorProto_options) } -inline upb::reffed_ptr value() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumDescriptorProto_value) } -} // namespace EnumDescriptorProto -} // namespace protobuf -} // namespace google + /* Sets the handler data that will be passed as the second parameter of the + * handler. To free this pointer when the handlers are freed, call + * Handlers::AddCleanup(). */ + bool SetHandlerData(const void *handler_data); + const void* handler_data() const; -namespace google { -namespace protobuf { -namespace EnumOptions { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_EnumOptions) } -inline upb::reffed_ptr allow_alias() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumOptions_allow_alias) } -inline upb::reffed_ptr uninterpreted_option() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumOptions_uninterpreted_option) } -} // namespace EnumOptions -} // namespace protobuf -} // namespace google + /* Use this to specify the type of the closure. This will be checked against + * all other closure types for handler that use the same closure. + * Registration will fail if this does not match all other non-NULL closure + * types. */ + bool SetClosureType(const void *closure_type); + const void* closure_type() const; -namespace google { -namespace protobuf { -namespace EnumValueDescriptorProto { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_EnumValueDescriptorProto) } -inline upb::reffed_ptr name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumValueDescriptorProto_name) } -inline upb::reffed_ptr number() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumValueDescriptorProto_number) } -inline upb::reffed_ptr options() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumValueDescriptorProto_options) } -} // namespace EnumValueDescriptorProto -} // namespace protobuf -} // namespace google + /* Use this to specify the type of the returned closure. Only used for + * Start*{String,SubMessage,Sequence} handlers. This must match the closure + * type of any handlers that use it (for example, the StringBuf handler must + * match the closure returned from StartString). */ + bool SetReturnClosureType(const void *return_closure_type); + const void* return_closure_type() const; -namespace google { -namespace protobuf { -namespace EnumValueOptions { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_EnumValueOptions) } -inline upb::reffed_ptr uninterpreted_option() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumValueOptions_uninterpreted_option) } -} // namespace EnumValueOptions -} // namespace protobuf -} // namespace google + /* Set to indicate that the handler always returns "ok" (either "true" or a + * non-NULL closure). This is a hint that can allow code generators to + * generate more efficient code. */ + bool SetAlwaysOk(bool always_ok); + bool always_ok() const; -namespace google { -namespace protobuf { -namespace FieldDescriptorProto { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_FieldDescriptorProto) } -inline upb::reffed_ptr default_value() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_default_value) } -inline upb::reffed_ptr extendee() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_extendee) } -inline upb::reffed_ptr label() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_label) } -inline upb::reffed_ptr name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_name) } -inline upb::reffed_ptr number() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_number) } -inline upb::reffed_ptr options() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_options) } -inline upb::reffed_ptr type() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_type) } -inline upb::reffed_ptr type_name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_type_name) } -inline upb::reffed_ptr Label() { RETURN_REFFED(upb::EnumDef, upbdefs_google_protobuf_FieldDescriptorProto_Label) } -inline upb::reffed_ptr Type() { RETURN_REFFED(upb::EnumDef, upbdefs_google_protobuf_FieldDescriptorProto_Type) } -} // namespace FieldDescriptorProto -} // namespace protobuf -} // namespace google - -namespace google { -namespace protobuf { -namespace FieldOptions { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_FieldOptions) } -inline upb::reffed_ptr ctype() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldOptions_ctype) } -inline upb::reffed_ptr deprecated() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldOptions_deprecated) } -inline upb::reffed_ptr experimental_map_key() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldOptions_experimental_map_key) } -inline upb::reffed_ptr lazy() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldOptions_lazy) } -inline upb::reffed_ptr packed() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldOptions_packed) } -inline upb::reffed_ptr uninterpreted_option() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldOptions_uninterpreted_option) } -inline upb::reffed_ptr weak() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldOptions_weak) } -inline upb::reffed_ptr CType() { RETURN_REFFED(upb::EnumDef, upbdefs_google_protobuf_FieldOptions_CType) } -} // namespace FieldOptions -} // namespace protobuf -} // namespace google - -namespace google { -namespace protobuf { -namespace FileDescriptorProto { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_FileDescriptorProto) } -inline upb::reffed_ptr dependency() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_dependency) } -inline upb::reffed_ptr enum_type() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_enum_type) } -inline upb::reffed_ptr extension() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_extension) } -inline upb::reffed_ptr message_type() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_message_type) } -inline upb::reffed_ptr name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_name) } -inline upb::reffed_ptr options() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_options) } -inline upb::reffed_ptr package() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_package) } -inline upb::reffed_ptr public_dependency() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_public_dependency) } -inline upb::reffed_ptr service() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_service) } -inline upb::reffed_ptr source_code_info() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_source_code_info) } -inline upb::reffed_ptr weak_dependency() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_weak_dependency) } -} // namespace FileDescriptorProto -} // namespace protobuf -} // namespace google - -namespace google { -namespace protobuf { -namespace FileDescriptorSet { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_FileDescriptorSet) } -inline upb::reffed_ptr file() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorSet_file) } -} // namespace FileDescriptorSet -} // namespace protobuf -} // namespace google - -namespace google { -namespace protobuf { -namespace FileOptions { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_FileOptions) } -inline upb::reffed_ptr cc_generic_services() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_cc_generic_services) } -inline upb::reffed_ptr go_package() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_go_package) } -inline upb::reffed_ptr java_generate_equals_and_hash() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_java_generate_equals_and_hash) } -inline upb::reffed_ptr java_generic_services() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_java_generic_services) } -inline upb::reffed_ptr java_multiple_files() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_java_multiple_files) } -inline upb::reffed_ptr java_outer_classname() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_java_outer_classname) } -inline upb::reffed_ptr java_package() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_java_package) } -inline upb::reffed_ptr optimize_for() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_optimize_for) } -inline upb::reffed_ptr py_generic_services() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_py_generic_services) } -inline upb::reffed_ptr uninterpreted_option() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_uninterpreted_option) } -inline upb::reffed_ptr OptimizeMode() { RETURN_REFFED(upb::EnumDef, upbdefs_google_protobuf_FileOptions_OptimizeMode) } -} // namespace FileOptions -} // namespace protobuf -} // namespace google + private: + friend UPB_INLINE const void * ::upb_handlerattr_handlerdata( + const upb_handlerattr *attr); +#else +struct upb_handlerattr { +#endif + const void *handler_data_; + const void *closure_type_; + const void *return_closure_type_; + bool alwaysok_; +}; -namespace google { -namespace protobuf { -namespace MessageOptions { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_MessageOptions) } -inline upb::reffed_ptr message_set_wire_format() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MessageOptions_message_set_wire_format) } -inline upb::reffed_ptr no_standard_descriptor_accessor() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MessageOptions_no_standard_descriptor_accessor) } -inline upb::reffed_ptr uninterpreted_option() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MessageOptions_uninterpreted_option) } -} // namespace MessageOptions -} // namespace protobuf -} // namespace google +#define UPB_HANDLERATTR_INITIALIZER {NULL, NULL, NULL, false} -namespace google { -namespace protobuf { -namespace MethodDescriptorProto { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_MethodDescriptorProto) } -inline upb::reffed_ptr input_type() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MethodDescriptorProto_input_type) } -inline upb::reffed_ptr name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MethodDescriptorProto_name) } -inline upb::reffed_ptr options() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MethodDescriptorProto_options) } -inline upb::reffed_ptr output_type() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MethodDescriptorProto_output_type) } -} // namespace MethodDescriptorProto -} // namespace protobuf -} // namespace google +typedef struct { + upb_func *func; -namespace google { -namespace protobuf { -namespace MethodOptions { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_MethodOptions) } -inline upb::reffed_ptr uninterpreted_option() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MethodOptions_uninterpreted_option) } -} // namespace MethodOptions -} // namespace protobuf -} // namespace google + /* It is wasteful to include the entire attributes here: + * + * * Some of the information is redundant (like storing the closure type + * separately for each handler that must match). + * * Some of the info is only needed prior to freeze() (like closure types). + * * alignment padding wastes a lot of space for alwaysok_. + * + * If/when the size and locality of handlers is an issue, we can optimize this + * not to store the entire attr like this. We do not expose the table's + * layout to allow this optimization in the future. */ + upb_handlerattr attr; +} upb_handlers_tabent; -namespace google { -namespace protobuf { -namespace ServiceDescriptorProto { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_ServiceDescriptorProto) } -inline upb::reffed_ptr method() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_ServiceDescriptorProto_method) } -inline upb::reffed_ptr name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_ServiceDescriptorProto_name) } -inline upb::reffed_ptr options() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_ServiceDescriptorProto_options) } -} // namespace ServiceDescriptorProto -} // namespace protobuf -} // namespace google +#ifdef __cplusplus -namespace google { -namespace protobuf { -namespace ServiceOptions { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_ServiceOptions) } -inline upb::reffed_ptr uninterpreted_option() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_ServiceOptions_uninterpreted_option) } -} // namespace ServiceOptions -} // namespace protobuf -} // namespace google +/* Extra information about a buffer that is passed to a StringBuf handler. + * TODO(haberman): allow the handle to be pinned so that it will outlive + * the handler invocation. */ +class upb::BufferHandle { + public: + BufferHandle(); + ~BufferHandle(); -namespace google { -namespace protobuf { -namespace SourceCodeInfo { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_SourceCodeInfo) } -inline upb::reffed_ptr location() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_SourceCodeInfo_location) } -} // namespace SourceCodeInfo -} // namespace protobuf -} // namespace google + /* The beginning of the buffer. This may be different than the pointer + * passed to a StringBuf handler because the handler may receive data + * that is from the middle or end of a larger buffer. */ + const char* buffer() const; -namespace google { -namespace protobuf { -namespace SourceCodeInfo { -namespace Location { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_SourceCodeInfo_Location) } -inline upb::reffed_ptr leading_comments() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_SourceCodeInfo_Location_leading_comments) } -inline upb::reffed_ptr path() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_SourceCodeInfo_Location_path) } -inline upb::reffed_ptr span() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_SourceCodeInfo_Location_span) } -inline upb::reffed_ptr trailing_comments() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_SourceCodeInfo_Location_trailing_comments) } -} // namespace Location -} // namespace SourceCodeInfo -} // namespace protobuf -} // namespace google + /* The offset within the attached object where this buffer begins. Only + * meaningful if there is an attached object. */ + size_t object_offset() const; -namespace google { -namespace protobuf { -namespace UninterpretedOption { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_UninterpretedOption) } -inline upb::reffed_ptr aggregate_value() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_aggregate_value) } -inline upb::reffed_ptr double_value() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_double_value) } -inline upb::reffed_ptr identifier_value() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_identifier_value) } -inline upb::reffed_ptr name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_name) } -inline upb::reffed_ptr negative_int_value() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_negative_int_value) } -inline upb::reffed_ptr positive_int_value() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_positive_int_value) } -inline upb::reffed_ptr string_value() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_string_value) } -} // namespace UninterpretedOption -} // namespace protobuf -} // namespace google + /* Note that object_offset is the offset of "buf" within the attached + * object. */ + void SetBuffer(const char* buf, size_t object_offset); -namespace google { -namespace protobuf { -namespace UninterpretedOption { -namespace NamePart { -inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_UninterpretedOption_NamePart) } -inline upb::reffed_ptr is_extension() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_NamePart_is_extension) } -inline upb::reffed_ptr name_part() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_NamePart_name_part) } -} // namespace NamePart -} // namespace UninterpretedOption -} // namespace protobuf -} // namespace google + /* The BufferHandle can have an "attached object", which can be used to + * tunnel through a pointer to the buffer's underlying representation. */ + template + void SetAttachedObject(const T* obj); -} // namespace upbdefs + /* Returns NULL if the attached object is not of this type. */ + template + const T* GetAttachedObject() const; + private: + friend UPB_INLINE void ::upb_bufhandle_init(upb_bufhandle *h); + friend UPB_INLINE void ::upb_bufhandle_setobj(upb_bufhandle *h, + const void *obj, + const void *type); + friend UPB_INLINE void ::upb_bufhandle_setbuf(upb_bufhandle *h, + const char *buf, size_t ofs); + friend UPB_INLINE const void* ::upb_bufhandle_obj(const upb_bufhandle *h); + friend UPB_INLINE const void* ::upb_bufhandle_objtype( + const upb_bufhandle *h); + friend UPB_INLINE const char* ::upb_bufhandle_buf(const upb_bufhandle *h); +#else +struct upb_bufhandle { +#endif + const char *buf_; + const void *obj_; + const void *objtype_; + size_t objofs_; +}; -#undef RETURN_REFFED -#endif // __cplusplus +#ifdef __cplusplus -#endif // GOOGLE_PROTOBUF_DESCRIPTOR_UPB_H_ -/* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2010-2012 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * A upb_handlers is like a virtual table for a upb_msgdef. Each field of the - * message can have associated functions that will be called when we are - * parsing or visiting a stream of data. This is similar to how handlers work - * in SAX (the Simple API for XML). +/* A upb::Handlers object represents the set of handlers associated with a + * message in the graph of messages. You can think of it as a big virtual + * table with functions corresponding to all the events that can fire while + * parsing or visiting a message of a specific type. * - * The handlers have no idea where the data is coming from, so a single set of - * handlers could be used with two completely different data sources (for - * example, a parser and a visitor over in-memory objects). This decoupling is - * the most important feature of upb, because it allows parsers and serializers - * to be highly reusable. + * Any handlers that are not set behave as if they had successfully consumed + * the value. Any unset Start* handlers will propagate their closure to the + * inner frame. * - * This is a mixed C/C++ interface that offers a full API to both languages. - * See the top-level README for more information. - */ + * The easiest way to create the *Handler objects needed by the Set* methods is + * with the UpbBind() and UpbMakeHandler() macros; see below. */ +class upb::Handlers { + public: + typedef upb_selector_t Selector; + typedef upb_handlertype_t Type; -#ifndef UPB_HANDLERS_H -#define UPB_HANDLERS_H + typedef Handler StartFieldHandler; + typedef Handler EndFieldHandler; + typedef Handler StartMessageHandler; + typedef Handler EndMessageHandler; + typedef Handler StartStringHandler; + typedef Handler StringHandler; + template struct ValueHandler { + typedef Handler H; + }; -#ifdef __cplusplus -namespace upb { -class BufferHandle; -class BytesHandler; -class HandlerAttributes; -class Handlers; -template class Handler; -template struct CanonicalType; -} // namespace upb -#endif + typedef ValueHandler::H Int32Handler; + typedef ValueHandler::H Int64Handler; + typedef ValueHandler::H UInt32Handler; + typedef ValueHandler::H UInt64Handler; + typedef ValueHandler::H FloatHandler; + typedef ValueHandler::H DoubleHandler; + typedef ValueHandler::H BoolHandler; -UPB_DECLARE_TYPE(upb::BufferHandle, upb_bufhandle); -UPB_DECLARE_TYPE(upb::BytesHandler, upb_byteshandler); -UPB_DECLARE_TYPE(upb::HandlerAttributes, upb_handlerattr); -UPB_DECLARE_TYPE(upb::Handlers, upb_handlers); - -// The maximum depth that the handler graph can have. This is a resource limit -// for the C stack since we sometimes need to recursively traverse the graph. -// Cycles are ok; the traversal will stop when it detects a cycle, but we must -// hit the cycle before the maximum depth is reached. -// -// If having a single static limit is too inflexible, we can add another variant -// of Handlers::Freeze that allows specifying this as a parameter. -#define UPB_MAX_HANDLER_DEPTH 64 + /* Any function pointer can be converted to this and converted back to its + * correct type. */ + typedef void GenericFunction(); -// All the different types of handlers that can be registered. -// Only needed for the advanced functions in upb::Handlers. -typedef enum { - UPB_HANDLER_INT32, - UPB_HANDLER_INT64, - UPB_HANDLER_UINT32, - UPB_HANDLER_UINT64, - UPB_HANDLER_FLOAT, - UPB_HANDLER_DOUBLE, - UPB_HANDLER_BOOL, - UPB_HANDLER_STARTSTR, - UPB_HANDLER_STRING, - UPB_HANDLER_ENDSTR, - UPB_HANDLER_STARTSUBMSG, - UPB_HANDLER_ENDSUBMSG, - UPB_HANDLER_STARTSEQ, - UPB_HANDLER_ENDSEQ, -} upb_handlertype_t; + typedef void HandlersCallback(const void *closure, upb_handlers *h); -#define UPB_HANDLER_MAX (UPB_HANDLER_ENDSEQ+1) + /* Returns a new handlers object for the given frozen msgdef. + * Returns NULL if memory allocation failed. */ + static reffed_ptr New(const MessageDef *m); -#define UPB_BREAK NULL + /* Convenience function for registering a graph of handlers that mirrors the + * graph of msgdefs for some message. For "m" and all its children a new set + * of handlers will be created and the given callback will be invoked, + * allowing the client to register handlers for this message. Note that any + * subhandlers set by the callback will be overwritten. */ + static reffed_ptr NewFrozen(const MessageDef *m, + HandlersCallback *callback, + const void *closure); -// A convenient definition for when no closure is needed. -extern char _upb_noclosure; -#define UPB_NO_CLOSURE &_upb_noclosure + /* Functionality from upb::RefCounted. */ + UPB_REFCOUNTED_CPPMETHODS -// A selector refers to a specific field handler in the Handlers object -// (for example: the STARTSUBMSG handler for field "field15"). -typedef int32_t upb_selector_t; + /* All handler registration functions return bool to indicate success or + * failure; details about failures are stored in this status object. If a + * failure does occur, it must be cleared before the Handlers are frozen, + * otherwise the freeze() operation will fail. The functions may *only* be + * used while the Handlers are mutable. */ + const Status* status(); + void ClearError(); -UPB_BEGIN_EXTERN_C + /* Call to freeze these Handlers. Requires that any SubHandlers are already + * frozen. For cycles, you must use the static version below and freeze the + * whole graph at once. */ + bool Freeze(Status* s); -// Forward-declares for C inline accessors. We need to declare these here -// so we can "friend" them in the class declarations in C++. -UPB_INLINE upb_func *upb_handlers_gethandler(const upb_handlers *h, - upb_selector_t s); -UPB_INLINE const void *upb_handlerattr_handlerdata(const upb_handlerattr *attr); -UPB_INLINE const void *upb_handlers_gethandlerdata(const upb_handlers *h, - upb_selector_t s); - -UPB_INLINE void upb_bufhandle_init(upb_bufhandle *h); -UPB_INLINE void upb_bufhandle_setobj(upb_bufhandle *h, const void *obj, - const void *type); -UPB_INLINE void upb_bufhandle_setbuf(upb_bufhandle *h, const char *buf, - size_t ofs); -UPB_INLINE const void *upb_bufhandle_obj(const upb_bufhandle *h); -UPB_INLINE const void *upb_bufhandle_objtype(const upb_bufhandle *h); -UPB_INLINE const char *upb_bufhandle_buf(const upb_bufhandle *h); - -UPB_END_EXTERN_C - - -// Static selectors for upb::Handlers. -#define UPB_STARTMSG_SELECTOR 0 -#define UPB_ENDMSG_SELECTOR 1 -#define UPB_STATIC_SELECTOR_COUNT 2 - -// Static selectors for upb::BytesHandler. -#define UPB_STARTSTR_SELECTOR 0 -#define UPB_STRING_SELECTOR 1 -#define UPB_ENDSTR_SELECTOR 2 - -typedef void upb_handlerfree(void *d); - -// A set of attributes that accompanies a handler's function pointer. -UPB_DEFINE_CLASS0(upb::HandlerAttributes, - public: - HandlerAttributes(); - ~HandlerAttributes(); - - // Sets the handler data that will be passed as the second parameter of the - // handler. To free this pointer when the handlers are freed, call - // Handlers::AddCleanup(). - bool SetHandlerData(const void *handler_data); - const void* handler_data() const; - - // Use this to specify the type of the closure. This will be checked against - // all other closure types for handler that use the same closure. - // Registration will fail if this does not match all other non-NULL closure - // types. - bool SetClosureType(const void *closure_type); - const void* closure_type() const; - - // Use this to specify the type of the returned closure. Only used for - // Start*{String,SubMessage,Sequence} handlers. This must match the closure - // type of any handlers that use it (for example, the StringBuf handler must - // match the closure returned from StartString). - bool SetReturnClosureType(const void *return_closure_type); - const void* return_closure_type() const; - - // Set to indicate that the handler always returns "ok" (either "true" or a - // non-NULL closure). This is a hint that can allow code generators to - // generate more efficient code. - bool SetAlwaysOk(bool always_ok); - bool always_ok() const; - - private: - friend UPB_INLINE const void * ::upb_handlerattr_handlerdata( - const upb_handlerattr *attr); -, -UPB_DEFINE_STRUCT0(upb_handlerattr, - const void *handler_data_; - const void *closure_type_; - const void *return_closure_type_; - bool alwaysok_; -)); - -#define UPB_HANDLERATTR_INITIALIZER {NULL, NULL, NULL, false} - -typedef struct { - upb_func *func; - // It is wasteful to include the entire attributes here: - // - // * Some of the information is redundant (like storing the closure type - // separately for each handler that must match). - // * Some of the info is only needed prior to freeze() (like closure types). - // * alignment padding wastes a lot of space for alwaysok_. - // - // If/when the size and locality of handlers is an issue, we can optimize this - // not to store the entire attr like this. We do not expose the table's - // layout to allow this optimization in the future. - upb_handlerattr attr; -} upb_handlers_tabent; - -// Extra information about a buffer that is passed to a StringBuf handler. -// TODO(haberman): allow the handle to be pinned so that it will outlive -// the handler invocation. -UPB_DEFINE_CLASS0(upb::BufferHandle, - public: - BufferHandle(); - ~BufferHandle(); - - // The beginning of the buffer. This may be different than the pointer - // passed to a StringBuf handler because the handler may receive data - // that is from the middle or end of a larger buffer. - const char* buffer() const; - - // The offset within the attached object where this buffer begins. Only - // meaningful if there is an attached object. - size_t object_offset() const; - - // Note that object_offset is the offset of "buf" within the attached object. - void SetBuffer(const char* buf, size_t object_offset); - - // The BufferHandle can have an "attached object", which can be used to - // tunnel through a pointer to the buffer's underlying representation. - template - void SetAttachedObject(const T* obj); - - // Returns NULL if the attached object is not of this type. - template - const T* GetAttachedObject() const; - - private: - friend UPB_INLINE void ::upb_bufhandle_init(upb_bufhandle *h); - friend UPB_INLINE void ::upb_bufhandle_setobj(upb_bufhandle *h, - const void *obj, - const void *type); - friend UPB_INLINE void ::upb_bufhandle_setbuf(upb_bufhandle *h, - const char *buf, size_t ofs); - friend UPB_INLINE const void* ::upb_bufhandle_obj(const upb_bufhandle *h); - friend UPB_INLINE const void* ::upb_bufhandle_objtype( - const upb_bufhandle *h); - friend UPB_INLINE const char* ::upb_bufhandle_buf(const upb_bufhandle *h); -, -UPB_DEFINE_STRUCT0(upb_bufhandle, - const char *buf_; - const void *obj_; - const void *objtype_; - size_t objofs_; -)); - -// A upb::Handlers object represents the set of handlers associated with a -// message in the graph of messages. You can think of it as a big virtual -// table with functions corresponding to all the events that can fire while -// parsing or visiting a message of a specific type. -// -// Any handlers that are not set behave as if they had successfully consumed -// the value. Any unset Start* handlers will propagate their closure to the -// inner frame. -// -// The easiest way to create the *Handler objects needed by the Set* methods is -// with the UpbBind() and UpbMakeHandler() macros; see below. -UPB_DEFINE_CLASS1(upb::Handlers, upb::RefCounted, - public: - typedef upb_selector_t Selector; - typedef upb_handlertype_t Type; - - typedef Handler StartFieldHandler; - typedef Handler EndFieldHandler; - typedef Handler StartMessageHandler; - typedef Handler EndMessageHandler; - typedef Handler StartStringHandler; - typedef Handler StringHandler; - - template struct ValueHandler { - typedef Handler H; - }; - - typedef ValueHandler::H Int32Handler; - typedef ValueHandler::H Int64Handler; - typedef ValueHandler::H UInt32Handler; - typedef ValueHandler::H UInt64Handler; - typedef ValueHandler::H FloatHandler; - typedef ValueHandler::H DoubleHandler; - typedef ValueHandler::H BoolHandler; - - // Any function pointer can be converted to this and converted back to its - // correct type. - typedef void GenericFunction(); - - typedef void HandlersCallback(const void *closure, upb_handlers *h); - - // Returns a new handlers object for the given frozen msgdef. - // Returns NULL if memory allocation failed. - static reffed_ptr New(const MessageDef *m); - - // Convenience function for registering a graph of handlers that mirrors the - // graph of msgdefs for some message. For "m" and all its children a new set - // of handlers will be created and the given callback will be invoked, - // allowing the client to register handlers for this message. Note that any - // subhandlers set by the callback will be overwritten. - static reffed_ptr NewFrozen(const MessageDef *m, - HandlersCallback *callback, - const void *closure); - - // 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; - - // All handler registration functions return bool to indicate success or - // failure; details about failures are stored in this status object. If a - // failure does occur, it must be cleared before the Handlers are frozen, - // otherwise the freeze() operation will fail. The functions may *only* be - // used while the Handlers are mutable. - const Status* status(); - void ClearError(); - - // Call to freeze these Handlers. Requires that any SubHandlers are already - // frozen. For cycles, you must use the static version below and freeze the - // whole graph at once. - bool Freeze(Status* s); - - // Freezes the given set of handlers. You may not freeze a handler without - // also freezing any handlers they point to. + /* Freezes the given set of handlers. You may not freeze a handler without + * also freezing any handlers they point to. */ static bool Freeze(Handlers*const* handlers, int n, Status* s); static bool Freeze(const std::vector& handlers, Status* s); - // Returns the msgdef associated with this handlers object. + /* Returns the msgdef associated with this handlers object. */ const MessageDef* message_def() const; - // Adds the given pointer and function to the list of cleanup functions that - // will be run when these handlers are freed. If this pointer has previously - // been registered, the function returns false and does nothing. + /* Adds the given pointer and function to the list of cleanup functions that + * will be run when these handlers are freed. If this pointer has previously + * been registered, the function returns false and does nothing. */ bool AddCleanup(void *ptr, upb_handlerfree *cleanup); - // Sets the startmsg handler for the message, which is defined as follows: - // - // bool startmsg(MyType* closure) { - // // Called when the message begins. Returns true if processing should - // // continue. - // return true; - // } + /* Sets the startmsg handler for the message, which is defined as follows: + * + * bool startmsg(MyType* closure) { + * // Called when the message begins. Returns true if processing should + * // continue. + * return true; + * } + */ bool SetStartMessageHandler(const StartMessageHandler& handler); - // Sets the endmsg handler for the message, which is defined as follows: - // - // bool endmsg(MyType* closure, upb_status *status) { - // // Called when processing of this message ends, whether in success or - // // failure. "status" indicates the final status of processing, and - // // can also be modified in-place to update the final status. - // } + /* Sets the endmsg handler for the message, which is defined as follows: + * + * bool endmsg(MyType* closure, upb_status *status) { + * // Called when processing of this message ends, whether in success or + * // failure. "status" indicates the final status of processing, and + * // can also be modified in-place to update the final status. + * } + */ bool SetEndMessageHandler(const EndMessageHandler& handler); - // Sets the value handler for the given field, which is defined as follows - // (this is for an int32 field; other field types will pass their native - // C/C++ type for "val"): - // - // bool OnValue(MyClosure* c, const MyHandlerData* d, int32_t val) { - // // Called when the field's value is encountered. "d" contains - // // whatever data was bound to this field when it was registered. - // // Returns true if processing should continue. - // return true; - // } - // - // handers->SetInt32Handler(f, UpbBind(OnValue, new MyHandlerData(...))); - // - // The value type must exactly match f->type(). - // For example, a handler that takes an int32_t parameter may only be used for - // fields of type UPB_TYPE_INT32 and UPB_TYPE_ENUM. - // - // Returns false if the handler failed to register; in this case the cleanup - // handler (if any) will be called immediately. + /* Sets the value handler for the given field, which is defined as follows + * (this is for an int32 field; other field types will pass their native + * C/C++ type for "val"): + * + * bool OnValue(MyClosure* c, const MyHandlerData* d, int32_t val) { + * // Called when the field's value is encountered. "d" contains + * // whatever data was bound to this field when it was registered. + * // Returns true if processing should continue. + * return true; + * } + * + * handers->SetInt32Handler(f, UpbBind(OnValue, new MyHandlerData(...))); + * + * The value type must exactly match f->type(). + * For example, a handler that takes an int32_t parameter may only be used for + * fields of type UPB_TYPE_INT32 and UPB_TYPE_ENUM. + * + * Returns false if the handler failed to register; in this case the cleanup + * handler (if any) will be called immediately. + */ bool SetInt32Handler (const FieldDef* f, const Int32Handler& h); bool SetInt64Handler (const FieldDef* f, const Int64Handler& h); bool SetUInt32Handler(const FieldDef* f, const UInt32Handler& h); @@ -4415,240 +3546,247 @@ UPB_DEFINE_CLASS1(upb::Handlers, upb::RefCounted, bool SetDoubleHandler(const FieldDef* f, const DoubleHandler& h); bool SetBoolHandler (const FieldDef* f, const BoolHandler& h); - // Like the previous, but templated on the type on the value (ie. int32). - // This is mostly useful to call from other templates. To call this you must - // specify the template parameter explicitly, ie: - // h->SetValueHandler(f, UpbBind(MyHandler, MyData)); + /* Like the previous, but templated on the type on the value (ie. int32). + * This is mostly useful to call from other templates. To call this you must + * specify the template parameter explicitly, ie: + * h->SetValueHandler(f, UpbBind(MyHandler, MyData)); */ template bool SetValueHandler( const FieldDef *f, const typename ValueHandler::Type>::H& handler); - // Sets handlers for a string field, which are defined as follows: - // - // MySubClosure* startstr(MyClosure* c, const MyHandlerData* d, - // size_t size_hint) { - // // Called when a string value begins. The return value indicates the - // // closure for the string. "size_hint" indicates the size of the - // // string if it is known, however if the string is length-delimited - // // and the end-of-string is not available size_hint will be zero. - // // This case is indistinguishable from the case where the size is - // // known to be zero. - // // - // // TODO(haberman): is it important to distinguish these cases? - // // If we had ssize_t as a type we could make -1 "unknown", but - // // ssize_t is POSIX (not ANSI) and therefore less portable. - // // In practice I suspect it won't be important to distinguish. - // return closure; - // } - // - // size_t str(MyClosure* closure, const MyHandlerData* d, - // const char *str, size_t len) { - // // Called for each buffer of string data; the multiple physical buffers - // // are all part of the same logical string. The return value indicates - // // how many bytes were consumed. If this number is less than "len", - // // this will also indicate that processing should be halted for now, - // // like returning false or UPB_BREAK from any other callback. If - // // number is greater than "len", the excess bytes will be skipped over - // // and not passed to the callback. - // return len; - // } - // - // bool endstr(MyClosure* c, const MyHandlerData* d) { - // // Called when a string value ends. Return value indicates whether - // // processing should continue. - // return true; - // } + /* Sets handlers for a string field, which are defined as follows: + * + * MySubClosure* startstr(MyClosure* c, const MyHandlerData* d, + * size_t size_hint) { + * // Called when a string value begins. The return value indicates the + * // closure for the string. "size_hint" indicates the size of the + * // string if it is known, however if the string is length-delimited + * // and the end-of-string is not available size_hint will be zero. + * // This case is indistinguishable from the case where the size is + * // known to be zero. + * // + * // TODO(haberman): is it important to distinguish these cases? + * // If we had ssize_t as a type we could make -1 "unknown", but + * // ssize_t is POSIX (not ANSI) and therefore less portable. + * // In practice I suspect it won't be important to distinguish. + * return closure; + * } + * + * size_t str(MyClosure* closure, const MyHandlerData* d, + * const char *str, size_t len) { + * // Called for each buffer of string data; the multiple physical buffers + * // are all part of the same logical string. The return value indicates + * // how many bytes were consumed. If this number is less than "len", + * // this will also indicate that processing should be halted for now, + * // like returning false or UPB_BREAK from any other callback. If + * // number is greater than "len", the excess bytes will be skipped over + * // and not passed to the callback. + * return len; + * } + * + * bool endstr(MyClosure* c, const MyHandlerData* d) { + * // Called when a string value ends. Return value indicates whether + * // processing should continue. + * return true; + * } + */ bool SetStartStringHandler(const FieldDef* f, const StartStringHandler& h); bool SetStringHandler(const FieldDef* f, const StringHandler& h); bool SetEndStringHandler(const FieldDef* f, const EndFieldHandler& h); - // Sets the startseq handler, which is defined as follows: - // - // MySubClosure *startseq(MyClosure* c, const MyHandlerData* d) { - // // Called when a sequence (repeated field) begins. The returned - // // pointer indicates the closure for the sequence (or UPB_BREAK - // // to interrupt processing). - // return closure; - // } - // - // h->SetStartSequenceHandler(f, UpbBind(startseq, new MyHandlerData(...))); - // - // Returns "false" if "f" does not belong to this message or is not a - // repeated field. + /* Sets the startseq handler, which is defined as follows: + * + * MySubClosure *startseq(MyClosure* c, const MyHandlerData* d) { + * // Called when a sequence (repeated field) begins. The returned + * // pointer indicates the closure for the sequence (or UPB_BREAK + * // to interrupt processing). + * return closure; + * } + * + * h->SetStartSequenceHandler(f, UpbBind(startseq, new MyHandlerData(...))); + * + * Returns "false" if "f" does not belong to this message or is not a + * repeated field. + */ bool SetStartSequenceHandler(const FieldDef* f, const StartFieldHandler& h); - // Sets the startsubmsg handler for the given field, which is defined as - // follows: - // - // MySubClosure* startsubmsg(MyClosure* c, const MyHandlerData* d) { - // // Called when a submessage begins. The returned pointer indicates the - // // closure for the sequence (or UPB_BREAK to interrupt processing). - // return closure; - // } - // - // h->SetStartSubMessageHandler(f, UpbBind(startsubmsg, - // new MyHandlerData(...))); - // - // Returns "false" if "f" does not belong to this message or is not a - // submessage/group field. + /* Sets the startsubmsg handler for the given field, which is defined as + * follows: + * + * MySubClosure* startsubmsg(MyClosure* c, const MyHandlerData* d) { + * // Called when a submessage begins. The returned pointer indicates the + * // closure for the sequence (or UPB_BREAK to interrupt processing). + * return closure; + * } + * + * h->SetStartSubMessageHandler(f, UpbBind(startsubmsg, + * new MyHandlerData(...))); + * + * Returns "false" if "f" does not belong to this message or is not a + * submessage/group field. + */ bool SetStartSubMessageHandler(const FieldDef* f, const StartFieldHandler& h); - // Sets the endsubmsg handler for the given field, which is defined as - // follows: - // - // bool endsubmsg(MyClosure* c, const MyHandlerData* d) { - // // Called when a submessage ends. Returns true to continue processing. - // return true; - // } - // - // Returns "false" if "f" does not belong to this message or is not a - // submessage/group field. + /* Sets the endsubmsg handler for the given field, which is defined as + * follows: + * + * bool endsubmsg(MyClosure* c, const MyHandlerData* d) { + * // Called when a submessage ends. Returns true to continue processing. + * return true; + * } + * + * Returns "false" if "f" does not belong to this message or is not a + * submessage/group field. + */ bool SetEndSubMessageHandler(const FieldDef *f, const EndFieldHandler &h); - // Starts the endsubseq handler for the given field, which is defined as - // follows: - // - // bool endseq(MyClosure* c, const MyHandlerData* d) { - // // Called when a sequence ends. Returns true continue processing. - // return true; - // } - // - // Returns "false" if "f" does not belong to this message or is not a - // repeated field. + /* Starts the endsubseq handler for the given field, which is defined as + * follows: + * + * bool endseq(MyClosure* c, const MyHandlerData* d) { + * // Called when a sequence ends. Returns true continue processing. + * return true; + * } + * + * Returns "false" if "f" does not belong to this message or is not a + * repeated field. + */ bool SetEndSequenceHandler(const FieldDef* f, const EndFieldHandler& h); - // Sets or gets the object that specifies handlers for the given field, which - // must be a submessage or group. Returns NULL if no handlers are set. + /* Sets or gets the object that specifies handlers for the given field, which + * must be a submessage or group. Returns NULL if no handlers are set. */ bool SetSubHandlers(const FieldDef* f, const Handlers* sub); const Handlers* GetSubHandlers(const FieldDef* f) const; - // Equivalent to GetSubHandlers, but takes the STARTSUBMSG selector for the - // field. + /* Equivalent to GetSubHandlers, but takes the STARTSUBMSG selector for the + * field. */ const Handlers* GetSubHandlers(Selector startsubmsg) const; - // A selector refers to a specific field handler in the Handlers object - // (for example: the STARTSUBMSG handler for field "field15"). - // On success, returns true and stores the selector in "s". - // If the FieldDef or Type are invalid, returns false. - // The returned selector is ONLY valid for Handlers whose MessageDef - // contains this FieldDef. + /* A selector refers to a specific field handler in the Handlers object + * (for example: the STARTSUBMSG handler for field "field15"). + * On success, returns true and stores the selector in "s". + * If the FieldDef or Type are invalid, returns false. + * The returned selector is ONLY valid for Handlers whose MessageDef + * contains this FieldDef. */ static bool GetSelector(const FieldDef* f, Type type, Selector* s); - // Given a START selector of any kind, returns the corresponding END selector. + /* Given a START selector of any kind, returns the corresponding END selector. */ static Selector GetEndSelector(Selector start_selector); - // Returns the function pointer for this handler. It is the client's - // responsibility to cast to the correct function type before calling it. + /* Returns the function pointer for this handler. It is the client's + * responsibility to cast to the correct function type before calling it. */ GenericFunction* GetHandler(Selector selector); - // Sets the given attributes to the attributes for this selector. + /* Sets the given attributes to the attributes for this selector. */ bool GetAttributes(Selector selector, HandlerAttributes* attr); - // Returns the handler data that was registered with this handler. + /* Returns the handler data that was registered with this handler. */ const void* GetHandlerData(Selector selector); - // Could add any of the following functions as-needed, with some minor - // implementation changes: - // - // const FieldDef* GetFieldDef(Selector selector); - // static bool IsSequence(Selector selector); + /* Could add any of the following functions as-needed, with some minor + * implementation changes: + * + * const FieldDef* GetFieldDef(Selector selector); + * static bool IsSequence(Selector selector); */ private: - UPB_DISALLOW_POD_OPS(Handlers, upb::Handlers); + UPB_DISALLOW_POD_OPS(Handlers, upb::Handlers) friend UPB_INLINE GenericFunction *::upb_handlers_gethandler( const upb_handlers *h, upb_selector_t s); friend UPB_INLINE const void *::upb_handlers_gethandlerdata( const upb_handlers *h, upb_selector_t s); +#else +struct upb_handlers { +#endif + upb_refcounted base; -, -UPB_DEFINE_STRUCT(upb_handlers, upb_refcounted, const upb_msgdef *msg; const upb_handlers **sub; const void *top_closure_type; upb_inttable cleanup_; - upb_status status_; // Used only when mutable. - upb_handlers_tabent table[1]; // Dynamically-sized field handler array. -)); - + upb_status status_; /* Used only when mutable. */ + upb_handlers_tabent table[1]; /* Dynamically-sized field handler array. */ +}; #ifdef __cplusplus namespace upb { -// Convenience macros for creating a Handler object that is wrapped with a -// type-safe wrapper function that converts the "void*" parameters/returns -// of the underlying C API into nice C++ function. -// -// Sample usage: -// void OnValue1(MyClosure* c, const MyHandlerData* d, int32_t val) { -// // do stuff ... -// } -// -// // Handler that doesn't need any data bound to it. -// void OnValue2(MyClosure* c, int32_t val) { -// // do stuff ... -// } -// -// // Handler that returns bool so it can return failure if necessary. -// bool OnValue3(MyClosure* c, int32_t val) { -// // do stuff ... -// return ok; -// } -// -// // Member function handler. -// class MyClosure { -// public: -// void OnValue(int32_t val) { -// // do stuff ... -// } -// }; -// -// // Takes ownership of the MyHandlerData. -// handlers->SetInt32Handler(f1, UpbBind(OnValue1, new MyHandlerData(...))); -// handlers->SetInt32Handler(f2, UpbMakeHandler(OnValue2)); -// handlers->SetInt32Handler(f1, UpbMakeHandler(OnValue3)); -// handlers->SetInt32Handler(f2, UpbMakeHandler(&MyClosure::OnValue)); +/* Convenience macros for creating a Handler object that is wrapped with a + * type-safe wrapper function that converts the "void*" parameters/returns + * of the underlying C API into nice C++ function. + * + * Sample usage: + * void OnValue1(MyClosure* c, const MyHandlerData* d, int32_t val) { + * // do stuff ... + * } + * + * // Handler that doesn't need any data bound to it. + * void OnValue2(MyClosure* c, int32_t val) { + * // do stuff ... + * } + * + * // Handler that returns bool so it can return failure if necessary. + * bool OnValue3(MyClosure* c, int32_t val) { + * // do stuff ... + * return ok; + * } + * + * // Member function handler. + * class MyClosure { + * public: + * void OnValue(int32_t val) { + * // do stuff ... + * } + * }; + * + * // Takes ownership of the MyHandlerData. + * handlers->SetInt32Handler(f1, UpbBind(OnValue1, new MyHandlerData(...))); + * handlers->SetInt32Handler(f2, UpbMakeHandler(OnValue2)); + * handlers->SetInt32Handler(f1, UpbMakeHandler(OnValue3)); + * handlers->SetInt32Handler(f2, UpbMakeHandler(&MyClosure::OnValue)); + */ #ifdef UPB_CXX11 -// In C++11, the "template" disambiguator can appear even outside templates, -// so all calls can safely use this pair of macros. +/* In C++11, the "template" disambiguator can appear even outside templates, + * so all calls can safely use this pair of macros. */ #define UpbMakeHandler(f) upb::MatchFunc(f).template GetFunc() -// We have to be careful to only evaluate "d" once. +/* We have to be careful to only evaluate "d" once. */ #define UpbBind(f, d) upb::MatchFunc(f).template GetFunc((d)) #else -// Prior to C++11, the "template" disambiguator may only appear inside a -// template, so the regular macro must not use "template" +/* Prior to C++11, the "template" disambiguator may only appear inside a + * template, so the regular macro must not use "template" */ #define UpbMakeHandler(f) upb::MatchFunc(f).GetFunc() #define UpbBind(f, d) upb::MatchFunc(f).GetFunc((d)) -#endif // UPB_CXX11 +#endif /* UPB_CXX11 */ -// This macro must be used in C++98 for calls from inside a template. But we -// define this variant in all cases; code that wants to be compatible with both -// C++98 and C++11 should always use this macro when calling from a template. +/* This macro must be used in C++98 for calls from inside a template. But we + * define this variant in all cases; code that wants to be compatible with both + * C++98 and C++11 should always use this macro when calling from a template. */ #define UpbMakeHandlerT(f) upb::MatchFunc(f).template GetFunc() -// We have to be careful to only evaluate "d" once. +/* We have to be careful to only evaluate "d" once. */ #define UpbBindT(f, d) upb::MatchFunc(f).template GetFunc((d)) -// Handler: a struct that contains the (handler, data, deleter) tuple that is -// used to register all handlers. Users can Make() these directly but it's -// more convenient to use the UpbMakeHandler/UpbBind macros above. +/* Handler: a struct that contains the (handler, data, deleter) tuple that is + * used to register all handlers. Users can Make() these directly but it's + * more convenient to use the UpbMakeHandler/UpbBind macros above. */ template class Handler { public: - // The underlying, handler function signature that upb uses internally. + /* The underlying, handler function signature that upb uses internally. */ typedef T FuncPtr; - // Intentionally implicit. + /* Intentionally implicit. */ template Handler(F func); ~Handler(); @@ -4660,7 +3798,7 @@ template class Handler { } } - UPB_DISALLOW_COPY_AND_ASSIGN(Handler); + UPB_DISALLOW_COPY_AND_ASSIGN(Handler) friend class Handlers; FuncPtr handler_; mutable HandlerAttributes attr_; @@ -4669,15 +3807,15 @@ template class Handler { upb_handlerfree *cleanup_func_; }; -} // namespace upb +} /* namespace upb */ -#endif // __cplusplus +#endif /* __cplusplus */ UPB_BEGIN_EXTERN_C -// Native C API. +/* Native C API. */ -// Handler function typedefs. +/* Handler function typedefs. */ typedef bool upb_startmsg_handlerfunc(void *c, const void*); typedef bool upb_endmsg_handlerfunc(void *c, const void *, upb_status *status); typedef void* upb_startfield_handlerfunc(void *c, const void *hd); @@ -4694,10 +3832,10 @@ typedef void *upb_startstr_handlerfunc(void *c, const void *hd, typedef size_t upb_string_handlerfunc(void *c, const void *hd, const char *buf, size_t n, const upb_bufhandle* handle); -// upb_bufhandle +/* upb_bufhandle */ size_t upb_bufhandle_objofs(const upb_bufhandle *h); -// upb_handlerattr +/* upb_handlerattr */ void upb_handlerattr_init(upb_handlerattr *attr); void upb_handlerattr_uninit(upb_handlerattr *attr); @@ -4715,7 +3853,7 @@ UPB_INLINE const void *upb_handlerattr_handlerdata( return attr->handler_data_; } -// upb_handlers +/* upb_handlers */ typedef void upb_handlers_callback(const void *closure, upb_handlers *h); upb_handlers *upb_handlers_new(const upb_msgdef *m, const void *owner); @@ -4723,12 +3861,9 @@ const upb_handlers *upb_handlers_newfrozen(const upb_msgdef *m, const void *owner, upb_handlers_callback *callback, const void *closure); -bool upb_handlers_isfrozen(const upb_handlers *h); -void upb_handlers_ref(const upb_handlers *h, const void *owner); -void upb_handlers_unref(const upb_handlers *h, const void *owner); -void upb_handlers_donateref(const upb_handlers *h, const void *from, - const void *to); -void upb_handlers_checkref(const upb_handlers *h, const void *owner); + +/* Include refcounted methods like upb_handlers_ref(). */ +UPB_REFCOUNTED_CMETHODS(upb_handlers, upb_handlers_upcast) const upb_status *upb_handlers_status(upb_handlers *h); void upb_handlers_clearerr(upb_handlers *h); @@ -4799,26 +3934,29 @@ UPB_INLINE const void *upb_handlers_gethandlerdata(const upb_handlers *h, return upb_handlerattr_handlerdata(&h->table[s].attr); } -// Handler types for single fields. -// Right now we only have one for TYPE_BYTES but ones for other types -// should follow. -// -// These follow the same handlers protocol for fields of a message. -UPB_DEFINE_CLASS0(upb::BytesHandler, +#ifdef __cplusplus + +/* Handler types for single fields. + * Right now we only have one for TYPE_BYTES but ones for other types + * should follow. + * + * These follow the same handlers protocol for fields of a message. */ +class upb::BytesHandler { public: BytesHandler(); ~BytesHandler(); -, -UPB_DEFINE_STRUCT0(upb_byteshandler, +#else +struct upb_byteshandler { +#endif upb_handlers_tabent table[3]; -)); +}; void upb_byteshandler_init(upb_byteshandler *h); -// Caller must ensure that "d" outlives the handlers. -// TODO(haberman): should this have a "freeze" operation? It's not necessary -// for memory management, but could be useful to force immutability and provide -// a convenient moment to verify that all registration succeeded. +/* Caller must ensure that "d" outlives the handlers. + * TODO(haberman): should this have a "freeze" operation? It's not necessary + * for memory management, but could be useful to force immutability and provide + * a convenient moment to verify that all registration succeeded. */ bool upb_byteshandler_setstartstr(upb_byteshandler *h, upb_startstr_handlerfunc *func, void *d); bool upb_byteshandler_setstring(upb_byteshandler *h, @@ -4826,7 +3964,7 @@ bool upb_byteshandler_setstring(upb_byteshandler *h, bool upb_byteshandler_setendstr(upb_byteshandler *h, upb_endfield_handlerfunc *func, void *d); -// "Static" methods +/* "Static" methods */ bool upb_handlers_freeze(upb_handlers *const *handlers, int n, upb_status *s); upb_handlertype_t upb_handlers_getprimitivehandlertype(const upb_fielddef *f); bool upb_handlers_getselector(const upb_fielddef *f, upb_handlertype_t type, @@ -4835,7 +3973,7 @@ UPB_INLINE upb_selector_t upb_handlers_getendselector(upb_selector_t start) { return start + 1; } -// Internal-only. +/* Internal-only. */ uint32_t upb_handlers_selectorbaseoffset(const upb_fielddef *f); uint32_t upb_handlers_selectorcount(const upb_fielddef *f); @@ -4856,21 +3994,56 @@ UPB_END_EXTERN_C #include -// Type detection and typedefs for integer types. -// For platforms where there are multiple 32-bit or 64-bit types, we need to be -// able to enumerate them so we can properly create overloads for all variants. -// -// If any platform existed where there were three integer types with the same -// size, this would have to become more complicated. For example, short, int, -// and long could all be 32-bits. Even more diabolically, short, int, long, -// and long long could all be 64 bits and still be standard-compliant. -// However, few platforms are this strange, and it's unlikely that upb will be -// used on the strangest ones. - -// Can't count on stdint.h limits like INT32_MAX, because in C++ these are -// only defined when __STDC_LIMIT_MACROS are defined before the *first* include -// of stdint.h. We can't guarantee that someone else didn't include these first -// without defining __STDC_LIMIT_MACROS. +/* C inline methods. */ + +/* upb_bufhandle */ +UPB_INLINE void upb_bufhandle_init(upb_bufhandle *h) { + h->obj_ = NULL; + h->objtype_ = NULL; + h->buf_ = NULL; + h->objofs_ = 0; +} +UPB_INLINE void upb_bufhandle_uninit(upb_bufhandle *h) { + UPB_UNUSED(h); +} +UPB_INLINE void upb_bufhandle_setobj(upb_bufhandle *h, const void *obj, + const void *type) { + h->obj_ = obj; + h->objtype_ = type; +} +UPB_INLINE void upb_bufhandle_setbuf(upb_bufhandle *h, const char *buf, + size_t ofs) { + h->buf_ = buf; + h->objofs_ = ofs; +} +UPB_INLINE const void *upb_bufhandle_obj(const upb_bufhandle *h) { + return h->obj_; +} +UPB_INLINE const void *upb_bufhandle_objtype(const upb_bufhandle *h) { + return h->objtype_; +} +UPB_INLINE const char *upb_bufhandle_buf(const upb_bufhandle *h) { + return h->buf_; +} + + +#ifdef __cplusplus + +/* Type detection and typedefs for integer types. + * For platforms where there are multiple 32-bit or 64-bit types, we need to be + * able to enumerate them so we can properly create overloads for all variants. + * + * If any platform existed where there were three integer types with the same + * size, this would have to become more complicated. For example, short, int, + * and long could all be 32-bits. Even more diabolically, short, int, long, + * and long long could all be 64 bits and still be standard-compliant. + * However, few platforms are this strange, and it's unlikely that upb will be + * used on the strangest ones. */ + +/* Can't count on stdint.h limits like INT32_MAX, because in C++ these are + * only defined when __STDC_LIMIT_MACROS are defined before the *first* include + * of stdint.h. We can't guarantee that someone else didn't include these first + * without defining __STDC_LIMIT_MACROS. */ #define UPB_INT32_MAX 0x7fffffffLL #define UPB_INT32_MIN (-UPB_INT32_MAX - 1) #define UPB_INT64_MAX 0x7fffffffffffffffLL @@ -4892,8 +4065,8 @@ UPB_END_EXTERN_C #define UPB_LLONG_IS_64BITS 1 #endif -// We use macros instead of typedefs so we can undefine them later and avoid -// leaking them outside this header file. +/* We use macros instead of typedefs so we can undefine them later and avoid + * leaking them outside this header file. */ #if UPB_INT_IS_32BITS #define UPB_INT32_T int #define UPB_UINT32_T unsigned int @@ -4902,12 +4075,12 @@ UPB_END_EXTERN_C #define UPB_TWO_32BIT_TYPES 1 #define UPB_INT32ALT_T long #define UPB_UINT32ALT_T unsigned long -#endif // UPB_LONG_IS_32BITS +#endif /* UPB_LONG_IS_32BITS */ -#elif UPB_LONG_IS_32BITS // && !UPB_INT_IS_32BITS +#elif UPB_LONG_IS_32BITS /* && !UPB_INT_IS_32BITS */ #define UPB_INT32_T long #define UPB_UINT32_T unsigned long -#endif // UPB_INT_IS_32BITS +#endif /* UPB_INT_IS_32BITS */ #if UPB_LONG_IS_64BITS @@ -4918,12 +4091,12 @@ UPB_END_EXTERN_C #define UPB_TWO_64BIT_TYPES 1 #define UPB_INT64ALT_T long long #define UPB_UINT64ALT_T unsigned long long -#endif // UPB_LLONG_IS_64BITS +#endif /* UPB_LLONG_IS_64BITS */ -#elif UPB_LLONG_IS_64BITS // && !UPB_LONG_IS_64BITS +#elif UPB_LLONG_IS_64BITS /* && !UPB_LONG_IS_64BITS */ #define UPB_INT64_T long long #define UPB_UINT64_T unsigned long long -#endif // UPB_LONG_IS_64BITS +#endif /* UPB_LONG_IS_64BITS */ #undef UPB_INT32_MAX #undef UPB_INT32_MIN @@ -4934,56 +4107,22 @@ UPB_END_EXTERN_C #undef UPB_LONG_IS_64BITS #undef UPB_LLONG_IS_64BITS -// C inline methods. - -// upb_bufhandle -UPB_INLINE void upb_bufhandle_init(upb_bufhandle *h) { - h->obj_ = NULL; - h->objtype_ = NULL; - h->buf_ = NULL; - h->objofs_ = 0; -} -UPB_INLINE void upb_bufhandle_uninit(upb_bufhandle *h) { - UPB_UNUSED(h); -} -UPB_INLINE void upb_bufhandle_setobj(upb_bufhandle *h, const void *obj, - const void *type) { - h->obj_ = obj; - h->objtype_ = type; -} -UPB_INLINE void upb_bufhandle_setbuf(upb_bufhandle *h, const char *buf, - size_t ofs) { - h->buf_ = buf; - h->objofs_ = ofs; -} -UPB_INLINE const void *upb_bufhandle_obj(const upb_bufhandle *h) { - return h->obj_; -} -UPB_INLINE const void *upb_bufhandle_objtype(const upb_bufhandle *h) { - return h->objtype_; -} -UPB_INLINE const char *upb_bufhandle_buf(const upb_bufhandle *h) { - return h->buf_; -} - - -#ifdef __cplusplus namespace upb { typedef void CleanupFunc(void *ptr); -// Template to remove "const" from "const T*" and just return "T*". -// -// We define a nonsense default because otherwise it will fail to instantiate as -// a function parameter type even in cases where we don't expect any caller to -// actually match the overload. +/* Template to remove "const" from "const T*" and just return "T*". + * + * We define a nonsense default because otherwise it will fail to instantiate as + * a function parameter type even in cases where we don't expect any caller to + * actually match the overload. */ class CouldntRemoveConst {}; template struct remove_constptr { typedef CouldntRemoveConst type; }; template struct remove_constptr { typedef T *type; }; -// Template that we use below to remove a template specialization from -// consideration if it matches a specific type. +/* Template that we use below to remove a template specialization from + * consideration if it matches a specific type. */ template struct disable_if_same { typedef void Type; }; template struct disable_if_same {}; @@ -5020,27 +4159,27 @@ bool is_same::value = false; template bool is_same::value = true; -// FuncInfo //////////////////////////////////////////////////////////////////// +/* FuncInfo *******************************************************************/ -// Info about the user's original, pre-wrapped function. +/* Info about the user's original, pre-wrapped function. */ template struct FuncInfo { - // The type of the closure that the function takes (its first param). + /* The type of the closure that the function takes (its first param). */ typedef C Closure; - // The return type. + /* The return type. */ typedef R Return; }; -// Func //////////////////////////////////////////////////////////////////////// +/* Func ***********************************************************************/ -// Func1, Func2, Func3: Template classes representing a function and its -// signature. -// -// Since the function is a template parameter, calling the function can be -// inlined at compile-time and does not require a function pointer at runtime. -// These functions are not bound to a handler data so have no data or cleanup -// handler. +/* Func1, Func2, Func3: Template classes representing a function and its + * signature. + * + * Since the function is a template parameter, calling the function can be + * inlined at compile-time and does not require a function pointer at runtime. + * These functions are not bound to a handler data so have no data or cleanup + * handler. */ struct UnboundFunc { CleanupFunc *GetCleanup() { return NULL; } void *GetData() { return NULL; } @@ -5085,13 +4224,13 @@ struct Func5 : public UnboundFunc { } }; -// BoundFunc /////////////////////////////////////////////////////////////////// +/* BoundFunc ******************************************************************/ -// BoundFunc2, BoundFunc3: Like Func2/Func3 except also contains a value that -// shall be bound to the function's second parameter. -// -// Note that the second parameter is a const pointer, but our stored bound value -// is non-const so we can free it when the handlers are destroyed. +/* BoundFunc2, BoundFunc3: Like Func2/Func3 except also contains a value that + * shall be bound to the function's second parameter. + * + * Note that the second parameter is a const pointer, but our stored bound value + * is non-const so we can free it when the handlers are destroyed. */ template struct BoundFunc { typedef typename remove_constptr::type MutableP2; @@ -5131,13 +4270,13 @@ struct BoundFunc5 : public BoundFunc { explicit BoundFunc5(typename Base::MutableP2 arg) : Base(arg) {} }; -// FuncSig ///////////////////////////////////////////////////////////////////// +/* FuncSig ********************************************************************/ -// FuncSig1, FuncSig2, FuncSig3: template classes reflecting a function -// *signature*, but without a specific function attached. -// -// These classes contain member functions that can be invoked with a -// specific function to return a Func/BoundFunc class. +/* FuncSig1, FuncSig2, FuncSig3: template classes reflecting a function + * *signature*, but without a specific function attached. + * + * These classes contain member functions that can be invoked with a + * specific function to return a Func/BoundFunc class. */ template struct FuncSig1 { template @@ -5202,1716 +4341,2731 @@ struct FuncSig5 { } }; -// Overloaded template function that can construct the appropriate FuncSig* -// class given a function pointer by deducing the template parameters. +/* Overloaded template function that can construct the appropriate FuncSig* + * class given a function pointer by deducing the template parameters. */ template inline FuncSig1 MatchFunc(R (*f)(P1)) { - UPB_UNUSED(f); // Only used for template parameter deduction. + UPB_UNUSED(f); /* Only used for template parameter deduction. */ return FuncSig1(); } - -template -inline FuncSig2 MatchFunc(R (*f)(P1, P2)) { - UPB_UNUSED(f); // Only used for template parameter deduction. - return FuncSig2(); + +template +inline FuncSig2 MatchFunc(R (*f)(P1, P2)) { + UPB_UNUSED(f); /* Only used for template parameter deduction. */ + return FuncSig2(); +} + +template +inline FuncSig3 MatchFunc(R (*f)(P1, P2, P3)) { + UPB_UNUSED(f); /* Only used for template parameter deduction. */ + return FuncSig3(); +} + +template +inline FuncSig4 MatchFunc(R (*f)(P1, P2, P3, P4)) { + UPB_UNUSED(f); /* Only used for template parameter deduction. */ + return FuncSig4(); +} + +template +inline FuncSig5 MatchFunc(R (*f)(P1, P2, P3, P4, P5)) { + UPB_UNUSED(f); /* Only used for template parameter deduction. */ + return FuncSig5(); +} + +/* MethodSig ******************************************************************/ + +/* CallMethod*: a function template that calls a given method. */ +template +R CallMethod0(C *obj) { + return ((*obj).*F)(); +} + +template +R CallMethod1(C *obj, P1 arg1) { + return ((*obj).*F)(arg1); +} + +template +R CallMethod2(C *obj, P1 arg1, P2 arg2) { + return ((*obj).*F)(arg1, arg2); +} + +template +R CallMethod3(C *obj, P1 arg1, P2 arg2, P3 arg3) { + return ((*obj).*F)(arg1, arg2, arg3); +} + +template +R CallMethod4(C *obj, P1 arg1, P2 arg2, P3 arg3, P4 arg4) { + return ((*obj).*F)(arg1, arg2, arg3, arg4); +} + +/* MethodSig: like FuncSig, but for member functions. + * + * GetFunc() returns a normal FuncN object, so after calling GetFunc() no + * more logic is required to special-case methods. */ +template +struct MethodSig0 { + template + Func1, FuncInfo > GetFunc() { + return Func1, FuncInfo >(); + } +}; + +template +struct MethodSig1 { + template + Func2, FuncInfo > GetFunc() { + return Func2, FuncInfo >(); + } + + template + BoundFunc2, FuncInfo > GetFunc( + typename remove_constptr::type param1) { + return BoundFunc2, FuncInfo >( + param1); + } +}; + +template +struct MethodSig2 { + template + Func3, FuncInfo > + GetFunc() { + return Func3, + FuncInfo >(); + } + + template + BoundFunc3, FuncInfo > + GetFunc(typename remove_constptr::type param1) { + return BoundFunc3, + FuncInfo >(param1); + } +}; + +template +struct MethodSig3 { + template + Func4, FuncInfo > + GetFunc() { + return Func4, + FuncInfo >(); + } + + template + BoundFunc4, + FuncInfo > + GetFunc(typename remove_constptr::type param1) { + return BoundFunc4, + FuncInfo >(param1); + } +}; + +template +struct MethodSig4 { + template + Func5, + FuncInfo > + GetFunc() { + return Func5, + FuncInfo >(); + } + + template + BoundFunc5, + FuncInfo > + GetFunc(typename remove_constptr::type param1) { + return BoundFunc5, FuncInfo >( + param1); + } +}; + +template +inline MethodSig0 MatchFunc(R (C::*f)()) { + UPB_UNUSED(f); /* Only used for template parameter deduction. */ + return MethodSig0(); +} + +template +inline MethodSig1 MatchFunc(R (C::*f)(P1)) { + UPB_UNUSED(f); /* Only used for template parameter deduction. */ + return MethodSig1(); +} + +template +inline MethodSig2 MatchFunc(R (C::*f)(P1, P2)) { + UPB_UNUSED(f); /* Only used for template parameter deduction. */ + return MethodSig2(); +} + +template +inline MethodSig3 MatchFunc(R (C::*f)(P1, P2, P3)) { + UPB_UNUSED(f); /* Only used for template parameter deduction. */ + return MethodSig3(); +} + +template +inline MethodSig4 MatchFunc(R (C::*f)(P1, P2, P3, P4)) { + UPB_UNUSED(f); /* Only used for template parameter deduction. */ + return MethodSig4(); +} + +/* MaybeWrapReturn ************************************************************/ + +/* Template class that attempts to wrap the return value of the function so it + * matches the expected type. There are two main adjustments it may make: + * + * 1. If the function returns void, make it return the expected type and with + * a value that always indicates success. + * 2. If the function returns bool, make it return the expected type with a + * value that indicates success or failure. + * + * The "expected type" for return is: + * 1. void* for start handlers. If the closure parameter has a different type + * we will cast it to void* for the return in the success case. + * 2. size_t for string buffer handlers. + * 3. bool for everything else. */ + +/* Template parameters are FuncN type and desired return type. */ +template +struct MaybeWrapReturn; + +/* If the return type matches, return the given function unwrapped. */ +template +struct MaybeWrapReturn { + typedef F Func; +}; + +/* Function wrapper that munges the return value from void to (bool)true. */ +template +bool ReturnTrue2(P1 p1, P2 p2) { + F(p1, p2); + return true; +} + +template +bool ReturnTrue3(P1 p1, P2 p2, P3 p3) { + F(p1, p2, p3); + return true; +} + +/* Function wrapper that munges the return value from void to (void*)arg1 */ +template +void *ReturnClosure2(P1 p1, P2 p2) { + F(p1, p2); + return p1; +} + +template +void *ReturnClosure3(P1 p1, P2 p2, P3 p3) { + F(p1, p2, p3); + return p1; +} + +/* Function wrapper that munges the return value from R to void*. */ +template +void *CastReturnToVoidPtr2(P1 p1, P2 p2) { + return F(p1, p2); +} + +template +void *CastReturnToVoidPtr3(P1 p1, P2 p2, P3 p3) { + return F(p1, p2, p3); +} + +/* Function wrapper that munges the return value from bool to void*. */ +template +void *ReturnClosureOrBreak2(P1 p1, P2 p2) { + return F(p1, p2) ? p1 : UPB_BREAK; +} + +template +void *ReturnClosureOrBreak3(P1 p1, P2 p2, P3 p3) { + return F(p1, p2, p3) ? p1 : UPB_BREAK; +} + +/* For the string callback, which takes five params, returns the size param. */ +template +size_t ReturnStringLen(P1 p1, P2 p2, const char *p3, size_t p4, + const BufferHandle *p5) { + F(p1, p2, p3, p4, p5); + return p4; +} + +/* For the string callback, which takes five params, returns the size param or + * zero. */ +template +size_t ReturnNOr0(P1 p1, P2 p2, const char *p3, size_t p4, + const BufferHandle *p5) { + return F(p1, p2, p3, p4, p5) ? p4 : 0; +} + +/* If we have a function returning void but want a function returning bool, wrap + * it in a function that returns true. */ +template +struct MaybeWrapReturn, bool> { + typedef Func2, I> Func; +}; + +template +struct MaybeWrapReturn, bool> { + typedef Func3, I> Func; +}; + +/* If our function returns void but we want one returning void*, wrap it in a + * function that returns the first argument. */ +template +struct MaybeWrapReturn, void *> { + typedef Func2, I> Func; +}; + +template +struct MaybeWrapReturn, void *> { + typedef Func3, I> Func; +}; + +/* If our function returns R* but we want one returning void*, wrap it in a + * function that casts to void*. */ +template +struct MaybeWrapReturn, void *, + typename disable_if_same::Type> { + typedef Func2, I> Func; +}; + +template +struct MaybeWrapReturn, void *, + typename disable_if_same::Type> { + typedef Func3, I> + Func; +}; + +/* If our function returns bool but we want one returning void*, wrap it in a + * function that returns either the first param or UPB_BREAK. */ +template +struct MaybeWrapReturn, void *> { + typedef Func2, I> Func; +}; + +template +struct MaybeWrapReturn, void *> { + typedef Func3, I> + Func; +}; + +/* If our function returns void but we want one returning size_t, wrap it in a + * function that returns the size argument. */ +template +struct MaybeWrapReturn< + Func5, + size_t> { + typedef Func5, I> Func; +}; + +/* If our function returns bool but we want one returning size_t, wrap it in a + * function that returns either 0 or the buf size. */ +template +struct MaybeWrapReturn< + Func5, + size_t> { + typedef Func5, I> Func; +}; + +/* ConvertParams **************************************************************/ + +/* Template class that converts the function parameters if necessary, and + * ignores the HandlerData parameter if appropriate. + * + * Template parameter is the are FuncN function type. */ +template +struct ConvertParams; + +/* Function that discards the handler data parameter. */ +template +R IgnoreHandlerData2(void *p1, const void *hd) { + UPB_UNUSED(hd); + return F(static_cast(p1)); +} + +template +R IgnoreHandlerData3(void *p1, const void *hd, P2Wrapper p2) { + UPB_UNUSED(hd); + return F(static_cast(p1), p2); +} + +template +R IgnoreHandlerData4(void *p1, const void *hd, P2 p2, P3 p3) { + UPB_UNUSED(hd); + return F(static_cast(p1), p2, p3); +} + +template +R IgnoreHandlerData5(void *p1, const void *hd, P2 p2, P3 p3, P4 p4) { + UPB_UNUSED(hd); + return F(static_cast(p1), p2, p3, p4); +} + +template +R IgnoreHandlerDataIgnoreHandle(void *p1, const void *hd, const char *p2, + size_t p3, const BufferHandle *handle) { + UPB_UNUSED(hd); + UPB_UNUSED(handle); + return F(static_cast(p1), p2, p3); +} + +/* Function that casts the handler data parameter. */ +template +R CastHandlerData2(void *c, const void *hd) { + return F(static_cast(c), static_cast(hd)); +} + +template +R CastHandlerData3(void *c, const void *hd, P3Wrapper p3) { + return F(static_cast(c), static_cast(hd), p3); +} + +template +R CastHandlerData5(void *c, const void *hd, P3 p3, P4 p4, P5 p5) { + return F(static_cast(c), static_cast(hd), p3, p4, p5); +} + +template +R CastHandlerDataIgnoreHandle(void *c, const void *hd, const char *p3, + size_t p4, const BufferHandle *handle) { + UPB_UNUSED(handle); + return F(static_cast(c), static_cast(hd), p3, p4); +} + +/* For unbound functions, ignore the handler data. */ +template +struct ConvertParams, T> { + typedef Func2, I> Func; +}; + +template +struct ConvertParams, + R2 (*)(P1_2, P2_2, P3_2)> { + typedef Func3, I> Func; +}; + +/* For StringBuffer only; this ignores both the handler data and the + * BufferHandle. */ +template +struct ConvertParams, T> { + typedef Func5, + I> Func; +}; + +template +struct ConvertParams, T> { + typedef Func5, I> Func; +}; + +/* For bound functions, cast the handler data. */ +template +struct ConvertParams, T> { + typedef Func2, I> + Func; +}; + +template +struct ConvertParams, + R2 (*)(P1_2, P2_2, P3_2)> { + typedef Func3, I> Func; +}; + +/* For StringBuffer only; this ignores the BufferHandle. */ +template +struct ConvertParams, T> { + typedef Func5, + I> Func; +}; + +template +struct ConvertParams, T> { + typedef Func5, I> Func; +}; + +/* utype/ltype are upper/lower-case, ctype is canonical C type, vtype is + * variant C type. */ +#define TYPE_METHODS(utype, ltype, ctype, vtype) \ + template <> struct CanonicalType { \ + typedef ctype Type; \ + }; \ + template <> \ + inline bool Handlers::SetValueHandler( \ + const FieldDef *f, \ + const Handlers::utype ## Handler& handler) { \ + assert(!handler.registered_); \ + handler.AddCleanup(this); \ + handler.registered_ = true; \ + return upb_handlers_set##ltype(this, f, handler.handler_, &handler.attr_); \ + } \ + +TYPE_METHODS(Double, double, double, double) +TYPE_METHODS(Float, float, float, float) +TYPE_METHODS(UInt64, uint64, uint64_t, UPB_UINT64_T) +TYPE_METHODS(UInt32, uint32, uint32_t, UPB_UINT32_T) +TYPE_METHODS(Int64, int64, int64_t, UPB_INT64_T) +TYPE_METHODS(Int32, int32, int32_t, UPB_INT32_T) +TYPE_METHODS(Bool, bool, bool, bool) + +#ifdef UPB_TWO_32BIT_TYPES +TYPE_METHODS(Int32, int32, int32_t, UPB_INT32ALT_T) +TYPE_METHODS(UInt32, uint32, uint32_t, UPB_UINT32ALT_T) +#endif + +#ifdef UPB_TWO_64BIT_TYPES +TYPE_METHODS(Int64, int64, int64_t, UPB_INT64ALT_T) +TYPE_METHODS(UInt64, uint64, uint64_t, UPB_UINT64ALT_T) +#endif +#undef TYPE_METHODS + +template <> struct CanonicalType { + typedef Status* Type; +}; + +/* Type methods that are only one-per-canonical-type and not + * one-per-cvariant. */ + +#define TYPE_METHODS(utype, ctype) \ + inline bool Handlers::Set##utype##Handler(const FieldDef *f, \ + const utype##Handler &h) { \ + return SetValueHandler(f, h); \ + } \ + +TYPE_METHODS(Double, double) +TYPE_METHODS(Float, float) +TYPE_METHODS(UInt64, uint64_t) +TYPE_METHODS(UInt32, uint32_t) +TYPE_METHODS(Int64, int64_t) +TYPE_METHODS(Int32, int32_t) +TYPE_METHODS(Bool, bool) +#undef TYPE_METHODS + +template struct ReturnOf; + +template +struct ReturnOf { + typedef R Return; +}; + +template +struct ReturnOf { + typedef R Return; +}; + +template +struct ReturnOf { + typedef R Return; +}; + +template +struct ReturnOf { + typedef R Return; +}; + +template const void *UniquePtrForType() { + static const char ch = 0; + return &ch; +} + +template +template +inline Handler::Handler(F func) + : registered_(false), + cleanup_data_(func.GetData()), + cleanup_func_(func.GetCleanup()) { + upb_handlerattr_sethandlerdata(&attr_, func.GetData()); + typedef typename ReturnOf::Return Return; + typedef typename ConvertParams::Func ConvertedParamsFunc; + typedef typename MaybeWrapReturn::Func + ReturnWrappedFunc; + handler_ = ReturnWrappedFunc().Call; + + /* Set attributes based on what templates can statically tell us about the + * user's function. */ + + /* If the original function returns void, then we know that we wrapped it to + * always return ok. */ + bool always_ok = is_same::value; + attr_.SetAlwaysOk(always_ok); + + /* Closure parameter and return type. */ + attr_.SetClosureType(UniquePtrForType()); + + /* We use the closure type (from the first parameter) if the return type is + * void or bool, since these are the two cases we wrap to return the closure's + * type anyway. + * + * This is all nonsense for non START* handlers, but it doesn't matter because + * in that case the value will be ignored. */ + typedef typename FirstUnlessVoidOrBool::value + EffectiveReturn; + attr_.SetReturnClosureType(UniquePtrForType()); +} + +template +inline Handler::~Handler() { + assert(registered_); +} + +inline HandlerAttributes::HandlerAttributes() { upb_handlerattr_init(this); } +inline HandlerAttributes::~HandlerAttributes() { upb_handlerattr_uninit(this); } +inline bool HandlerAttributes::SetHandlerData(const void *hd) { + return upb_handlerattr_sethandlerdata(this, hd); +} +inline const void* HandlerAttributes::handler_data() const { + return upb_handlerattr_handlerdata(this); +} +inline bool HandlerAttributes::SetClosureType(const void *type) { + return upb_handlerattr_setclosuretype(this, type); +} +inline const void* HandlerAttributes::closure_type() const { + return upb_handlerattr_closuretype(this); +} +inline bool HandlerAttributes::SetReturnClosureType(const void *type) { + return upb_handlerattr_setreturnclosuretype(this, type); +} +inline const void* HandlerAttributes::return_closure_type() const { + return upb_handlerattr_returnclosuretype(this); +} +inline bool HandlerAttributes::SetAlwaysOk(bool always_ok) { + return upb_handlerattr_setalwaysok(this, always_ok); +} +inline bool HandlerAttributes::always_ok() const { + return upb_handlerattr_alwaysok(this); +} + +inline BufferHandle::BufferHandle() { upb_bufhandle_init(this); } +inline BufferHandle::~BufferHandle() { upb_bufhandle_uninit(this); } +inline const char* BufferHandle::buffer() const { + return upb_bufhandle_buf(this); +} +inline size_t BufferHandle::object_offset() const { + return upb_bufhandle_objofs(this); +} +inline void BufferHandle::SetBuffer(const char* buf, size_t ofs) { + upb_bufhandle_setbuf(this, buf, ofs); +} +template +void BufferHandle::SetAttachedObject(const T* obj) { + upb_bufhandle_setobj(this, obj, UniquePtrForType()); +} +template +const T* BufferHandle::GetAttachedObject() const { + return upb_bufhandle_objtype(this) == UniquePtrForType() + ? static_cast(upb_bufhandle_obj(this)) + : NULL; +} + +inline reffed_ptr Handlers::New(const MessageDef *m) { + upb_handlers *h = upb_handlers_new(m, &h); + return reffed_ptr(h, &h); +} +inline reffed_ptr Handlers::NewFrozen( + const MessageDef *m, upb_handlers_callback *callback, + const void *closure) { + const upb_handlers *h = upb_handlers_newfrozen(m, &h, callback, closure); + return reffed_ptr(h, &h); +} +inline const Status* Handlers::status() { + return upb_handlers_status(this); +} +inline void Handlers::ClearError() { + return upb_handlers_clearerr(this); +} +inline bool Handlers::Freeze(Status *s) { + upb::Handlers* h = this; + return upb_handlers_freeze(&h, 1, s); +} +inline bool Handlers::Freeze(Handlers *const *handlers, int n, Status *s) { + return upb_handlers_freeze(handlers, n, s); +} +inline bool Handlers::Freeze(const std::vector& h, Status* status) { + return upb_handlers_freeze((Handlers* const*)&h[0], h.size(), status); +} +inline const MessageDef *Handlers::message_def() const { + return upb_handlers_msgdef(this); +} +inline bool Handlers::AddCleanup(void *p, upb_handlerfree *func) { + return upb_handlers_addcleanup(this, p, func); +} +inline bool Handlers::SetStartMessageHandler( + const Handlers::StartMessageHandler &handler) { + assert(!handler.registered_); + handler.registered_ = true; + handler.AddCleanup(this); + return upb_handlers_setstartmsg(this, handler.handler_, &handler.attr_); +} +inline bool Handlers::SetEndMessageHandler( + const Handlers::EndMessageHandler &handler) { + assert(!handler.registered_); + handler.registered_ = true; + handler.AddCleanup(this); + return upb_handlers_setendmsg(this, handler.handler_, &handler.attr_); +} +inline bool Handlers::SetStartStringHandler(const FieldDef *f, + const StartStringHandler &handler) { + assert(!handler.registered_); + handler.registered_ = true; + handler.AddCleanup(this); + return upb_handlers_setstartstr(this, f, handler.handler_, &handler.attr_); +} +inline bool Handlers::SetEndStringHandler(const FieldDef *f, + const EndFieldHandler &handler) { + assert(!handler.registered_); + handler.registered_ = true; + handler.AddCleanup(this); + return upb_handlers_setendstr(this, f, handler.handler_, &handler.attr_); +} +inline bool Handlers::SetStringHandler(const FieldDef *f, + const StringHandler& handler) { + assert(!handler.registered_); + handler.registered_ = true; + handler.AddCleanup(this); + return upb_handlers_setstring(this, f, handler.handler_, &handler.attr_); +} +inline bool Handlers::SetStartSequenceHandler( + const FieldDef *f, const StartFieldHandler &handler) { + assert(!handler.registered_); + handler.registered_ = true; + handler.AddCleanup(this); + return upb_handlers_setstartseq(this, f, handler.handler_, &handler.attr_); +} +inline bool Handlers::SetStartSubMessageHandler( + const FieldDef *f, const StartFieldHandler &handler) { + assert(!handler.registered_); + handler.registered_ = true; + handler.AddCleanup(this); + return upb_handlers_setstartsubmsg(this, f, handler.handler_, &handler.attr_); +} +inline bool Handlers::SetEndSubMessageHandler(const FieldDef *f, + const EndFieldHandler &handler) { + assert(!handler.registered_); + handler.registered_ = true; + handler.AddCleanup(this); + return upb_handlers_setendsubmsg(this, f, handler.handler_, &handler.attr_); +} +inline bool Handlers::SetEndSequenceHandler(const FieldDef *f, + const EndFieldHandler &handler) { + assert(!handler.registered_); + handler.registered_ = true; + handler.AddCleanup(this); + return upb_handlers_setendseq(this, f, handler.handler_, &handler.attr_); +} +inline bool Handlers::SetSubHandlers(const FieldDef *f, const Handlers *sub) { + return upb_handlers_setsubhandlers(this, f, sub); +} +inline const Handlers *Handlers::GetSubHandlers(const FieldDef *f) const { + return upb_handlers_getsubhandlers(this, f); +} +inline const Handlers *Handlers::GetSubHandlers(Handlers::Selector sel) const { + return upb_handlers_getsubhandlers_sel(this, sel); +} +inline bool Handlers::GetSelector(const FieldDef *f, Handlers::Type type, + Handlers::Selector *s) { + return upb_handlers_getselector(f, type, s); +} +inline Handlers::Selector Handlers::GetEndSelector(Handlers::Selector start) { + return upb_handlers_getendselector(start); +} +inline Handlers::GenericFunction *Handlers::GetHandler( + Handlers::Selector selector) { + return upb_handlers_gethandler(this, selector); +} +inline const void *Handlers::GetHandlerData(Handlers::Selector selector) { + return upb_handlers_gethandlerdata(this, selector); } -template -inline FuncSig3 MatchFunc(R (*f)(P1, P2, P3)) { - UPB_UNUSED(f); // Only used for template parameter deduction. - return FuncSig3(); +inline BytesHandler::BytesHandler() { + upb_byteshandler_init(this); } -template -inline FuncSig4 MatchFunc(R (*f)(P1, P2, P3, P4)) { - UPB_UNUSED(f); // Only used for template parameter deduction. - return FuncSig4(); -} +inline BytesHandler::~BytesHandler() {} -template -inline FuncSig5 MatchFunc(R (*f)(P1, P2, P3, P4, P5)) { - UPB_UNUSED(f); // Only used for template parameter deduction. - return FuncSig5(); -} +} /* namespace upb */ -// MethodSig /////////////////////////////////////////////////////////////////// +#endif /* __cplusplus */ -// CallMethod*: a function template that calls a given method. -template -R CallMethod0(C *obj) { - return ((*obj).*F)(); -} -template -R CallMethod1(C *obj, P1 arg1) { - return ((*obj).*F)(arg1); -} +#undef UPB_TWO_32BIT_TYPES +#undef UPB_TWO_64BIT_TYPES +#undef UPB_INT32_T +#undef UPB_UINT32_T +#undef UPB_INT32ALT_T +#undef UPB_UINT32ALT_T +#undef UPB_INT64_T +#undef UPB_UINT64_T +#undef UPB_INT64ALT_T +#undef UPB_UINT64ALT_T -template -R CallMethod2(C *obj, P1 arg1, P2 arg2) { - return ((*obj).*F)(arg1, arg2); -} +#endif /* UPB_HANDLERS_INL_H_ */ -template -R CallMethod3(C *obj, P1 arg1, P2 arg2, P3 arg3) { - return ((*obj).*F)(arg1, arg2, arg3); -} +#endif /* UPB_HANDLERS_H */ +/* + * upb - a minimalist implementation of protocol buffers. + * + * Copyright (c) 2014 Google Inc. See LICENSE for details. + * Author: Josh Haberman + * + * A upb::Environment provides a means for injecting malloc and an + * error-reporting callback into encoders/decoders. This allows them to be + * independent of nearly all assumptions about their actual environment. + * + * It is also a container for allocating the encoders/decoders themselves that + * insulates clients from knowing their actual size. This provides ABI + * compatibility even if the size of the objects change. And this allows the + * structure definitions to be in the .c files instead of the .h files, making + * the .h files smaller and more readable. + */ -template -R CallMethod4(C *obj, P1 arg1, P2 arg2, P3 arg3, P4 arg4) { - return ((*obj).*F)(arg1, arg2, arg3, arg4); -} -// MethodSig: like FuncSig, but for member functions. -// -// GetFunc() returns a normal FuncN object, so after calling GetFunc() no -// more logic is required to special-case methods. -template -struct MethodSig0 { - template - Func1, FuncInfo > GetFunc() { - return Func1, FuncInfo >(); - } -}; +#ifndef UPB_ENV_H_ +#define UPB_ENV_H_ -template -struct MethodSig1 { - template - Func2, FuncInfo > GetFunc() { - return Func2, FuncInfo >(); - } +#ifdef __cplusplus +namespace upb { +class Environment; +class SeededAllocator; +} +#endif - template - BoundFunc2, FuncInfo > GetFunc( - typename remove_constptr::type param1) { - return BoundFunc2, FuncInfo >( - param1); - } -}; +UPB_DECLARE_TYPE(upb::Environment, upb_env) +UPB_DECLARE_TYPE(upb::SeededAllocator, upb_seededalloc) -template -struct MethodSig2 { - template - Func3, FuncInfo > - GetFunc() { - return Func3, - FuncInfo >(); - } +typedef void *upb_alloc_func(void *ud, void *ptr, size_t oldsize, size_t size); +typedef void upb_cleanup_func(void *ud); +typedef bool upb_error_func(void *ud, const upb_status *status); - template - BoundFunc3, FuncInfo > - GetFunc(typename remove_constptr::type param1) { - return BoundFunc3, - FuncInfo >(param1); - } -}; +#ifdef __cplusplus -template -struct MethodSig3 { - template - Func4, FuncInfo > - GetFunc() { - return Func4, - FuncInfo >(); - } +/* An environment is *not* thread-safe. */ +class upb::Environment { + public: + Environment(); + ~Environment(); - template - BoundFunc4, - FuncInfo > - GetFunc(typename remove_constptr::type param1) { - return BoundFunc4, - FuncInfo >(param1); - } -}; + /* Set a custom memory allocation function for the environment. May ONLY + * be called before any calls to Malloc()/Realloc()/AddCleanup() below. + * If this is not called, the system realloc() function will be used. + * The given user pointer "ud" will be passed to the allocation function. + * + * The allocation function will not receive corresponding "free" calls. it + * must ensure that the memory is valid for the lifetime of the Environment, + * but it may be reclaimed any time thereafter. The likely usage is that + * "ud" points to a stateful allocator, and that the allocator frees all + * memory, arena-style, when it is destroyed. In this case the allocator must + * outlive the Environment. Another possibility is that the allocation + * function returns GC-able memory that is guaranteed to be GC-rooted for the + * life of the Environment. */ + void SetAllocationFunction(upb_alloc_func* alloc, void* ud); -template -struct MethodSig4 { - template - Func5, - FuncInfo > - GetFunc() { - return Func5, - FuncInfo >(); + template + void SetAllocator(T* allocator) { + SetAllocationFunction(allocator->GetAllocationFunction(), allocator); } - template - BoundFunc5, - FuncInfo > - GetFunc(typename remove_constptr::type param1) { - return BoundFunc5, FuncInfo >( - param1); - } -}; + /* Set a custom error reporting function. */ + void SetErrorFunction(upb_error_func* func, void* ud); -template -inline MethodSig0 MatchFunc(R (C::*f)()) { - UPB_UNUSED(f); // Only used for template parameter deduction. - return MethodSig0(); -} + /* Set the error reporting function to simply copy the status to the given + * status and abort. */ + void ReportErrorsTo(Status* status); -template -inline MethodSig1 MatchFunc(R (C::*f)(P1)) { - UPB_UNUSED(f); // Only used for template parameter deduction. - return MethodSig1(); -} + /* Returns true if all allocations and AddCleanup() calls have succeeded, + * and no errors were reported with ReportError() (except ones that recovered + * successfully). */ + bool ok() const; -template -inline MethodSig2 MatchFunc(R (C::*f)(P1, P2)) { - UPB_UNUSED(f); // Only used for template parameter deduction. - return MethodSig2(); -} + /* Functions for use by encoders/decoders. **********************************/ -template -inline MethodSig3 MatchFunc(R (C::*f)(P1, P2, P3)) { - UPB_UNUSED(f); // Only used for template parameter deduction. - return MethodSig3(); -} + /* Reports an error to this environment's callback, returning true if + * the caller should try to recover. */ + bool ReportError(const Status* status); -template -inline MethodSig4 MatchFunc(R (C::*f)(P1, P2, P3, P4)) { - UPB_UNUSED(f); // Only used for template parameter deduction. - return MethodSig4(); -} + /* Allocate memory. Uses the environment's allocation function. + * + * There is no need to free(). All memory will be freed automatically, but is + * guaranteed to outlive the Environment. */ + void* Malloc(size_t size); -// MaybeWrapReturn ///////////////////////////////////////////////////////////// + /* Reallocate memory. Preserves "oldsize" bytes from the existing buffer + * Requires: oldsize <= existing_size. + * + * TODO(haberman): should we also enforce that oldsize <= size? */ + void* Realloc(void* ptr, size_t oldsize, size_t size); -// Template class that attempts to wrap the return value of the function so it -// matches the expected type. There are two main adjustments it may make: -// -// 1. If the function returns void, make it return the expected type and with -// a value that always indicates success. -// 2. If the function returns bool, make it return the expected type with a -// value that indicates success or failure. -// -// The "expected type" for return is: -// 1. void* for start handlers. If the closure parameter has a different type -// we will cast it to void* for the return in the success case. -// 2. size_t for string buffer handlers. -// 3. bool for everything else. + /* Add a cleanup function to run when the environment is destroyed. + * Returns false on out-of-memory. + * + * The first call to AddCleanup() after SetAllocationFunction() is guaranteed + * to return true -- this makes it possible to robustly set a cleanup handler + * for a custom allocation function. */ + bool AddCleanup(upb_cleanup_func* func, void* ud); -// Template parameters are FuncN type and desired return type. -template -struct MaybeWrapReturn; + /* Total number of bytes that have been allocated. It is undefined what + * Realloc() does to this counter. */ + size_t BytesAllocated() const; -// If the return type matches, return the given function unwrapped. -template -struct MaybeWrapReturn { - typedef F Func; -}; + private: + UPB_DISALLOW_COPY_AND_ASSIGN(Environment) -// Function wrapper that munges the return value from void to (bool)true. -template -bool ReturnTrue2(P1 p1, P2 p2) { - F(p1, p2); - return true; -} +#else +struct upb_env { +#endif /* __cplusplus */ -template -bool ReturnTrue3(P1 p1, P2 p2, P3 p3) { - F(p1, p2, p3); - return true; -} + bool ok_; + size_t bytes_allocated; -// Function wrapper that munges the return value from void to (void*)arg1 -template -void *ReturnClosure2(P1 p1, P2 p2) { - F(p1, p2); - return p1; -} + /* Alloc function. */ + upb_alloc_func *alloc; + void *alloc_ud; -template -void *ReturnClosure3(P1 p1, P2 p2, P3 p3) { - F(p1, p2, p3); - return p1; -} + /* Error-reporting function. */ + upb_error_func *err; + void *err_ud; -// Function wrapper that munges the return value from R to void*. -template -void *CastReturnToVoidPtr2(P1 p1, P2 p2) { - return F(p1, p2); -} + /* Userdata for default alloc func. */ + void *default_alloc_ud; -template -void *CastReturnToVoidPtr3(P1 p1, P2 p2, P3 p3) { - return F(p1, p2, p3); -} + /* Cleanup entries. Pointer to a cleanup_ent, defined in env.c */ + void *cleanup_head; -// Function wrapper that munges the return value from bool to void*. -template -void *ReturnClosureOrBreak2(P1 p1, P2 p2) { - return F(p1, p2) ? p1 : UPB_BREAK; -} + /* For future expansion, since the size of this struct is exposed to users. */ + void *future1; + void *future2; +}; -template -void *ReturnClosureOrBreak3(P1 p1, P2 p2, P3 p3) { - return F(p1, p2, p3) ? p1 : UPB_BREAK; -} +UPB_BEGIN_EXTERN_C -// For the string callback, which takes five params, returns the size param. -template -size_t ReturnStringLen(P1 p1, P2 p2, const char *p3, size_t p4, - const BufferHandle *p5) { - F(p1, p2, p3, p4, p5); - return p4; -} +void upb_env_init(upb_env *e); +void upb_env_uninit(upb_env *e); +void upb_env_setallocfunc(upb_env *e, upb_alloc_func *func, void *ud); +void upb_env_seterrorfunc(upb_env *e, upb_error_func *func, void *ud); +void upb_env_reporterrorsto(upb_env *e, upb_status *status); +bool upb_env_ok(const upb_env *e); +bool upb_env_reporterror(upb_env *e, const upb_status *status); +void *upb_env_malloc(upb_env *e, size_t size); +void *upb_env_realloc(upb_env *e, void *ptr, size_t oldsize, size_t size); +bool upb_env_addcleanup(upb_env *e, upb_cleanup_func *func, void *ud); +size_t upb_env_bytesallocated(const upb_env *e); -// For the string callback, which takes five params, returns the size param or -// zero. -template -size_t ReturnNOr0(P1 p1, P2 p2, const char *p3, size_t p4, - const BufferHandle *p5) { - return F(p1, p2, p3, p4, p5) ? p4 : 0; -} +UPB_END_EXTERN_C -// If we have a function returning void but want a function returning bool, wrap -// it in a function that returns true. -template -struct MaybeWrapReturn, bool> { - typedef Func2, I> Func; -}; +#ifdef __cplusplus -template -struct MaybeWrapReturn, bool> { - typedef Func3, I> Func; -}; +/* An allocator that allocates from an initial memory region (likely the stack) + * before falling back to another allocator. */ +class upb::SeededAllocator { + public: + SeededAllocator(void *mem, size_t len); + ~SeededAllocator(); -// If our function returns void but we want one returning void*, wrap it in a -// function that returns the first argument. -template -struct MaybeWrapReturn, void *> { - typedef Func2, I> Func; -}; + /* Set a custom fallback memory allocation function for the allocator, to use + * once the initial region runs out. + * + * May ONLY be called before GetAllocationFunction(). If this is not + * called, the system realloc() will be the fallback allocator. */ + void SetFallbackAllocator(upb_alloc_func *alloc, void *ud); -template -struct MaybeWrapReturn, void *> { - typedef Func3, I> Func; -}; + /* Gets the allocation function for this allocator. */ + upb_alloc_func* GetAllocationFunction(); -// If our function returns R* but we want one returning void*, wrap it in a -// function that casts to void*. -template -struct MaybeWrapReturn, void *, - typename disable_if_same::Type> { - typedef Func2, I> Func; -}; + private: + UPB_DISALLOW_COPY_AND_ASSIGN(SeededAllocator) -template -struct MaybeWrapReturn, void *, - typename disable_if_same::Type> { - typedef Func3, I> - Func; -}; +#else +struct upb_seededalloc { +#endif /* __cplusplus */ -// If our function returns bool but we want one returning void*, wrap it in a -// function that returns either the first param or UPB_BREAK. -template -struct MaybeWrapReturn, void *> { - typedef Func2, I> Func; -}; + /* Fallback alloc function. */ + upb_alloc_func *alloc; + upb_cleanup_func *alloc_cleanup; + void *alloc_ud; + bool need_cleanup; + bool returned_allocfunc; -template -struct MaybeWrapReturn, void *> { - typedef Func3, I> - Func; -}; + /* Userdata for default alloc func. */ + void *default_alloc_ud; -// If our function returns void but we want one returning size_t, wrap it in a -// function that returns the size argument. -template -struct MaybeWrapReturn< - Func5, - size_t> { - typedef Func5, I> Func; -}; + /* Pointers for the initial memory region. */ + char *mem_base; + char *mem_ptr; + char *mem_limit; -// If our function returns bool but we want one returning size_t, wrap it in a -// function that returns either 0 or the buf size. -template -struct MaybeWrapReturn< - Func5, - size_t> { - typedef Func5, I> Func; + /* For future expansion, since the size of this struct is exposed to users. */ + void *future1; + void *future2; }; -// ConvertParams /////////////////////////////////////////////////////////////// +UPB_BEGIN_EXTERN_C -// Template class that converts the function parameters if necessary, and -// ignores the HandlerData parameter if appropriate. -// -// Template parameter is the are FuncN function type. -template -struct ConvertParams; +void upb_seededalloc_init(upb_seededalloc *a, void *mem, size_t len); +void upb_seededalloc_uninit(upb_seededalloc *a); +void upb_seededalloc_setfallbackalloc(upb_seededalloc *a, upb_alloc_func *func, + void *ud); +upb_alloc_func *upb_seededalloc_getallocfunc(upb_seededalloc *a); -// Function that discards the handler data parameter. -template -R IgnoreHandlerData2(void *p1, const void *hd) { - UPB_UNUSED(hd); - return F(static_cast(p1)); -} +UPB_END_EXTERN_C -template -R IgnoreHandlerData3(void *p1, const void *hd, P2Wrapper p2) { - UPB_UNUSED(hd); - return F(static_cast(p1), p2); -} +#ifdef __cplusplus -template -R IgnoreHandlerData4(void *p1, const void *hd, P2 p2, P3 p3) { - UPB_UNUSED(hd); - return F(static_cast(p1), p2, p3); -} +namespace upb { -template -R IgnoreHandlerData5(void *p1, const void *hd, P2 p2, P3 p3, P4 p4) { - UPB_UNUSED(hd); - return F(static_cast(p1), p2, p3, p4); +inline Environment::Environment() { + upb_env_init(this); } - -template -R IgnoreHandlerDataIgnoreHandle(void *p1, const void *hd, const char *p2, - size_t p3, const BufferHandle *handle) { - UPB_UNUSED(hd); - UPB_UNUSED(handle); - return F(static_cast(p1), p2, p3); +inline Environment::~Environment() { + upb_env_uninit(this); } - -// Function that casts the handler data parameter. -template -R CastHandlerData2(void *c, const void *hd) { - return F(static_cast(c), static_cast(hd)); +inline void Environment::SetAllocationFunction(upb_alloc_func *alloc, + void *ud) { + upb_env_setallocfunc(this, alloc, ud); } - -template -R CastHandlerData3(void *c, const void *hd, P3Wrapper p3) { - return F(static_cast(c), static_cast(hd), p3); +inline void Environment::SetErrorFunction(upb_error_func *func, void *ud) { + upb_env_seterrorfunc(this, func, ud); +} +inline void Environment::ReportErrorsTo(Status* status) { + upb_env_reporterrorsto(this, status); +} +inline bool Environment::ok() const { + return upb_env_ok(this); +} +inline bool Environment::ReportError(const Status* status) { + return upb_env_reporterror(this, status); +} +inline void *Environment::Malloc(size_t size) { + return upb_env_malloc(this, size); +} +inline void *Environment::Realloc(void *ptr, size_t oldsize, size_t size) { + return upb_env_realloc(this, ptr, oldsize, size); +} +inline bool Environment::AddCleanup(upb_cleanup_func *func, void *ud) { + return upb_env_addcleanup(this, func, ud); +} +inline size_t Environment::BytesAllocated() const { + return upb_env_bytesallocated(this); } -template -R CastHandlerData5(void *c, const void *hd, P3 p3, P4 p4, P5 p5) { - return F(static_cast(c), static_cast(hd), p3, p4, p5); +inline SeededAllocator::SeededAllocator(void *mem, size_t len) { + upb_seededalloc_init(this, mem, len); +} +inline SeededAllocator::~SeededAllocator() { + upb_seededalloc_uninit(this); +} +inline void SeededAllocator::SetFallbackAllocator(upb_alloc_func *alloc, + void *ud) { + upb_seededalloc_setfallbackalloc(this, alloc, ud); +} +inline upb_alloc_func *SeededAllocator::GetAllocationFunction() { + return upb_seededalloc_getallocfunc(this); } -template -R CastHandlerDataIgnoreHandle(void *c, const void *hd, const char *p3, - size_t p4, const BufferHandle *handle) { - UPB_UNUSED(handle); - return F(static_cast(c), static_cast(hd), p3, p4); +} /* namespace upb */ + +#endif /* __cplusplus */ + +#endif /* UPB_ENV_H_ */ +/* + * upb - a minimalist implementation of protocol buffers. + * + * Copyright (c) 2010-2012 Google Inc. See LICENSE for details. + * Author: Josh Haberman + * + * A upb_sink is an object that binds a upb_handlers object to some runtime + * state. It is the object that can actually receive data via the upb_handlers + * interface. + * + * Unlike upb_def and upb_handlers, upb_sink is never frozen, immutable, or + * thread-safe. You can create as many of them as you want, but each one may + * only be used in a single thread at a time. + * + * If we compare with class-based OOP, a you can think of a upb_def as an + * abstract base class, a upb_handlers as a concrete derived class, and a + * upb_sink as an object (class instance). + */ + +#ifndef UPB_SINK_H +#define UPB_SINK_H + + +#ifdef __cplusplus +namespace upb { +class BufferSource; +class BytesSink; +class Sink; } +#endif -// For unbound functions, ignore the handler data. -template -struct ConvertParams, T> { - typedef Func2, I> Func; -}; +UPB_DECLARE_TYPE(upb::BufferSource, upb_bufsrc) +UPB_DECLARE_TYPE(upb::BytesSink, upb_bytessink) +UPB_DECLARE_TYPE(upb::Sink, upb_sink) -template -struct ConvertParams, - R2 (*)(P1_2, P2_2, P3_2)> { - typedef Func3, I> Func; -}; +#ifdef __cplusplus -// For StringBuffer only; this ignores both the handler data and the -// BufferHandle. -template -struct ConvertParams, T> { - typedef Func5, - I> Func; -}; +/* A upb::Sink is an object that binds a upb::Handlers object to some runtime + * state. It represents an endpoint to which data can be sent. + * + * TODO(haberman): right now all of these functions take selectors. Should they + * take selectorbase instead? + * + * ie. instead of calling: + * sink->StartString(FOO_FIELD_START_STRING, ...) + * a selector base would let you say: + * sink->StartString(FOO_FIELD, ...) + * + * This would make call sites a little nicer and require emitting fewer selector + * definitions in .h files. + * + * But the current scheme has the benefit that you can retrieve a function + * pointer for any handler with handlers->GetHandler(selector), without having + * to have a separate GetHandler() function for each handler type. The JIT + * compiler uses this. To accommodate we'd have to expose a separate + * GetHandler() for every handler type. + * + * Also to ponder: selectors right now are independent of a specific Handlers + * instance. In other words, they allocate a number to every possible handler + * that *could* be registered, without knowing anything about what handlers + * *are* registered. That means that using selectors as table offsets prohibits + * us from compacting the handler table at Freeze() time. If the table is very + * sparse, this could be wasteful. + * + * Having another selector-like thing that is specific to a Handlers instance + * would allow this compacting, but then it would be impossible to write code + * ahead-of-time that can be bound to any Handlers instance at runtime. For + * example, a .proto file parser written as straight C will not know what + * Handlers it will be bound to, so when it calls sink->StartString() what + * selector will it pass? It needs a selector like we have today, that is + * independent of any particular upb::Handlers. + * + * Is there a way then to allow Handlers table compaction? */ +class upb::Sink { + public: + /* Constructor with no initialization; must be Reset() before use. */ + Sink() {} -template -struct ConvertParams, T> { - typedef Func5, I> Func; -}; + /* Constructs a new sink for the given frozen handlers and closure. + * + * TODO: once the Handlers know the expected closure type, verify that T + * matches it. */ + template Sink(const Handlers* handlers, T* closure); -// For bound functions, cast the handler data. -template -struct ConvertParams, T> { - typedef Func2, I> - Func; -}; + /* Resets the value of the sink. */ + template void Reset(const Handlers* handlers, T* closure); + + /* Returns the top-level object that is bound to this sink. + * + * TODO: once the Handlers know the expected closure type, verify that T + * matches it. */ + template T* GetObject() const; + + /* Functions for pushing data into the sink. + * + * These return false if processing should stop (either due to error or just + * to suspend). + * + * These may not be called from within one of the same sink's handlers (in + * other words, handlers are not re-entrant). */ + + /* Should be called at the start and end of every message; both the top-level + * message and submessages. This means that submessages should use the + * following sequence: + * sink->StartSubMessage(startsubmsg_selector); + * sink->StartMessage(); + * // ... + * sink->EndMessage(&status); + * sink->EndSubMessage(endsubmsg_selector); */ + bool StartMessage(); + bool EndMessage(Status* status); + + /* Putting of individual values. These work for both repeated and + * non-repeated fields, but for repeated fields you must wrap them in + * calls to StartSequence()/EndSequence(). */ + bool PutInt32(Handlers::Selector s, int32_t val); + bool PutInt64(Handlers::Selector s, int64_t val); + bool PutUInt32(Handlers::Selector s, uint32_t val); + bool PutUInt64(Handlers::Selector s, uint64_t val); + bool PutFloat(Handlers::Selector s, float val); + bool PutDouble(Handlers::Selector s, double val); + bool PutBool(Handlers::Selector s, bool val); -template -struct ConvertParams, - R2 (*)(P1_2, P2_2, P3_2)> { - typedef Func3, I> Func; -}; + /* Putting of string/bytes values. Each string can consist of zero or more + * non-contiguous buffers of data. + * + * For StartString(), the function will write a sink for the string to "sub." + * The sub-sink must be used for any/all PutStringBuffer() calls. */ + bool StartString(Handlers::Selector s, size_t size_hint, Sink* sub); + size_t PutStringBuffer(Handlers::Selector s, const char *buf, size_t len, + const BufferHandle *handle); + bool EndString(Handlers::Selector s); -// For StringBuffer only; this ignores the BufferHandle. -template -struct ConvertParams, T> { - typedef Func5, - I> Func; -}; + /* For submessage fields. + * + * For StartSubMessage(), the function will write a sink for the string to + * "sub." The sub-sink must be used for any/all handlers called within the + * submessage. */ + bool StartSubMessage(Handlers::Selector s, Sink* sub); + bool EndSubMessage(Handlers::Selector s); -template -struct ConvertParams, T> { - typedef Func5, I> Func; + /* For repeated fields of any type, the sequence of values must be wrapped in + * these calls. + * + * For StartSequence(), the function will write a sink for the string to + * "sub." The sub-sink must be used for any/all handlers called within the + * sequence. */ + bool StartSequence(Handlers::Selector s, Sink* sub); + bool EndSequence(Handlers::Selector s); + + /* Copy and assign specifically allowed. + * We don't even bother making these members private because so many + * functions need them and this is mainly just a dumb data container anyway. + */ +#else +struct upb_sink { +#endif + const upb_handlers *handlers; + void *closure; }; -// utype/ltype are upper/lower-case, ctype is canonical C type, vtype is -// variant C type. -#define TYPE_METHODS(utype, ltype, ctype, vtype) \ - template <> struct CanonicalType { \ - typedef ctype Type; \ - }; \ - template <> \ - inline bool Handlers::SetValueHandler( \ - const FieldDef *f, \ - const Handlers::utype ## Handler& handler) { \ - assert(!handler.registered_); \ - handler.AddCleanup(this); \ - handler.registered_ = true; \ - return upb_handlers_set##ltype(this, f, handler.handler_, &handler.attr_); \ - } \ +#ifdef __cplusplus +class upb::BytesSink { + public: + BytesSink() {} -TYPE_METHODS(Double, double, double, double); -TYPE_METHODS(Float, float, float, float); -TYPE_METHODS(UInt64, uint64, uint64_t, UPB_UINT64_T); -TYPE_METHODS(UInt32, uint32, uint32_t, UPB_UINT32_T); -TYPE_METHODS(Int64, int64, int64_t, UPB_INT64_T); -TYPE_METHODS(Int32, int32, int32_t, UPB_INT32_T); -TYPE_METHODS(Bool, bool, bool, bool); + /* Constructs a new sink for the given frozen handlers and closure. + * + * TODO(haberman): once the Handlers know the expected closure type, verify + * that T matches it. */ + template BytesSink(const BytesHandler* handler, T* closure); -#ifdef UPB_TWO_32BIT_TYPES -TYPE_METHODS(Int32, int32, int32_t, UPB_INT32ALT_T); -TYPE_METHODS(UInt32, uint32, uint32_t, UPB_UINT32ALT_T); -#endif + /* Resets the value of the sink. */ + template void Reset(const BytesHandler* handler, T* closure); -#ifdef UPB_TWO_64BIT_TYPES -TYPE_METHODS(Int64, int64, int64_t, UPB_INT64ALT_T); -TYPE_METHODS(UInt64, uint64, uint64_t, UPB_UINT64ALT_T); + bool Start(size_t size_hint, void **subc); + size_t PutBuffer(void *subc, const char *buf, size_t len, + const BufferHandle *handle); + bool End(); +#else +struct upb_bytessink { #endif -#undef TYPE_METHODS - -template <> struct CanonicalType { - typedef Status* Type; + const upb_byteshandler *handler; + void *closure; }; -// Type methods that are only one-per-canonical-type and not one-per-cvariant. - -#define TYPE_METHODS(utype, ctype) \ - inline bool Handlers::Set##utype##Handler(const FieldDef *f, \ - const utype##Handler &h) { \ - return SetValueHandler(f, h); \ - } \ +#ifdef __cplusplus -TYPE_METHODS(Double, double); -TYPE_METHODS(Float, float); -TYPE_METHODS(UInt64, uint64_t); -TYPE_METHODS(UInt32, uint32_t); -TYPE_METHODS(Int64, int64_t); -TYPE_METHODS(Int32, int32_t); -TYPE_METHODS(Bool, bool); -#undef TYPE_METHODS +/* A class for pushing a flat buffer of data to a BytesSink. + * You can construct an instance of this to get a resumable source, + * or just call the static PutBuffer() to do a non-resumable push all in one + * go. */ +class upb::BufferSource { + public: + BufferSource(); + BufferSource(const char* buf, size_t len, BytesSink* sink); -template struct ReturnOf; + /* Returns true if the entire buffer was pushed successfully. Otherwise the + * next call to PutNext() will resume where the previous one left off. + * TODO(haberman): implement this. */ + bool PutNext(); -template -struct ReturnOf { - typedef R Return; -}; + /* A static version; with this version is it not possible to resume in the + * case of failure or a partially-consumed buffer. */ + static bool PutBuffer(const char* buf, size_t len, BytesSink* sink); -template -struct ReturnOf { - typedef R Return; + template static bool PutBuffer(const T& str, BytesSink* sink) { + return PutBuffer(str.c_str(), str.size(), sink); + } +#else +struct upb_bufsrc { + char dummy; +#endif }; -template -struct ReturnOf { - typedef R Return; -}; +UPB_BEGIN_EXTERN_C -template -struct ReturnOf { - typedef R Return; -}; +/* Inline definitions. */ -template const void *UniquePtrForType() { - static const char ch = 0; - return &ch; +UPB_INLINE void upb_bytessink_reset(upb_bytessink *s, const upb_byteshandler *h, + void *closure) { + s->handler = h; + s->closure = closure; } -template -template -inline Handler::Handler(F func) - : registered_(false), - cleanup_data_(func.GetData()), - cleanup_func_(func.GetCleanup()) { - upb_handlerattr_sethandlerdata(&attr_, func.GetData()); - typedef typename ReturnOf::Return Return; - typedef typename ConvertParams::Func ConvertedParamsFunc; - typedef typename MaybeWrapReturn::Func - ReturnWrappedFunc; - handler_ = ReturnWrappedFunc().Call; - - // Set attributes based on what templates can statically tell us about the - // user's function. +UPB_INLINE bool upb_bytessink_start(upb_bytessink *s, size_t size_hint, + void **subc) { + typedef upb_startstr_handlerfunc func; + func *start; + *subc = s->closure; + if (!s->handler) return true; + start = (func *)s->handler->table[UPB_STARTSTR_SELECTOR].func; - // If the original function returns void, then we know that we wrapped it to - // always return ok. - bool always_ok = is_same::value; - attr_.SetAlwaysOk(always_ok); + if (!start) return true; + *subc = start(s->closure, upb_handlerattr_handlerdata( + &s->handler->table[UPB_STARTSTR_SELECTOR].attr), + size_hint); + return *subc != NULL; +} - // Closure parameter and return type. - attr_.SetClosureType(UniquePtrForType()); +UPB_INLINE size_t upb_bytessink_putbuf(upb_bytessink *s, void *subc, + const char *buf, size_t size, + const upb_bufhandle* handle) { + typedef upb_string_handlerfunc func; + func *putbuf; + if (!s->handler) return true; + putbuf = (func *)s->handler->table[UPB_STRING_SELECTOR].func; - // We use the closure type (from the first parameter) if the return type is - // void or bool, since these are the two cases we wrap to return the closure's - // type anyway. - // - // This is all nonsense for non START* handlers, but it doesn't matter because - // in that case the value will be ignored. - typedef typename FirstUnlessVoidOrBool::value - EffectiveReturn; - attr_.SetReturnClosureType(UniquePtrForType()); + if (!putbuf) return true; + return putbuf(subc, upb_handlerattr_handlerdata( + &s->handler->table[UPB_STRING_SELECTOR].attr), + buf, size, handle); } -template -inline Handler::~Handler() { - assert(registered_); -} +UPB_INLINE bool upb_bytessink_end(upb_bytessink *s) { + typedef upb_endfield_handlerfunc func; + func *end; + if (!s->handler) return true; + end = (func *)s->handler->table[UPB_ENDSTR_SELECTOR].func; -inline HandlerAttributes::HandlerAttributes() { upb_handlerattr_init(this); } -inline HandlerAttributes::~HandlerAttributes() { upb_handlerattr_uninit(this); } -inline bool HandlerAttributes::SetHandlerData(const void *hd) { - return upb_handlerattr_sethandlerdata(this, hd); -} -inline const void* HandlerAttributes::handler_data() const { - return upb_handlerattr_handlerdata(this); -} -inline bool HandlerAttributes::SetClosureType(const void *type) { - return upb_handlerattr_setclosuretype(this, type); -} -inline const void* HandlerAttributes::closure_type() const { - return upb_handlerattr_closuretype(this); -} -inline bool HandlerAttributes::SetReturnClosureType(const void *type) { - return upb_handlerattr_setreturnclosuretype(this, type); -} -inline const void* HandlerAttributes::return_closure_type() const { - return upb_handlerattr_returnclosuretype(this); -} -inline bool HandlerAttributes::SetAlwaysOk(bool always_ok) { - return upb_handlerattr_setalwaysok(this, always_ok); -} -inline bool HandlerAttributes::always_ok() const { - return upb_handlerattr_alwaysok(this); + if (!end) return true; + return end(s->closure, + upb_handlerattr_handlerdata( + &s->handler->table[UPB_ENDSTR_SELECTOR].attr)); } -inline BufferHandle::BufferHandle() { upb_bufhandle_init(this); } -inline BufferHandle::~BufferHandle() { upb_bufhandle_uninit(this); } -inline const char* BufferHandle::buffer() const { - return upb_bufhandle_buf(this); +UPB_INLINE bool upb_bufsrc_putbuf(const char *buf, size_t len, + upb_bytessink *sink) { + void *subc; + bool ret; + upb_bufhandle handle; + upb_bufhandle_init(&handle); + upb_bufhandle_setbuf(&handle, buf, 0); + ret = upb_bytessink_start(sink, len, &subc); + if (ret && len != 0) { + ret = (upb_bytessink_putbuf(sink, subc, buf, len, &handle) == len); + } + if (ret) { + ret = upb_bytessink_end(sink); + } + upb_bufhandle_uninit(&handle); + return ret; } -inline size_t BufferHandle::object_offset() const { - return upb_bufhandle_objofs(this); + +#define PUTVAL(type, ctype) \ + UPB_INLINE bool upb_sink_put##type(upb_sink *s, upb_selector_t sel, \ + ctype val) { \ + typedef upb_##type##_handlerfunc functype; \ + functype *func; \ + const void *hd; \ + if (!s->handlers) return true; \ + func = (functype *)upb_handlers_gethandler(s->handlers, sel); \ + if (!func) return true; \ + hd = upb_handlers_gethandlerdata(s->handlers, sel); \ + return func(s->closure, hd, val); \ + } + +PUTVAL(int32, int32_t) +PUTVAL(int64, int64_t) +PUTVAL(uint32, uint32_t) +PUTVAL(uint64, uint64_t) +PUTVAL(float, float) +PUTVAL(double, double) +PUTVAL(bool, bool) +#undef PUTVAL + +UPB_INLINE void upb_sink_reset(upb_sink *s, const upb_handlers *h, void *c) { + s->handlers = h; + s->closure = c; } -inline void BufferHandle::SetBuffer(const char* buf, size_t ofs) { - upb_bufhandle_setbuf(this, buf, ofs); + +UPB_INLINE size_t upb_sink_putstring(upb_sink *s, upb_selector_t sel, + const char *buf, size_t n, + const upb_bufhandle *handle) { + typedef upb_string_handlerfunc func; + func *handler; + const void *hd; + if (!s->handlers) return n; + handler = (func *)upb_handlers_gethandler(s->handlers, sel); + + if (!handler) return n; + hd = upb_handlers_gethandlerdata(s->handlers, sel); + return handler(s->closure, hd, buf, n, handle); } -template -void BufferHandle::SetAttachedObject(const T* obj) { - upb_bufhandle_setobj(this, obj, UniquePtrForType()); + +UPB_INLINE bool upb_sink_startmsg(upb_sink *s) { + typedef upb_startmsg_handlerfunc func; + func *startmsg; + const void *hd; + if (!s->handlers) return true; + startmsg = (func*)upb_handlers_gethandler(s->handlers, UPB_STARTMSG_SELECTOR); + + if (!startmsg) return true; + hd = upb_handlers_gethandlerdata(s->handlers, UPB_STARTMSG_SELECTOR); + return startmsg(s->closure, hd); } -template -const T* BufferHandle::GetAttachedObject() const { - return upb_bufhandle_objtype(this) == UniquePtrForType() - ? static_cast(upb_bufhandle_obj(this)) - : NULL; + +UPB_INLINE bool upb_sink_endmsg(upb_sink *s, upb_status *status) { + typedef upb_endmsg_handlerfunc func; + func *endmsg; + const void *hd; + if (!s->handlers) return true; + endmsg = (func *)upb_handlers_gethandler(s->handlers, UPB_ENDMSG_SELECTOR); + + if (!endmsg) return true; + hd = upb_handlers_gethandlerdata(s->handlers, UPB_ENDMSG_SELECTOR); + return endmsg(s->closure, hd, status); } -inline reffed_ptr Handlers::New(const MessageDef *m) { - upb_handlers *h = upb_handlers_new(m, &h); - return reffed_ptr(h, &h); +UPB_INLINE bool upb_sink_startseq(upb_sink *s, upb_selector_t sel, + upb_sink *sub) { + typedef upb_startfield_handlerfunc func; + func *startseq; + const void *hd; + sub->closure = s->closure; + sub->handlers = s->handlers; + if (!s->handlers) return true; + startseq = (func*)upb_handlers_gethandler(s->handlers, sel); + + if (!startseq) return true; + hd = upb_handlers_gethandlerdata(s->handlers, sel); + sub->closure = startseq(s->closure, hd); + return sub->closure ? true : false; } -inline reffed_ptr Handlers::NewFrozen( - const MessageDef *m, upb_handlers_callback *callback, - const void *closure) { - const upb_handlers *h = upb_handlers_newfrozen(m, &h, callback, closure); - return reffed_ptr(h, &h); + +UPB_INLINE bool upb_sink_endseq(upb_sink *s, upb_selector_t sel) { + typedef upb_endfield_handlerfunc func; + func *endseq; + const void *hd; + if (!s->handlers) return true; + endseq = (func*)upb_handlers_gethandler(s->handlers, sel); + + if (!endseq) return true; + hd = upb_handlers_gethandlerdata(s->handlers, sel); + return endseq(s->closure, hd); } -inline bool Handlers::IsFrozen() const { return upb_handlers_isfrozen(this); } -inline void Handlers::Ref(const void *owner) const { - upb_handlers_ref(this, owner); + +UPB_INLINE bool upb_sink_startstr(upb_sink *s, upb_selector_t sel, + size_t size_hint, upb_sink *sub) { + typedef upb_startstr_handlerfunc func; + func *startstr; + const void *hd; + sub->closure = s->closure; + sub->handlers = s->handlers; + if (!s->handlers) return true; + startstr = (func*)upb_handlers_gethandler(s->handlers, sel); + + if (!startstr) return true; + hd = upb_handlers_gethandlerdata(s->handlers, sel); + sub->closure = startstr(s->closure, hd, size_hint); + return sub->closure ? true : false; } -inline void Handlers::Unref(const void *owner) const { - upb_handlers_unref(this, owner); + +UPB_INLINE bool upb_sink_endstr(upb_sink *s, upb_selector_t sel) { + typedef upb_endfield_handlerfunc func; + func *endstr; + const void *hd; + if (!s->handlers) return true; + endstr = (func*)upb_handlers_gethandler(s->handlers, sel); + + if (!endstr) return true; + hd = upb_handlers_gethandlerdata(s->handlers, sel); + return endstr(s->closure, hd); } -inline void Handlers::DonateRef(const void *from, const void *to) const { - upb_handlers_donateref(this, from, to); + +UPB_INLINE bool upb_sink_startsubmsg(upb_sink *s, upb_selector_t sel, + upb_sink *sub) { + typedef upb_startfield_handlerfunc func; + func *startsubmsg; + const void *hd; + sub->closure = s->closure; + if (!s->handlers) { + sub->handlers = NULL; + return true; + } + sub->handlers = upb_handlers_getsubhandlers_sel(s->handlers, sel); + startsubmsg = (func*)upb_handlers_gethandler(s->handlers, sel); + + if (!startsubmsg) return true; + hd = upb_handlers_gethandlerdata(s->handlers, sel); + sub->closure = startsubmsg(s->closure, hd); + return sub->closure ? true : false; } -inline void Handlers::CheckRef(const void *owner) const { - upb_handlers_checkref(this, owner); + +UPB_INLINE bool upb_sink_endsubmsg(upb_sink *s, upb_selector_t sel) { + typedef upb_endfield_handlerfunc func; + func *endsubmsg; + const void *hd; + if (!s->handlers) return true; + endsubmsg = (func*)upb_handlers_gethandler(s->handlers, sel); + + if (!endsubmsg) return s->closure; + hd = upb_handlers_gethandlerdata(s->handlers, sel); + return endsubmsg(s->closure, hd); } -inline const Status* Handlers::status() { - return upb_handlers_status(this); + +UPB_END_EXTERN_C + +#ifdef __cplusplus + +namespace upb { + +template Sink::Sink(const Handlers* handlers, T* closure) { + upb_sink_reset(this, handlers, closure); } -inline void Handlers::ClearError() { - return upb_handlers_clearerr(this); +template +inline void Sink::Reset(const Handlers* handlers, T* closure) { + upb_sink_reset(this, handlers, closure); } -inline bool Handlers::Freeze(Status *s) { - upb::Handlers* h = this; - return upb_handlers_freeze(&h, 1, s); +inline bool Sink::StartMessage() { + return upb_sink_startmsg(this); } -inline bool Handlers::Freeze(Handlers *const *handlers, int n, Status *s) { - return upb_handlers_freeze(handlers, n, s); +inline bool Sink::EndMessage(Status* status) { + return upb_sink_endmsg(this, status); } -inline bool Handlers::Freeze(const std::vector& h, Status* status) { - return upb_handlers_freeze((Handlers* const*)&h[0], h.size(), status); +inline bool Sink::PutInt32(Handlers::Selector sel, int32_t val) { + return upb_sink_putint32(this, sel, val); } -inline const MessageDef *Handlers::message_def() const { - return upb_handlers_msgdef(this); +inline bool Sink::PutInt64(Handlers::Selector sel, int64_t val) { + return upb_sink_putint64(this, sel, val); } -inline bool Handlers::AddCleanup(void *p, upb_handlerfree *func) { - return upb_handlers_addcleanup(this, p, func); +inline bool Sink::PutUInt32(Handlers::Selector sel, uint32_t val) { + return upb_sink_putuint32(this, sel, val); } -inline bool Handlers::SetStartMessageHandler( - const Handlers::StartMessageHandler &handler) { - assert(!handler.registered_); - handler.registered_ = true; - handler.AddCleanup(this); - return upb_handlers_setstartmsg(this, handler.handler_, &handler.attr_); +inline bool Sink::PutUInt64(Handlers::Selector sel, uint64_t val) { + return upb_sink_putuint64(this, sel, val); } -inline bool Handlers::SetEndMessageHandler( - const Handlers::EndMessageHandler &handler) { - assert(!handler.registered_); - handler.registered_ = true; - handler.AddCleanup(this); - return upb_handlers_setendmsg(this, handler.handler_, &handler.attr_); +inline bool Sink::PutFloat(Handlers::Selector sel, float val) { + return upb_sink_putfloat(this, sel, val); } -inline bool Handlers::SetStartStringHandler(const FieldDef *f, - const StartStringHandler &handler) { - assert(!handler.registered_); - handler.registered_ = true; - handler.AddCleanup(this); - return upb_handlers_setstartstr(this, f, handler.handler_, &handler.attr_); +inline bool Sink::PutDouble(Handlers::Selector sel, double val) { + return upb_sink_putdouble(this, sel, val); } -inline bool Handlers::SetEndStringHandler(const FieldDef *f, - const EndFieldHandler &handler) { - assert(!handler.registered_); - handler.registered_ = true; - handler.AddCleanup(this); - return upb_handlers_setendstr(this, f, handler.handler_, &handler.attr_); +inline bool Sink::PutBool(Handlers::Selector sel, bool val) { + return upb_sink_putbool(this, sel, val); } -inline bool Handlers::SetStringHandler(const FieldDef *f, - const StringHandler& handler) { - assert(!handler.registered_); - handler.registered_ = true; - handler.AddCleanup(this); - return upb_handlers_setstring(this, f, handler.handler_, &handler.attr_); +inline bool Sink::StartString(Handlers::Selector sel, size_t size_hint, + Sink *sub) { + return upb_sink_startstr(this, sel, size_hint, sub); } -inline bool Handlers::SetStartSequenceHandler( - const FieldDef *f, const StartFieldHandler &handler) { - assert(!handler.registered_); - handler.registered_ = true; - handler.AddCleanup(this); - return upb_handlers_setstartseq(this, f, handler.handler_, &handler.attr_); +inline size_t Sink::PutStringBuffer(Handlers::Selector sel, const char *buf, + size_t len, const BufferHandle* handle) { + return upb_sink_putstring(this, sel, buf, len, handle); } -inline bool Handlers::SetStartSubMessageHandler( - const FieldDef *f, const StartFieldHandler &handler) { - assert(!handler.registered_); - handler.registered_ = true; - handler.AddCleanup(this); - return upb_handlers_setstartsubmsg(this, f, handler.handler_, &handler.attr_); +inline bool Sink::EndString(Handlers::Selector sel) { + return upb_sink_endstr(this, sel); } -inline bool Handlers::SetEndSubMessageHandler(const FieldDef *f, - const EndFieldHandler &handler) { - assert(!handler.registered_); - handler.registered_ = true; - handler.AddCleanup(this); - return upb_handlers_setendsubmsg(this, f, handler.handler_, &handler.attr_); +inline bool Sink::StartSubMessage(Handlers::Selector sel, Sink* sub) { + return upb_sink_startsubmsg(this, sel, sub); } -inline bool Handlers::SetEndSequenceHandler(const FieldDef *f, - const EndFieldHandler &handler) { - assert(!handler.registered_); - handler.registered_ = true; - handler.AddCleanup(this); - return upb_handlers_setendseq(this, f, handler.handler_, &handler.attr_); +inline bool Sink::EndSubMessage(Handlers::Selector sel) { + return upb_sink_endsubmsg(this, sel); } -inline bool Handlers::SetSubHandlers(const FieldDef *f, const Handlers *sub) { - return upb_handlers_setsubhandlers(this, f, sub); +inline bool Sink::StartSequence(Handlers::Selector sel, Sink* sub) { + return upb_sink_startseq(this, sel, sub); } -inline const Handlers *Handlers::GetSubHandlers(const FieldDef *f) const { - return upb_handlers_getsubhandlers(this, f); +inline bool Sink::EndSequence(Handlers::Selector sel) { + return upb_sink_endseq(this, sel); } -inline const Handlers *Handlers::GetSubHandlers(Handlers::Selector sel) const { - return upb_handlers_getsubhandlers_sel(this, sel); + +template +BytesSink::BytesSink(const BytesHandler* handler, T* closure) { + Reset(handler, closure); } -inline bool Handlers::GetSelector(const FieldDef *f, Handlers::Type type, - Handlers::Selector *s) { - return upb_handlers_getselector(f, type, s); + +template +void BytesSink::Reset(const BytesHandler *handler, T *closure) { + upb_bytessink_reset(this, handler, closure); } -inline Handlers::Selector Handlers::GetEndSelector(Handlers::Selector start) { - return upb_handlers_getendselector(start); +inline bool BytesSink::Start(size_t size_hint, void **subc) { + return upb_bytessink_start(this, size_hint, subc); } -inline Handlers::GenericFunction *Handlers::GetHandler( - Handlers::Selector selector) { - return upb_handlers_gethandler(this, selector); +inline size_t BytesSink::PutBuffer(void *subc, const char *buf, size_t len, + const BufferHandle *handle) { + return upb_bytessink_putbuf(this, subc, buf, len, handle); } -inline const void *Handlers::GetHandlerData(Handlers::Selector selector) { - return upb_handlers_gethandlerdata(this, selector); +inline bool BytesSink::End() { + return upb_bytessink_end(this); } -inline BytesHandler::BytesHandler() { - upb_byteshandler_init(this); +inline bool BufferSource::PutBuffer(const char *buf, size_t len, + BytesSink *sink) { + return upb_bufsrc_putbuf(buf, len, sink); } -inline BytesHandler::~BytesHandler() {} - -} // namespace upb - -#endif // __cplusplus - - -#undef UPB_TWO_32BIT_TYPES -#undef UPB_TWO_64BIT_TYPES -#undef UPB_INT32_T -#undef UPB_UINT32_T -#undef UPB_INT32ALT_T -#undef UPB_UINT32ALT_T -#undef UPB_INT64_T -#undef UPB_UINT64_T -#undef UPB_INT64ALT_T -#undef UPB_UINT64ALT_T - -#endif // UPB_HANDLERS_INL_H_ +} /* namespace upb */ +#endif -#endif // UPB_HANDLERS_H +#endif /* * upb - a minimalist implementation of protocol buffers. * - * Copyright (c) 2014 Google Inc. See LICENSE for details. + * Copyright (c) 2013 Google Inc. See LICENSE for details. * Author: Josh Haberman * - * A upb::Environment provides a means for injecting malloc and an - * error-reporting callback into encoders/decoders. This allows them to be - * independent of nearly all assumptions about their actual environment. + * For handlers that do very tiny, very simple operations, the function call + * overhead of calling a handler can be significant. This file allows the + * user to define handlers that do something very simple like store the value + * to memory and/or set a hasbit. JIT compilers can then special-case these + * handlers and emit specialized code for them instead of actually calling the + * handler. * - * It is also a container for allocating the encoders/decoders themselves that - * insulates clients from knowing their actual size. This provides ABI - * compatibility even if the size of the objects change. And this allows the - * structure definitions to be in the .c files instead of the .h files, making - * the .h files smaller and more readable. - */ - - -#ifndef UPB_ENV_H_ -#define UPB_ENV_H_ - -#ifdef __cplusplus -namespace upb { -class Environment; -class SeededAllocator; -} -#endif - -UPB_DECLARE_TYPE(upb::Environment, upb_env); -UPB_DECLARE_TYPE(upb::SeededAllocator, upb_seededalloc); - -typedef void *upb_alloc_func(void *ud, void *ptr, size_t oldsize, size_t size); -typedef void upb_cleanup_func(void *ud); -typedef bool upb_error_func(void *ud, const upb_status *status); - -// An environment is *not* thread-safe. -UPB_DEFINE_CLASS0(upb::Environment, - public: - Environment(); - ~Environment(); + * The functionality is very simple/limited right now but may expand to be able + * to call another function. + */ - // Set a custom memory allocation function for the environment. May ONLY - // be called before any calls to Malloc()/Realloc()/AddCleanup() below. - // If this is not called, the system realloc() function will be used. - // The given user pointer "ud" will be passed to the allocation function. - // - // The allocation function will not receive corresponding "free" calls. it - // must ensure that the memory is valid for the lifetime of the Environment, - // but it may be reclaimed any time thereafter. The likely usage is that - // "ud" points to a stateful allocator, and that the allocator frees all - // memory, arena-style, when it is destroyed. In this case the allocator must - // outlive the Environment. Another possibility is that the allocation - // function returns GC-able memory that is guaranteed to be GC-rooted for the - // life of the Environment. - void SetAllocationFunction(upb_alloc_func* alloc, void* ud); +#ifndef UPB_SHIM_H +#define UPB_SHIM_H - template - void SetAllocator(T* allocator) { - SetAllocationFunction(allocator->GetAllocationFunction(), allocator); - } - // Set a custom error reporting function. - void SetErrorFunction(upb_error_func* func, void* ud); +typedef struct { + size_t offset; + int32_t hasbit; +} upb_shim_data; - // Set the error reporting function to simply copy the status to the given - // status and abort. - void ReportErrorsTo(Status* status); +#ifdef __cplusplus - // Returns true if all allocations and AddCleanup() calls have succeeded, - // and no errors were reported with ReportError() (except ones that recovered - // successfully). - bool ok() const; +namespace upb { - ////////////////////////////////////////////////////////////////////////////// - // Functions for use by encoders/decoders. +struct Shim { + typedef upb_shim_data Data; - // Reports an error to this environment's callback, returning true if - // the caller should try to recover. - bool ReportError(const Status* status); + /* Sets a handler for the given field that writes the value to the given + * offset and, if hasbit >= 0, sets a bit at the given bit offset. Returns + * true if the handler was set successfully. */ + static bool Set(Handlers *h, const FieldDef *f, size_t ofs, int32_t hasbit); - // Allocate memory. Uses the environment's allocation function. - // - // There is no need to free(). All memory will be freed automatically, but is - // guaranteed to outlive the Environment. - void* Malloc(size_t size); + /* If this handler is a shim, returns the corresponding upb::Shim::Data and + * stores the type in "type". Otherwise returns NULL. */ + static const Data* GetData(const Handlers* h, Handlers::Selector s, + FieldDef::Type* type); +}; - // Reallocate memory. Preserves "oldsize" bytes from the existing buffer - // Requires: oldsize <= existing_size. - // - // TODO(haberman): should we also enforce that oldsize <= size? - void* Realloc(void* ptr, size_t oldsize, size_t size); +} /* namespace upb */ - // Add a cleanup function to run when the environment is destroyed. - // Returns false on out-of-memory. - // - // The first call to AddCleanup() after SetAllocationFunction() is guaranteed - // to return true -- this makes it possible to robustly set a cleanup handler - // for a custom allocation function. - bool AddCleanup(upb_cleanup_func* func, void* ud); +#endif - // Total number of bytes that have been allocated. It is undefined what - // Realloc() does to this counter. - size_t BytesAllocated() const; +UPB_BEGIN_EXTERN_C - private: - UPB_DISALLOW_COPY_AND_ASSIGN(Environment); -, -UPB_DEFINE_STRUCT0(upb_env, - bool ok_; - size_t bytes_allocated; +/* C API. */ +bool upb_shim_set(upb_handlers *h, const upb_fielddef *f, size_t offset, + int32_t hasbit); +const upb_shim_data *upb_shim_getdata(const upb_handlers *h, upb_selector_t s, + upb_fieldtype_t *type); - // Alloc function. - upb_alloc_func *alloc; - void *alloc_ud; +UPB_END_EXTERN_C - // Error-reporting function. - upb_error_func *err; - void *err_ud; +#ifdef __cplusplus +/* C++ Wrappers. */ +namespace upb { +inline bool Shim::Set(Handlers* h, const FieldDef* f, size_t ofs, + int32_t hasbit) { + return upb_shim_set(h, f, ofs, hasbit); +} +inline const Shim::Data* Shim::GetData(const Handlers* h, Handlers::Selector s, + FieldDef::Type* type) { + return upb_shim_getdata(h, s, type); +} +} /* namespace upb */ +#endif - // Userdata for default alloc func. - void *default_alloc_ud; +#endif /* UPB_SHIM_H */ +/* + * upb - a minimalist implementation of protocol buffers. + * + * Copyright (c) 2009-2012 Google Inc. See LICENSE for details. + * Author: Josh Haberman + * + * A symtab (symbol table) stores a name->def map of upb_defs. Clients could + * always create such tables themselves, but upb_symtab has logic for resolving + * symbolic references, and in particular, for keeping a whole set of consistent + * defs when replacing some subset of those defs. This logic is nontrivial. + * + * This is a mixed C/C++ interface that offers a full API to both languages. + * See the top-level README for more information. + */ - // Cleanup entries. Pointer to a cleanup_ent, defined in env.c - void *cleanup_head; +#ifndef UPB_SYMTAB_H_ +#define UPB_SYMTAB_H_ - // For future expansion, since the size of this struct is exposed to users. - void *future1; - void *future2; -)); -UPB_BEGIN_EXTERN_C +#ifdef __cplusplus +#include +namespace upb { class SymbolTable; } +#endif -void upb_env_init(upb_env *e); -void upb_env_uninit(upb_env *e); -void upb_env_setallocfunc(upb_env *e, upb_alloc_func *func, void *ud); -void upb_env_seterrorfunc(upb_env *e, upb_error_func *func, void *ud); -void upb_env_reporterrorsto(upb_env *e, upb_status *status); -bool upb_env_ok(const upb_env *e); -bool upb_env_reporterror(upb_env *e, const upb_status *status); -void *upb_env_malloc(upb_env *e, size_t size); -void *upb_env_realloc(upb_env *e, void *ptr, size_t oldsize, size_t size); -bool upb_env_addcleanup(upb_env *e, upb_cleanup_func *func, void *ud); -size_t upb_env_bytesallocated(const upb_env *e); +UPB_DECLARE_DERIVED_TYPE(upb::SymbolTable, upb::RefCounted, + upb_symtab, upb_refcounted) -UPB_END_EXTERN_C +typedef struct { + UPB_PRIVATE_FOR_CPP + upb_strtable_iter iter; + upb_deftype_t type; +} upb_symtab_iter; + +#ifdef __cplusplus -// An allocator that allocates from an initial memory region (likely the stack) -// before falling back to another allocator. -UPB_DEFINE_CLASS0(upb::SeededAllocator, +/* Non-const methods in upb::SymbolTable are NOT thread-safe. */ +class upb::SymbolTable { public: - SeededAllocator(void *mem, size_t len); - ~SeededAllocator(); + /* Returns a new symbol table with a single ref owned by "owner." + * Returns NULL if memory allocation failed. */ + static reffed_ptr New(); - // Set a custom fallback memory allocation function for the allocator, to use - // once the initial region runs out. - // - // May ONLY be called before GetAllocationFunction(). If this is not - // called, the system realloc() will be the fallback allocator. - void SetFallbackAllocator(upb_alloc_func *alloc, void *ud); + /* Include RefCounted base methods. */ + UPB_REFCOUNTED_CPPMETHODS + + /* For all lookup functions, the returned pointer is not owned by the + * caller; it may be invalidated by any non-const call or unref of the + * SymbolTable! To protect against this, take a ref if desired. */ + + /* Freezes the symbol table: prevents further modification of it. + * After the Freeze() operation is successful, the SymbolTable must only be + * accessed via a const pointer. + * + * Unlike with upb::MessageDef/upb::EnumDef/etc, freezing a SymbolTable is not + * a necessary step in using a SymbolTable. If you have no need for it to be + * immutable, there is no need to freeze it ever. However sometimes it is + * useful, and SymbolTables that are statically compiled into the binary are + * always frozen by nature. */ + void Freeze(); - // Gets the allocation function for this allocator. - upb_alloc_func* GetAllocationFunction(); + /* Resolves the given symbol using the rules described in descriptor.proto, + * namely: + * + * If the name starts with a '.', it is fully-qualified. Otherwise, + * C++-like scoping rules are used to find the type (i.e. first the nested + * types within this message are searched, then within the parent, on up + * to the root namespace). + * + * If not found, returns NULL. */ + const Def* Resolve(const char* base, const char* sym) const; - private: - UPB_DISALLOW_COPY_AND_ASSIGN(SeededAllocator); -, -UPB_DEFINE_STRUCT0(upb_seededalloc, - // Fallback alloc function. - upb_alloc_func *alloc; - upb_cleanup_func *alloc_cleanup; - void *alloc_ud; - bool need_cleanup; - bool returned_allocfunc; + /* Finds an entry in the symbol table with this exact name. If not found, + * returns NULL. */ + const Def* Lookup(const char *sym) const; + const MessageDef* LookupMessage(const char *sym) const; + const EnumDef* LookupEnum(const char *sym) const; - // Userdata for default alloc func. - void *default_alloc_ud; + /* TODO: introduce a C++ iterator, but make it nice and templated so that if + * you ask for an iterator of MessageDef the iterated elements are strongly + * typed as MessageDef*. */ + + /* Adds the given mutable defs to the symtab, resolving all symbols + * (including enum default values) and finalizing the defs. Only one def per + * name may be in the list, but defs can replace existing defs in the symtab. + * All defs must have a name -- anonymous defs are not allowed. Anonymous + * defs can still be frozen by calling upb_def_freeze() directly. + * + * Any existing defs that can reach defs that are being replaced will + * themselves be replaced also, so that the resulting set of defs is fully + * consistent. + * + * This logic implemented in this method is a convenience; ultimately it + * calls some combination of upb_fielddef_setsubdef(), upb_def_dup(), and + * upb_freeze(), any of which the client could call themself. However, since + * the logic for doing so is nontrivial, we provide it here. + * + * The entire operation either succeeds or fails. If the operation fails, + * the symtab is unchanged, false is returned, and status indicates the + * error. The caller passes a ref on all defs to the symtab (even if the + * operation fails). + * + * TODO(haberman): currently failure will leave the symtab unchanged, but may + * leave the defs themselves partially resolved. Does this matter? If so we + * could do a prepass that ensures that all symbols are resolvable and bail + * if not, so we don't mutate anything until we know the operation will + * succeed. + * + * TODO(haberman): since the defs must be mutable, refining a frozen def + * requires making mutable copies of the entire tree. This is wasteful if + * only a few messages are changing. We may want to add a way of adding a + * tree of frozen defs to the symtab (perhaps an alternate constructor where + * you pass the root of the tree?) */ + bool Add(Def*const* defs, int n, void* ref_donor, upb_status* status); - // Pointers for the initial memory region. - char *mem_base; - char *mem_ptr; - char *mem_limit; + bool Add(const std::vector& defs, void *owner, Status* status) { + return Add((Def*const*)&defs[0], defs.size(), owner, status); + } - // For future expansion, since the size of this struct is exposed to users. - void *future1; - void *future2; -)); + private: + UPB_DISALLOW_POD_OPS(SymbolTable, upb::SymbolTable) +}; + +#endif /* __cplusplus */ UPB_BEGIN_EXTERN_C -void upb_seededalloc_init(upb_seededalloc *a, void *mem, size_t len); -void upb_seededalloc_uninit(upb_seededalloc *a); -void upb_seededalloc_setfallbackalloc(upb_seededalloc *a, upb_alloc_func *func, - void *ud); -upb_alloc_func *upb_seededalloc_getallocfunc(upb_seededalloc *a); +/* Native C API. */ + +/* Include refcounted methods like upb_symtab_ref(). */ +UPB_REFCOUNTED_CMETHODS(upb_symtab, upb_symtab_upcast) + +upb_symtab *upb_symtab_new(const void *owner); +void upb_symtab_freeze(upb_symtab *s); +const upb_def *upb_symtab_resolve(const upb_symtab *s, const char *base, + const char *sym); +const upb_def *upb_symtab_lookup(const upb_symtab *s, const char *sym); +const upb_msgdef *upb_symtab_lookupmsg(const upb_symtab *s, const char *sym); +const upb_enumdef *upb_symtab_lookupenum(const upb_symtab *s, const char *sym); +bool upb_symtab_add(upb_symtab *s, upb_def *const*defs, int n, void *ref_donor, + upb_status *status); + +/* upb_symtab_iter i; + * for(upb_symtab_begin(&i, s, type); !upb_symtab_done(&i); + * upb_symtab_next(&i)) { + * const upb_def *def = upb_symtab_iter_def(&i); + * // ... + * } + * + * 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_symtab_begin(upb_symtab_iter *iter, const upb_symtab *s, + upb_deftype_t type); +void upb_symtab_next(upb_symtab_iter *iter); +bool upb_symtab_done(const upb_symtab_iter *iter); +const upb_def *upb_symtab_iter_def(const upb_symtab_iter *iter); UPB_END_EXTERN_C #ifdef __cplusplus - +/* C++ inline wrappers. */ namespace upb { - -inline Environment::Environment() { - upb_env_init(this); -} -inline Environment::~Environment() { - upb_env_uninit(this); -} -inline void Environment::SetAllocationFunction(upb_alloc_func *alloc, - void *ud) { - upb_env_setallocfunc(this, alloc, ud); -} -inline void Environment::SetErrorFunction(upb_error_func *func, void *ud) { - upb_env_seterrorfunc(this, func, ud); -} -inline void Environment::ReportErrorsTo(Status* status) { - upb_env_reporterrorsto(this, status); -} -inline bool Environment::ok() const { - return upb_env_ok(this); -} -inline bool Environment::ReportError(const Status* status) { - return upb_env_reporterror(this, status); -} -inline void *Environment::Malloc(size_t size) { - return upb_env_malloc(this, size); -} -inline void *Environment::Realloc(void *ptr, size_t oldsize, size_t size) { - return upb_env_realloc(this, ptr, oldsize, size); -} -inline bool Environment::AddCleanup(upb_cleanup_func *func, void *ud) { - return upb_env_addcleanup(this, func, ud); -} -inline size_t Environment::BytesAllocated() const { - return upb_env_bytesallocated(this); +inline reffed_ptr SymbolTable::New() { + upb_symtab *s = upb_symtab_new(&s); + return reffed_ptr(s, &s); } -inline SeededAllocator::SeededAllocator(void *mem, size_t len) { - upb_seededalloc_init(this, mem, len); +inline void SymbolTable::Freeze() { + return upb_symtab_freeze(this); } -inline SeededAllocator::~SeededAllocator() { - upb_seededalloc_uninit(this); +inline const Def *SymbolTable::Resolve(const char *base, + const char *sym) const { + return upb_symtab_resolve(this, base, sym); } -inline void SeededAllocator::SetFallbackAllocator(upb_alloc_func *alloc, - void *ud) { - upb_seededalloc_setfallbackalloc(this, alloc, ud); +inline const Def* SymbolTable::Lookup(const char *sym) const { + return upb_symtab_lookup(this, sym); } -inline upb_alloc_func *SeededAllocator::GetAllocationFunction() { - return upb_seededalloc_getallocfunc(this); +inline const MessageDef *SymbolTable::LookupMessage(const char *sym) const { + return upb_symtab_lookupmsg(this, sym); } +inline bool SymbolTable::Add( + Def*const* defs, int n, void* ref_donor, upb_status* status) { + return upb_symtab_add(this, (upb_def*const*)defs, n, ref_donor, status); +} +} /* namespace upb */ +#endif -} // namespace upb - -#endif // __cplusplus - -#endif // UPB_ENV_H_ +#endif /* UPB_SYMTAB_H_ */ /* * upb - a minimalist implementation of protocol buffers. * - * Copyright (c) 2010-2012 Google Inc. See LICENSE for details. + * Copyright (c) 2011 Google Inc. See LICENSE for details. * Author: Josh Haberman * - * A upb_sink is an object that binds a upb_handlers object to some runtime - * state. It is the object that can actually receive data via the upb_handlers - * interface. - * - * Unlike upb_def and upb_handlers, upb_sink is never frozen, immutable, or - * thread-safe. You can create as many of them as you want, but each one may - * only be used in a single thread at a time. - * - * If we compare with class-based OOP, a you can think of a upb_def as an - * abstract base class, a upb_handlers as a concrete derived class, and a - * upb_sink as an object (class instance). + * upb::descriptor::Reader provides a way of building upb::Defs from + * data in descriptor.proto format. */ -#ifndef UPB_SINK_H -#define UPB_SINK_H +#ifndef UPB_DESCRIPTOR_H +#define UPB_DESCRIPTOR_H #ifdef __cplusplus namespace upb { -class BufferSource; -class BytesSink; -class Sink; -} +namespace descriptor { +class Reader; +} /* namespace descriptor */ +} /* namespace upb */ #endif -UPB_DECLARE_TYPE(upb::BufferSource, upb_bufsrc); -UPB_DECLARE_TYPE(upb::BytesSink, upb_bytessink); -UPB_DECLARE_TYPE(upb::Sink, upb_sink); - -// A upb::Sink is an object that binds a upb::Handlers object to some runtime -// state. It represents an endpoint to which data can be sent. -// -// TODO(haberman): right now all of these functions take selectors. Should they -// take selectorbase instead? -// -// ie. instead of calling: -// sink->StartString(FOO_FIELD_START_STRING, ...) -// a selector base would let you say: -// sink->StartString(FOO_FIELD, ...) -// -// This would make call sites a little nicer and require emitting fewer selector -// definitions in .h files. -// -// But the current scheme has the benefit that you can retrieve a function -// pointer for any handler with handlers->GetHandler(selector), without having -// to have a separate GetHandler() function for each handler type. The JIT -// compiler uses this. To accommodate we'd have to expose a separate -// GetHandler() for every handler type. -// -// Also to ponder: selectors right now are independent of a specific Handlers -// instance. In other words, they allocate a number to every possible handler -// that *could* be registered, without knowing anything about what handlers -// *are* registered. That means that using selectors as table offsets prohibits -// us from compacting the handler table at Freeze() time. If the table is very -// sparse, this could be wasteful. -// -// Having another selector-like thing that is specific to a Handlers instance -// would allow this compacting, but then it would be impossible to write code -// ahead-of-time that can be bound to any Handlers instance at runtime. For -// example, a .proto file parser written as straight C will not know what -// Handlers it will be bound to, so when it calls sink->StartString() what -// selector will it pass? It needs a selector like we have today, that is -// independent of any particular upb::Handlers. -// -// Is there a way then to allow Handlers table compaction? -UPB_DEFINE_CLASS0(upb::Sink, - public: - // Constructor with no initialization; must be Reset() before use. - Sink() {} - - // Constructs a new sink for the given frozen handlers and closure. - // - // TODO: once the Handlers know the expected closure type, verify that T - // matches it. - template Sink(const Handlers* handlers, T* closure); - - // Resets the value of the sink. - template void Reset(const Handlers* handlers, T* closure); - - // Returns the top-level object that is bound to this sink. - // - // TODO: once the Handlers know the expected closure type, verify that T - // matches it. - template T* GetObject() const; - - // Functions for pushing data into the sink. - // - // These return false if processing should stop (either due to error or just - // to suspend). - // - // These may not be called from within one of the same sink's handlers (in - // other words, handlers are not re-entrant). - - // Should be called at the start and end of every message; both the top-level - // message and submessages. This means that submessages should use the - // following sequence: - // sink->StartSubMessage(startsubmsg_selector); - // sink->StartMessage(); - // // ... - // sink->EndMessage(&status); - // sink->EndSubMessage(endsubmsg_selector); - bool StartMessage(); - bool EndMessage(Status* status); +UPB_DECLARE_TYPE(upb::descriptor::Reader, upb_descreader) - // Putting of individual values. These work for both repeated and - // non-repeated fields, but for repeated fields you must wrap them in - // calls to StartSequence()/EndSequence(). - bool PutInt32(Handlers::Selector s, int32_t val); - bool PutInt64(Handlers::Selector s, int64_t val); - bool PutUInt32(Handlers::Selector s, uint32_t val); - bool PutUInt64(Handlers::Selector s, uint64_t val); - bool PutFloat(Handlers::Selector s, float val); - bool PutDouble(Handlers::Selector s, double val); - bool PutBool(Handlers::Selector s, bool val); +#ifdef __cplusplus - // Putting of string/bytes values. Each string can consist of zero or more - // non-contiguous buffers of data. - // - // For StartString(), the function will write a sink for the string to "sub." - // The sub-sink must be used for any/all PutStringBuffer() calls. - bool StartString(Handlers::Selector s, size_t size_hint, Sink* sub); - size_t PutStringBuffer(Handlers::Selector s, const char *buf, size_t len, - const BufferHandle *handle); - bool EndString(Handlers::Selector s); +/* Class that receives descriptor data according to the descriptor.proto schema + * and use it to build upb::Defs corresponding to that schema. */ +class upb::descriptor::Reader { + public: + /* These handlers must have come from NewHandlers() and must outlive the + * Reader. + * + * TODO: generate the handlers statically (like we do with the + * descriptor.proto defs) so that there is no need to pass this parameter (or + * to build/memory-manage the handlers at runtime at all). Unfortunately this + * is a bit tricky to implement for Handlers, but necessary to simplify this + * interface. */ + static Reader* Create(Environment* env, const Handlers* handlers); - // For submessage fields. - // - // For StartSubMessage(), the function will write a sink for the string to - // "sub." The sub-sink must be used for any/all handlers called within the - // submessage. - bool StartSubMessage(Handlers::Selector s, Sink* sub); - bool EndSubMessage(Handlers::Selector s); + /* The reader's input; this is where descriptor.proto data should be sent. */ + Sink* input(); - // For repeated fields of any type, the sequence of values must be wrapped in - // these calls. - // - // For StartSequence(), the function will write a sink for the string to - // "sub." The sub-sink must be used for any/all handlers called within the - // sequence. - bool StartSequence(Handlers::Selector s, Sink* sub); - bool EndSequence(Handlers::Selector s); + /* Returns an array of all defs that have been parsed, and transfers ownership + * of them to "owner". The number of defs is stored in *n. Ownership of the + * returned array is retained and is invalidated by any other call into + * Reader. + * + * These defs are not frozen or resolved; they are ready to be added to a + * symtab. */ + upb::Def** GetDefs(void* owner, int* n); - // Copy and assign specifically allowed. - // We don't even bother making these members private because so many - // functions need them and this is mainly just a dumb data container anyway. -, -UPB_DEFINE_STRUCT0(upb_sink, - const upb_handlers *handlers; - void *closure; -)); + /* Builds and returns handlers for the reader, owned by "owner." */ + static Handlers* NewHandlers(const void* owner); -UPB_DEFINE_CLASS0(upb::BytesSink, - public: - BytesSink() {} + private: + UPB_DISALLOW_POD_OPS(Reader, upb::descriptor::Reader) +}; - // Constructs a new sink for the given frozen handlers and closure. - // - // TODO(haberman): once the Handlers know the expected closure type, verify - // that T matches it. - template BytesSink(const BytesHandler* handler, T* closure); +#endif - // Resets the value of the sink. - template void Reset(const BytesHandler* handler, T* closure); +UPB_BEGIN_EXTERN_C - bool Start(size_t size_hint, void **subc); - size_t PutBuffer(void *subc, const char *buf, size_t len, - const BufferHandle *handle); - bool End(); -, -UPB_DEFINE_STRUCT0(upb_bytessink, - const upb_byteshandler *handler; - void *closure; -)); +/* C API. */ +upb_descreader *upb_descreader_create(upb_env *e, const upb_handlers *h); +upb_sink *upb_descreader_input(upb_descreader *r); +upb_def **upb_descreader_getdefs(upb_descreader *r, void *owner, int *n); +const upb_handlers *upb_descreader_newhandlers(const void *owner); -// A class for pushing a flat buffer of data to a BytesSink. -// You can construct an instance of this to get a resumable source, -// or just call the static PutBuffer() to do a non-resumable push all in one go. -UPB_DEFINE_CLASS0(upb::BufferSource, - public: - BufferSource(); - BufferSource(const char* buf, size_t len, BytesSink* sink); +UPB_END_EXTERN_C - // Returns true if the entire buffer was pushed successfully. Otherwise the - // next call to PutNext() will resume where the previous one left off. - // TODO(haberman): implement this. - bool PutNext(); +#ifdef __cplusplus +/* C++ implementation details. ************************************************/ +namespace upb { +namespace descriptor { +inline Reader* Reader::Create(Environment* e, const Handlers *h) { + return upb_descreader_create(e, h); +} +inline Sink* Reader::input() { return upb_descreader_input(this); } +inline upb::Def** Reader::GetDefs(void* owner, int* n) { + return upb_descreader_getdefs(this, owner, n); +} +} /* namespace descriptor */ +} /* namespace upb */ +#endif - // A static version; with this version is it not possible to resume in the - // case of failure or a partially-consumed buffer. - static bool PutBuffer(const char* buf, size_t len, BytesSink* sink); +#endif /* UPB_DESCRIPTOR_H */ +/* This file contains accessors for a set of compiled-in defs. + * Note that unlike Google's protobuf, it does *not* define + * generated classes or any other kind of data structure for + * actually storing protobufs. It only contains *defs* which + * let you reflect over a protobuf *schema*. + */ +/* This file was generated by upbc (the upb compiler). + * Do not edit -- your changes will be discarded when the file is + * regenerated. */ - template static bool PutBuffer(const T& str, BytesSink* sink) { - return PutBuffer(str.c_str(), str.size(), sink); - } -, -UPB_DEFINE_STRUCT0(upb_bufsrc, -)); +#ifndef GOOGLE_PROTOBUF_DESCRIPTOR_UPB_H_ +#define GOOGLE_PROTOBUF_DESCRIPTOR_UPB_H_ -UPB_BEGIN_EXTERN_C // { -// Inline definitions. +#ifdef __cplusplus +UPB_BEGIN_EXTERN_C +#endif -UPB_INLINE void upb_bytessink_reset(upb_bytessink *s, const upb_byteshandler *h, - void *closure) { - s->handler = h; - s->closure = closure; -} +/* Enums */ -UPB_INLINE bool upb_bytessink_start(upb_bytessink *s, size_t size_hint, - void **subc) { - *subc = s->closure; - if (!s->handler) return true; - upb_startstr_handlerfunc *start = - (upb_startstr_handlerfunc *)s->handler->table[UPB_STARTSTR_SELECTOR].func; +typedef enum { + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_LABEL_OPTIONAL = 1, + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_LABEL_REQUIRED = 2, + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_LABEL_REPEATED = 3 +} google_protobuf_FieldDescriptorProto_Label; - if (!start) return true; - *subc = start(s->closure, upb_handlerattr_handlerdata( - &s->handler->table[UPB_STARTSTR_SELECTOR].attr), - size_hint); - return *subc != NULL; -} +typedef enum { + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_DOUBLE = 1, + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_FLOAT = 2, + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_INT64 = 3, + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_UINT64 = 4, + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_INT32 = 5, + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_FIXED64 = 6, + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_FIXED32 = 7, + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_BOOL = 8, + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_STRING = 9, + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_GROUP = 10, + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_MESSAGE = 11, + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_BYTES = 12, + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_UINT32 = 13, + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_ENUM = 14, + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_SFIXED32 = 15, + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_SFIXED64 = 16, + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_SINT32 = 17, + GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_SINT64 = 18 +} google_protobuf_FieldDescriptorProto_Type; -UPB_INLINE size_t upb_bytessink_putbuf(upb_bytessink *s, void *subc, - const char *buf, size_t size, - const upb_bufhandle* handle) { - if (!s->handler) return true; - upb_string_handlerfunc *putbuf = - (upb_string_handlerfunc *)s->handler->table[UPB_STRING_SELECTOR].func; +typedef enum { + GOOGLE_PROTOBUF_FIELDOPTIONS_STRING = 0, + GOOGLE_PROTOBUF_FIELDOPTIONS_CORD = 1, + GOOGLE_PROTOBUF_FIELDOPTIONS_STRING_PIECE = 2 +} google_protobuf_FieldOptions_CType; - if (!putbuf) return true; - return putbuf(subc, upb_handlerattr_handlerdata( - &s->handler->table[UPB_STRING_SELECTOR].attr), - buf, size, handle); -} +typedef enum { + GOOGLE_PROTOBUF_FILEOPTIONS_SPEED = 1, + GOOGLE_PROTOBUF_FILEOPTIONS_CODE_SIZE = 2, + GOOGLE_PROTOBUF_FILEOPTIONS_LITE_RUNTIME = 3 +} google_protobuf_FileOptions_OptimizeMode; -UPB_INLINE bool upb_bytessink_end(upb_bytessink *s) { - if (!s->handler) return true; - upb_endfield_handlerfunc *end = - (upb_endfield_handlerfunc *)s->handler->table[UPB_ENDSTR_SELECTOR].func; +/* Selectors */ - if (!end) return true; - return end(s->closure, - upb_handlerattr_handlerdata( - &s->handler->table[UPB_ENDSTR_SELECTOR].attr)); -} +/* google.protobuf.DescriptorProto */ +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_FIELD_STARTSUBMSG 2 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_NESTED_TYPE_STARTSUBMSG 3 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_ENUM_TYPE_STARTSUBMSG 4 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSION_RANGE_STARTSUBMSG 5 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSION_STARTSUBMSG 6 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_OPTIONS_STARTSUBMSG 7 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_FIELD_STARTSEQ 8 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_FIELD_ENDSEQ 9 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_FIELD_ENDSUBMSG 10 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_NESTED_TYPE_STARTSEQ 11 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_NESTED_TYPE_ENDSEQ 12 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_NESTED_TYPE_ENDSUBMSG 13 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_ENUM_TYPE_STARTSEQ 14 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_ENUM_TYPE_ENDSEQ 15 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_ENUM_TYPE_ENDSUBMSG 16 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSION_RANGE_STARTSEQ 17 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSION_RANGE_ENDSEQ 18 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSION_RANGE_ENDSUBMSG 19 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSION_STARTSEQ 20 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSION_ENDSEQ 21 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSION_ENDSUBMSG 22 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_OPTIONS_ENDSUBMSG 23 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_NAME_STRING 24 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_NAME_STARTSTR 25 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_NAME_ENDSTR 26 -UPB_INLINE bool upb_bufsrc_putbuf(const char *buf, size_t len, - upb_bytessink *sink) { - void *subc; - upb_bufhandle handle; - upb_bufhandle_init(&handle); - upb_bufhandle_setbuf(&handle, buf, 0); - bool ret = upb_bytessink_start(sink, len, &subc); - if (ret && len != 0) { - ret = (upb_bytessink_putbuf(sink, subc, buf, len, &handle) == len); - } - if (ret) { - ret = upb_bytessink_end(sink); - } - upb_bufhandle_uninit(&handle); - return ret; -} +/* google.protobuf.DescriptorProto.ExtensionRange */ +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSIONRANGE_START_INT32 2 +#define SEL_GOOGLE_PROTOBUF_DESCRIPTORPROTO_EXTENSIONRANGE_END_INT32 3 -#define PUTVAL(type, ctype) \ - UPB_INLINE bool upb_sink_put##type(upb_sink *s, upb_selector_t sel, \ - ctype val) { \ - if (!s->handlers) return true; \ - upb_##type##_handlerfunc *func = \ - (upb_##type##_handlerfunc *)upb_handlers_gethandler(s->handlers, sel); \ - if (!func) return true; \ - const void *hd = upb_handlers_gethandlerdata(s->handlers, sel); \ - return func(s->closure, hd, val); \ - } +/* google.protobuf.EnumDescriptorProto */ +#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_VALUE_STARTSUBMSG 2 +#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_OPTIONS_STARTSUBMSG 3 +#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_VALUE_STARTSEQ 4 +#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_VALUE_ENDSEQ 5 +#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_VALUE_ENDSUBMSG 6 +#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_OPTIONS_ENDSUBMSG 7 +#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_NAME_STRING 8 +#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_NAME_STARTSTR 9 +#define SEL_GOOGLE_PROTOBUF_ENUMDESCRIPTORPROTO_NAME_ENDSTR 10 -PUTVAL(int32, int32_t); -PUTVAL(int64, int64_t); -PUTVAL(uint32, uint32_t); -PUTVAL(uint64, uint64_t); -PUTVAL(float, float); -PUTVAL(double, double); -PUTVAL(bool, bool); -#undef PUTVAL +/* google.protobuf.EnumOptions */ +#define SEL_GOOGLE_PROTOBUF_ENUMOPTIONS_UNINTERPRETED_OPTION_STARTSUBMSG 2 +#define SEL_GOOGLE_PROTOBUF_ENUMOPTIONS_UNINTERPRETED_OPTION_STARTSEQ 3 +#define SEL_GOOGLE_PROTOBUF_ENUMOPTIONS_UNINTERPRETED_OPTION_ENDSEQ 4 +#define SEL_GOOGLE_PROTOBUF_ENUMOPTIONS_UNINTERPRETED_OPTION_ENDSUBMSG 5 +#define SEL_GOOGLE_PROTOBUF_ENUMOPTIONS_ALLOW_ALIAS_BOOL 6 -UPB_INLINE void upb_sink_reset(upb_sink *s, const upb_handlers *h, void *c) { - s->handlers = h; - s->closure = c; -} +/* google.protobuf.EnumValueDescriptorProto */ +#define SEL_GOOGLE_PROTOBUF_ENUMVALUEDESCRIPTORPROTO_OPTIONS_STARTSUBMSG 2 +#define SEL_GOOGLE_PROTOBUF_ENUMVALUEDESCRIPTORPROTO_OPTIONS_ENDSUBMSG 3 +#define SEL_GOOGLE_PROTOBUF_ENUMVALUEDESCRIPTORPROTO_NAME_STRING 4 +#define SEL_GOOGLE_PROTOBUF_ENUMVALUEDESCRIPTORPROTO_NAME_STARTSTR 5 +#define SEL_GOOGLE_PROTOBUF_ENUMVALUEDESCRIPTORPROTO_NAME_ENDSTR 6 +#define SEL_GOOGLE_PROTOBUF_ENUMVALUEDESCRIPTORPROTO_NUMBER_INT32 7 -UPB_INLINE size_t upb_sink_putstring(upb_sink *s, upb_selector_t sel, - const char *buf, size_t n, - const upb_bufhandle *handle) { - if (!s->handlers) return n; - upb_string_handlerfunc *handler = - (upb_string_handlerfunc *)upb_handlers_gethandler(s->handlers, sel); +/* google.protobuf.EnumValueOptions */ +#define SEL_GOOGLE_PROTOBUF_ENUMVALUEOPTIONS_UNINTERPRETED_OPTION_STARTSUBMSG 2 +#define SEL_GOOGLE_PROTOBUF_ENUMVALUEOPTIONS_UNINTERPRETED_OPTION_STARTSEQ 3 +#define SEL_GOOGLE_PROTOBUF_ENUMVALUEOPTIONS_UNINTERPRETED_OPTION_ENDSEQ 4 +#define SEL_GOOGLE_PROTOBUF_ENUMVALUEOPTIONS_UNINTERPRETED_OPTION_ENDSUBMSG 5 - if (!handler) return n; - const void *hd = upb_handlers_gethandlerdata(s->handlers, sel); - return handler(s->closure, hd, buf, n, handle); -} +/* google.protobuf.FieldDescriptorProto */ +#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_OPTIONS_STARTSUBMSG 2 +#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_OPTIONS_ENDSUBMSG 3 +#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_NAME_STRING 4 +#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_NAME_STARTSTR 5 +#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_NAME_ENDSTR 6 +#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_EXTENDEE_STRING 7 +#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_EXTENDEE_STARTSTR 8 +#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_EXTENDEE_ENDSTR 9 +#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_NUMBER_INT32 10 +#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_LABEL_INT32 11 +#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_INT32 12 +#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_NAME_STRING 13 +#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_NAME_STARTSTR 14 +#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_TYPE_NAME_ENDSTR 15 +#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_DEFAULT_VALUE_STRING 16 +#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_DEFAULT_VALUE_STARTSTR 17 +#define SEL_GOOGLE_PROTOBUF_FIELDDESCRIPTORPROTO_DEFAULT_VALUE_ENDSTR 18 + +/* google.protobuf.FieldOptions */ +#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_UNINTERPRETED_OPTION_STARTSUBMSG 2 +#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_UNINTERPRETED_OPTION_STARTSEQ 3 +#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_UNINTERPRETED_OPTION_ENDSEQ 4 +#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_UNINTERPRETED_OPTION_ENDSUBMSG 5 +#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_CTYPE_INT32 6 +#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_PACKED_BOOL 7 +#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_DEPRECATED_BOOL 8 +#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_LAZY_BOOL 9 +#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_EXPERIMENTAL_MAP_KEY_STRING 10 +#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_EXPERIMENTAL_MAP_KEY_STARTSTR 11 +#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_EXPERIMENTAL_MAP_KEY_ENDSTR 12 +#define SEL_GOOGLE_PROTOBUF_FIELDOPTIONS_WEAK_BOOL 13 + +/* google.protobuf.FileDescriptorProto */ +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_MESSAGE_TYPE_STARTSUBMSG 2 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_ENUM_TYPE_STARTSUBMSG 3 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_SERVICE_STARTSUBMSG 4 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_EXTENSION_STARTSUBMSG 5 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_OPTIONS_STARTSUBMSG 6 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_SOURCE_CODE_INFO_STARTSUBMSG 7 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_MESSAGE_TYPE_STARTSEQ 8 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_MESSAGE_TYPE_ENDSEQ 9 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_MESSAGE_TYPE_ENDSUBMSG 10 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_ENUM_TYPE_STARTSEQ 11 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_ENUM_TYPE_ENDSEQ 12 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_ENUM_TYPE_ENDSUBMSG 13 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_SERVICE_STARTSEQ 14 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_SERVICE_ENDSEQ 15 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_SERVICE_ENDSUBMSG 16 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_EXTENSION_STARTSEQ 17 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_EXTENSION_ENDSEQ 18 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_EXTENSION_ENDSUBMSG 19 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_OPTIONS_ENDSUBMSG 20 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_SOURCE_CODE_INFO_ENDSUBMSG 21 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_NAME_STRING 22 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_NAME_STARTSTR 23 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_NAME_ENDSTR 24 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_PACKAGE_STRING 25 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_PACKAGE_STARTSTR 26 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_PACKAGE_ENDSTR 27 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_DEPENDENCY_STARTSEQ 28 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_DEPENDENCY_ENDSEQ 29 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_DEPENDENCY_STRING 30 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_DEPENDENCY_STARTSTR 31 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_DEPENDENCY_ENDSTR 32 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_PUBLIC_DEPENDENCY_STARTSEQ 33 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_PUBLIC_DEPENDENCY_ENDSEQ 34 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_PUBLIC_DEPENDENCY_INT32 35 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_WEAK_DEPENDENCY_STARTSEQ 36 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_WEAK_DEPENDENCY_ENDSEQ 37 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORPROTO_WEAK_DEPENDENCY_INT32 38 -UPB_INLINE bool upb_sink_startmsg(upb_sink *s) { - if (!s->handlers) return true; - upb_startmsg_handlerfunc *startmsg = - (upb_startmsg_handlerfunc *)upb_handlers_gethandler(s->handlers, - UPB_STARTMSG_SELECTOR); - if (!startmsg) return true; - const void *hd = - upb_handlers_gethandlerdata(s->handlers, UPB_STARTMSG_SELECTOR); - return startmsg(s->closure, hd); -} +/* google.protobuf.FileDescriptorSet */ +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORSET_FILE_STARTSUBMSG 2 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORSET_FILE_STARTSEQ 3 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORSET_FILE_ENDSEQ 4 +#define SEL_GOOGLE_PROTOBUF_FILEDESCRIPTORSET_FILE_ENDSUBMSG 5 -UPB_INLINE bool upb_sink_endmsg(upb_sink *s, upb_status *status) { - if (!s->handlers) return true; - upb_endmsg_handlerfunc *endmsg = - (upb_endmsg_handlerfunc *)upb_handlers_gethandler(s->handlers, - UPB_ENDMSG_SELECTOR); +/* google.protobuf.FileOptions */ +#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_UNINTERPRETED_OPTION_STARTSUBMSG 2 +#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_UNINTERPRETED_OPTION_STARTSEQ 3 +#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_UNINTERPRETED_OPTION_ENDSEQ 4 +#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_UNINTERPRETED_OPTION_ENDSUBMSG 5 +#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_PACKAGE_STRING 6 +#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_PACKAGE_STARTSTR 7 +#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_PACKAGE_ENDSTR 8 +#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_OUTER_CLASSNAME_STRING 9 +#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_OUTER_CLASSNAME_STARTSTR 10 +#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_OUTER_CLASSNAME_ENDSTR 11 +#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_OPTIMIZE_FOR_INT32 12 +#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_MULTIPLE_FILES_BOOL 13 +#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_GO_PACKAGE_STRING 14 +#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_GO_PACKAGE_STARTSTR 15 +#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_GO_PACKAGE_ENDSTR 16 +#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_CC_GENERIC_SERVICES_BOOL 17 +#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_GENERIC_SERVICES_BOOL 18 +#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_PY_GENERIC_SERVICES_BOOL 19 +#define SEL_GOOGLE_PROTOBUF_FILEOPTIONS_JAVA_GENERATE_EQUALS_AND_HASH_BOOL 20 - if (!endmsg) return true; - const void *hd = - upb_handlers_gethandlerdata(s->handlers, UPB_ENDMSG_SELECTOR); - return endmsg(s->closure, hd, status); -} +/* google.protobuf.MessageOptions */ +#define SEL_GOOGLE_PROTOBUF_MESSAGEOPTIONS_UNINTERPRETED_OPTION_STARTSUBMSG 2 +#define SEL_GOOGLE_PROTOBUF_MESSAGEOPTIONS_UNINTERPRETED_OPTION_STARTSEQ 3 +#define SEL_GOOGLE_PROTOBUF_MESSAGEOPTIONS_UNINTERPRETED_OPTION_ENDSEQ 4 +#define SEL_GOOGLE_PROTOBUF_MESSAGEOPTIONS_UNINTERPRETED_OPTION_ENDSUBMSG 5 +#define SEL_GOOGLE_PROTOBUF_MESSAGEOPTIONS_MESSAGE_SET_WIRE_FORMAT_BOOL 6 +#define SEL_GOOGLE_PROTOBUF_MESSAGEOPTIONS_NO_STANDARD_DESCRIPTOR_ACCESSOR_BOOL 7 -UPB_INLINE bool upb_sink_startseq(upb_sink *s, upb_selector_t sel, - upb_sink *sub) { - sub->closure = s->closure; - sub->handlers = s->handlers; - if (!s->handlers) return true; - upb_startfield_handlerfunc *startseq = - (upb_startfield_handlerfunc*)upb_handlers_gethandler(s->handlers, sel); +/* google.protobuf.MethodDescriptorProto */ +#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_OPTIONS_STARTSUBMSG 2 +#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_OPTIONS_ENDSUBMSG 3 +#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_NAME_STRING 4 +#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_NAME_STARTSTR 5 +#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_NAME_ENDSTR 6 +#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_INPUT_TYPE_STRING 7 +#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_INPUT_TYPE_STARTSTR 8 +#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_INPUT_TYPE_ENDSTR 9 +#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_OUTPUT_TYPE_STRING 10 +#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_OUTPUT_TYPE_STARTSTR 11 +#define SEL_GOOGLE_PROTOBUF_METHODDESCRIPTORPROTO_OUTPUT_TYPE_ENDSTR 12 - if (!startseq) return true; - const void *hd = upb_handlers_gethandlerdata(s->handlers, sel); - sub->closure = startseq(s->closure, hd); - return sub->closure ? true : false; -} +/* google.protobuf.MethodOptions */ +#define SEL_GOOGLE_PROTOBUF_METHODOPTIONS_UNINTERPRETED_OPTION_STARTSUBMSG 2 +#define SEL_GOOGLE_PROTOBUF_METHODOPTIONS_UNINTERPRETED_OPTION_STARTSEQ 3 +#define SEL_GOOGLE_PROTOBUF_METHODOPTIONS_UNINTERPRETED_OPTION_ENDSEQ 4 +#define SEL_GOOGLE_PROTOBUF_METHODOPTIONS_UNINTERPRETED_OPTION_ENDSUBMSG 5 -UPB_INLINE bool upb_sink_endseq(upb_sink *s, upb_selector_t sel) { - if (!s->handlers) return true; - upb_endfield_handlerfunc *endseq = - (upb_endfield_handlerfunc*)upb_handlers_gethandler(s->handlers, sel); +/* google.protobuf.ServiceDescriptorProto */ +#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_METHOD_STARTSUBMSG 2 +#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_OPTIONS_STARTSUBMSG 3 +#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_METHOD_STARTSEQ 4 +#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_METHOD_ENDSEQ 5 +#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_METHOD_ENDSUBMSG 6 +#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_OPTIONS_ENDSUBMSG 7 +#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_NAME_STRING 8 +#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_NAME_STARTSTR 9 +#define SEL_GOOGLE_PROTOBUF_SERVICEDESCRIPTORPROTO_NAME_ENDSTR 10 - if (!endseq) return true; - const void *hd = upb_handlers_gethandlerdata(s->handlers, sel); - return endseq(s->closure, hd); -} +/* google.protobuf.ServiceOptions */ +#define SEL_GOOGLE_PROTOBUF_SERVICEOPTIONS_UNINTERPRETED_OPTION_STARTSUBMSG 2 +#define SEL_GOOGLE_PROTOBUF_SERVICEOPTIONS_UNINTERPRETED_OPTION_STARTSEQ 3 +#define SEL_GOOGLE_PROTOBUF_SERVICEOPTIONS_UNINTERPRETED_OPTION_ENDSEQ 4 +#define SEL_GOOGLE_PROTOBUF_SERVICEOPTIONS_UNINTERPRETED_OPTION_ENDSUBMSG 5 -UPB_INLINE bool upb_sink_startstr(upb_sink *s, upb_selector_t sel, - size_t size_hint, upb_sink *sub) { - sub->closure = s->closure; - sub->handlers = s->handlers; - if (!s->handlers) return true; - upb_startstr_handlerfunc *startstr = - (upb_startstr_handlerfunc*)upb_handlers_gethandler(s->handlers, sel); +/* google.protobuf.SourceCodeInfo */ +#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_STARTSUBMSG 2 +#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_STARTSEQ 3 +#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_ENDSEQ 4 +#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_ENDSUBMSG 5 - if (!startstr) return true; - const void *hd = upb_handlers_gethandlerdata(s->handlers, sel); - sub->closure = startstr(s->closure, hd, size_hint); - return sub->closure ? true : false; -} +/* google.protobuf.SourceCodeInfo.Location */ +#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_PATH_STARTSEQ 2 +#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_PATH_ENDSEQ 3 +#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_PATH_INT32 4 +#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_SPAN_STARTSEQ 5 +#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_SPAN_ENDSEQ 6 +#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_SPAN_INT32 7 +#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_LEADING_COMMENTS_STRING 8 +#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_LEADING_COMMENTS_STARTSTR 9 +#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_LEADING_COMMENTS_ENDSTR 10 +#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_TRAILING_COMMENTS_STRING 11 +#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_TRAILING_COMMENTS_STARTSTR 12 +#define SEL_GOOGLE_PROTOBUF_SOURCECODEINFO_LOCATION_TRAILING_COMMENTS_ENDSTR 13 -UPB_INLINE bool upb_sink_endstr(upb_sink *s, upb_selector_t sel) { - if (!s->handlers) return true; - upb_endfield_handlerfunc *endstr = - (upb_endfield_handlerfunc*)upb_handlers_gethandler(s->handlers, sel); +/* google.protobuf.UninterpretedOption */ +#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NAME_STARTSUBMSG 2 +#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NAME_STARTSEQ 3 +#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NAME_ENDSEQ 4 +#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NAME_ENDSUBMSG 5 +#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_IDENTIFIER_VALUE_STRING 6 +#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_IDENTIFIER_VALUE_STARTSTR 7 +#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_IDENTIFIER_VALUE_ENDSTR 8 +#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_POSITIVE_INT_VALUE_UINT64 9 +#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NEGATIVE_INT_VALUE_INT64 10 +#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_DOUBLE_VALUE_DOUBLE 11 +#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_STRING_VALUE_STRING 12 +#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_STRING_VALUE_STARTSTR 13 +#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_STRING_VALUE_ENDSTR 14 +#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_AGGREGATE_VALUE_STRING 15 +#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_AGGREGATE_VALUE_STARTSTR 16 +#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_AGGREGATE_VALUE_ENDSTR 17 - if (!endstr) return true; - const void *hd = upb_handlers_gethandlerdata(s->handlers, sel); - return endstr(s->closure, hd); -} +/* google.protobuf.UninterpretedOption.NamePart */ +#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NAMEPART_NAME_PART_STRING 2 +#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NAMEPART_NAME_PART_STARTSTR 3 +#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NAMEPART_NAME_PART_ENDSTR 4 +#define SEL_GOOGLE_PROTOBUF_UNINTERPRETEDOPTION_NAMEPART_IS_EXTENSION_BOOL 5 -UPB_INLINE bool upb_sink_startsubmsg(upb_sink *s, upb_selector_t sel, - upb_sink *sub) { - sub->closure = s->closure; - if (!s->handlers) { - sub->handlers = NULL; - return true; - } - sub->handlers = upb_handlers_getsubhandlers_sel(s->handlers, sel); - upb_startfield_handlerfunc *startsubmsg = - (upb_startfield_handlerfunc*)upb_handlers_gethandler(s->handlers, sel); +const upb_symtab *upbdefs_google_protobuf_descriptor(const void *owner); - if (!startsubmsg) return true; - const void *hd = upb_handlers_gethandlerdata(s->handlers, sel); - sub->closure = startsubmsg(s->closure, hd); - return sub->closure ? true : false; +/* MessageDefs */ +UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_DescriptorProto(const upb_symtab *s) { + const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.DescriptorProto"); + assert(m); + return m; } - -UPB_INLINE bool upb_sink_endsubmsg(upb_sink *s, upb_selector_t sel) { - if (!s->handlers) return true; - upb_endfield_handlerfunc *endsubmsg = - (upb_endfield_handlerfunc*)upb_handlers_gethandler(s->handlers, sel); - - if (!endsubmsg) return s->closure; - const void *hd = upb_handlers_gethandlerdata(s->handlers, sel); - return endsubmsg(s->closure, hd); +UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_DescriptorProto_ExtensionRange(const upb_symtab *s) { + const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.DescriptorProto.ExtensionRange"); + assert(m); + return m; } - -UPB_END_EXTERN_C // } - -#ifdef __cplusplus - -namespace upb { - -template Sink::Sink(const Handlers* handlers, T* closure) { - upb_sink_reset(this, handlers, closure); +UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_EnumDescriptorProto(const upb_symtab *s) { + const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.EnumDescriptorProto"); + assert(m); + return m; } -template -inline void Sink::Reset(const Handlers* handlers, T* closure) { - upb_sink_reset(this, handlers, closure); +UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_EnumOptions(const upb_symtab *s) { + const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.EnumOptions"); + assert(m); + return m; } -inline bool Sink::StartMessage() { - return upb_sink_startmsg(this); +UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_EnumValueDescriptorProto(const upb_symtab *s) { + const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.EnumValueDescriptorProto"); + assert(m); + return m; } -inline bool Sink::EndMessage(Status* status) { - return upb_sink_endmsg(this, status); +UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_EnumValueOptions(const upb_symtab *s) { + const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.EnumValueOptions"); + assert(m); + return m; } -inline bool Sink::PutInt32(Handlers::Selector sel, int32_t val) { - return upb_sink_putint32(this, sel, val); +UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_FieldDescriptorProto(const upb_symtab *s) { + const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.FieldDescriptorProto"); + assert(m); + return m; } -inline bool Sink::PutInt64(Handlers::Selector sel, int64_t val) { - return upb_sink_putint64(this, sel, val); +UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_FieldOptions(const upb_symtab *s) { + const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.FieldOptions"); + assert(m); + return m; } -inline bool Sink::PutUInt32(Handlers::Selector sel, uint32_t val) { - return upb_sink_putuint32(this, sel, val); +UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_FileDescriptorProto(const upb_symtab *s) { + const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.FileDescriptorProto"); + assert(m); + return m; } -inline bool Sink::PutUInt64(Handlers::Selector sel, uint64_t val) { - return upb_sink_putuint64(this, sel, val); +UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_FileDescriptorSet(const upb_symtab *s) { + const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.FileDescriptorSet"); + assert(m); + return m; } -inline bool Sink::PutFloat(Handlers::Selector sel, float val) { - return upb_sink_putfloat(this, sel, val); +UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_FileOptions(const upb_symtab *s) { + const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.FileOptions"); + assert(m); + return m; } -inline bool Sink::PutDouble(Handlers::Selector sel, double val) { - return upb_sink_putdouble(this, sel, val); +UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_MessageOptions(const upb_symtab *s) { + const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.MessageOptions"); + assert(m); + return m; } -inline bool Sink::PutBool(Handlers::Selector sel, bool val) { - return upb_sink_putbool(this, sel, val); +UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_MethodDescriptorProto(const upb_symtab *s) { + const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.MethodDescriptorProto"); + assert(m); + return m; } -inline bool Sink::StartString(Handlers::Selector sel, size_t size_hint, - Sink *sub) { - return upb_sink_startstr(this, sel, size_hint, sub); +UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_MethodOptions(const upb_symtab *s) { + const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.MethodOptions"); + assert(m); + return m; } -inline size_t Sink::PutStringBuffer(Handlers::Selector sel, const char *buf, - size_t len, const BufferHandle* handle) { - return upb_sink_putstring(this, sel, buf, len, handle); +UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_ServiceDescriptorProto(const upb_symtab *s) { + const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.ServiceDescriptorProto"); + assert(m); + return m; } -inline bool Sink::EndString(Handlers::Selector sel) { - return upb_sink_endstr(this, sel); +UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_ServiceOptions(const upb_symtab *s) { + const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.ServiceOptions"); + assert(m); + return m; } -inline bool Sink::StartSubMessage(Handlers::Selector sel, Sink* sub) { - return upb_sink_startsubmsg(this, sel, sub); +UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_SourceCodeInfo(const upb_symtab *s) { + const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.SourceCodeInfo"); + assert(m); + return m; } -inline bool Sink::EndSubMessage(Handlers::Selector sel) { - return upb_sink_endsubmsg(this, sel); +UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_SourceCodeInfo_Location(const upb_symtab *s) { + const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.SourceCodeInfo.Location"); + assert(m); + return m; } -inline bool Sink::StartSequence(Handlers::Selector sel, Sink* sub) { - return upb_sink_startseq(this, sel, sub); +UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_UninterpretedOption(const upb_symtab *s) { + const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.UninterpretedOption"); + assert(m); + return m; } -inline bool Sink::EndSequence(Handlers::Selector sel) { - return upb_sink_endseq(this, sel); +UPB_INLINE const upb_msgdef *upbdefs_google_protobuf_UninterpretedOption_NamePart(const upb_symtab *s) { + const upb_msgdef *m = upb_symtab_lookupmsg(s, "google.protobuf.UninterpretedOption.NamePart"); + assert(m); + return m; } -template -BytesSink::BytesSink(const BytesHandler* handler, T* closure) { - Reset(handler, closure); -} -template -void BytesSink::Reset(const BytesHandler *handler, T *closure) { - upb_bytessink_reset(this, handler, closure); -} -inline bool BytesSink::Start(size_t size_hint, void **subc) { - return upb_bytessink_start(this, size_hint, subc); +/* EnumDefs */ +UPB_INLINE const upb_enumdef *upbdefs_google_protobuf_FieldDescriptorProto_Label(const upb_symtab *s) { + const upb_enumdef *e = upb_symtab_lookupenum(s, "google.protobuf.FieldDescriptorProto.Label"); + assert(e); + return e; } -inline size_t BytesSink::PutBuffer(void *subc, const char *buf, size_t len, - const BufferHandle *handle) { - return upb_bytessink_putbuf(this, subc, buf, len, handle); +UPB_INLINE const upb_enumdef *upbdefs_google_protobuf_FieldDescriptorProto_Type(const upb_symtab *s) { + const upb_enumdef *e = upb_symtab_lookupenum(s, "google.protobuf.FieldDescriptorProto.Type"); + assert(e); + return e; } -inline bool BytesSink::End() { - return upb_bytessink_end(this); +UPB_INLINE const upb_enumdef *upbdefs_google_protobuf_FieldOptions_CType(const upb_symtab *s) { + const upb_enumdef *e = upb_symtab_lookupenum(s, "google.protobuf.FieldOptions.CType"); + assert(e); + return e; } - -inline bool BufferSource::PutBuffer(const char *buf, size_t len, - BytesSink *sink) { - return upb_bufsrc_putbuf(buf, len, sink); +UPB_INLINE const upb_enumdef *upbdefs_google_protobuf_FileOptions_OptimizeMode(const upb_symtab *s) { + const upb_enumdef *e = upb_symtab_lookupenum(s, "google.protobuf.FileOptions.OptimizeMode"); + assert(e); + return e; } -} // namespace upb -#endif - -#endif -/* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2013 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * For handlers that do very tiny, very simple operations, the function call - * overhead of calling a handler can be significant. This file allows the - * user to define handlers that do something very simple like store the value - * to memory and/or set a hasbit. JIT compilers can then special-case these - * handlers and emit specialized code for them instead of actually calling the - * handler. - * - * The functionality is very simple/limited right now but may expand to be able - * to call another function. - */ - -#ifndef UPB_SHIM_H -#define UPB_SHIM_H - +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_ExtensionRange_end(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto_ExtensionRange(s), 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_ExtensionRange_start(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto_ExtensionRange(s), 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_enum_type(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto(s), 4); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_extension(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto(s), 6); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_extension_range(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto(s), 5); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_field(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto(s), 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto(s), 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_nested_type(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto(s), 3); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_DescriptorProto_options(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_DescriptorProto(s), 7); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumDescriptorProto_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumDescriptorProto(s), 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumDescriptorProto_options(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumDescriptorProto(s), 3); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumDescriptorProto_value(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumDescriptorProto(s), 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumOptions_allow_alias(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumOptions(s), 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumOptions_uninterpreted_option(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumOptions(s), 999); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumValueDescriptorProto_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumValueDescriptorProto(s), 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumValueDescriptorProto_number(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumValueDescriptorProto(s), 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumValueDescriptorProto_options(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumValueDescriptorProto(s), 3); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_EnumValueOptions_uninterpreted_option(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_EnumValueOptions(s), 999); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_default_value(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 7); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_extendee(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_label(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 4); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_number(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 3); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_options(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 8); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_type(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 5); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldDescriptorProto_type_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldDescriptorProto(s), 6); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_ctype(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldOptions(s), 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_deprecated(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldOptions(s), 3); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_experimental_map_key(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldOptions(s), 9); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_lazy(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldOptions(s), 5); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_packed(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldOptions(s), 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_uninterpreted_option(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldOptions(s), 999); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FieldOptions_weak(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FieldOptions(s), 10); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_dependency(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 3); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_enum_type(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 5); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_extension(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 7); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_message_type(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 4); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_options(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 8); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_package(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_public_dependency(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 10); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_service(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 6); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_source_code_info(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 9); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorProto_weak_dependency(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorProto(s), 11); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileDescriptorSet_file(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileDescriptorSet(s), 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_cc_generic_services(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 16); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_go_package(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 11); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_java_generate_equals_and_hash(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 20); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_java_generic_services(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 17); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_java_multiple_files(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 10); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_java_outer_classname(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 8); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_java_package(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_optimize_for(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 9); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_py_generic_services(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 18); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_FileOptions_uninterpreted_option(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_FileOptions(s), 999); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MessageOptions_message_set_wire_format(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MessageOptions(s), 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MessageOptions_no_standard_descriptor_accessor(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MessageOptions(s), 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MessageOptions_uninterpreted_option(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MessageOptions(s), 999); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodDescriptorProto_input_type(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MethodDescriptorProto(s), 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodDescriptorProto_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MethodDescriptorProto(s), 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodDescriptorProto_options(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MethodDescriptorProto(s), 4); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodDescriptorProto_output_type(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MethodDescriptorProto(s), 3); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_MethodOptions_uninterpreted_option(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_MethodOptions(s), 999); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_ServiceDescriptorProto_method(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_ServiceDescriptorProto(s), 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_ServiceDescriptorProto_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_ServiceDescriptorProto(s), 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_ServiceDescriptorProto_options(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_ServiceDescriptorProto(s), 3); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_ServiceOptions_uninterpreted_option(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_ServiceOptions(s), 999); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_SourceCodeInfo_Location_leading_comments(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_SourceCodeInfo_Location(s), 3); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_SourceCodeInfo_Location_path(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_SourceCodeInfo_Location(s), 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_SourceCodeInfo_Location_span(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_SourceCodeInfo_Location(s), 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_SourceCodeInfo_Location_trailing_comments(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_SourceCodeInfo_Location(s), 4); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_SourceCodeInfo_location(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_SourceCodeInfo(s), 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_NamePart_is_extension(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption_NamePart(s), 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_NamePart_name_part(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption_NamePart(s), 1); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_aggregate_value(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption(s), 8); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_double_value(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption(s), 6); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_identifier_value(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption(s), 3); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_name(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption(s), 2); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_negative_int_value(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption(s), 5); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_positive_int_value(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption(s), 4); } +UPB_INLINE const upb_fielddef *upbdefs_google_protobuf_UninterpretedOption_string_value(const upb_symtab *s) { return upb_msgdef_itof(upbdefs_google_protobuf_UninterpretedOption(s), 7); } -typedef struct { - size_t offset; - int32_t hasbit; -} upb_shim_data; +UPB_END_EXTERN_C #ifdef __cplusplus -namespace upb { - -struct Shim { - typedef upb_shim_data Data; - - // Sets a handler for the given field that writes the value to the given - // offset and, if hasbit >= 0, sets a bit at the given bit offset. Returns - // true if the handler was set successfully. - static bool Set(Handlers *h, const FieldDef *f, size_t ofs, int32_t hasbit); - - // If this handler is a shim, returns the corresponding upb::Shim::Data and - // stores the type in "type". Otherwise returns NULL. - static const Data* GetData(const Handlers* h, Handlers::Selector s, - FieldDef::Type* type); -}; +namespace upbdefs { +namespace google { +namespace protobuf { +namespace descriptor { +inline upb::reffed_ptr SymbolTable() { + const upb::SymbolTable* s = upbdefs_google_protobuf_descriptor(&s); + return upb::reffed_ptr(s, &s); +} +} /* namespace descriptor */ +} /* namespace protobuf */ +} /* namespace google */ -} // namespace upb +#define RETURN_REFFED(type, func) \ + const type* obj = func(upbdefs::google::protobuf::descriptor::SymbolTable().get()); \ + return upb::reffed_ptr(obj); -#endif +namespace google { +namespace protobuf { +namespace DescriptorProto { +inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_DescriptorProto) } +inline upb::reffed_ptr enum_type() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_enum_type) } +inline upb::reffed_ptr extension() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_extension) } +inline upb::reffed_ptr extension_range() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_extension_range) } +inline upb::reffed_ptr field() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_field) } +inline upb::reffed_ptr name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_name) } +inline upb::reffed_ptr nested_type() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_nested_type) } +inline upb::reffed_ptr options() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_options) } +} /* namespace DescriptorProto */ +} /* namespace protobuf */ +} /* namespace google */ -UPB_BEGIN_EXTERN_C // { +namespace google { +namespace protobuf { +namespace DescriptorProto { +namespace ExtensionRange { +inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_DescriptorProto_ExtensionRange) } +inline upb::reffed_ptr end() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_ExtensionRange_end) } +inline upb::reffed_ptr start() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_DescriptorProto_ExtensionRange_start) } +} /* namespace ExtensionRange */ +} /* namespace DescriptorProto */ +} /* namespace protobuf */ +} /* namespace google */ -// C API. -bool upb_shim_set(upb_handlers *h, const upb_fielddef *f, size_t offset, - int32_t hasbit); -const upb_shim_data *upb_shim_getdata(const upb_handlers *h, upb_selector_t s, - upb_fieldtype_t *type); +namespace google { +namespace protobuf { +namespace EnumDescriptorProto { +inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_EnumDescriptorProto) } +inline upb::reffed_ptr name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumDescriptorProto_name) } +inline upb::reffed_ptr options() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumDescriptorProto_options) } +inline upb::reffed_ptr value() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumDescriptorProto_value) } +} /* namespace EnumDescriptorProto */ +} /* namespace protobuf */ +} /* namespace google */ -UPB_END_EXTERN_C // } +namespace google { +namespace protobuf { +namespace EnumOptions { +inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_EnumOptions) } +inline upb::reffed_ptr allow_alias() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumOptions_allow_alias) } +inline upb::reffed_ptr uninterpreted_option() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumOptions_uninterpreted_option) } +} /* namespace EnumOptions */ +} /* namespace protobuf */ +} /* namespace google */ -#ifdef __cplusplus -// C++ Wrappers. -namespace upb { -inline bool Shim::Set(Handlers* h, const FieldDef* f, size_t ofs, - int32_t hasbit) { - return upb_shim_set(h, f, ofs, hasbit); -} -inline const Shim::Data* Shim::GetData(const Handlers* h, Handlers::Selector s, - FieldDef::Type* type) { - return upb_shim_getdata(h, s, type); -} -} // namespace upb -#endif +namespace google { +namespace protobuf { +namespace EnumValueDescriptorProto { +inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_EnumValueDescriptorProto) } +inline upb::reffed_ptr name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumValueDescriptorProto_name) } +inline upb::reffed_ptr number() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumValueDescriptorProto_number) } +inline upb::reffed_ptr options() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumValueDescriptorProto_options) } +} /* namespace EnumValueDescriptorProto */ +} /* namespace protobuf */ +} /* namespace google */ -#endif // UPB_SHIM_H -/* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2011 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * upb::descriptor::Reader provides a way of building upb::Defs from - * data in descriptor.proto format. - */ +namespace google { +namespace protobuf { +namespace EnumValueOptions { +inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_EnumValueOptions) } +inline upb::reffed_ptr uninterpreted_option() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_EnumValueOptions_uninterpreted_option) } +} /* namespace EnumValueOptions */ +} /* namespace protobuf */ +} /* namespace google */ -#ifndef UPB_DESCRIPTOR_H -#define UPB_DESCRIPTOR_H +namespace google { +namespace protobuf { +namespace FieldDescriptorProto { +inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_FieldDescriptorProto) } +inline upb::reffed_ptr default_value() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_default_value) } +inline upb::reffed_ptr extendee() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_extendee) } +inline upb::reffed_ptr label() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_label) } +inline upb::reffed_ptr name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_name) } +inline upb::reffed_ptr number() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_number) } +inline upb::reffed_ptr options() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_options) } +inline upb::reffed_ptr type() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_type) } +inline upb::reffed_ptr type_name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldDescriptorProto_type_name) } +inline upb::reffed_ptr Label() { RETURN_REFFED(upb::EnumDef, upbdefs_google_protobuf_FieldDescriptorProto_Label) } +inline upb::reffed_ptr Type() { RETURN_REFFED(upb::EnumDef, upbdefs_google_protobuf_FieldDescriptorProto_Type) } +} /* namespace FieldDescriptorProto */ +} /* namespace protobuf */ +} /* namespace google */ +namespace google { +namespace protobuf { +namespace FieldOptions { +inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_FieldOptions) } +inline upb::reffed_ptr ctype() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldOptions_ctype) } +inline upb::reffed_ptr deprecated() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldOptions_deprecated) } +inline upb::reffed_ptr experimental_map_key() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldOptions_experimental_map_key) } +inline upb::reffed_ptr lazy() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldOptions_lazy) } +inline upb::reffed_ptr packed() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldOptions_packed) } +inline upb::reffed_ptr uninterpreted_option() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldOptions_uninterpreted_option) } +inline upb::reffed_ptr weak() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FieldOptions_weak) } +inline upb::reffed_ptr CType() { RETURN_REFFED(upb::EnumDef, upbdefs_google_protobuf_FieldOptions_CType) } +} /* namespace FieldOptions */ +} /* namespace protobuf */ +} /* namespace google */ -#ifdef __cplusplus -namespace upb { -namespace descriptor { -class Reader; -} // namespace descriptor -} // namespace upb -#endif +namespace google { +namespace protobuf { +namespace FileDescriptorProto { +inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_FileDescriptorProto) } +inline upb::reffed_ptr dependency() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_dependency) } +inline upb::reffed_ptr enum_type() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_enum_type) } +inline upb::reffed_ptr extension() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_extension) } +inline upb::reffed_ptr message_type() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_message_type) } +inline upb::reffed_ptr name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_name) } +inline upb::reffed_ptr options() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_options) } +inline upb::reffed_ptr package() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_package) } +inline upb::reffed_ptr public_dependency() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_public_dependency) } +inline upb::reffed_ptr service() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_service) } +inline upb::reffed_ptr source_code_info() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_source_code_info) } +inline upb::reffed_ptr weak_dependency() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorProto_weak_dependency) } +} /* namespace FileDescriptorProto */ +} /* namespace protobuf */ +} /* namespace google */ -UPB_DECLARE_TYPE(upb::descriptor::Reader, upb_descreader); +namespace google { +namespace protobuf { +namespace FileDescriptorSet { +inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_FileDescriptorSet) } +inline upb::reffed_ptr file() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileDescriptorSet_file) } +} /* namespace FileDescriptorSet */ +} /* namespace protobuf */ +} /* namespace google */ -#ifdef __cplusplus +namespace google { +namespace protobuf { +namespace FileOptions { +inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_FileOptions) } +inline upb::reffed_ptr cc_generic_services() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_cc_generic_services) } +inline upb::reffed_ptr go_package() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_go_package) } +inline upb::reffed_ptr java_generate_equals_and_hash() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_java_generate_equals_and_hash) } +inline upb::reffed_ptr java_generic_services() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_java_generic_services) } +inline upb::reffed_ptr java_multiple_files() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_java_multiple_files) } +inline upb::reffed_ptr java_outer_classname() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_java_outer_classname) } +inline upb::reffed_ptr java_package() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_java_package) } +inline upb::reffed_ptr optimize_for() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_optimize_for) } +inline upb::reffed_ptr py_generic_services() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_py_generic_services) } +inline upb::reffed_ptr uninterpreted_option() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_FileOptions_uninterpreted_option) } +inline upb::reffed_ptr OptimizeMode() { RETURN_REFFED(upb::EnumDef, upbdefs_google_protobuf_FileOptions_OptimizeMode) } +} /* namespace FileOptions */ +} /* namespace protobuf */ +} /* namespace google */ -// Class that receives descriptor data according to the descriptor.proto schema -// and use it to build upb::Defs corresponding to that schema. -class upb::descriptor::Reader { - public: - // These handlers must have come from NewHandlers() and must outlive the - // Reader. - // - // TODO: generate the handlers statically (like we do with the - // descriptor.proto defs) so that there is no need to pass this parameter (or - // to build/memory-manage the handlers at runtime at all). Unfortunately this - // is a bit tricky to implement for Handlers, but necessary to simplify this - // interface. - static Reader* Create(Environment* env, const Handlers* handlers); +namespace google { +namespace protobuf { +namespace MessageOptions { +inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_MessageOptions) } +inline upb::reffed_ptr message_set_wire_format() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MessageOptions_message_set_wire_format) } +inline upb::reffed_ptr no_standard_descriptor_accessor() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MessageOptions_no_standard_descriptor_accessor) } +inline upb::reffed_ptr uninterpreted_option() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MessageOptions_uninterpreted_option) } +} /* namespace MessageOptions */ +} /* namespace protobuf */ +} /* namespace google */ - // The reader's input; this is where descriptor.proto data should be sent. - Sink* input(); +namespace google { +namespace protobuf { +namespace MethodDescriptorProto { +inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_MethodDescriptorProto) } +inline upb::reffed_ptr input_type() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MethodDescriptorProto_input_type) } +inline upb::reffed_ptr name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MethodDescriptorProto_name) } +inline upb::reffed_ptr options() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MethodDescriptorProto_options) } +inline upb::reffed_ptr output_type() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MethodDescriptorProto_output_type) } +} /* namespace MethodDescriptorProto */ +} /* namespace protobuf */ +} /* namespace google */ - // Returns an array of all defs that have been parsed, and transfers ownership - // of them to "owner". The number of defs is stored in *n. Ownership of the - // returned array is retained and is invalidated by any other call into - // Reader. - // - // These defs are not frozen or resolved; they are ready to be added to a - // symtab. - upb::Def** GetDefs(void* owner, int* n); +namespace google { +namespace protobuf { +namespace MethodOptions { +inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_MethodOptions) } +inline upb::reffed_ptr uninterpreted_option() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_MethodOptions_uninterpreted_option) } +} /* namespace MethodOptions */ +} /* namespace protobuf */ +} /* namespace google */ - // Builds and returns handlers for the reader, owned by "owner." - static Handlers* NewHandlers(const void* owner); +namespace google { +namespace protobuf { +namespace ServiceDescriptorProto { +inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_ServiceDescriptorProto) } +inline upb::reffed_ptr method() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_ServiceDescriptorProto_method) } +inline upb::reffed_ptr name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_ServiceDescriptorProto_name) } +inline upb::reffed_ptr options() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_ServiceDescriptorProto_options) } +} /* namespace ServiceDescriptorProto */ +} /* namespace protobuf */ +} /* namespace google */ - private: - UPB_DISALLOW_POD_OPS(Reader, upb::descriptor::Reader); -}; +namespace google { +namespace protobuf { +namespace ServiceOptions { +inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_ServiceOptions) } +inline upb::reffed_ptr uninterpreted_option() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_ServiceOptions_uninterpreted_option) } +} /* namespace ServiceOptions */ +} /* namespace protobuf */ +} /* namespace google */ -#endif +namespace google { +namespace protobuf { +namespace SourceCodeInfo { +inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_SourceCodeInfo) } +inline upb::reffed_ptr location() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_SourceCodeInfo_location) } +} /* namespace SourceCodeInfo */ +} /* namespace protobuf */ +} /* namespace google */ -UPB_BEGIN_EXTERN_C +namespace google { +namespace protobuf { +namespace SourceCodeInfo { +namespace Location { +inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_SourceCodeInfo_Location) } +inline upb::reffed_ptr leading_comments() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_SourceCodeInfo_Location_leading_comments) } +inline upb::reffed_ptr path() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_SourceCodeInfo_Location_path) } +inline upb::reffed_ptr span() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_SourceCodeInfo_Location_span) } +inline upb::reffed_ptr trailing_comments() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_SourceCodeInfo_Location_trailing_comments) } +} /* namespace Location */ +} /* namespace SourceCodeInfo */ +} /* namespace protobuf */ +} /* namespace google */ -// C API. -upb_descreader *upb_descreader_create(upb_env *e, const upb_handlers *h); -upb_sink *upb_descreader_input(upb_descreader *r); -upb_def **upb_descreader_getdefs(upb_descreader *r, void *owner, int *n); -const upb_handlers *upb_descreader_newhandlers(const void *owner); +namespace google { +namespace protobuf { +namespace UninterpretedOption { +inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_UninterpretedOption) } +inline upb::reffed_ptr aggregate_value() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_aggregate_value) } +inline upb::reffed_ptr double_value() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_double_value) } +inline upb::reffed_ptr identifier_value() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_identifier_value) } +inline upb::reffed_ptr name() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_name) } +inline upb::reffed_ptr negative_int_value() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_negative_int_value) } +inline upb::reffed_ptr positive_int_value() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_positive_int_value) } +inline upb::reffed_ptr string_value() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_string_value) } +} /* namespace UninterpretedOption */ +} /* namespace protobuf */ +} /* namespace google */ -UPB_END_EXTERN_C +namespace google { +namespace protobuf { +namespace UninterpretedOption { +namespace NamePart { +inline upb::reffed_ptr MessageDef() { RETURN_REFFED(upb::MessageDef, upbdefs_google_protobuf_UninterpretedOption_NamePart) } +inline upb::reffed_ptr is_extension() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_NamePart_is_extension) } +inline upb::reffed_ptr name_part() { RETURN_REFFED(upb::FieldDef, upbdefs_google_protobuf_UninterpretedOption_NamePart_name_part) } +} /* namespace NamePart */ +} /* namespace UninterpretedOption */ +} /* namespace protobuf */ +} /* namespace google */ -#ifdef __cplusplus -// C++ implementation details. ///////////////////////////////////////////////// -namespace upb { -namespace descriptor { -inline Reader* Reader::Create(Environment* e, const Handlers *h) { - return upb_descreader_create(e, h); -} -inline Sink* Reader::input() { return upb_descreader_input(this); } -inline upb::Def** Reader::GetDefs(void* owner, int* n) { - return upb_descreader_getdefs(this, owner, n); -} -} // namespace descriptor -} // namespace upb -#endif +} /* namespace upbdefs */ + + +#undef RETURN_REFFED +#endif /* __cplusplus */ -#endif // UPB_DESCRIPTOR_H +#endif /* GOOGLE_PROTOBUF_DESCRIPTOR_UPB_H_ */ /* * upb - a minimalist implementation of protocol buffers. * @@ -6953,134 +7107,111 @@ class CodeCache; class Decoder; class DecoderMethod; class DecoderMethodOptions; -} // namespace pb -} // namespace upb +} /* namespace pb */ +} /* namespace upb */ #endif -UPB_DECLARE_TYPE(upb::pb::CodeCache, upb_pbcodecache); -UPB_DECLARE_TYPE(upb::pb::Decoder, upb_pbdecoder); -UPB_DECLARE_TYPE(upb::pb::DecoderMethod, upb_pbdecodermethod); -UPB_DECLARE_TYPE(upb::pb::DecoderMethodOptions, upb_pbdecodermethodopts); +UPB_DECLARE_TYPE(upb::pb::CodeCache, upb_pbcodecache) +UPB_DECLARE_TYPE(upb::pb::Decoder, upb_pbdecoder) +UPB_DECLARE_TYPE(upb::pb::DecoderMethodOptions, upb_pbdecodermethodopts) + +UPB_DECLARE_DERIVED_TYPE(upb::pb::DecoderMethod, upb::RefCounted, + upb_pbdecodermethod, upb_refcounted) + +#ifdef __cplusplus -// The parameters one uses to construct a DecoderMethod. -// TODO(haberman): move allowjit here? Seems more convenient for users. -UPB_DEFINE_CLASS0(upb::pb::DecoderMethodOptions, +/* The parameters one uses to construct a DecoderMethod. + * TODO(haberman): move allowjit here? Seems more convenient for users. + * TODO(haberman): move this to be heap allocated for ABI stability. */ +class upb::pb::DecoderMethodOptions { public: - // Parameter represents the destination handlers that this method will push - // to. + /* Parameter represents the destination handlers that this method will push + * to. */ explicit DecoderMethodOptions(const Handlers* dest_handlers); - // Should the decoder push submessages to lazy handlers for fields that have - // them? The caller should set this iff the lazy handlers expect data that is - // in protobuf binary format and the caller wishes to lazy parse it. + /* Should the decoder push submessages to lazy handlers for fields that have + * them? The caller should set this iff the lazy handlers expect data that is + * in protobuf binary format and the caller wishes to lazy parse it. */ void set_lazy(bool lazy); -, -UPB_DEFINE_STRUCT0(upb_pbdecodermethodopts, +#else +struct upb_pbdecodermethodopts { +#endif const upb_handlers *handlers; bool lazy; -)); +}; + +#ifdef __cplusplus -// Represents the code to parse a protobuf according to a destination Handlers. -UPB_DEFINE_CLASS1(upb::pb::DecoderMethod, upb::RefCounted, +/* Represents the code to parse a protobuf according to a destination + * Handlers. */ +class upb::pb::DecoderMethod { public: - // From upb::ReferenceCounted. - 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; - - // The destination handlers that are statically bound to this method. - // This method is only capable of outputting to a sink that uses these - // handlers. + /* Include base methods from upb::ReferenceCounted. */ + UPB_REFCOUNTED_CPPMETHODS + + /* The destination handlers that are statically bound to this method. + * This method is only capable of outputting to a sink that uses these + * handlers. */ const Handlers* dest_handlers() const; - // The input handlers for this decoder method. + /* The input handlers for this decoder method. */ const BytesHandler* input_handler() const; - // Whether this method is native. + /* Whether this method is native. */ bool is_native() const; - // Convenience method for generating a DecoderMethod without explicitly - // creating a CodeCache. + /* Convenience method for generating a DecoderMethod without explicitly + * creating a CodeCache. */ static reffed_ptr New(const DecoderMethodOptions& opts); private: - UPB_DISALLOW_POD_OPS(DecoderMethod, upb::pb::DecoderMethod); -, -UPB_DEFINE_STRUCT(upb_pbdecodermethod, upb_refcounted, - // While compiling, the base is relative in "ofs", after compiling it is - // absolute in "ptr". - union { - uint32_t ofs; // PC offset of method. - void *ptr; // Pointer to bytecode or machine code for this method. - } code_base; - - // The decoder method group to which this method belongs. We own a ref. - // Owning a ref on the entire group is more coarse-grained than is strictly - // necessary; all we truly require is that methods we directly reference - // outlive us, while the group could contain many other messages we don't - // require. But the group represents the messages that were - // allocated+compiled together, so it makes the most sense to free them - // together also. - const upb_refcounted *group; - - // Whether this method is native code or bytecode. - bool is_native_; - - // The handler one calls to invoke this method. - upb_byteshandler input_handler_; - - // The destination handlers this method is bound to. We own a ref. - const upb_handlers *dest_handlers_; + UPB_DISALLOW_POD_OPS(DecoderMethod, upb::pb::DecoderMethod) +}; - // Dispatch table -- used by both bytecode decoder and JIT when encountering a - // field number that wasn't the one we were expecting to see. See - // decoder.int.h for the layout of this table. - upb_inttable dispatch; -)); +#endif -// Preallocation hint: decoder won't allocate more bytes than this when first -// constructed. This hint may be an overestimate for some build configurations. -// But if the decoder library is upgraded without recompiling the application, -// it may be an underestimate. +/* Preallocation hint: decoder won't allocate more bytes than this when first + * constructed. This hint may be an overestimate for some build configurations. + * But if the decoder library is upgraded without recompiling the application, + * it may be an underestimate. */ #define UPB_PB_DECODER_SIZE 4400 #ifdef __cplusplus -// A Decoder receives binary protobuf data on its input sink and pushes the -// decoded data to its output sink. +/* A Decoder receives binary protobuf data on its input sink and pushes the + * decoded data to its output sink. */ class upb::pb::Decoder { public: - // Constructs a decoder instance for the given method, which must outlive this - // decoder. Any errors during parsing will be set on the given status, which - // must also outlive this decoder. - // - // The sink must match the given method. + /* Constructs a decoder instance for the given method, which must outlive this + * decoder. Any errors during parsing will be set on the given status, which + * must also outlive this decoder. + * + * The sink must match the given method. */ static Decoder* Create(Environment* env, const DecoderMethod* method, Sink* output); - // Returns the DecoderMethod this decoder is parsing from. + /* Returns the DecoderMethod this decoder is parsing from. */ const DecoderMethod* method() const; - // The sink on which this decoder receives input. + /* The sink on which this decoder receives input. */ BytesSink* input(); - // Returns number of bytes successfully parsed. - // - // This can be useful for determining the stream position where an error - // occurred. - // - // This value may not be up-to-date when called from inside a parsing - // callback. + /* Returns number of bytes successfully parsed. + * + * This can be useful for determining the stream position where an error + * occurred. + * + * This value may not be up-to-date when called from inside a parsing + * callback. */ uint64_t BytesParsed() const; - // Gets/sets the parsing nexting limit. If the total number of nested - // submessages and repeated fields hits this limit, parsing will fail. This - // is a resource limit that controls the amount of memory used by the parsing - // stack. - // - // Setting the limit will fail if the parser is currently suspended at a depth - // greater than this, or if memory allocation of the stack fails. + /* Gets/sets the parsing nexting limit. If the total number of nested + * submessages and repeated fields hits this limit, parsing will fail. This + * is a resource limit that controls the amount of memory used by the parsing + * stack. + * + * Setting the limit will fail if the parser is currently suspended at a depth + * greater than this, or if memory allocation of the stack fails. */ size_t max_nesting() const; bool set_max_nesting(size_t max); @@ -7089,57 +7220,62 @@ class upb::pb::Decoder { static const size_t kSize = UPB_PB_DECODER_SIZE; private: - UPB_DISALLOW_POD_OPS(Decoder, upb::pb::Decoder); + UPB_DISALLOW_POD_OPS(Decoder, upb::pb::Decoder) }; -#endif // __cplusplus +#endif /* __cplusplus */ + +#ifdef __cplusplus -// A class for caching protobuf processing code, whether bytecode for the -// interpreted decoder or machine code for the JIT. -// -// This class is not thread-safe. -UPB_DEFINE_CLASS0(upb::pb::CodeCache, +/* A class for caching protobuf processing code, whether bytecode for the + * interpreted decoder or machine code for the JIT. + * + * This class is not thread-safe. + * + * TODO(haberman): move this to be heap allocated for ABI stability. */ +class upb::pb::CodeCache { public: CodeCache(); ~CodeCache(); - // Whether the cache is allowed to generate machine code. Defaults to true. - // There is no real reason to turn it off except for testing or if you are - // having a specific problem with the JIT. - // - // Note that allow_jit = true does not *guarantee* that the code will be JIT - // compiled. If this platform is not supported or the JIT was not compiled - // in, the code may still be interpreted. + /* Whether the cache is allowed to generate machine code. Defaults to true. + * There is no real reason to turn it off except for testing or if you are + * having a specific problem with the JIT. + * + * Note that allow_jit = true does not *guarantee* that the code will be JIT + * compiled. If this platform is not supported or the JIT was not compiled + * in, the code may still be interpreted. */ bool allow_jit() const; - // This may only be called when the object is first constructed, and prior to - // any code generation, otherwise returns false and does nothing. + /* This may only be called when the object is first constructed, and prior to + * any code generation, otherwise returns false and does nothing. */ bool set_allow_jit(bool allow); - // Returns a DecoderMethod that can push data to the given handlers. - // If a suitable method already exists, it will be returned from the cache. - // - // Specifying the destination handlers here allows the DecoderMethod to be - // statically bound to the destination handlers if possible, which can allow - // more efficient decoding. However the returned method may or may not - // actually be statically bound. But in all cases, the returned method can - // push data to the given handlers. + /* Returns a DecoderMethod that can push data to the given handlers. + * If a suitable method already exists, it will be returned from the cache. + * + * Specifying the destination handlers here allows the DecoderMethod to be + * statically bound to the destination handlers if possible, which can allow + * more efficient decoding. However the returned method may or may not + * actually be statically bound. But in all cases, the returned method can + * push data to the given handlers. */ const DecoderMethod *GetDecoderMethod(const DecoderMethodOptions& opts); - // If/when someone needs to explicitly create a dynamically-bound - // DecoderMethod*, we can add a method to get it here. + /* If/when someone needs to explicitly create a dynamically-bound + * DecoderMethod*, we can add a method to get it here. */ private: - UPB_DISALLOW_COPY_AND_ASSIGN(CodeCache); -, -UPB_DEFINE_STRUCT0(upb_pbcodecache, + UPB_DISALLOW_COPY_AND_ASSIGN(CodeCache) +#else +struct upb_pbcodecache { +#endif bool allow_jit_; - // Array of mgroups. + /* Array of mgroups. */ upb_inttable groups; -)); +}; -UPB_BEGIN_EXTERN_C // { +UPB_BEGIN_EXTERN_C upb_pbdecoder *upb_pbdecoder_create(upb_env *e, const upb_pbdecodermethod *method, @@ -7155,12 +7291,10 @@ void upb_pbdecodermethodopts_init(upb_pbdecodermethodopts *opts, const upb_handlers *h); void upb_pbdecodermethodopts_setlazy(upb_pbdecodermethodopts *opts, bool lazy); -void upb_pbdecodermethod_ref(const upb_pbdecodermethod *m, const void *owner); -void upb_pbdecodermethod_unref(const upb_pbdecodermethod *m, const void *owner); -void upb_pbdecodermethod_donateref(const upb_pbdecodermethod *m, - const void *from, const void *to); -void upb_pbdecodermethod_checkref(const upb_pbdecodermethod *m, - const void *owner); + +/* Include refcounted methods like upb_pbdecodermethod_ref(). */ +UPB_REFCOUNTED_CMETHODS(upb_pbdecodermethod, upb_pbdecodermethod_upcast) + const upb_handlers *upb_pbdecodermethod_desthandlers( const upb_pbdecodermethod *m); const upb_byteshandler *upb_pbdecodermethod_inputhandler( @@ -7176,7 +7310,7 @@ bool upb_pbcodecache_setallowjit(upb_pbcodecache *c, bool allow); const upb_pbdecodermethod *upb_pbcodecache_getdecodermethod( upb_pbcodecache *c, const upb_pbdecodermethodopts *opts); -UPB_END_EXTERN_C // } +UPB_END_EXTERN_C #ifdef __cplusplus @@ -7184,7 +7318,7 @@ namespace upb { namespace pb { -// static +/* static */ inline Decoder* Decoder::Create(Environment* env, const DecoderMethod* m, Sink* sink) { return upb_pbdecoder_create(env, m, sink); @@ -7213,18 +7347,6 @@ inline void DecoderMethodOptions::set_lazy(bool lazy) { upb_pbdecodermethodopts_setlazy(this, lazy); } -inline void DecoderMethod::Ref(const void *owner) const { - upb_pbdecodermethod_ref(this, owner); -} -inline void DecoderMethod::Unref(const void *owner) const { - upb_pbdecodermethod_unref(this, owner); -} -inline void DecoderMethod::DonateRef(const void *from, const void *to) const { - upb_pbdecodermethod_donateref(this, from, to); -} -inline void DecoderMethod::CheckRef(const void *owner) const { - upb_pbdecodermethod_checkref(this, owner); -} inline const Handlers* DecoderMethod::dest_handlers() const { return upb_pbdecodermethod_desthandlers(this); } @@ -7234,7 +7356,7 @@ inline const BytesHandler* DecoderMethod::input_handler() const { inline bool DecoderMethod::is_native() const { return upb_pbdecodermethod_isnative(this); } -// static +/* static */ inline reffed_ptr DecoderMethod::New( const DecoderMethodOptions &opts) { const upb_pbdecodermethod *m = upb_pbdecodermethod_new(&opts, &m); @@ -7258,33 +7380,44 @@ inline const DecoderMethod *CodeCache::GetDecoderMethod( return upb_pbcodecache_getdecodermethod(this, &opts); } -} // namespace pb -} // namespace upb +} /* namespace pb */ +} /* namespace upb */ -#endif // __cplusplus +#endif /* __cplusplus */ #endif /* UPB_DECODER_H_ */ -// Opcode definitions. The canonical meaning of each opcode is its -// implementation in the interpreter (the JIT is written to match this). -// -// All instructions have the opcode in the low byte. -// Instruction format for most instructions is: -// -// +-------------------+--------+ -// | arg (24) | op (8) | -// +-------------------+--------+ -// -// Exceptions are indicated below. A few opcodes are multi-word. +/* C++ names are not actually used since this type isn't exposed to users. */ +#ifdef __cplusplus +namespace upb { +namespace pb { +class MessageGroup; +} /* namespace pb */ +} /* namespace upb */ +#endif +UPB_DECLARE_DERIVED_TYPE(upb::pb::MessageGroup, upb::RefCounted, + mgroup, upb_refcounted) + +/* Opcode definitions. The canonical meaning of each opcode is its + * implementation in the interpreter (the JIT is written to match this). + * + * All instructions have the opcode in the low byte. + * Instruction format for most instructions is: + * + * +-------------------+--------+ + * | arg (24) | op (8) | + * +-------------------+--------+ + * + * Exceptions are indicated below. A few opcodes are multi-word. */ typedef enum { - // Opcodes 1-8, 13, 15-18 parse their respective descriptor types. - // Arg for all of these is the upb selector for this field. + /* Opcodes 1-8, 13, 15-18 parse their respective descriptor types. + * Arg for all of these is the upb selector for this field. */ #define T(type) OP_PARSE_ ## type = UPB_DESCRIPTOR_TYPE_ ## type T(DOUBLE), T(FLOAT), T(INT64), T(UINT64), T(INT32), T(FIXED64), T(FIXED32), T(BOOL), T(UINT32), T(SFIXED32), T(SFIXED64), T(SINT32), T(SINT64), #undef T - OP_STARTMSG = 9, // No arg. - OP_ENDMSG = 10, // No arg. + OP_STARTMSG = 9, /* No arg. */ + OP_ENDMSG = 10, /* No arg. */ OP_STARTSEQ = 11, OP_ENDSEQ = 12, OP_STARTSUBMSG = 14, @@ -7293,148 +7426,185 @@ typedef enum { OP_STRING = 21, OP_ENDSTR = 22, - OP_PUSHTAGDELIM = 23, // No arg. - OP_PUSHLENDELIM = 24, // No arg. - OP_POP = 25, // No arg. - OP_SETDELIM = 26, // No arg. - OP_SETBIGGROUPNUM = 27, // two words: | unused (24) | opc || groupnum (32) | + OP_PUSHTAGDELIM = 23, /* No arg. */ + OP_PUSHLENDELIM = 24, /* No arg. */ + OP_POP = 25, /* No arg. */ + OP_SETDELIM = 26, /* No arg. */ + OP_SETBIGGROUPNUM = 27, /* two words: + * | unused (24) | opc (8) | + * | groupnum (32) | */ OP_CHECKDELIM = 28, OP_CALL = 29, OP_RET = 30, OP_BRANCH = 31, - // Different opcodes depending on how many bytes expected. - OP_TAG1 = 32, // | expected tag (16) | jump target (8) | opc (8) | - OP_TAG2 = 33, // | expected tag (16) | jump target (8) | opc (8) | - OP_TAGN = 34, // three words: - // | unused (16) | jump target(8) | opc (8) | - // | expected tag 1 (32) | - // | expected tag 2 (32) | + /* Different opcodes depending on how many bytes expected. */ + OP_TAG1 = 32, /* | match tag (16) | jump target (8) | opc (8) | */ + OP_TAG2 = 33, /* | match tag (16) | jump target (8) | opc (8) | */ + OP_TAGN = 34, /* three words: */ + /* | unused (16) | jump target(8) | opc (8) | */ + /* | match tag 1 (32) | */ + /* | match tag 2 (32) | */ - OP_SETDISPATCH = 35, // N words: - // | unused (24) | opc | - // | upb_inttable* (32 or 64) | + OP_SETDISPATCH = 35, /* N words: */ + /* | unused (24) | opc | */ + /* | upb_inttable* (32 or 64) | */ - OP_DISPATCH = 36, // No arg. + OP_DISPATCH = 36, /* No arg. */ - OP_HALT = 37, // No arg. + OP_HALT = 37 /* No arg. */ } opcode; #define OP_MAX OP_HALT UPB_INLINE opcode getop(uint32_t instr) { return instr & 0xff; } -// Method group; represents a set of decoder methods that had their code -// emitted together, and must therefore be freed together. Immutable once -// created. It is possible we may want to expose this to users at some point. -// -// Overall ownership of Decoder objects looks like this: -// -// +----------+ -// | | <---> DecoderMethod -// | method | -// CodeCache ---> | group | <---> DecoderMethod -// | | -// | (mgroup) | <---> DecoderMethod -// +----------+ -typedef struct { +/* Method group; represents a set of decoder methods that had their code + * emitted together, and must therefore be freed together. Immutable once + * created. It is possible we may want to expose this to users at some point. + * + * Overall ownership of Decoder objects looks like this: + * + * +----------+ + * | | <---> DecoderMethod + * | method | + * CodeCache ---> | group | <---> DecoderMethod + * | | + * | (mgroup) | <---> DecoderMethod + * +----------+ + */ +struct mgroup { upb_refcounted base; - // Maps upb_msgdef/upb_handlers -> upb_pbdecodermethod. We own refs on the - // methods. + /* Maps upb_msgdef/upb_handlers -> upb_pbdecodermethod. We own refs on the + * methods. */ upb_inttable methods; - // When we add the ability to link to previously existing mgroups, we'll - // need an array of mgroups we reference here, and own refs on them. + /* When we add the ability to link to previously existing mgroups, we'll + * need an array of mgroups we reference here, and own refs on them. */ - // The bytecode for our methods, if any exists. Owned by us. + /* The bytecode for our methods, if any exists. Owned by us. */ uint32_t *bytecode; uint32_t *bytecode_end; #ifdef UPB_USE_JIT_X64 - // JIT-generated machine code, if any. + /* JIT-generated machine code, if any. */ upb_string_handlerfunc *jit_code; - // The size of the jit_code (required to munmap()). + /* The size of the jit_code (required to munmap()). */ size_t jit_size; char *debug_info; void *dl; #endif -} mgroup; - -// The maximum that any submessages can be nested. Matches proto2's limit. -// This specifies the size of the decoder's statically-sized array and therefore -// setting it high will cause the upb::pb::Decoder object to be larger. -// -// If necessary we can add a runtime-settable property to Decoder that allow -// this to be larger than the compile-time setting, but this would add -// complexity, particularly since we would have to decide how/if to give users -// the ability to set a custom memory allocation function. +}; + +/* The maximum that any submessages can be nested. Matches proto2's limit. + * This specifies the size of the decoder's statically-sized array and therefore + * setting it high will cause the upb::pb::Decoder object to be larger. + * + * If necessary we can add a runtime-settable property to Decoder that allow + * this to be larger than the compile-time setting, but this would add + * complexity, particularly since we would have to decide how/if to give users + * the ability to set a custom memory allocation function. */ #define UPB_DECODER_MAX_NESTING 64 -// Internal-only struct used by the decoder. +/* Internal-only struct used by the decoder. */ typedef struct { - // Space optimization note: we store two pointers here that the JIT - // doesn't need at all; the upb_handlers* inside the sink and - // the dispatch table pointer. We can optimze so that the JIT uses - // smaller stack frames than the interpreter. The only thing we need - // to guarantee is that the fallback routines can find end_ofs. + /* Space optimization note: we store two pointers here that the JIT + * doesn't need at all; the upb_handlers* inside the sink and + * the dispatch table pointer. We can optimze so that the JIT uses + * smaller stack frames than the interpreter. The only thing we need + * to guarantee is that the fallback routines can find end_ofs. */ upb_sink sink; - // The absolute stream offset of the end-of-frame delimiter. - // Non-delimited frames (groups and non-packed repeated fields) reuse the - // delimiter of their parent, even though the frame may not end there. - // - // NOTE: the JIT stores a slightly different value here for non-top frames. - // It stores the value relative to the end of the enclosed message. But the - // top frame is still stored the same way, which is important for ensuring - // that calls from the JIT into C work correctly. + /* The absolute stream offset of the end-of-frame delimiter. + * Non-delimited frames (groups and non-packed repeated fields) reuse the + * delimiter of their parent, even though the frame may not end there. + * + * NOTE: the JIT stores a slightly different value here for non-top frames. + * It stores the value relative to the end of the enclosed message. But the + * top frame is still stored the same way, which is important for ensuring + * that calls from the JIT into C work correctly. */ uint64_t end_ofs; const uint32_t *base; - // 0 indicates a length-delimited field. - // A positive number indicates a known group. - // A negative number indicates an unknown group. + /* 0 indicates a length-delimited field. + * A positive number indicates a known group. + * A negative number indicates an unknown group. */ int32_t groupnum; - upb_inttable *dispatch; // Not used by the JIT. + upb_inttable *dispatch; /* Not used by the JIT. */ } upb_pbdecoder_frame; +struct upb_pbdecodermethod { + upb_refcounted base; + + /* While compiling, the base is relative in "ofs", after compiling it is + * absolute in "ptr". */ + union { + uint32_t ofs; /* PC offset of method. */ + void *ptr; /* Pointer to bytecode or machine code for this method. */ + } code_base; + + /* The decoder method group to which this method belongs. We own a ref. + * Owning a ref on the entire group is more coarse-grained than is strictly + * necessary; all we truly require is that methods we directly reference + * outlive us, while the group could contain many other messages we don't + * require. But the group represents the messages that were + * allocated+compiled together, so it makes the most sense to free them + * together also. */ + const upb_refcounted *group; + + /* Whether this method is native code or bytecode. */ + bool is_native_; + + /* The handler one calls to invoke this method. */ + upb_byteshandler input_handler_; + + /* The destination handlers this method is bound to. We own a ref. */ + const upb_handlers *dest_handlers_; + + /* Dispatch table -- used by both bytecode decoder and JIT when encountering a + * field number that wasn't the one we were expecting to see. See + * decoder.int.h for the layout of this table. */ + upb_inttable dispatch; +}; + struct upb_pbdecoder { upb_env *env; - // Our input sink. + /* Our input sink. */ upb_bytessink input_; - // The decoder method we are parsing with (owned). + /* The decoder method we are parsing with (owned). */ const upb_pbdecodermethod *method_; size_t call_len; const uint32_t *pc, *last; - // Current input buffer and its stream offset. + /* Current input buffer and its stream offset. */ const char *buf, *ptr, *end, *checkpoint; - // End of the delimited region, relative to ptr, or NULL if not in this buf. + /* End of the delimited region, relative to ptr, NULL if not in this buf. */ const char *delim_end; - // End of the delimited region, relative to ptr, or end if not in this buf. + /* End of the delimited region, relative to ptr, end if not in this buf. */ const char *data_end; - // Overall stream offset of "buf." + /* Overall stream offset of "buf." */ uint64_t bufstart_ofs; - // Buffer for residual bytes not parsed from the previous buffer. - // The maximum number of residual bytes we require is 12; a five-byte - // unknown tag plus an eight-byte value, less one because the value - // is only a partial value. + /* Buffer for residual bytes not parsed from the previous buffer. + * The maximum number of residual bytes we require is 12; a five-byte + * unknown tag plus an eight-byte value, less one because the value + * is only a partial value. */ char residual[12]; char *residual_end; - // Stores the user buffer passed to our decode function. + /* Stores the user buffer passed to our decode function. */ const char *buf_param; size_t size_param; const upb_bufhandle *handle; - // Our internal stack. + /* Our internal stack. */ upb_pbdecoder_frame *stack, *top, *limit; const uint32_t **callstack; size_t stack_size; @@ -7442,22 +7612,22 @@ struct upb_pbdecoder { upb_status *status; #ifdef UPB_USE_JIT_X64 - // Used momentarily by the generated code to store a value while a user - // function is called. + /* Used momentarily by the generated code to store a value while a user + * function is called. */ uint32_t tmp_len; const void *saved_rsp; #endif }; -// Decoder entry points; used as handlers. +/* Decoder entry points; used as handlers. */ void *upb_pbdecoder_startbc(void *closure, const void *pc, size_t size_hint); void *upb_pbdecoder_startjit(void *closure, const void *hd, size_t size_hint); size_t upb_pbdecoder_decode(void *closure, const void *hd, const char *buf, size_t size, const upb_bufhandle *handle); bool upb_pbdecoder_end(void *closure, const void *handler_data); -// Decoder-internal functions that the JIT calls to handle fallback paths. +/* Decoder-internal functions that the JIT calls to handle fallback paths. */ int32_t upb_pbdecoder_resume(upb_pbdecoder *d, void *p, const char *buf, size_t size, const upb_bufhandle *handle); size_t upb_pbdecoder_suspend(upb_pbdecoder *d); @@ -7469,41 +7639,42 @@ int32_t upb_pbdecoder_decode_f32(upb_pbdecoder *d, uint32_t *u32); int32_t upb_pbdecoder_decode_f64(upb_pbdecoder *d, uint64_t *u64); void upb_pbdecoder_seterr(upb_pbdecoder *d, const char *msg); -// Error messages that are shared between the bytecode and JIT decoders. +/* Error messages that are shared between the bytecode and JIT decoders. */ extern const char *kPbDecoderStackOverflow; -// Access to decoderplan members needed by the decoder. +/* Access to decoderplan members needed by the decoder. */ const char *upb_pbdecoder_getopname(unsigned int op); -// JIT codegen entry point. +/* JIT codegen entry point. */ void upb_pbdecoder_jit(mgroup *group); void upb_pbdecoder_freejit(mgroup *group); +UPB_REFCOUNTED_CMETHODS(mgroup, mgroup_upcast) -// A special label that means "do field dispatch for this message and branch to -// wherever that takes you." +/* A special label that means "do field dispatch for this message and branch to + * wherever that takes you." */ #define LABEL_DISPATCH 0 -// A special slot in the dispatch table that stores the epilogue (ENDMSG and/or -// RET) for branching to when we find an appropriate ENDGROUP tag. +/* A special slot in the dispatch table that stores the epilogue (ENDMSG and/or + * RET) for branching to when we find an appropriate ENDGROUP tag. */ #define DISPATCH_ENDMSG 0 -// It's important to use this invalid wire type instead of 0 (which is a valid -// wire type). +/* It's important to use this invalid wire type instead of 0 (which is a valid + * wire type). */ #define NO_WIRE_TYPE 0xff -// The dispatch table layout is: -// [field number] -> [ 48-bit offset ][ 8-bit wt2 ][ 8-bit wt1 ] -// -// If wt1 matches, jump to the 48-bit offset. If wt2 matches, lookup -// (UPB_MAX_FIELDNUMBER + fieldnum) and jump there. -// -// We need two wire types because of packed/non-packed compatibility. A -// primitive repeated field can use either wire type and be valid. While we -// could key the table on fieldnum+wiretype, the table would be 8x sparser. -// -// Storing two wire types in the primary value allows us to quickly rule out -// the second wire type without needing to do a separate lookup (this case is -// less common than an unknown field). +/* The dispatch table layout is: + * [field number] -> [ 48-bit offset ][ 8-bit wt2 ][ 8-bit wt1 ] + * + * If wt1 matches, jump to the 48-bit offset. If wt2 matches, lookup + * (UPB_MAX_FIELDNUMBER + fieldnum) and jump there. + * + * We need two wire types because of packed/non-packed compatibility. A + * primitive repeated field can use either wire type and be valid. While we + * could key the table on fieldnum+wiretype, the table would be 8x sparser. + * + * Storing two wire types in the primary value allows us to quickly rule out + * the second wire type without needing to do a separate lookup (this case is + * less common than an unknown field). */ UPB_INLINE uint64_t upb_pbdecoder_packdispatch(uint64_t ofs, uint8_t wt1, uint8_t wt2) { return (ofs << 16) | (wt2 << 8) | wt1; @@ -7516,20 +7687,20 @@ UPB_INLINE void upb_pbdecoder_unpackdispatch(uint64_t dispatch, uint64_t *ofs, *ofs = dispatch >> 16; } -// All of the functions in decoder.c that return int32_t return values according -// to the following scheme: -// 1. negative values indicate a return code from the following list. -// 2. positive values indicate that error or end of buffer was hit, and -// that the decode function should immediately return the given value -// (the decoder state has already been suspended and is ready to be -// resumed). +/* All of the functions in decoder.c that return int32_t return values according + * to the following scheme: + * 1. negative values indicate a return code from the following list. + * 2. positive values indicate that error or end of buffer was hit, and + * that the decode function should immediately return the given value + * (the decoder state has already been suspended and is ready to be + * resumed). */ #define DECODE_OK -1 -#define DECODE_MISMATCH -2 // Used only from checktag_slow(). -#define DECODE_ENDGROUP -3 // Used only from checkunknown(). +#define DECODE_MISMATCH -2 /* Used only from checktag_slow(). */ +#define DECODE_ENDGROUP -3 /* Used only from checkunknown(). */ #define CHECK_RETURN(x) { int32_t ret = x; if (ret >= 0) return ret; } -#endif // UPB_DECODER_INT_H_ +#endif /* UPB_DECODER_INT_H_ */ /* * upb - a minimalist implementation of protocol buffers. * @@ -7551,25 +7722,25 @@ UPB_INLINE void upb_pbdecoder_unpackdispatch(uint64_t dispatch, uint64_t *ofs, extern "C" { #endif -// A list of types as they are encoded on-the-wire. +/* A list of types as they are encoded on-the-wire. */ typedef enum { UPB_WIRE_TYPE_VARINT = 0, UPB_WIRE_TYPE_64BIT = 1, UPB_WIRE_TYPE_DELIMITED = 2, UPB_WIRE_TYPE_START_GROUP = 3, UPB_WIRE_TYPE_END_GROUP = 4, - UPB_WIRE_TYPE_32BIT = 5, + UPB_WIRE_TYPE_32BIT = 5 } upb_wiretype_t; #define UPB_MAX_WIRE_TYPE 5 -// The maximum number of bytes that it takes to encode a 64-bit varint. -// Note that with a better encoding this could be 9 (TODO: write up a -// wiki document about this). +/* The maximum number of bytes that it takes to encode a 64-bit varint. + * Note that with a better encoding this could be 9 (TODO: write up a + * wiki document about this). */ #define UPB_PB_VARINT_MAX_LEN 10 -// Array of the "native" (ie. non-packed-repeated) wire type for the given a -// descriptor type (upb_descriptortype_t). +/* Array of the "native" (ie. non-packed-repeated) wire type for the given a + * descriptor type (upb_descriptortype_t). */ extern const uint8_t upb_pb_native_wire_types[]; /* Zig-zag encoding/decoding **************************************************/ @@ -7585,44 +7756,59 @@ UPB_INLINE uint64_t upb_zzenc_64(int64_t n) { return (n << 1) ^ (n >> 63); } /* Decoding *******************************************************************/ -// All decoding functions return this struct by value. +/* All decoding functions return this struct by value. */ typedef struct { - const char *p; // NULL if the varint was unterminated. + const char *p; /* NULL if the varint was unterminated. */ uint64_t val; } upb_decoderet; -// Four functions for decoding a varint of at most eight bytes. They are all -// functionally identical, but are implemented in different ways and likely have -// different performance profiles. We keep them around for performance testing. -// -// Note that these functions may not read byte-by-byte, so they must not be used -// unless there are at least eight bytes left in the buffer! +UPB_INLINE upb_decoderet upb_decoderet_make(const char *p, uint64_t val) { + upb_decoderet ret; + ret.p = p; + ret.val = val; + return ret; +} + +/* Four functions for decoding a varint of at most eight bytes. They are all + * functionally identical, but are implemented in different ways and likely have + * different performance profiles. We keep them around for performance testing. + * + * Note that these functions may not read byte-by-byte, so they must not be used + * unless there are at least eight bytes left in the buffer! */ upb_decoderet upb_vdecode_max8_branch32(upb_decoderet r); upb_decoderet upb_vdecode_max8_branch64(upb_decoderet r); upb_decoderet upb_vdecode_max8_wright(upb_decoderet r); upb_decoderet upb_vdecode_max8_massimino(upb_decoderet r); -// Template for a function that checks the first two bytes with branching -// and dispatches 2-10 bytes with a separate function. Note that this may read -// up to 10 bytes, so it must not be used unless there are at least ten bytes -// left in the buffer! +/* Template for a function that checks the first two bytes with branching + * and dispatches 2-10 bytes with a separate function. Note that this may read + * up to 10 bytes, so it must not be used unless there are at least ten bytes + * left in the buffer! */ #define UPB_VARINT_DECODER_CHECK2(name, decode_max8_function) \ UPB_INLINE upb_decoderet upb_vdecode_check2_ ## name(const char *_p) { \ uint8_t *p = (uint8_t*)_p; \ - if ((*p & 0x80) == 0) { upb_decoderet r = {_p + 1, *p & 0x7fU}; return r; } \ - upb_decoderet r = {_p + 2, (*p & 0x7fU) | ((*(p + 1) & 0x7fU) << 7)}; \ - if ((*(p + 1) & 0x80) == 0) return r; \ + upb_decoderet r; \ + if ((*p & 0x80) == 0) { \ + /* Common case: one-byte varint. */ \ + return upb_decoderet_make(_p + 1, *p & 0x7fU); \ + } \ + r = upb_decoderet_make(_p + 2, (*p & 0x7fU) | ((*(p + 1) & 0x7fU) << 7)); \ + if ((*(p + 1) & 0x80) == 0) { \ + /* Two-byte varint. */ \ + return r; \ + } \ + /* Longer varint, fallback to out-of-line function. */ \ return decode_max8_function(r); \ } -UPB_VARINT_DECODER_CHECK2(branch32, upb_vdecode_max8_branch32); -UPB_VARINT_DECODER_CHECK2(branch64, upb_vdecode_max8_branch64); -UPB_VARINT_DECODER_CHECK2(wright, upb_vdecode_max8_wright); -UPB_VARINT_DECODER_CHECK2(massimino, upb_vdecode_max8_massimino); +UPB_VARINT_DECODER_CHECK2(branch32, upb_vdecode_max8_branch32) +UPB_VARINT_DECODER_CHECK2(branch64, upb_vdecode_max8_branch64) +UPB_VARINT_DECODER_CHECK2(wright, upb_vdecode_max8_wright) +UPB_VARINT_DECODER_CHECK2(massimino, upb_vdecode_max8_massimino) #undef UPB_VARINT_DECODER_CHECK2 -// Our canonical functions for decoding varints, based on the currently -// favored best-performing implementations. +/* Our canonical functions for decoding varints, based on the currently + * favored best-performing implementations. */ UPB_INLINE upb_decoderet upb_vdecode_fast(const char *p) { if (sizeof(long) == 8) return upb_vdecode_check2_branch64(p); @@ -7639,7 +7825,7 @@ UPB_INLINE upb_decoderet upb_vdecode_max8_fast(upb_decoderet r) { UPB_INLINE int upb_value_size(uint64_t val) { #ifdef __GNUC__ - int high_bit = 63 - __builtin_clzll(val); // 0-based, undef if val == 0. + int high_bit = 63 - __builtin_clzll(val); /* 0-based, undef if val == 0. */ #else int high_bit = 0; uint64_t tmp = val; @@ -7648,13 +7834,14 @@ UPB_INLINE int upb_value_size(uint64_t val) { return val == 0 ? 1 : high_bit / 8 + 1; } -// Encodes a 64-bit varint into buf (which must be >=UPB_PB_VARINT_MAX_LEN -// bytes long), returning how many bytes were used. -// -// TODO: benchmark and optimize if necessary. +/* Encodes a 64-bit varint into buf (which must be >=UPB_PB_VARINT_MAX_LEN + * bytes long), returning how many bytes were used. + * + * TODO: benchmark and optimize if necessary. */ UPB_INLINE size_t upb_vencode64(uint64_t val, char *buf) { + size_t i; if (val == 0) { buf[0] = 0; return 1; } - size_t i = 0; + i = 0; while (val) { uint8_t byte = val & 0x7fU; val >>= 7; @@ -7669,7 +7856,7 @@ UPB_INLINE size_t upb_varint_size(uint64_t val) { return upb_vencode64(val, buf); } -// Encodes a 32-bit varint, *not* sign-extended. +/* Encodes a 32-bit varint, *not* sign-extended. */ UPB_INLINE uint64_t upb_vencode32(uint32_t val) { char buf[UPB_PB_VARINT_MAX_LEN]; size_t bytes = upb_vencode64(val, buf); @@ -7707,35 +7894,35 @@ UPB_INLINE uint64_t upb_vencode32(uint32_t val) { namespace upb { namespace pb { class Encoder; -} // namespace pb -} // namespace upb +} /* namespace pb */ +} /* namespace upb */ #endif -UPB_DECLARE_TYPE(upb::pb::Encoder, upb_pb_encoder); +UPB_DECLARE_TYPE(upb::pb::Encoder, upb_pb_encoder) #define UPB_PBENCODER_MAX_NESTING 100 /* upb::pb::Encoder ***********************************************************/ -// Preallocation hint: decoder won't allocate more bytes than this when first -// constructed. This hint may be an overestimate for some build configurations. -// But if the decoder library is upgraded without recompiling the application, -// it may be an underestimate. +/* Preallocation hint: decoder won't allocate more bytes than this when first + * constructed. This hint may be an overestimate for some build configurations. + * But if the decoder library is upgraded without recompiling the application, + * it may be an underestimate. */ #define UPB_PB_ENCODER_SIZE 768 #ifdef __cplusplus class upb::pb::Encoder { public: - // Creates a new encoder in the given environment. The Handlers must have - // come from NewHandlers() below. + /* Creates a new encoder in the given environment. The Handlers must have + * come from NewHandlers() below. */ static Encoder* Create(Environment* env, const Handlers* handlers, BytesSink* output); - // The input to the encoder. + /* The input to the encoder. */ Sink* input(); - // Creates a new set of handlers for this MessageDef. + /* Creates a new set of handlers for this MessageDef. */ static reffed_ptr NewHandlers(const MessageDef* msg); static const size_t kSize = UPB_PB_ENCODER_SIZE; @@ -7772,8 +7959,8 @@ inline reffed_ptr Encoder::NewHandlers( const Handlers* h = upb_pb_encoder_newhandlers(md, &h); return reffed_ptr(h, &h); } -} // namespace pb -} // namespace upb +} /* namespace pb */ +} /* namespace upb */ #endif @@ -7812,23 +7999,23 @@ inline reffed_ptr Encoder::NewHandlers( extern "C" { #endif -// Loads all defs from the given protobuf binary descriptor, setting default -// accessors and a default layout on all messages. The caller owns the -// returned array of defs, which will be of length *n. On error NULL is -// returned and status is set (if non-NULL). +/* Loads all defs from the given protobuf binary descriptor, setting default + * accessors and a default layout on all messages. The caller owns the + * returned array of defs, which will be of length *n. On error NULL is + * returned and status is set (if non-NULL). */ upb_def **upb_load_defs_from_descriptor(const char *str, size_t len, int *n, void *owner, upb_status *status); -// Like the previous but also adds the loaded defs to the given symtab. +/* Like the previous but also adds the loaded defs to the given symtab. */ bool upb_load_descriptor_into_symtab(upb_symtab *symtab, const char *str, size_t len, upb_status *status); -// Like the previous but also reads the descriptor from the given filename. +/* Like the previous but also reads the descriptor from the given filename. */ bool upb_load_descriptor_file_into_symtab(upb_symtab *symtab, const char *fname, upb_status *status); -// Reads the given filename into a character string, returning NULL if there -// was an error. +/* Reads the given filename into a character string, returning NULL if there + * was an error. */ char *upb_readfile(const char *filename, size_t *len); #ifdef __cplusplus @@ -7836,8 +8023,8 @@ char *upb_readfile(const char *filename, size_t *len); namespace upb { -// All routines that load descriptors expect the descriptor to be a -// FileDescriptorSet. +/* All routines that load descriptors expect the descriptor to be a + * FileDescriptorSet. */ inline bool LoadDescriptorFileIntoSymtab(SymbolTable* s, const char *fname, Status* status) { return upb_load_descriptor_file_into_symtab(s, fname, status); @@ -7848,17 +8035,17 @@ inline bool LoadDescriptorIntoSymtab(SymbolTable* s, const char* str, return upb_load_descriptor_into_symtab(s, str, len, status); } -// Templated so it can accept both string and std::string. +/* Templated so it can accept both string and std::string. */ template bool LoadDescriptorIntoSymtab(SymbolTable* s, const T& desc, Status* status) { return upb_load_descriptor_into_symtab(s, desc.c_str(), desc.size(), status); } -} // namespace upb +} /* namespace upb */ #endif -#endif +#endif /* UPB_GLUE_H */ /* * upb - a minimalist implementation of protocol buffers. * @@ -7874,18 +8061,18 @@ bool LoadDescriptorIntoSymtab(SymbolTable* s, const T& desc, Status* status) { namespace upb { namespace pb { class TextPrinter; -} // namespace pb -} // namespace upb +} /* namespace pb */ +} /* namespace upb */ #endif -UPB_DECLARE_TYPE(upb::pb::TextPrinter, upb_textprinter); +UPB_DECLARE_TYPE(upb::pb::TextPrinter, upb_textprinter) #ifdef __cplusplus class upb::pb::TextPrinter { public: - // The given handlers must have come from NewHandlers(). It must outlive the - // TextPrinter. + /* The given handlers must have come from NewHandlers(). It must outlive the + * TextPrinter. */ static TextPrinter *Create(Environment *env, const upb::Handlers *handlers, BytesSink *output); @@ -7893,8 +8080,8 @@ class upb::pb::TextPrinter { Sink* input(); - // If handler caching becomes a requirement we can add a code cache as in - // decoder.h + /* If handler caching becomes a requirement we can add a code cache as in + * decoder.h */ static reffed_ptr NewHandlers(const MessageDef* md); }; @@ -7902,7 +8089,7 @@ class upb::pb::TextPrinter { UPB_BEGIN_EXTERN_C -// C API. +/* C API. */ upb_textprinter *upb_textprinter_create(upb_env *env, const upb_handlers *h, upb_bytessink *output); void upb_textprinter_setsingleline(upb_textprinter *p, bool single_line); @@ -7933,8 +8120,8 @@ inline reffed_ptr TextPrinter::NewHandlers( const Handlers* h = upb_textprinter_newhandlers(md, &h); return reffed_ptr(h, &h); } -} // namespace pb -} // namespace upb +} /* namespace pb */ +} /* namespace upb */ #endif @@ -7957,23 +8144,24 @@ inline reffed_ptr TextPrinter::NewHandlers( namespace upb { namespace json { class Parser; -} // namespace json -} // namespace upb +} /* namespace json */ +} /* namespace upb */ #endif -UPB_DECLARE_TYPE(upb::json::Parser, upb_json_parser); +UPB_DECLARE_TYPE(upb::json::Parser, upb_json_parser) /* upb::json::Parser **********************************************************/ -// Preallocation hint: parser won't allocate more bytes than this when first -// constructed. This hint may be an overestimate for some build configurations. -// But if the parser library is upgraded without recompiling the application, -// it may be an underestimate. +/* Preallocation hint: parser won't allocate more bytes than this when first + * constructed. This hint may be an overestimate for some build configurations. + * But if the parser library is upgraded without recompiling the application, + * it may be an underestimate. */ #define UPB_JSON_PARSER_SIZE 3568 #ifdef __cplusplus -// Parses an incoming BytesStream, pushing the results to the destination sink. +/* Parses an incoming BytesStream, pushing the results to the destination + * sink. */ class upb::json::Parser { public: static Parser* Create(Environment* env, Sink* output); @@ -7981,7 +8169,7 @@ class upb::json::Parser { BytesSink* input(); private: - UPB_DISALLOW_POD_OPS(Parser, upb::json::Parser); + UPB_DISALLOW_POD_OPS(Parser, upb::json::Parser) }; #endif @@ -8003,13 +8191,13 @@ inline Parser* Parser::Create(Environment* env, Sink* output) { inline BytesSink* Parser::input() { return upb_json_parser_input(this); } -} // namespace json -} // namespace upb +} /* namespace json */ +} /* namespace upb */ #endif -#endif // UPB_JSON_PARSER_H_ +#endif /* UPB_JSON_PARSER_H_ */ /* * upb - a minimalist implementation of protocol buffers. * @@ -8028,11 +8216,11 @@ inline BytesSink* Parser::input() { namespace upb { namespace json { class Printer; -} // namespace json -} // namespace upb +} /* namespace json */ +} /* namespace upb */ #endif -UPB_DECLARE_TYPE(upb::json::Printer, upb_json_printer); +UPB_DECLARE_TYPE(upb::json::Printer, upb_json_printer) /* upb::json::Printer *********************************************************/ @@ -8041,29 +8229,29 @@ UPB_DECLARE_TYPE(upb::json::Printer, upb_json_printer); #ifdef __cplusplus -// Prints an incoming stream of data to a BytesSink in JSON format. +/* Prints an incoming stream of data to a BytesSink in JSON format. */ class upb::json::Printer { public: static Printer* Create(Environment* env, const upb::Handlers* handlers, BytesSink* output); - // The input to the printer. + /* The input to the printer. */ Sink* input(); - // Returns handlers for printing according to the specified schema. + /* Returns handlers for printing according to the specified schema. */ static reffed_ptr NewHandlers(const upb::MessageDef* md); static const size_t kSize = UPB_JSON_PRINTER_SIZE; private: - UPB_DISALLOW_POD_OPS(Printer, upb::json::Printer); + UPB_DISALLOW_POD_OPS(Printer, upb::json::Printer) }; #endif UPB_BEGIN_EXTERN_C -// Native C API. +/* Native C API. */ upb_json_printer *upb_json_printer_create(upb_env *e, const upb_handlers *h, upb_bytessink *output); upb_sink *upb_json_printer_input(upb_json_printer *p); @@ -8086,9 +8274,9 @@ inline reffed_ptr Printer::NewHandlers( const Handlers* h = upb_json_printer_newhandlers(md, &h); return reffed_ptr(h, &h); } -} // namespace json -} // namespace upb +} /* namespace json */ +} /* namespace upb */ #endif -#endif // UPB_JSON_TYPED_PRINTER_H_ +#endif /* UPB_JSON_TYPED_PRINTER_H_ */ -- cgit v1.2.3 From e3ce451b6047941cd574d3f70a7e4c2d150c6f20 Mon Sep 17 00:00:00 2001 From: Josh Haberman Date: Tue, 9 Jun 2015 11:08:25 -0700 Subject: Fixed compiler warnings and added -std=c99. upb no longer requires -std=c99 but the Ruby/C code still uses C99 features. --- ruby/ext/google/protobuf_c/defs.c | 15 ++++++--------- ruby/ext/google/protobuf_c/extconf.rb | 2 +- 2 files changed, 7 insertions(+), 10 deletions(-) (limited to 'ruby') diff --git a/ruby/ext/google/protobuf_c/defs.c b/ruby/ext/google/protobuf_c/defs.c index e3341cca..8f9f33e2 100644 --- a/ruby/ext/google/protobuf_c/defs.c +++ b/ruby/ext/google/protobuf_c/defs.c @@ -546,11 +546,9 @@ upb_fieldtype_t ruby_to_fieldtype(VALUE type) { rb_raise(rb_eArgError, "Expected symbol for field type."); } - upb_fieldtype_t upb_type = -1; - #define CONVERT(upb, ruby) \ if (SYM2ID(type) == rb_intern( # ruby )) { \ - upb_type = UPB_TYPE_ ## upb; \ + return UPB_TYPE_ ## upb; \ } CONVERT(FLOAT, float); @@ -567,11 +565,8 @@ upb_fieldtype_t ruby_to_fieldtype(VALUE type) { #undef CONVERT - if (upb_type == -1) { - rb_raise(rb_eArgError, "Unknown field type."); - } - - return upb_type; + rb_raise(rb_eArgError, "Unknown field type."); + return 0; } VALUE fieldtype_to_ruby(upb_fieldtype_t type) { @@ -666,10 +661,12 @@ VALUE FieldDescriptor_label_set(VALUE _self, VALUE label) { } upb_label_t upb_label = -1; + bool converted = false; #define CONVERT(upb, ruby) \ if (SYM2ID(label) == rb_intern( # ruby )) { \ upb_label = UPB_LABEL_ ## upb; \ + converted = true; \ } CONVERT(OPTIONAL, optional); @@ -678,7 +675,7 @@ VALUE FieldDescriptor_label_set(VALUE _self, VALUE label) { #undef CONVERT - if (upb_label == -1) { + if (!converted) { rb_raise(rb_eArgError, "Unknown field label."); } diff --git a/ruby/ext/google/protobuf_c/extconf.rb b/ruby/ext/google/protobuf_c/extconf.rb index 9675f57f..b368dcc6 100644 --- a/ruby/ext/google/protobuf_c/extconf.rb +++ b/ruby/ext/google/protobuf_c/extconf.rb @@ -2,7 +2,7 @@ require 'mkmf' -$CFLAGS += " -O3 -DNDEBUG" +$CFLAGS += " -std=c99 -O3 -DNDEBUG" $objs = ["protobuf.o", "defs.o", "storage.o", "message.o", "repeated_field.o", "map.o", "encode_decode.o", "upb.o"] -- cgit v1.2.3 From fb8ed707a2e8d6bd0290b747f40c783b8bc433ba Mon Sep 17 00:00:00 2001 From: Josh Haberman Date: Mon, 22 Jun 2015 17:23:55 -0700 Subject: Update upb to fix two bugs in the Ruby library. Fixes: https://github.com/google/protobuf/issues/502 https://github.com/google/protobuf/issues/425 --- ruby/ext/google/protobuf_c/upb.c | 71 ++++++++++++++++++++++++++++++---------- ruby/ext/google/protobuf_c/upb.h | 2 +- 2 files changed, 54 insertions(+), 19 deletions(-) (limited to 'ruby') diff --git a/ruby/ext/google/protobuf_c/upb.c b/ruby/ext/google/protobuf_c/upb.c index f50ff6ad..f99c7a70 100644 --- a/ruby/ext/google/protobuf_c/upb.c +++ b/ruby/ext/google/protobuf_c/upb.c @@ -1770,6 +1770,7 @@ static void *default_alloc(void *_ud, void *ptr, size_t oldsize, size_t size) { * updated to its new location. */ if (block->next) block->next->prev = block; if (block->prev) block->prev->next = block; + if (ud->head == from) ud->head = block; } } else { /* Insert at head of linked list. */ @@ -1798,7 +1799,7 @@ static void default_alloc_cleanup(void *_ud) { static bool default_err(void *ud, const upb_status *status) { UPB_UNUSED(ud); - fprintf(stderr, "upb error: %s\n", upb_status_errmsg(status)); + UPB_UNUSED(status); return false; } @@ -1919,7 +1920,6 @@ static size_t align_up(size_t size) { UPB_FORCEINLINE static void *seeded_alloc(void *ud, void *ptr, size_t oldsize, size_t size) { upb_seededalloc *a = ud; - UPB_UNUSED(ptr); size = align_up(size); @@ -1937,7 +1937,11 @@ UPB_FORCEINLINE static void *seeded_alloc(void *ud, void *ptr, size_t oldsize, /* Is `ptr` part of the user-provided initial block? Don't pass it to the * default allocator if so; otherwise, it may try to realloc() the block. */ if (chptr >= a->mem_base && chptr < a->mem_limit) { - return a->alloc(a->alloc_ud, NULL, 0, size); + void *ret; + assert(chptr + oldsize <= a->mem_limit); + ret = a->alloc(a->alloc_ud, NULL, 0, size); + if (ret) memcpy(ret, ptr, oldsize); + return ret; } else { return a->alloc(a->alloc_ud, ptr, oldsize, size); } @@ -3692,24 +3696,48 @@ const upb_def *upb_symtab_resolve(const upb_symtab *s, const char *base, return ret; } -/* Searches def and its children to find defs that have the same name as any - * def in "addtab." Returns true if any where found, and as a side-effect adds - * duplicates of these defs into addtab. +/* Starts a depth-first traversal at "def", recursing into any subdefs + * (ie. submessage types). Adds duplicates of existing defs to addtab + * wherever necessary, so that the resulting symtab will be consistent once + * addtab is added. + * + * More specifically, if any def D is found in the DFS that: + * + * 1. can reach a def that is being replaced by something in addtab, AND + * + * 2. is not itself being replaced already (ie. this name doesn't already + * exist in addtab) + * + * ...then a duplicate (new copy) of D will be added to addtab. + * + * Returns true if this happened for any def reachable from "def." + * + * It is slightly tricky to do this correctly in the presence of cycles. If we + * detect that our DFS has hit a cycle, we might not yet know if any SCCs on + * our stack can reach a def in addtab or not. Once we figure this out, that + * answer needs to apply to *all* defs in these SCCs, even if we visited them + * already. So a straight up one-pass cycle-detecting DFS won't work. * - * We use a modified depth-first traversal that traverses each SCC (which we - * already computed) as if it were a single node. This allows us to traverse - * the possibly-cyclic graph as if it were a DAG and to dup the correct set of - * nodes with O(n) time. */ + * To work around this problem, we traverse each SCC (which we already + * computed, since these defs are frozen) as a single node. We first compute + * whether the SCC as a whole can reach any def in addtab, then we dup (or not) + * the entire SCC. This requires breaking the encapsulation of upb_refcounted, + * since that is where we get the data about what SCC we are in. */ static bool upb_resolve_dfs(const upb_def *def, upb_strtable *addtab, const void *new_owner, upb_inttable *seen, upb_status *s) { - /* Memoize results of this function for efficiency (since we're traversing a - * DAG this is not needed to limit the depth of the search). */ upb_value v; bool need_dup; const upb_def *base; + const void* memoize_key; + + /* Memoize results of this function for efficiency (since we're traversing a + * DAG this is not needed to limit the depth of the search). + * + * We memoize by SCC instead of by individual def. */ + memoize_key = def->base.group; - if (upb_inttable_lookup(seen, (uintptr_t)def, &v)) + if (upb_inttable_lookupptr(seen, memoize_key, &v)) return upb_value_getbool(v); /* Visit submessages for all messages in the SCC. */ @@ -3725,7 +3753,8 @@ static bool upb_resolve_dfs(const upb_def *def, upb_strtable *addtab, need_dup = true; } - /* For messages, continue the recursion by visiting all subdefs. */ + /* For messages, continue the recursion by visiting all subdefs, but only + * ones in different SCCs. */ m = upb_dyncast_msgdef(def); if (m) { upb_msg_field_iter i; @@ -3733,17 +3762,23 @@ static bool upb_resolve_dfs(const upb_def *def, upb_strtable *addtab, !upb_msg_field_done(&i); upb_msg_field_next(&i)) { upb_fielddef *f = upb_msg_iter_field(&i); + const upb_def *subdef; + if (!upb_fielddef_hassubdef(f)) continue; + subdef = upb_fielddef_subdef(f); + + /* Skip subdefs in this SCC. */ + if (def->base.group == subdef->base.group) continue; + /* |= to avoid short-circuit; we need its side-effects. */ - need_dup |= upb_resolve_dfs( - upb_fielddef_subdef(f), addtab, new_owner, seen, s); + need_dup |= upb_resolve_dfs(subdef, addtab, new_owner, seen, s); if (!upb_ok(s)) return false; } } } while ((def = (upb_def*)def->base.next) != base); if (need_dup) { - /* Dup any defs that don't already have entries in addtab. */ + /* Dup all defs in this SCC that don't already have entries in addtab. */ def = base; do { const char *name; @@ -3760,7 +3795,7 @@ static bool upb_resolve_dfs(const upb_def *def, upb_strtable *addtab, } while ((def = (upb_def*)def->base.next) != base); } - upb_inttable_insert(seen, (uintptr_t)def, upb_value_bool(need_dup)); + upb_inttable_insertptr(seen, memoize_key, upb_value_bool(need_dup)); return need_dup; oom: diff --git a/ruby/ext/google/protobuf_c/upb.h b/ruby/ext/google/protobuf_c/upb.h index 4855e754..b4dcd558 100644 --- a/ruby/ext/google/protobuf_c/upb.h +++ b/ruby/ext/google/protobuf_c/upb.h @@ -7928,7 +7928,7 @@ class upb::pb::Encoder { static const size_t kSize = UPB_PB_ENCODER_SIZE; private: - UPB_DISALLOW_POD_OPS(Encoder, upb::pb::Encoder); + UPB_DISALLOW_POD_OPS(Encoder, upb::pb::Encoder) }; #endif -- cgit v1.2.3 From 8c717ad5300c69046a84d34e1ba603b5f75d1cb3 Mon Sep 17 00:00:00 2001 From: Josh Haberman Date: Wed, 8 Jul 2015 14:14:58 -0700 Subject: Worked around memory leak bug in Ruby interpreter. Change-Id: I8e2b425f9008e6b82d41d59783bb8b04af1f886f Fixes: https://github.com/google/protobuf/issues/474. --- ruby/ext/google/protobuf_c/encode_decode.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'ruby') diff --git a/ruby/ext/google/protobuf_c/encode_decode.c b/ruby/ext/google/protobuf_c/encode_decode.c index fe249d45..f789f6d4 100644 --- a/ruby/ext/google/protobuf_c/encode_decode.c +++ b/ruby/ext/google/protobuf_c/encode_decode.c @@ -30,6 +30,18 @@ #include "protobuf.h" +// This function is equivalent to rb_str_cat(), but unlike the real +// rb_str_cat(), it doesn't leak memory in some versions of Ruby. +// For more information, see: +// https://bugs.ruby-lang.org/issues/11328 +VALUE noleak_rb_str_cat(VALUE rb_str, const char *str, long len) { + size_t oldlen = RSTRING_LEN(rb_str); + rb_str_modify_expand(rb_str, len); + char *p = RSTRING_PTR(rb_str); + memcpy(p + oldlen, str, len); + rb_str_set_len(rb_str, oldlen + len); +} + // ----------------------------------------------------------------------------- // Parsing. // ----------------------------------------------------------------------------- @@ -164,7 +176,7 @@ static size_t stringdata_handler(void* closure, const void* hd, const char* str, size_t len, const upb_bufhandle* handle) { VALUE rb_str = (VALUE)closure; - rb_str_cat(rb_str, str, len); + noleak_rb_str_cat(rb_str, str, len); return len; } -- cgit v1.2.3 From 181c7f26360429b236ab833c746d10d97811931f Mon Sep 17 00:00:00 2001 From: Josh Haberman Date: Wed, 15 Jul 2015 11:05:10 -0700 Subject: Added Ruby to conformance tests. This involved fixing a few important bugs in the Ruby implementation -- mostly cases of mixing upb field types and descriptor types (upb field types do not distinguish between int/sint/fixed/sfixed like descriptor types do). Also added protobuf-specific exceptions so parse errors can be caught specifically. Change-Id: Ib49d3db976900b2c6f3455c8b88af52cfb86e036 --- conformance/Makefile.am | 7 +- conformance/conformance_ruby.rb | 111 +++++ conformance/conformance_test.h | 2 + conformance/conformance_test_runner.cc | 8 +- conformance/failure_list_ruby.txt | 17 + ruby/ext/google/protobuf_c/defs.c | 68 ++- ruby/ext/google/protobuf_c/encode_decode.c | 6 +- ruby/ext/google/protobuf_c/protobuf.c | 6 + ruby/ext/google/protobuf_c/protobuf.h | 3 + ruby/ext/google/protobuf_c/upb.c | 483 +++++++++------------ ruby/ext/google/protobuf_c/upb.h | 460 +++++++++----------- ruby/lib/google/protobuf.rb | 9 + ruby/travis-test.sh | 4 +- .../protobuf/compiler/ruby/ruby_generated_code.rb | 6 +- .../protobuf/compiler/ruby/ruby_generator.cc | 34 +- travis.sh | 29 +- 16 files changed, 678 insertions(+), 575 deletions(-) create mode 100755 conformance/conformance_ruby.rb create mode 100644 conformance/failure_list_ruby.txt (limited to 'ruby') diff --git a/conformance/Makefile.am b/conformance/Makefile.am index cccbac9e..97251715 100644 --- a/conformance/Makefile.am +++ b/conformance/Makefile.am @@ -22,7 +22,7 @@ conformance_cpp_CPPFLAGS = -I$(top_srcdir)/src if USE_EXTERNAL_PROTOC protoc_middleman: $(protoc_inputs) - $(PROTOC) -I$(srcdir) --cpp_out=. --java_out=. $^ + $(PROTOC) -I$(srcdir) --cpp_out=. --java_out=. --ruby_out=. $^ touch protoc_middleman else @@ -31,7 +31,7 @@ else # relative to srcdir, which may not be the same as the current directory when # building out-of-tree. protoc_middleman: $(top_srcdir)/src/protoc$(EXEEXT) $(protoc_inputs) - oldpwd=`pwd` && ( cd $(srcdir) && $$oldpwd/../src/protoc$(EXEEXT) -I. --cpp_out=$$oldpwd --java_out=$$oldpwd $(protoc_inputs) ) + oldpwd=`pwd` && ( cd $(srcdir) && $$oldpwd/../src/protoc$(EXEEXT) -I. --cpp_out=$$oldpwd --java_out=$$oldpwd --ruby_out=$$oldpwd $(protoc_inputs) ) touch protoc_middleman endif @@ -61,3 +61,6 @@ test_cpp: protoc_middleman conformance-test-runner conformance-cpp test_java: protoc_middleman conformance-test-runner conformance-java ./conformance-test-runner ./conformance-java + +test_ruby: protoc_middleman conformance-test-runner + RUBYLIB=../ruby/lib:. ./conformance-test-runner --failure_list failure_list_ruby.txt ./conformance_ruby.rb diff --git a/conformance/conformance_ruby.rb b/conformance/conformance_ruby.rb new file mode 100755 index 00000000..e7bd4ed5 --- /dev/null +++ b/conformance/conformance_ruby.rb @@ -0,0 +1,111 @@ +#!/usr/bin/env ruby +# +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# https://developers.google.com/protocol-buffers/ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +require 'conformance' + +test_count = 0; +verbose = false; + +def do_test(request) + test_message = Conformance::TestAllTypes.new + response = Conformance::ConformanceResponse.new + + begin + case request.payload + when :protobuf_payload + begin + test_message = Conformance::TestAllTypes.decode(request.protobuf_payload) + rescue Google::Protobuf::ParseError => err + response.parse_error = err.message.encode("utf-8") + return response + end + + when :json_payload + test_message = Conformance::TestAllTypes.decode_json(request.json_payload) + + when nil + raise "Request didn't have payload."; + end + + case request.requested_output_format + when :UNSPECIFIED + raise "Unspecified output format" + + when :PROTOBUF + response.protobuf_payload = Conformance::TestAllTypes.encode(test_message) + + when :JSON + response.json_payload = Conformance::TestAllTypes.encode_json(test_message) + end + rescue Exception => err + response.runtime_error = err.message.encode("utf-8") + err.backtrace.join("\n") + end + + return response +end + +def do_test_io + length_bytes = STDIN.read(4) + return false if length_bytes.nil? + + length = length_bytes.unpack("V").first + serialized_request = STDIN.read(length) + if serialized_request.nil? or serialized_request.length != length + raise "I/O error" + end + + request = Conformance::ConformanceRequest.decode(serialized_request) + + response = do_test(request) + + serialized_response = Conformance::ConformanceResponse.encode(response) + STDOUT.write([serialized_response.length].pack("V")) + STDOUT.write(serialized_response) + STDOUT.flush + + #if verbose + # fprintf(stderr, "conformance-cpp: request=%s, response=%s\n", + # request.ShortDebugString().c_str(), + # response.ShortDebugString().c_str()); + + #test_count++; + + return true; +end + +while true + if not do_test_io() + STDERR.puts("conformance-cpp: received EOF from test runner " + + "after #{test_count} tests, exiting") + exit 0 + end +end diff --git a/conformance/conformance_test.h b/conformance/conformance_test.h index cadda828..9e6cdaee 100644 --- a/conformance/conformance_test.h +++ b/conformance/conformance_test.h @@ -85,6 +85,8 @@ class ConformanceTestSuite { public: ConformanceTestSuite() : verbose_(false) {} + void SetVerbose(bool verbose) { verbose_ = verbose; } + // Sets the list of tests that are expected to fail when RunSuite() is called. // RunSuite() will fail unless the set of failing tests is exactly the same // as this list. diff --git a/conformance/conformance_test_runner.cc b/conformance/conformance_test_runner.cc index b56e19cf..780e1c44 100644 --- a/conformance/conformance_test_runner.cc +++ b/conformance/conformance_test_runner.cc @@ -219,12 +219,16 @@ void ParseFailureList(const char *filename, vector* failure_list) { int main(int argc, char *argv[]) { int arg = 1; char *program; - vector failure_list; + google::protobuf::ConformanceTestSuite suite; for (int arg = 1; arg < argc; ++arg) { if (strcmp(argv[arg], "--failure_list") == 0) { if (++arg == argc) UsageError(); + vector failure_list; ParseFailureList(argv[arg], &failure_list); + suite.SetFailureList(failure_list); + } else if (strcmp(argv[arg], "--verbose") == 0) { + suite.SetVerbose(true); } else if (argv[arg][0] == '-') { fprintf(stderr, "Unknown option: %s\n", argv[arg]); UsageError(); @@ -238,8 +242,6 @@ int main(int argc, char *argv[]) { } ForkPipeRunner runner(program); - google::protobuf::ConformanceTestSuite suite; - suite.SetFailureList(failure_list); std::string output; bool ok = suite.RunSuite(&runner, &output); diff --git a/conformance/failure_list_ruby.txt b/conformance/failure_list_ruby.txt new file mode 100644 index 00000000..35d1ff91 --- /dev/null +++ b/conformance/failure_list_ruby.txt @@ -0,0 +1,17 @@ +JsonInput.HelloWorld.JsonOutput +JsonInput.HelloWorld.ProtobufOutput +ProtobufInput.PrematureEofBeforeUnknownValue.DOUBLE +ProtobufInput.PrematureEofBeforeUnknownValue.FIXED32 +ProtobufInput.PrematureEofBeforeUnknownValue.FIXED64 +ProtobufInput.PrematureEofBeforeUnknownValue.FLOAT +ProtobufInput.PrematureEofBeforeUnknownValue.SFIXED32 +ProtobufInput.PrematureEofBeforeUnknownValue.SFIXED64 +ProtobufInput.PrematureEofInDelimitedDataForUnknownValue.BYTES +ProtobufInput.PrematureEofInDelimitedDataForUnknownValue.MESSAGE +ProtobufInput.PrematureEofInDelimitedDataForUnknownValue.STRING +ProtobufInput.PrematureEofInsideUnknownValue.DOUBLE +ProtobufInput.PrematureEofInsideUnknownValue.FIXED32 +ProtobufInput.PrematureEofInsideUnknownValue.FIXED64 +ProtobufInput.PrematureEofInsideUnknownValue.FLOAT +ProtobufInput.PrematureEofInsideUnknownValue.SFIXED32 +ProtobufInput.PrematureEofInsideUnknownValue.SFIXED64 diff --git a/ruby/ext/google/protobuf_c/defs.c b/ruby/ext/google/protobuf_c/defs.c index 8f9f33e2..0b2f977f 100644 --- a/ruby/ext/google/protobuf_c/defs.c +++ b/ruby/ext/google/protobuf_c/defs.c @@ -548,7 +548,7 @@ upb_fieldtype_t ruby_to_fieldtype(VALUE type) { #define CONVERT(upb, ruby) \ if (SYM2ID(type) == rb_intern( # ruby )) { \ - return UPB_TYPE_ ## upb; \ + return UPB_TYPE_ ## upb; \ } CONVERT(FLOAT, float); @@ -589,6 +589,68 @@ VALUE fieldtype_to_ruby(upb_fieldtype_t type) { return Qnil; } +upb_descriptortype_t ruby_to_descriptortype(VALUE type) { + if (TYPE(type) != T_SYMBOL) { + rb_raise(rb_eArgError, "Expected symbol for field type."); + } + +#define CONVERT(upb, ruby) \ + if (SYM2ID(type) == rb_intern( # ruby )) { \ + return UPB_DESCRIPTOR_TYPE_ ## upb; \ + } + + CONVERT(FLOAT, float); + CONVERT(DOUBLE, double); + CONVERT(BOOL, bool); + CONVERT(STRING, string); + CONVERT(BYTES, bytes); + CONVERT(MESSAGE, message); + CONVERT(GROUP, group); + CONVERT(ENUM, enum); + CONVERT(INT32, int32); + CONVERT(INT64, int64); + CONVERT(UINT32, uint32); + CONVERT(UINT64, uint64); + CONVERT(SINT32, sint32); + CONVERT(SINT64, sint64); + CONVERT(FIXED32, fixed32); + CONVERT(FIXED64, fixed64); + CONVERT(SFIXED32, sfixed32); + CONVERT(SFIXED64, sfixed64); + +#undef CONVERT + + rb_raise(rb_eArgError, "Unknown field type."); + return 0; +} + +VALUE descriptortype_to_ruby(upb_descriptortype_t type) { + switch (type) { +#define CONVERT(upb, ruby) \ + case UPB_DESCRIPTOR_TYPE_ ## upb : return ID2SYM(rb_intern( # ruby )); + CONVERT(FLOAT, float); + CONVERT(DOUBLE, double); + CONVERT(BOOL, bool); + CONVERT(STRING, string); + CONVERT(BYTES, bytes); + CONVERT(MESSAGE, message); + CONVERT(GROUP, group); + CONVERT(ENUM, enum); + CONVERT(INT32, int32); + CONVERT(INT64, int64); + CONVERT(UINT32, uint32); + CONVERT(UINT64, uint64); + CONVERT(SINT32, sint32); + CONVERT(SINT64, sint64); + CONVERT(FIXED32, fixed32); + CONVERT(FIXED64, fixed64); + CONVERT(SFIXED32, sfixed32); + CONVERT(SFIXED64, sfixed64); +#undef CONVERT + } + return Qnil; +} + /* * call-seq: * FieldDescriptor.type => type @@ -604,7 +666,7 @@ VALUE FieldDescriptor_type(VALUE _self) { if (!upb_fielddef_typeisset(self->fielddef)) { return Qnil; } - return fieldtype_to_ruby(upb_fielddef_type(self->fielddef)); + return descriptortype_to_ruby(upb_fielddef_descriptortype(self->fielddef)); } /* @@ -617,7 +679,7 @@ VALUE FieldDescriptor_type(VALUE _self) { VALUE FieldDescriptor_type_set(VALUE _self, VALUE type) { DEFINE_SELF(FieldDescriptor, self, _self); upb_fielddef* mut_def = check_field_notfrozen(self->fielddef); - upb_fielddef_settype(mut_def, ruby_to_fieldtype(type)); + upb_fielddef_setdescriptortype(mut_def, ruby_to_descriptortype(type)); return Qnil; } diff --git a/ruby/ext/google/protobuf_c/encode_decode.c b/ruby/ext/google/protobuf_c/encode_decode.c index f789f6d4..e488f05b 100644 --- a/ruby/ext/google/protobuf_c/encode_decode.c +++ b/ruby/ext/google/protobuf_c/encode_decode.c @@ -656,8 +656,10 @@ static bool env_error_func(void* ud, const upb_status* status) { // Free the env -- rb_raise will longjmp up the stack past the encode/decode // function so it would not otherwise have been freed. stackenv_uninit(se); - rb_raise(rb_eRuntimeError, se->ruby_error_template, - upb_status_errmsg(status)); + + // TODO(haberman): have a way to verify that this is actually a parse error, + // instead of just throwing "parse error" unconditionally. + rb_raise(cParseError, se->ruby_error_template, upb_status_errmsg(status)); // Never reached: rb_raise() always longjmp()s up the stack, past all of our // code, back to Ruby. return false; diff --git a/ruby/ext/google/protobuf_c/protobuf.c b/ruby/ext/google/protobuf_c/protobuf.c index d0625a10..ca0f7b7c 100644 --- a/ruby/ext/google/protobuf_c/protobuf.c +++ b/ruby/ext/google/protobuf_c/protobuf.c @@ -39,6 +39,9 @@ // Ruby integers) to MessageDef/EnumDef instances (as Ruby values). VALUE upb_def_to_ruby_obj_map; +VALUE cError; +VALUE cParseError; + void add_def_obj(const void* def, VALUE value) { rb_hash_aset(upb_def_to_ruby_obj_map, ULL2NUM((intptr_t)def), value); } @@ -96,6 +99,9 @@ void Init_protobuf_c() { RepeatedField_register(protobuf); Map_register(protobuf); + cError = rb_const_get(protobuf, rb_intern("Error")); + cParseError = rb_const_get(protobuf, rb_intern("ParseError")); + rb_define_singleton_method(protobuf, "deep_copy", Google_Protobuf_deep_copy, 1); diff --git a/ruby/ext/google/protobuf_c/protobuf.h b/ruby/ext/google/protobuf_c/protobuf.h index f8667486..8750c93d 100644 --- a/ruby/ext/google/protobuf_c/protobuf.h +++ b/ruby/ext/google/protobuf_c/protobuf.h @@ -161,6 +161,9 @@ extern VALUE cOneofBuilderContext; extern VALUE cEnumBuilderContext; extern VALUE cBuilder; +extern VALUE cError; +extern VALUE cParseError; + // We forward-declare all of the Ruby method implementations here because we // sometimes call the methods directly across .c files, rather than going // through Ruby's method dispatching (e.g. during message parse). It's cleaner diff --git a/ruby/ext/google/protobuf_c/upb.c b/ruby/ext/google/protobuf_c/upb.c index f99c7a70..8ef8e31d 100644 --- a/ruby/ext/google/protobuf_c/upb.c +++ b/ruby/ext/google/protobuf_c/upb.c @@ -1,11 +1,5 @@ // Amalgamated source file #include "upb.h" -/* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2008-2012 Google Inc. See LICENSE for details. - * Author: Josh Haberman - */ #include @@ -1701,12 +1695,6 @@ upb_fielddef *upb_oneof_iter_field(const upb_oneof_iter *iter) { void upb_oneof_iter_setdone(upb_oneof_iter *iter) { upb_inttable_iter_setdone(iter); } -/* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2014 Google Inc. See LICENSE for details. - * Author: Josh Haberman - */ #include @@ -1980,14 +1968,9 @@ upb_alloc_func *upb_seededalloc_getallocfunc(upb_seededalloc *a) { return seeded_alloc; } /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2011-2012 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * TODO(haberman): it's unclear whether a lot of the consistency checks should - * assert() or return false. - */ +** TODO(haberman): it's unclear whether a lot of the consistency checks should +** assert() or return false. +*/ #include @@ -2668,24 +2651,21 @@ bool upb_byteshandler_setendstr(upb_byteshandler *h, return true; } /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2012 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * Our key invariants are: - * 1. reference cycles never span groups - * 2. for ref2(to, from), we increment to's count iff group(from) != group(to) - * - * The previous two are how we avoid leaking cycles. Other important - * invariants are: - * 3. for mutable objects "from" and "to", if there exists a ref2(to, from) - * this implies group(from) == group(to). (In practice, what we implement - * is even stronger; "from" and "to" will share a group if there has *ever* - * been a ref2(to, from), but all that is necessary for correctness is the - * weaker one). - * 4. mutable and immutable objects are never in the same group. - */ +** upb::RefCounted Implementation +** +** Our key invariants are: +** 1. reference cycles never span groups +** 2. for ref2(to, from), we increment to's count iff group(from) != group(to) +** +** The previous two are how we avoid leaking cycles. Other important +** invariants are: +** 3. for mutable objects "from" and "to", if there exists a ref2(to, from) +** this implies group(from) == group(to). (In practice, what we implement +** is even stronger; "from" and "to" will share a group if there has *ever* +** been a ref2(to, from), but all that is necessary for correctness is the +** weaker one). +** 4. mutable and immutable objects are never in the same group. +*/ #include @@ -3514,12 +3494,6 @@ bool upb_refcounted_freeze(upb_refcounted *const*roots, int n, upb_status *s, } return freeze(roots, n, s, maxdepth); } -/* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2013 Google Inc. See LICENSE for details. - * Author: Josh Haberman - */ #include @@ -3605,12 +3579,6 @@ const upb_shim_data *upb_shim_getdata(const upb_handlers *h, upb_selector_t s, return (const upb_shim_data*)upb_handlers_gethandlerdata(h, s); } -/* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2008-2012 Google Inc. See LICENSE for details. - * Author: Josh Haberman - */ #include @@ -4041,13 +4009,10 @@ const upb_def *upb_symtab_iter_def(const upb_symtab_iter *iter) { return upb_value_getptr(upb_strtable_iter_value(&iter->iter)); } /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2009 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * Implementation is heavily inspired by Lua's ltable.c. - */ +** upb_table Implementation +** +** Implementation is heavily inspired by Lua's ltable.c. +*/ #include @@ -4931,12 +4896,6 @@ uint32_t MurmurHash2(const void * key, size_t len, uint32_t seed) { #undef MIX #endif /* UPB_UNALIGNED_READS_OK */ -/* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2009-2012 Google Inc. See LICENSE for details. - * Author: Josh Haberman - */ #include #include @@ -5860,17 +5819,12 @@ static upb_inttable reftables[212] = { #endif /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2008-2009 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * XXX: The routines in this file that consume a string do not currently - * support having the string span buffers. In the future, as upb_sink and - * its buffering/sharing functionality evolve there should be an easy and - * idiomatic way of correctly handling this case. For now, we accept this - * limitation since we currently only parse descriptors from single strings. - */ +** XXX: The routines in this file that consume a string do not currently +** support having the string span buffers. In the future, as upb_sink and +** its buffering/sharing functionality evolve there should be an easy and +** idiomatic way of correctly handling this case. For now, we accept this +** limitation since we currently only parse descriptors from single strings. +*/ #include @@ -6518,21 +6472,18 @@ const upb_handlers *upb_descreader_newhandlers(const void *owner) { return h; } /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2013 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * Code to compile a upb::Handlers into bytecode for decoding a protobuf - * according to that specific schema and destination handlers. - * - * Compiling to bytecode is always the first step. If we are using the - * interpreted decoder we leave it as bytecode and interpret that. If we are - * using a JIT decoder we use a code generator to turn the bytecode into native - * code, LLVM IR, etc. - * - * Bytecode definition is in decoder.int.h. - */ +** protobuf decoder bytecode compiler +** +** Code to compile a upb::Handlers into bytecode for decoding a protobuf +** according to that specific schema and destination handlers. +** +** Compiling to bytecode is always the first step. If we are using the +** interpreted decoder we leave it as bytecode and interpret that. If we are +** using a JIT decoder we use a code generator to turn the bytecode into native +** code, LLVM IR, etc. +** +** Bytecode definition is in decoder.int.h. +*/ #include @@ -7502,24 +7453,19 @@ void upb_pbdecodermethodopts_setlazy(upb_pbdecodermethodopts *opts, bool lazy) { opts->lazy = lazy; } /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2008-2013 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * This file implements a VM for the interpreted (bytecode) decoder. - * - * Bytecode must previously have been generated using the bytecode compiler in - * compile_decoder.c. This decoder then walks through the bytecode op-by-op to - * parse the input. - * - * Decoding is fully resumable; we just keep a pointer to the current bytecode - * instruction and resume from there. A fair amount of the logic here is to - * handle the fact that values can span buffer seams and we have to be able to - * be capable of suspending/resuming from any byte in the stream. This - * sometimes requires keeping a few trailing bytes from the last buffer around - * in the "residual" buffer. - */ +** upb::Decoder (Bytecode Decoder VM) +** +** Bytecode must previously have been generated using the bytecode compiler in +** compile_decoder.c. This decoder then walks through the bytecode op-by-op to +** parse the input. +** +** Decoding is fully resumable; we just keep a pointer to the current bytecode +** instruction and resume from there. A fair amount of the logic here is to +** handle the fact that values can span buffer seams and we have to be able to +** be capable of suspending/resuming from any byte in the stream. This +** sometimes requires keeping a few trailing bytes from the last buffer around +** in the "residual" buffer. +*/ #include #include @@ -8529,63 +8475,60 @@ bool upb_pbdecoder_setmaxnesting(upb_pbdecoder *d, size_t max) { return true; } /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2014 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * Since we are implementing pure handlers (ie. without any out-of-band access - * to pre-computed lengths), we have to buffer all submessages before we can - * emit even their first byte. - * - * Not knowing the size of submessages also means we can't write a perfect - * zero-copy implementation, even with buffering. Lengths are stored as - * varints, which means that we don't know how many bytes to reserve for the - * length until we know what the length is. - * - * This leaves us with three main choices: - * - * 1. buffer all submessage data in a temporary buffer, then copy it exactly - * once into the output buffer. - * - * 2. attempt to buffer data directly into the output buffer, estimating how - * many bytes each length will take. When our guesses are wrong, use - * memmove() to grow or shrink the allotted space. - * - * 3. buffer directly into the output buffer, allocating a max length - * ahead-of-time for each submessage length. If we overallocated, we waste - * space, but no memcpy() or memmove() is required. This approach requires - * defining a maximum size for submessages and rejecting submessages that - * exceed that size. - * - * (2) and (3) have the potential to have better performance, but they are more - * complicated and subtle to implement: - * - * (3) requires making an arbitrary choice of the maximum message size; it - * wastes space when submessages are shorter than this and fails - * completely when they are longer. This makes it more finicky and - * requires configuration based on the input. It also makes it impossible - * to perfectly match the output of reference encoders that always use the - * optimal amount of space for each length. - * - * (2) requires guessing the the size upfront, and if multiple lengths are - * guessed wrong the minimum required number of memmove() operations may - * be complicated to compute correctly. Implemented properly, it may have - * a useful amortized or average cost, but more investigation is required - * to determine this and what the optimal algorithm is to achieve it. - * - * (1) makes you always pay for exactly one copy, but its implementation is - * the simplest and its performance is predictable. - * - * So for now, we implement (1) only. If we wish to optimize later, we should - * be able to do it without affecting users. - * - * The strategy is to buffer the segments of data that do *not* depend on - * unknown lengths in one buffer, and keep a separate buffer of segment pointers - * and lengths. When the top-level submessage ends, we can go beginning to end, - * alternating the writing of lengths with memcpy() of the rest of the data. - * At the top level though, no buffering is required. - */ +** upb::Encoder +** +** Since we are implementing pure handlers (ie. without any out-of-band access +** to pre-computed lengths), we have to buffer all submessages before we can +** emit even their first byte. +** +** Not knowing the size of submessages also means we can't write a perfect +** zero-copy implementation, even with buffering. Lengths are stored as +** varints, which means that we don't know how many bytes to reserve for the +** length until we know what the length is. +** +** This leaves us with three main choices: +** +** 1. buffer all submessage data in a temporary buffer, then copy it exactly +** once into the output buffer. +** +** 2. attempt to buffer data directly into the output buffer, estimating how +** many bytes each length will take. When our guesses are wrong, use +** memmove() to grow or shrink the allotted space. +** +** 3. buffer directly into the output buffer, allocating a max length +** ahead-of-time for each submessage length. If we overallocated, we waste +** space, but no memcpy() or memmove() is required. This approach requires +** defining a maximum size for submessages and rejecting submessages that +** exceed that size. +** +** (2) and (3) have the potential to have better performance, but they are more +** complicated and subtle to implement: +** +** (3) requires making an arbitrary choice of the maximum message size; it +** wastes space when submessages are shorter than this and fails +** completely when they are longer. This makes it more finicky and +** requires configuration based on the input. It also makes it impossible +** to perfectly match the output of reference encoders that always use the +** optimal amount of space for each length. +** +** (2) requires guessing the the size upfront, and if multiple lengths are +** guessed wrong the minimum required number of memmove() operations may +** be complicated to compute correctly. Implemented properly, it may have +** a useful amortized or average cost, but more investigation is required +** to determine this and what the optimal algorithm is to achieve it. +** +** (1) makes you always pay for exactly one copy, but its implementation is +** the simplest and its performance is predictable. +** +** So for now, we implement (1) only. If we wish to optimize later, we should +** be able to do it without affecting users. +** +** The strategy is to buffer the segments of data that do *not* depend on +** unknown lengths in one buffer, and keep a separate buffer of segment pointers +** and lengths. When the top-level submessage ends, we can go beginning to end, +** alternating the writing of lengths with memcpy() of the rest of the data. +** At the top level though, no buffering is required. +*/ #include @@ -9095,12 +9038,6 @@ upb_pb_encoder *upb_pb_encoder_create(upb_env *env, const upb_handlers *h, } upb_sink *upb_pb_encoder_input(upb_pb_encoder *e) { return &e->input_; } -/* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2010-2012 Google Inc. See LICENSE for details. - * Author: Josh Haberman - */ #include @@ -9189,10 +9126,7 @@ bool upb_load_descriptor_file_into_symtab(upb_symtab *symtab, const char *fname, return success; } /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2009 Google Inc. See LICENSE for details. - * Author: Josh Haberman + * upb::pb::TextPrinter * * OPT: This is not optimized at all. It uses printf() which parses the format * string every time, and it allocates memory for every put. @@ -9529,12 +9463,6 @@ upb_sink *upb_textprinter_input(upb_textprinter *p) { return &p->input_; } void upb_textprinter_setsingleline(upb_textprinter *p, bool single_line) { p->single_line_ = single_line; } -/* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2011 Google Inc. See LICENSE for details. - * Author: Josh Haberman - */ /* Index is descriptor type. */ @@ -9662,28 +9590,25 @@ upb_decoderet upb_vdecode_max8_wright(upb_decoderet r) { #line 1 "upb/json/parser.rl" /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2014 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * A parser that uses the Ragel State Machine Compiler to generate - * the finite automata. - * - * Ragel only natively handles regular languages, but we can manually - * program it a bit to handle context-free languages like JSON, by using - * the "fcall" and "fret" constructs. - * - * This parser can handle the basics, but needs several things to be fleshed - * out: - * - * - handling of unicode escape sequences (including high surrogate pairs). - * - properly check and report errors for unknown fields, stack overflow, - * improper array nesting (or lack of nesting). - * - handling of base64 sequences with padding characters. - * - handling of push-back (non-success returns from sink functions). - * - handling of keys/escape-sequences/etc that span input buffers. - */ +** upb::json::Parser (upb_json_parser) +** +** A parser that uses the Ragel State Machine Compiler to generate +** the finite automata. +** +** Ragel only natively handles regular languages, but we can manually +** program it a bit to handle context-free languages like JSON, by using +** the "fcall" and "fret" constructs. +** +** This parser can handle the basics, but needs several things to be fleshed +** out: +** +** - handling of unicode escape sequences (including high surrogate pairs). +** - properly check and report errors for unknown fields, stack overflow, +** improper array nesting (or lack of nesting). +** - handling of base64 sequences with padding characters. +** - handling of push-back (non-success returns from sink functions). +** - handling of keys/escape-sequences/etc that span input buffers. +*/ #include #include @@ -9731,7 +9656,7 @@ struct upb_json_parser { upb_jsonparser_frame *top; upb_jsonparser_frame *limit; - upb_status *status; + upb_status status; /* Ragel's internal parsing stack for the parsing state machine. */ int current_state; @@ -9778,7 +9703,8 @@ static upb_selector_t parser_getsel(upb_json_parser *p) { static bool check_stack(upb_json_parser *p) { if ((p->top + 1) == p->limit) { - upb_status_seterrmsg(p->status, "Nesting too deep"); + upb_status_seterrmsg(&p->status, "Nesting too deep"); + upb_env_reporterror(p->env, &p->status); return false; } @@ -9860,9 +9786,10 @@ static bool base64_push(upb_json_parser *p, upb_selector_t sel, const char *ptr, char output[3]; if (limit - ptr < 4) { - upb_status_seterrf(p->status, + upb_status_seterrf(&p->status, "Base64 input for bytes field not a multiple of 4: %s", upb_fielddef_name(p->top->f)); + upb_env_reporterror(p->env, &p->status); return false; } @@ -9886,9 +9813,10 @@ static bool base64_push(upb_json_parser *p, upb_selector_t sel, const char *ptr, otherchar: if (nonbase64(ptr[0]) || nonbase64(ptr[1]) || nonbase64(ptr[2]) || nonbase64(ptr[3]) ) { - upb_status_seterrf(p->status, + upb_status_seterrf(&p->status, "Non-base64 characters in bytes field: %s", upb_fielddef_name(p->top->f)); + upb_env_reporterror(p->env, &p->status); return false; } if (ptr[2] == '=') { uint32_t val; @@ -9926,10 +9854,11 @@ otherchar: } badpadding: - upb_status_seterrf(p->status, + upb_status_seterrf(&p->status, "Incorrect base64 padding for field: %s (%.*s)", upb_fielddef_name(p->top->f), 4, ptr); + upb_env_reporterror(p->env, &p->status); return false; } @@ -9976,7 +9905,8 @@ static bool accumulate_realloc(upb_json_parser *p, size_t need) { mem = upb_env_realloc(p->env, p->accumulate_buf, old_size, new_size); if (!mem) { - upb_status_seterrmsg(p->status, "Out of memory allocating buffer."); + upb_status_seterrmsg(&p->status, "Out of memory allocating buffer."); + upb_env_reporterror(p->env, &p->status); return false; } @@ -9999,7 +9929,8 @@ static bool accumulate_append(upb_json_parser *p, const char *buf, size_t len, } if (!checked_add(p->accumulated_len, len, &need)) { - upb_status_seterrmsg(p->status, "Integer overflow."); + upb_status_seterrmsg(&p->status, "Integer overflow."); + upb_env_reporterror(p->env, &p->status); return false; } @@ -10077,7 +10008,8 @@ static bool multipart_text(upb_json_parser *p, const char *buf, size_t len, switch (p->multipart_state) { case MULTIPART_INACTIVE: upb_status_seterrmsg( - p->status, "Internal error: unexpected state MULTIPART_INACTIVE"); + &p->status, "Internal error: unexpected state MULTIPART_INACTIVE"); + upb_env_reporterror(p->env, &p->status); return false; case MULTIPART_ACCUMULATE: @@ -10336,7 +10268,8 @@ static bool parse_number(upb_json_parser *p) { return true; err: - upb_status_seterrf(p->status, "error parsing number: %s", buf); + upb_status_seterrf(&p->status, "error parsing number: %s", buf); + upb_env_reporterror(p->env, &p->status); multipart_end(p); return false; } @@ -10345,9 +10278,10 @@ static bool parser_putbool(upb_json_parser *p, bool val) { bool ok; if (upb_fielddef_type(p->top->f) != UPB_TYPE_BOOL) { - upb_status_seterrf(p->status, + upb_status_seterrf(&p->status, "Boolean value specified for non-bool field: %s", upb_fielddef_name(p->top->f)); + upb_env_reporterror(p->env, &p->status); return false; } @@ -10398,9 +10332,10 @@ static bool start_stringval(upb_json_parser *p) { multipart_startaccum(p); return true; } else { - upb_status_seterrf(p->status, + upb_status_seterrf(&p->status, "String specified for non-string/non-enum field: %s", upb_fielddef_name(p->top->f)); + upb_env_reporterror(p->env, &p->status); return false; } } @@ -10438,7 +10373,8 @@ static bool end_stringval(upb_json_parser *p) { upb_selector_t sel = parser_getsel(p); upb_sink_putint32(&p->top->sink, sel, int_val); } else { - upb_status_seterrf(p->status, "Enum value unknown: '%.*s'", len, buf); + upb_status_seterrf(&p->status, "Enum value unknown: '%.*s'", len, buf); + upb_env_reporterror(p->env, &p->status); } break; @@ -10446,7 +10382,8 @@ static bool end_stringval(upb_json_parser *p) { default: assert(false); - upb_status_seterrmsg(p->status, "Internal error in JSON decoder"); + upb_status_seterrmsg(&p->status, "Internal error in JSON decoder"); + upb_env_reporterror(p->env, &p->status); ok = false; break; } @@ -10476,7 +10413,8 @@ static bool parse_mapentry_key(upb_json_parser *p) { 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"); + upb_status_seterrmsg(&p->status, "mapentry message has no key"); + upb_env_reporterror(p->env, &p->status); return false; } switch (upb_fielddef_type(p->top->f)) { @@ -10499,8 +10437,9 @@ static bool parse_mapentry_key(upb_json_parser *p) { return false; } } else { - upb_status_seterrmsg(p->status, + upb_status_seterrmsg(&p->status, "Map bool key not 'true' or 'false'"); + upb_env_reporterror(p->env, &p->status); return false; } multipart_end(p); @@ -10518,7 +10457,8 @@ static bool parse_mapentry_key(upb_json_parser *p) { break; } default: - upb_status_seterrmsg(p->status, "Invalid field type for map key"); + upb_status_seterrmsg(&p->status, "Invalid field type for map key"); + upb_env_reporterror(p->env, &p->status); return false; } @@ -10573,7 +10513,8 @@ static bool handle_mapentry(upb_json_parser *p) { 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"); + upb_status_seterrmsg(&p->status, "mapentry message has no value"); + upb_env_reporterror(p->env, &p->status); return false; } @@ -10593,7 +10534,8 @@ static bool end_membername(upb_json_parser *p) { 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); + upb_status_seterrf(&p->status, "No such field: %.*s\n", (int)len, buf); + upb_env_reporterror(p->env, &p->status); return false; } @@ -10669,9 +10611,10 @@ static bool start_subobject(upb_json_parser *p) { return true; } else { - upb_status_seterrf(p->status, + upb_status_seterrf(&p->status, "Object specified for non-message/group field: %s", upb_fielddef_name(p->top->f)); + upb_env_reporterror(p->env, &p->status); return false; } } @@ -10697,9 +10640,10 @@ static bool start_array(upb_json_parser *p) { assert(p->top->f); if (!upb_fielddef_isseq(p->top->f)) { - upb_status_seterrf(p->status, + upb_status_seterrf(&p->status, "Array specified for non-repeated field: %s", upb_fielddef_name(p->top->f)); + upb_env_reporterror(p->env, &p->status); return false; } @@ -10736,7 +10680,11 @@ static void start_object(upb_json_parser *p) { static void end_object(upb_json_parser *p) { if (!p->top->is_map) { upb_status status; + upb_status_clear(&status); upb_sink_endmsg(&p->top->sink, &status); + if (!upb_ok(&status)) { + upb_env_reporterror(p->env, &status); + } } } @@ -10762,11 +10710,11 @@ static void end_object(upb_json_parser *p) { * final state once, when the closing '"' is seen. */ -#line 1198 "upb/json/parser.rl" +#line 1218 "upb/json/parser.rl" -#line 1110 "upb/json/parser.c" +#line 1130 "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, @@ -10915,7 +10863,7 @@ static const int json_en_value_machine = 27; static const int json_en_main = 1; -#line 1201 "upb/json/parser.rl" +#line 1221 "upb/json/parser.rl" size_t parse(void *closure, const void *hd, const char *buf, size_t size, const upb_bufhandle *handle) { @@ -10937,7 +10885,7 @@ size_t parse(void *closure, const void *hd, const char *buf, size_t size, capture_resume(parser, buf); -#line 1281 "upb/json/parser.c" +#line 1301 "upb/json/parser.c" { int _klen; unsigned int _trans; @@ -11012,118 +10960,118 @@ _match: switch ( *_acts++ ) { case 0: -#line 1113 "upb/json/parser.rl" +#line 1133 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; case 1: -#line 1114 "upb/json/parser.rl" +#line 1134 "upb/json/parser.rl" { p--; {stack[top++] = cs; cs = 10; goto _again;} } break; case 2: -#line 1118 "upb/json/parser.rl" +#line 1138 "upb/json/parser.rl" { start_text(parser, p); } break; case 3: -#line 1119 "upb/json/parser.rl" +#line 1139 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_text(parser, p)); } break; case 4: -#line 1125 "upb/json/parser.rl" +#line 1145 "upb/json/parser.rl" { start_hex(parser); } break; case 5: -#line 1126 "upb/json/parser.rl" +#line 1146 "upb/json/parser.rl" { hexdigit(parser, p); } break; case 6: -#line 1127 "upb/json/parser.rl" +#line 1147 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_hex(parser)); } break; case 7: -#line 1133 "upb/json/parser.rl" +#line 1153 "upb/json/parser.rl" { CHECK_RETURN_TOP(escape(parser, p)); } break; case 8: -#line 1139 "upb/json/parser.rl" +#line 1159 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; case 9: -#line 1142 "upb/json/parser.rl" +#line 1162 "upb/json/parser.rl" { {stack[top++] = cs; cs = 19; goto _again;} } break; case 10: -#line 1144 "upb/json/parser.rl" +#line 1164 "upb/json/parser.rl" { p--; {stack[top++] = cs; cs = 27; goto _again;} } break; case 11: -#line 1149 "upb/json/parser.rl" +#line 1169 "upb/json/parser.rl" { start_member(parser); } break; case 12: -#line 1150 "upb/json/parser.rl" +#line 1170 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_membername(parser)); } break; case 13: -#line 1153 "upb/json/parser.rl" +#line 1173 "upb/json/parser.rl" { end_member(parser); } break; case 14: -#line 1159 "upb/json/parser.rl" +#line 1179 "upb/json/parser.rl" { start_object(parser); } break; case 15: -#line 1162 "upb/json/parser.rl" +#line 1182 "upb/json/parser.rl" { end_object(parser); } break; case 16: -#line 1168 "upb/json/parser.rl" +#line 1188 "upb/json/parser.rl" { CHECK_RETURN_TOP(start_array(parser)); } break; case 17: -#line 1172 "upb/json/parser.rl" +#line 1192 "upb/json/parser.rl" { end_array(parser); } break; case 18: -#line 1177 "upb/json/parser.rl" +#line 1197 "upb/json/parser.rl" { start_number(parser, p); } break; case 19: -#line 1178 "upb/json/parser.rl" +#line 1198 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_number(parser, p)); } break; case 20: -#line 1180 "upb/json/parser.rl" +#line 1200 "upb/json/parser.rl" { CHECK_RETURN_TOP(start_stringval(parser)); } break; case 21: -#line 1181 "upb/json/parser.rl" +#line 1201 "upb/json/parser.rl" { CHECK_RETURN_TOP(end_stringval(parser)); } break; case 22: -#line 1183 "upb/json/parser.rl" +#line 1203 "upb/json/parser.rl" { CHECK_RETURN_TOP(parser_putbool(parser, true)); } break; case 23: -#line 1185 "upb/json/parser.rl" +#line 1205 "upb/json/parser.rl" { CHECK_RETURN_TOP(parser_putbool(parser, false)); } break; case 24: -#line 1187 "upb/json/parser.rl" +#line 1207 "upb/json/parser.rl" { /* null value */ } break; case 25: -#line 1189 "upb/json/parser.rl" +#line 1209 "upb/json/parser.rl" { CHECK_RETURN_TOP(start_subobject(parser)); } break; case 26: -#line 1190 "upb/json/parser.rl" +#line 1210 "upb/json/parser.rl" { end_subobject(parser); } break; case 27: -#line 1195 "upb/json/parser.rl" +#line 1215 "upb/json/parser.rl" { p--; {cs = stack[--top]; goto _again;} } break; -#line 1467 "upb/json/parser.c" +#line 1487 "upb/json/parser.c" } } @@ -11136,10 +11084,11 @@ _again: _out: {} } -#line 1222 "upb/json/parser.rl" +#line 1242 "upb/json/parser.rl" if (p != pe) { - upb_status_seterrf(parser->status, "Parse error at %s\n", p); + upb_status_seterrf(&parser->status, "Parse error at %s\n", p); + upb_env_reporterror(parser->env, &parser->status); } else { capture_suspend(parser, &p); } @@ -11176,19 +11125,20 @@ static void json_parser_reset(upb_json_parser *p) { /* Emit Ragel initialization of the parser. */ -#line 1520 "upb/json/parser.c" +#line 1541 "upb/json/parser.c" { cs = json_start; top = 0; } -#line 1261 "upb/json/parser.rl" +#line 1282 "upb/json/parser.rl" p->current_state = cs; p->parser_top = top; accumulate_clear(p); p->multipart_state = MULTIPART_INACTIVE; p->capture = NULL; p->accumulated = NULL; + upb_status_clear(&p->status); } @@ -11214,8 +11164,8 @@ upb_json_parser *upb_json_parser_create(upb_env *env, upb_sink *output) { upb_sink_reset(&p->top->sink, output->handlers, output->closure); p->top->m = upb_handlers_msgdef(output->handlers); - /* If this fails, uncomment and increase the value in parser.h. - * fprintf(stderr, "%zd\n", upb_env_bytesallocated(env) - size_before); */ + /* If this fails, uncomment and increase the value in parser.h. */ + /* fprintf(stderr, "%zd\n", upb_env_bytesallocated(env) - size_before); */ assert(upb_env_bytesallocated(env) - size_before <= UPB_JSON_PARSER_SIZE); return p; } @@ -11224,14 +11174,9 @@ upb_bytessink *upb_json_parser_input(upb_json_parser *p) { return &p->input_; } /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2014 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * This currently uses snprintf() to format primitives, and could be optimized - * further. - */ +** This currently uses snprintf() to format primitives, and could be optimized +** further. +*/ #include diff --git a/ruby/ext/google/protobuf_c/upb.h b/ruby/ext/google/protobuf_c/upb.h index b4dcd558..b31f5c08 100644 --- a/ruby/ext/google/protobuf_c/upb.h +++ b/ruby/ext/google/protobuf_c/upb.h @@ -1,70 +1,62 @@ // Amalgamated source file /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2009-2012 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * Defs are upb's internal representation of the constructs that can appear - * in a .proto file: - * - * - upb_msgdef: describes a "message" construct. - * - upb_fielddef: describes a message field. - * - upb_enumdef: describes an enum. - * (TODO: definitions of services). - * - * Like upb_refcounted objects, defs are mutable only until frozen, and are - * only thread-safe once frozen. - * - * This is a mixed C/C++ interface that offers a full API to both languages. - * See the top-level README for more information. - */ +** Defs are upb's internal representation of the constructs that can appear +** in a .proto file: +** +** - upb::MessageDef (upb_msgdef): describes a "message" construct. +** - upb::FieldDef (upb_fielddef): describes a message field. +** - upb::EnumDef (upb_enumdef): describes an enum. +** - upb::OneofDef (upb_oneofdef): describes a oneof. +** - upb::Def (upb_def): base class of all the others. +** +** TODO: definitions of services. +** +** Like upb_refcounted objects, defs are mutable only until frozen, and are +** only thread-safe once frozen. +** +** This is a mixed C/C++ interface that offers a full API to both languages. +** See the top-level README for more information. +*/ #ifndef UPB_DEF_H_ #define UPB_DEF_H_ /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2009-2012 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * A refcounting scheme that supports circular refs. It accomplishes this by - * partitioning the set of objects into groups such that no cycle spans groups; - * we can then reference-count the group as a whole and ignore refs within the - * group. When objects are mutable, these groups are computed very - * conservatively; we group any objects that have ever had a link between them. - * When objects are frozen, we compute strongly-connected components which - * allows us to be precise and only group objects that are actually cyclic. - * - * This is a mixed C/C++ interface that offers a full API to both languages. - * See the top-level README for more information. - */ +** upb::RefCounted (upb_refcounted) +** +** A refcounting scheme that supports circular refs. It accomplishes this by +** partitioning the set of objects into groups such that no cycle spans groups; +** we can then reference-count the group as a whole and ignore refs within the +** group. When objects are mutable, these groups are computed very +** conservatively; we group any objects that have ever had a link between them. +** When objects are frozen, we compute strongly-connected components which +** allows us to be precise and only group objects that are actually cyclic. +** +** This is a mixed C/C++ interface that offers a full API to both languages. +** See the top-level README for more information. +*/ #ifndef UPB_REFCOUNTED_H_ #define UPB_REFCOUNTED_H_ /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2009 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * This header is INTERNAL-ONLY! Its interfaces are not public or stable! - * This file defines very fast int->upb_value (inttable) and string->upb_value - * (strtable) hash tables. - * - * The table uses chained scatter with Brent's variation (inspired by the Lua - * implementation of hash tables). The hash function for strings is Austin - * Appleby's "MurmurHash." - * - * The inttable uses uintptr_t as its key, which guarantees it can be used to - * store pointers or integers of at least 32 bits (upb isn't really useful on - * systems where sizeof(void*) < 4). - * - * The table must be homogenous (all values of the same type). In debug - * mode, we check this on insert and lookup. - */ +** upb_table +** +** This header is INTERNAL-ONLY! Its interfaces are not public or stable! +** This file defines very fast int->upb_value (inttable) and string->upb_value +** (strtable) hash tables. +** +** The table uses chained scatter with Brent's variation (inspired by the Lua +** implementation of hash tables). The hash function for strings is Austin +** Appleby's "MurmurHash." +** +** The inttable uses uintptr_t as its key, which guarantees it can be used to +** store pointers or integers of at least 32 bits (upb isn't really useful on +** systems where sizeof(void*) < 4). +** +** The table must be homogenous (all values of the same type). In debug +** mode, we check this on insert and lookup. +*/ #ifndef UPB_TABLE_H_ #define UPB_TABLE_H_ @@ -73,16 +65,11 @@ #include #include /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2009 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * This file contains shared definitions that are widely used across upb. - * - * This is a mixed C/C++ interface that offers a full API to both languages. - * See the top-level README for more information. - */ +** This file contains shared definitions that are widely used across upb. +** +** This is a mixed C/C++ interface that offers a full API to both languages. +** See the top-level README for more information. +*/ #ifndef UPB_H_ #define UPB_H_ @@ -3006,25 +2993,20 @@ inline bool OneofDef::const_iterator::operator!=( #endif /* UPB_DEF_H_ */ /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2015 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * This file contains definitions of structs that should be considered private - * and NOT stable across versions of upb. - * - * The only reason they are declared here and not in .c files is to allow upb - * and the application (if desired) to embed statically-initialized instances - * of structures like defs. - * - * If you include this file, all guarantees of ABI compatibility go out the - * window! Any code that includes this file needs to recompile against the - * exact same version of upb that they are linking against. - * - * You also need to recompile if you change the value of the UPB_DEBUG_REFS - * flag. - */ +** This file contains definitions of structs that should be considered private +** and NOT stable across versions of upb. +** +** The only reason they are declared here and not in .c files is to allow upb +** and the application (if desired) to embed statically-initialized instances +** of structures like defs. +** +** If you include this file, all guarantees of ABI compatibility go out the +** window! Any code that includes this file needs to recompile against the +** exact same version of upb that they are linking against. +** +** You also need to recompile if you change the value of the UPB_DEBUG_REFS +** flag. +*/ #ifndef UPB_STATICINIT_H_ @@ -3181,25 +3163,22 @@ struct upb_symtab { #endif /* UPB_STATICINIT_H_ */ /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2010-2012 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * A upb_handlers is like a virtual table for a upb_msgdef. Each field of the - * message can have associated functions that will be called when we are - * parsing or visiting a stream of data. This is similar to how handlers work - * in SAX (the Simple API for XML). - * - * The handlers have no idea where the data is coming from, so a single set of - * handlers could be used with two completely different data sources (for - * example, a parser and a visitor over in-memory objects). This decoupling is - * the most important feature of upb, because it allows parsers and serializers - * to be highly reusable. - * - * This is a mixed C/C++ interface that offers a full API to both languages. - * See the top-level README for more information. - */ +** upb::Handlers (upb_handlers) +** +** A upb_handlers is like a virtual table for a upb_msgdef. Each field of the +** message can have associated functions that will be called when we are +** parsing or visiting a stream of data. This is similar to how handlers work +** in SAX (the Simple API for XML). +** +** The handlers have no idea where the data is coming from, so a single set of +** handlers could be used with two completely different data sources (for +** example, a parser and a visitor over in-memory objects). This decoupling is +** the most important feature of upb, because it allows parsers and serializers +** to be highly reusable. +** +** This is a mixed C/C++ interface that offers a full API to both languages. +** See the top-level README for more information. +*/ #ifndef UPB_HANDLERS_H #define UPB_HANDLERS_H @@ -3980,14 +3959,9 @@ uint32_t upb_handlers_selectorcount(const upb_fielddef *f); UPB_END_EXTERN_C /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2011-2012 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * Inline definitions for handlers.h, which are particularly long and a bit - * tricky. - */ +** Inline definitions for handlers.h, which are particularly long and a bit +** tricky. +*/ #ifndef UPB_HANDLERS_INL_H_ #define UPB_HANDLERS_INL_H_ @@ -5128,21 +5102,18 @@ inline BytesHandler::~BytesHandler() {} #endif /* UPB_HANDLERS_H */ /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2014 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * A upb::Environment provides a means for injecting malloc and an - * error-reporting callback into encoders/decoders. This allows them to be - * independent of nearly all assumptions about their actual environment. - * - * It is also a container for allocating the encoders/decoders themselves that - * insulates clients from knowing their actual size. This provides ABI - * compatibility even if the size of the objects change. And this allows the - * structure definitions to be in the .c files instead of the .h files, making - * the .h files smaller and more readable. - */ +** upb::Environment (upb_env) +** +** A upb::Environment provides a means for injecting malloc and an +** error-reporting callback into encoders/decoders. This allows them to be +** independent of nearly all assumptions about their actual environment. +** +** It is also a container for allocating the encoders/decoders themselves that +** insulates clients from knowing their actual size. This provides ABI +** compatibility even if the size of the objects change. And this allows the +** structure definitions to be in the .c files instead of the .h files, making +** the .h files smaller and more readable. +*/ #ifndef UPB_ENV_H_ @@ -5392,23 +5363,21 @@ inline upb_alloc_func *SeededAllocator::GetAllocationFunction() { #endif /* UPB_ENV_H_ */ /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2010-2012 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * A upb_sink is an object that binds a upb_handlers object to some runtime - * state. It is the object that can actually receive data via the upb_handlers - * interface. - * - * Unlike upb_def and upb_handlers, upb_sink is never frozen, immutable, or - * thread-safe. You can create as many of them as you want, but each one may - * only be used in a single thread at a time. - * - * If we compare with class-based OOP, a you can think of a upb_def as an - * abstract base class, a upb_handlers as a concrete derived class, and a - * upb_sink as an object (class instance). - */ +** upb::Sink (upb_sink) +** upb::BytesSink (upb_bytessink) +** +** A upb_sink is an object that binds a upb_handlers object to some runtime +** state. It is the object that can actually receive data via the upb_handlers +** interface. +** +** Unlike upb_def and upb_handlers, upb_sink is never frozen, immutable, or +** thread-safe. You can create as many of them as you want, but each one may +** only be used in a single thread at a time. +** +** If we compare with class-based OOP, a you can think of a upb_def as an +** abstract base class, a upb_handlers as a concrete derived class, and a +** upb_sink as an object (class instance). +*/ #ifndef UPB_SINK_H #define UPB_SINK_H @@ -5921,21 +5890,16 @@ inline bool BufferSource::PutBuffer(const char *buf, size_t len, #endif /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2013 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * For handlers that do very tiny, very simple operations, the function call - * overhead of calling a handler can be significant. This file allows the - * user to define handlers that do something very simple like store the value - * to memory and/or set a hasbit. JIT compilers can then special-case these - * handlers and emit specialized code for them instead of actually calling the - * handler. - * - * The functionality is very simple/limited right now but may expand to be able - * to call another function. - */ +** For handlers that do very tiny, very simple operations, the function call +** overhead of calling a handler can be significant. This file allows the +** user to define handlers that do something very simple like store the value +** to memory and/or set a hasbit. JIT compilers can then special-case these +** handlers and emit specialized code for them instead of actually calling the +** handler. +** +** The functionality is very simple/limited right now but may expand to be able +** to call another function. +*/ #ifndef UPB_SHIM_H #define UPB_SHIM_H @@ -5994,19 +5958,16 @@ inline const Shim::Data* Shim::GetData(const Handlers* h, Handlers::Selector s, #endif /* UPB_SHIM_H */ /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2009-2012 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * A symtab (symbol table) stores a name->def map of upb_defs. Clients could - * always create such tables themselves, but upb_symtab has logic for resolving - * symbolic references, and in particular, for keeping a whole set of consistent - * defs when replacing some subset of those defs. This logic is nontrivial. - * - * This is a mixed C/C++ interface that offers a full API to both languages. - * See the top-level README for more information. - */ +** upb::SymbolTable (upb_symtab) +** +** A symtab (symbol table) stores a name->def map of upb_defs. Clients could +** always create such tables themselves, but upb_symtab has logic for resolving +** symbolic references, and in particular, for keeping a whole set of consistent +** defs when replacing some subset of those defs. This logic is nontrivial. +** +** This is a mixed C/C++ interface that offers a full API to both languages. +** See the top-level README for more information. +*/ #ifndef UPB_SYMTAB_H_ #define UPB_SYMTAB_H_ @@ -6182,14 +6143,10 @@ inline bool SymbolTable::Add( #endif /* UPB_SYMTAB_H_ */ /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2011 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * upb::descriptor::Reader provides a way of building upb::Defs from - * data in descriptor.proto format. - */ +** upb::descriptor::Reader (upb_descreader) +** +** Provides a way of building upb::Defs from data in descriptor.proto format. +*/ #ifndef UPB_DESCRIPTOR_H #define UPB_DESCRIPTOR_H @@ -7067,34 +7024,26 @@ inline upb::reffed_ptr name_part() { RETURN_REFFED(upb::Fie #endif /* GOOGLE_PROTOBUF_DESCRIPTOR_UPB_H_ */ /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2009-2014 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * Internal-only definitions for the decoder. - */ +** Internal-only definitions for the decoder. +*/ #ifndef UPB_DECODER_INT_H_ #define UPB_DECODER_INT_H_ #include /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2009-2014 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * upb::pb::Decoder implements a high performance, streaming, resumable decoder - * for the binary protobuf format. - * - * This interface works the same regardless of what decoder backend is being - * used. A client of this class does not need to know whether decoding is using - * a JITted decoder (DynASM, LLVM, etc) or an interpreted decoder. By default, - * it will always use the fastest available decoder. However, you can call - * set_allow_jit(false) to disable any JIT decoder that might be available. - * This is primarily useful for testing purposes. - */ +** upb::pb::Decoder +** +** A high performance, streaming, resumable decoder for the binary protobuf +** format. +** +** This interface works the same regardless of what decoder backend is being +** used. A client of this class does not need to know whether decoding is using +** a JITted decoder (DynASM, LLVM, etc) or an interpreted decoder. By default, +** it will always use the fastest available decoder. However, you can call +** set_allow_jit(false) to disable any JIT decoder that might be available. +** This is primarily useful for testing purposes. +*/ #ifndef UPB_DECODER_H_ #define UPB_DECODER_H_ @@ -7702,14 +7651,9 @@ UPB_INLINE void upb_pbdecoder_unpackdispatch(uint64_t dispatch, uint64_t *ofs, #endif /* UPB_DECODER_INT_H_ */ /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2011 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * A number of routines for varint manipulation (we keep them all around to - * have multiple approaches available for benchmarking). - */ +** A number of routines for varint manipulation (we keep them all around to +** have multiple approaches available for benchmarking). +*/ #ifndef UPB_VARINT_DECODER_H_ #define UPB_VARINT_DECODER_H_ @@ -7873,18 +7817,15 @@ UPB_INLINE uint64_t upb_vencode32(uint32_t val) { #endif /* UPB_VARINT_DECODER_H_ */ /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2009-2010 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * Implements a set of upb_handlers that write protobuf data to the binary wire - * format. - * - * This encoder implementation does not have any access to any out-of-band or - * precomputed lengths for submessages, so it must buffer submessages internally - * before it can emit the first byte. - */ +** upb::pb::Encoder (upb_pb_encoder) +** +** Implements a set of upb_handlers that write protobuf data to the binary wire +** format. +** +** This encoder implementation does not have any access to any out-of-band or +** precomputed lengths for submessages, so it must buffer submessages internally +** before it can emit the first byte. +*/ #ifndef UPB_ENCODER_H_ #define UPB_ENCODER_H_ @@ -7966,29 +7907,24 @@ inline reffed_ptr Encoder::NewHandlers( #endif /* UPB_ENCODER_H_ */ /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2011-2012 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * upb's core components like upb_decoder and upb_msg are carefully designed to - * avoid depending on each other for maximum orthogonality. In other words, - * you can use a upb_decoder to decode into *any* kind of structure; upb_msg is - * just one such structure. A upb_msg can be serialized/deserialized into any - * format, protobuf binary format is just one such format. - * - * However, for convenience we provide functions here for doing common - * operations like deserializing protobuf binary format into a upb_msg. The - * compromise is that this file drags in almost all of upb as a dependency, - * which could be undesirable if you're trying to use a trimmed-down build of - * upb. - * - * While these routines are convenient, they do not reuse any encoding/decoding - * state. For example, if a decoder is JIT-based, it will be re-JITted every - * time these functions are called. For this reason, if you are parsing lots - * of data and efficiency is an issue, these may not be the best functions to - * use (though they are useful for prototyping, before optimizing). - */ +** upb's core components like upb_decoder and upb_msg are carefully designed to +** avoid depending on each other for maximum orthogonality. In other words, +** you can use a upb_decoder to decode into *any* kind of structure; upb_msg is +** just one such structure. A upb_msg can be serialized/deserialized into any +** format, protobuf binary format is just one such format. +** +** However, for convenience we provide functions here for doing common +** operations like deserializing protobuf binary format into a upb_msg. The +** compromise is that this file drags in almost all of upb as a dependency, +** which could be undesirable if you're trying to use a trimmed-down build of +** upb. +** +** While these routines are convenient, they do not reuse any encoding/decoding +** state. For example, if a decoder is JIT-based, it will be re-JITted every +** time these functions are called. For this reason, if you are parsing lots +** of data and efficiency is an issue, these may not be the best functions to +** use (though they are useful for prototyping, before optimizing). +*/ #ifndef UPB_GLUE_H #define UPB_GLUE_H @@ -8047,11 +7983,10 @@ bool LoadDescriptorIntoSymtab(SymbolTable* s, const T& desc, Status* status) { #endif /* UPB_GLUE_H */ /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2009 Google Inc. See LICENSE for details. - * Author: Josh Haberman - */ +** upb::pb::TextPrinter (upb_textprinter) +** +** Handlers for writing to protobuf text format. +*/ #ifndef UPB_TEXT_H_ #define UPB_TEXT_H_ @@ -8127,14 +8062,11 @@ inline reffed_ptr TextPrinter::NewHandlers( #endif /* UPB_TEXT_H_ */ /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2014 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * upb::json::Parser can parse JSON according to a specific schema. - * Support for parsing arbitrary JSON (schema-less) will be added later. - */ +** upb::json::Parser (upb_json_parser) +** +** Parses JSON according to a specific schema. +** Support for parsing arbitrary JSON (schema-less) will be added later. +*/ #ifndef UPB_JSON_PARSER_H_ #define UPB_JSON_PARSER_H_ @@ -8156,7 +8088,7 @@ UPB_DECLARE_TYPE(upb::json::Parser, upb_json_parser) * constructed. This hint may be an overestimate for some build configurations. * But if the parser library is upgraded without recompiling the application, * it may be an underestimate. */ -#define UPB_JSON_PARSER_SIZE 3568 +#define UPB_JSON_PARSER_SIZE 3704 #ifdef __cplusplus @@ -8199,14 +8131,10 @@ inline BytesSink* Parser::input() { #endif /* UPB_JSON_PARSER_H_ */ /* - * upb - a minimalist implementation of protocol buffers. - * - * Copyright (c) 2014 Google Inc. See LICENSE for details. - * Author: Josh Haberman - * - * upb::json::Printer allows you to create handlers that emit JSON - * according to a specific protobuf schema. - */ +** upb::json::Printer +** +** Handlers that emit JSON according to a specific protobuf schema. +*/ #ifndef UPB_JSON_TYPED_PRINTER_H_ #define UPB_JSON_TYPED_PRINTER_H_ diff --git a/ruby/lib/google/protobuf.rb b/ruby/lib/google/protobuf.rb index 99b17929..74ea770d 100644 --- a/ruby/lib/google/protobuf.rb +++ b/ruby/lib/google/protobuf.rb @@ -31,6 +31,15 @@ # require mixins before we hook them into the java & c code require 'google/protobuf/message_exts' +# We define these before requiring the platform-specific modules. +# That way the module init can grab references to these. +module Google + module Protobuf + class Error < RuntimeError; end + class ParseError < Error; end + end +end + if RUBY_PLATFORM == "java" require 'json' require 'google/protobuf_java' diff --git a/ruby/travis-test.sh b/ruby/travis-test.sh index a240dd65..4a2536a5 100755 --- a/ruby/travis-test.sh +++ b/ruby/travis-test.sh @@ -9,7 +9,9 @@ test_version() { "rvm install $version && rvm use $version && \ which ruby && \ gem install bundler && bundle && \ - rake test" + rake test && \ + cd ../conformance && \ + make test_ruby" } test_version $1 diff --git a/src/google/protobuf/compiler/ruby/ruby_generated_code.rb b/src/google/protobuf/compiler/ruby/ruby_generated_code.rb index 100d6fa7..49b23fbe 100644 --- a/src/google/protobuf/compiler/ruby/ruby_generated_code.rb +++ b/src/google/protobuf/compiler/ruby/ruby_generated_code.rb @@ -13,7 +13,7 @@ Google::Protobuf::DescriptorPool.generated_pool.build do optional :optional_double, :double, 6 optional :optional_float, :float, 7 optional :optional_string, :string, 8 - optional :optional_bytes, :string, 9 + optional :optional_bytes, :bytes, 9 optional :optional_enum, :enum, 10, "A.B.C.TestEnum" optional :optional_msg, :message, 11, "A.B.C.TestMessage" repeated :repeated_int32, :int32, 21 @@ -24,7 +24,7 @@ Google::Protobuf::DescriptorPool.generated_pool.build do repeated :repeated_double, :double, 26 repeated :repeated_float, :float, 27 repeated :repeated_string, :string, 28 - repeated :repeated_bytes, :string, 29 + repeated :repeated_bytes, :bytes, 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 @@ -47,7 +47,7 @@ Google::Protobuf::DescriptorPool.generated_pool.build do optional :oneof_double, :double, 46 optional :oneof_float, :float, 47 optional :oneof_string, :string, 48 - optional :oneof_bytes, :string, 49 + optional :oneof_bytes, :bytes, 49 optional :oneof_enum, :enum, 50, "A.B.C.TestEnum" optional :oneof_msg, :message, 51, "A.B.C.TestMessage" end diff --git a/src/google/protobuf/compiler/ruby/ruby_generator.cc b/src/google/protobuf/compiler/ruby/ruby_generator.cc index a9b6837e..9692f1bf 100644 --- a/src/google/protobuf/compiler/ruby/ruby_generator.cc +++ b/src/google/protobuf/compiler/ruby/ruby_generator.cc @@ -47,7 +47,7 @@ namespace compiler { namespace ruby { // Forward decls. -std::string IntToString(uint32 value); +std::string IntToString(int32 value); std::string StripDotProto(const std::string& proto_file); std::string LabelForField(google::protobuf::FieldDescriptor* field); std::string TypeName(google::protobuf::FieldDescriptor* field); @@ -64,7 +64,7 @@ void GenerateEnumAssignment( const google::protobuf::EnumDescriptor* en, google::protobuf::io::Printer* printer); -std::string IntToString(uint32 value) { +std::string IntToString(int32 value) { std::ostringstream os; os << value; return os.str(); @@ -85,17 +85,25 @@ std::string LabelForField(const google::protobuf::FieldDescriptor* field) { } std::string TypeName(const google::protobuf::FieldDescriptor* field) { - switch (field->cpp_type()) { - case FieldDescriptor::CPPTYPE_INT32: return "int32"; - case FieldDescriptor::CPPTYPE_INT64: return "int64"; - case FieldDescriptor::CPPTYPE_UINT32: return "uint32"; - case FieldDescriptor::CPPTYPE_UINT64: return "uint64"; - case FieldDescriptor::CPPTYPE_DOUBLE: return "double"; - case FieldDescriptor::CPPTYPE_FLOAT: return "float"; - case FieldDescriptor::CPPTYPE_BOOL: return "bool"; - case FieldDescriptor::CPPTYPE_ENUM: return "enum"; - case FieldDescriptor::CPPTYPE_STRING: return "string"; - case FieldDescriptor::CPPTYPE_MESSAGE: return "message"; + switch (field->type()) { + case FieldDescriptor::TYPE_INT32: return "int32"; + case FieldDescriptor::TYPE_INT64: return "int64"; + case FieldDescriptor::TYPE_UINT32: return "uint32"; + case FieldDescriptor::TYPE_UINT64: return "uint64"; + case FieldDescriptor::TYPE_SINT32: return "sint32"; + case FieldDescriptor::TYPE_SINT64: return "sint64"; + case FieldDescriptor::TYPE_FIXED32: return "fixed32"; + case FieldDescriptor::TYPE_FIXED64: return "fixed64"; + case FieldDescriptor::TYPE_SFIXED32: return "sfixed32"; + case FieldDescriptor::TYPE_SFIXED64: return "sfixed64"; + case FieldDescriptor::TYPE_DOUBLE: return "double"; + case FieldDescriptor::TYPE_FLOAT: return "float"; + case FieldDescriptor::TYPE_BOOL: return "bool"; + case FieldDescriptor::TYPE_ENUM: return "enum"; + case FieldDescriptor::TYPE_STRING: return "string"; + case FieldDescriptor::TYPE_BYTES: return "bytes"; + case FieldDescriptor::TYPE_MESSAGE: return "message"; + case FieldDescriptor::TYPE_GROUP: return "group"; default: assert(false); return ""; } } diff --git a/travis.sh b/travis.sh index 4aa67344..9514ec2b 100755 --- a/travis.sh +++ b/travis.sh @@ -8,10 +8,16 @@ # .travis.yml uses matrix.exclude to block the cases where app-get can't be # use to install things. -build_cpp() { +# For when some other test needs the C++ main build, including protoc and +# libprotobuf. +internal_build_cpp() { ./autogen.sh ./configure make -j2 +} + +build_cpp() { + internal_build_cpp make check -j2 cd conformance && make test_cpp && cd .. } @@ -62,18 +68,14 @@ use_java() { build_java() { # Java build needs `protoc`. - ./autogen.sh - ./configure - make -j2 + internal_build_cpp cd java && mvn test && cd .. cd conformance && make test_java && cd .. } build_javanano() { # Java build needs `protoc`. - ./autogen.sh - ./configure - make -j2 + internal_build_cpp cd javanano && mvn test && cd .. } @@ -104,9 +106,7 @@ build_javanano_oracle7() { } build_python() { - ./autogen.sh - ./configure - make -j2 + internal_build_cpp cd python python setup.py build python setup.py test @@ -116,9 +116,7 @@ build_python() { } build_python_cpp() { - ./autogen.sh - ./configure - make -j2 + internal_build_cpp export LD_LIBRARY_PATH=../src/.libs # for Linux export DYLD_LIBRARY_PATH=../src/.libs # for OS X cd python @@ -130,18 +128,23 @@ build_python_cpp() { } build_ruby19() { + internal_build_cpp # For conformance tests. cd ruby && bash travis-test.sh ruby-1.9 && cd .. } build_ruby20() { + internal_build_cpp # For conformance tests. cd ruby && bash travis-test.sh ruby-2.0 && cd .. } build_ruby21() { + internal_build_cpp # For conformance tests. cd ruby && bash travis-test.sh ruby-2.1 && cd .. } build_ruby22() { + internal_build_cpp # For conformance tests. cd ruby && bash travis-test.sh ruby-2.2 && cd .. } build_jruby() { + internal_build_cpp # For conformance tests. cd ruby && bash travis-test.sh jruby && cd .. } -- cgit v1.2.3 From 95ee8fb88ec8482cf64a2e564227dcb662ebcfa6 Mon Sep 17 00:00:00 2001 From: Josh Haberman Date: Fri, 17 Jul 2015 16:20:01 -0700 Subject: Exclude JRuby from conformance tests for now. Change-Id: Id008ebac5159f773e1bde8b85acb2626cbd16de8 --- ruby/travis-test.sh | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) (limited to 'ruby') diff --git a/ruby/travis-test.sh b/ruby/travis-test.sh index 4a2536a5..9ec7eb22 100755 --- a/ruby/travis-test.sh +++ b/ruby/travis-test.sh @@ -5,13 +5,22 @@ set -e test_version() { version=$1 - bash --login -c \ - "rvm install $version && rvm use $version && \ - which ruby && \ - gem install bundler && bundle && \ - rake test && \ - cd ../conformance && \ - make test_ruby" + if [ "$version" == "jruby" ] ; then + # No conformance tests yet -- JRuby is too broken to run them. + bash --login -c \ + "rvm install $version && rvm use $version && \ + which ruby && \ + gem install bundler && bundle && \ + rake test" + else + bash --login -c \ + "rvm install $version && rvm use $version && \ + which ruby && \ + gem install bundler && bundle && \ + rake test && \ + cd ../conformance && \ + make test_ruby" + fi } test_version $1 -- cgit v1.2.3 From c2c43a4917d7da263e360a4096d6603da8535bdf Mon Sep 17 00:00:00 2001 From: Josh Haberman Date: Fri, 17 Jul 2015 16:29:10 -0700 Subject: Fixed lint errors and responded to CR comments. Change-Id: If7b1cc0f03f609a7f43ddafc8509b44207c60910 --- conformance/conformance_ruby.rb | 55 ++++++++++++++++++++++------------------- ruby/lib/google/protobuf.rb | 2 +- 2 files changed, 30 insertions(+), 27 deletions(-) (limited to 'ruby') diff --git a/conformance/conformance_ruby.rb b/conformance/conformance_ruby.rb index e7bd4ed5..cd065673 100755 --- a/conformance/conformance_ruby.rb +++ b/conformance/conformance_ruby.rb @@ -32,8 +32,8 @@ require 'conformance' -test_count = 0; -verbose = false; +$test_count = 0 +$verbose = false def do_test(request) test_message = Conformance::TestAllTypes.new @@ -43,9 +43,10 @@ def do_test(request) case request.payload when :protobuf_payload begin - test_message = Conformance::TestAllTypes.decode(request.protobuf_payload) + test_message = + Conformance::TestAllTypes.decode(request.protobuf_payload) rescue Google::Protobuf::ParseError => err - response.parse_error = err.message.encode("utf-8") + response.parse_error = err.message.encode('utf-8') return response end @@ -53,34 +54,36 @@ def do_test(request) test_message = Conformance::TestAllTypes.decode_json(request.json_payload) when nil - raise "Request didn't have payload."; + fail "Request didn't have payload" end case request.requested_output_format when :UNSPECIFIED - raise "Unspecified output format" + fail 'Unspecified output format' when :PROTOBUF - response.protobuf_payload = Conformance::TestAllTypes.encode(test_message) + response.protobuf_payload = test_message.to_proto when :JSON - response.json_payload = Conformance::TestAllTypes.encode_json(test_message) + response.json_payload = test_message.to_json end - rescue Exception => err - response.runtime_error = err.message.encode("utf-8") + err.backtrace.join("\n") + rescue StandardError => err + response.runtime_error = err.message.encode('utf-8') end - return response + response end +# Returns true if the test ran successfully, false on legitimate EOF. +# If EOF is encountered in an unexpected place, raises IOError. def do_test_io length_bytes = STDIN.read(4) return false if length_bytes.nil? - length = length_bytes.unpack("V").first + length = length_bytes.unpack('V').first serialized_request = STDIN.read(length) - if serialized_request.nil? or serialized_request.length != length - raise "I/O error" + if serialized_request.nil? || serialized_request.length != length + fail IOError end request = Conformance::ConformanceRequest.decode(serialized_request) @@ -88,24 +91,24 @@ def do_test_io response = do_test(request) serialized_response = Conformance::ConformanceResponse.encode(response) - STDOUT.write([serialized_response.length].pack("V")) + STDOUT.write([serialized_response.length].pack('V')) STDOUT.write(serialized_response) STDOUT.flush - #if verbose - # fprintf(stderr, "conformance-cpp: request=%s, response=%s\n", - # request.ShortDebugString().c_str(), - # response.ShortDebugString().c_str()); + if $verbose + STDERR.puts("conformance-cpp: request={request.to_json}, " \ + "response={response.to_json}\n") + end - #test_count++; + $test_count += 1 - return true; + true end -while true - if not do_test_io() - STDERR.puts("conformance-cpp: received EOF from test runner " + - "after #{test_count} tests, exiting") - exit 0 +loop do + unless do_test_io + STDERR.puts('conformance-cpp: received EOF from test runner ' \ + "after #{$test_count} tests, exiting") + break end end diff --git a/ruby/lib/google/protobuf.rb b/ruby/lib/google/protobuf.rb index 74ea770d..f0eb6268 100644 --- a/ruby/lib/google/protobuf.rb +++ b/ruby/lib/google/protobuf.rb @@ -35,7 +35,7 @@ require 'google/protobuf/message_exts' # That way the module init can grab references to these. module Google module Protobuf - class Error < RuntimeError; end + class Error < StandardError; end class ParseError < Error; end end end -- cgit v1.2.3