diff --git a/configure.ac b/configure.ac index f2ea0f3..9185c62 100644 --- a/configure.ac +++ b/configure.ac @@ -43,11 +43,17 @@ if test -n "$PKG_CONFIG"; then fi fi +proto3_supported="no" + AC_ARG_ENABLE([protoc], AS_HELP_STRING([--disable-protoc], [Disable building protoc_c (also disables tests)])) if test "x$enable_protoc" != "xno"; then AC_LANG_PUSH([C++]) - PKG_CHECK_MODULES([protobuf], [protobuf >= 2.6.0]) + + PKG_CHECK_MODULES([protobuf], [protobuf >= 3.0.0], + [proto3_supported=yes], + [PKG_CHECK_MODULES([protobuf], [protobuf >= 2.6.0])] + ) save_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$save_CPPFLAGS $protobuf_CFLAGS" @@ -62,13 +68,19 @@ if test "x$enable_protoc" != "xno"; then if test -z "$PROTOC"; then AC_MSG_ERROR([Please install the protobuf compiler from https://code.google.com/p/protobuf/.]) fi + PROTOBUF_VERSION="$($PROTOC --version)" + else PROTOBUF_VERSION="not required, not building compiler" fi + AM_CONDITIONAL([BUILD_COMPILER], [test "x$enable_protoc" != "xno"]) +AM_CONDITIONAL([BUILD_PROTO3], [test "x$proto3_supported" != "xno"]) AM_CONDITIONAL([CROSS_COMPILING], [test "x$cross_compiling" != "xno"]) +AM_COND_IF([BUILD_PROTO3], [AC_DEFINE([HAVE_PROTO3], [1], [Support proto3 syntax])]) + gl_LD_VERSION_SCRIPT gl_VALGRIND_TESTS diff --git a/protobuf-c/protobuf-c.c b/protobuf-c/protobuf-c.c index 2b74b48..c29502b 100644 --- a/protobuf-c/protobuf-c.c +++ b/protobuf-c/protobuf-c.c @@ -507,7 +507,7 @@ oneof_field_get_packed_size(const ProtobufCFieldDescriptor *field, */ static size_t optional_field_get_packed_size(const ProtobufCFieldDescriptor *field, - const protobuf_c_boolean *has, + const protobuf_c_boolean has, const void *member) { if (field->type == PROTOBUF_C_TYPE_MESSAGE || @@ -517,12 +517,36 @@ optional_field_get_packed_size(const ProtobufCFieldDescriptor *field, if (ptr == NULL || ptr == field->default_value) return 0; } else { - if (!*has) + if (!has) return 0; } return required_field_get_packed_size(field, member); } +/** + * Calculate the serialized size of a single unlabeled message field, including + * the space needed by the preceding tag. Returns 0 if the field isn't set or + * if it is set to a "zeroish" value (null pointer or 0 for numerical values). + * Unlabeled fields are supported only in proto3. + * + * \param field + * Field descriptor for member. + * \param member + * Field to encode. + * \return + * Number of bytes required. + */ +static size_t +unlabeled_field_get_packed_size(const ProtobufCFieldDescriptor *field, + const void *member) +{ + const void *ptr = *(const void * const *) member; + if (ptr == NULL) { + return 0; + } + return required_field_get_packed_size(field, member); +} + /** * Calculate the serialized size of repeated message fields, which may consist * of any number of values (including 0). Includes the space needed by the @@ -651,11 +675,21 @@ size_t protobuf_c_message_get_packed_size(const ProtobufCMessage *message) if (field->label == PROTOBUF_C_LABEL_REQUIRED) { rv += required_field_get_packed_size(field, member); + } else if ((field->label == PROTOBUF_C_LABEL_OPTIONAL || + field->label == PROTOBUF_C_LABEL_NONE) && + (0 != (field->flags & PROTOBUF_C_FIELD_FLAG_ONEOF))) { + rv += oneof_field_get_packed_size(field, qmember, member); } else if (field->label == PROTOBUF_C_LABEL_OPTIONAL) { - if (0 != (field->flags & PROTOBUF_C_FIELD_FLAG_ONEOF)) - rv += oneof_field_get_packed_size(field, qmember, member); - else - rv += optional_field_get_packed_size(field, qmember, member); + rv += optional_field_get_packed_size( + field, + *(protobuf_c_boolean *) qmember, + member + ); + } else if (field->label == PROTOBUF_C_LABEL_NONE) { + rv += unlabeled_field_get_packed_size( + field, + member + ); } else { rv += repeated_field_get_packed_size( field, @@ -1102,7 +1136,7 @@ oneof_field_pack(const ProtobufCFieldDescriptor *field, */ static size_t optional_field_pack(const ProtobufCFieldDescriptor *field, - const protobuf_c_boolean *has, + const protobuf_c_boolean has, const void *member, uint8_t *out) { if (field->type == PROTOBUF_C_TYPE_MESSAGE || @@ -1112,12 +1146,35 @@ optional_field_pack(const ProtobufCFieldDescriptor *field, if (ptr == NULL || ptr == field->default_value) return 0; } else { - if (!*has) + if (!has) return 0; } return required_field_pack(field, member, out); } +/** + * Pack an unlabeled field and return the number of bytes written. + * + * \param field + * Field descriptor. + * \param member + * The field member. + * \param[out] out + * Packed value. + * \return + * Number of bytes written to `out`. + */ +static size_t +unlabeled_field_pack(const ProtobufCFieldDescriptor *field, + const void *member, uint8_t *out) +{ + const void *ptr = *(const void * const *) member; + if (ptr == NULL) { + return 0; + } + return required_field_pack(field, member, out); +} + /** * Given a field type, return the in-memory size. * @@ -1389,11 +1446,19 @@ protobuf_c_message_pack(const ProtobufCMessage *message, uint8_t *out) if (field->label == PROTOBUF_C_LABEL_REQUIRED) { rv += required_field_pack(field, member, out + rv); + } else if ((field->label == PROTOBUF_C_LABEL_OPTIONAL || + field->label == PROTOBUF_C_LABEL_NONE) && + (0 != (field->flags & PROTOBUF_C_FIELD_FLAG_ONEOF))) { + rv += oneof_field_pack (field, qmember, member, out + rv); } else if (field->label == PROTOBUF_C_LABEL_OPTIONAL) { - if (0 != (field->flags & PROTOBUF_C_FIELD_FLAG_ONEOF)) - rv += oneof_field_pack (field, qmember, member, out + rv); - else - rv += optional_field_pack(field, qmember, member, out + rv); + rv += optional_field_pack( + field, + *(const protobuf_c_boolean *) qmember, + member, + out + rv + ); + } else if (field->label == PROTOBUF_C_LABEL_NONE) { + rv += unlabeled_field_pack(field, member, out + rv); } else { rv += repeated_field_pack(field, *(const size_t *) qmember, member, out + rv); @@ -1576,7 +1641,7 @@ oneof_field_pack_to_buffer(const ProtobufCFieldDescriptor *field, */ static size_t optional_field_pack_to_buffer(const ProtobufCFieldDescriptor *field, - const protobuf_c_boolean *has, + const protobuf_c_boolean has, const void *member, ProtobufCBuffer *buffer) { if (field->type == PROTOBUF_C_TYPE_MESSAGE || @@ -1586,12 +1651,35 @@ optional_field_pack_to_buffer(const ProtobufCFieldDescriptor *field, if (ptr == NULL || ptr == field->default_value) return 0; } else { - if (!*has) + if (!has) return 0; } return required_field_pack_to_buffer(field, member, buffer); } +/** + * Pack an unlabeled field to a buffer. + * + * \param field + * Field descriptor. + * \param member + * The element to be packed. + * \param[out] buffer + * Virtual buffer to append data to. + * \return + * Number of bytes serialised to `buffer`. + */ +static size_t +unlabeled_field_pack_to_buffer(const ProtobufCFieldDescriptor *field, + const void *member, ProtobufCBuffer *buffer) +{ + const void *ptr = *(const void *const *) member; + if (ptr == NULL) { + return 0; + } + return required_field_pack_to_buffer(field, member, buffer); +} + /** * Get the packed size of an array of same field type. * @@ -1837,22 +1925,28 @@ protobuf_c_message_pack_to_buffer(const ProtobufCMessage *message, if (field->label == PROTOBUF_C_LABEL_REQUIRED) { rv += required_field_pack_to_buffer(field, member, buffer); + } else if ((field->label == PROTOBUF_C_LABEL_OPTIONAL || + field->label == PROTOBUF_C_LABEL_NONE) && + (0 != (field->flags & PROTOBUF_C_FIELD_FLAG_ONEOF))) { + rv += oneof_field_pack_to_buffer( + field, + qmember, + member, + buffer + ); } else if (field->label == PROTOBUF_C_LABEL_OPTIONAL) { - if (0 != (field->flags & PROTOBUF_C_FIELD_FLAG_ONEOF)) { - rv += oneof_field_pack_to_buffer( - field, - qmember, - member, - buffer - ); - } else { - rv += optional_field_pack_to_buffer( - field, - qmember, - member, - buffer - ); - } + rv += optional_field_pack_to_buffer( + field, + *(const protobuf_c_boolean *) qmember, + member, + buffer + ); + } else if (field->label == PROTOBUF_C_LABEL_NONE) { + rv += unlabeled_field_pack_to_buffer( + field, + member, + buffer + ); } else { rv += repeated_field_pack_to_buffer( field, @@ -2070,7 +2164,8 @@ merge_messages(ProtobufCMessage *earlier_msg, *n_earlier = 0; *p_earlier = 0; } - } else if (fields[i].label == PROTOBUF_C_LABEL_OPTIONAL) { + } else if (fields[i].label == PROTOBUF_C_LABEL_OPTIONAL || + fields[i].label == PROTOBUF_C_LABEL_NONE) { // FIXME to check const ProtobufCFieldDescriptor *field; uint32_t *earlier_case_p = STRUCT_MEMBER_PTR(uint32_t, earlier_msg, @@ -2744,6 +2839,7 @@ parse_member(ScannedMember *scanned_member, return parse_required_member(scanned_member, member, allocator, TRUE); case PROTOBUF_C_LABEL_OPTIONAL: + case PROTOBUF_C_LABEL_NONE: if (0 != (field->flags & PROTOBUF_C_FIELD_FLAG_ONEOF)) { return parse_oneof_member(scanned_member, member, message, allocator); diff --git a/protobuf-c/protobuf-c.h b/protobuf-c/protobuf-c.h index 55cce8d..b25db8d 100644 --- a/protobuf-c/protobuf-c.h +++ b/protobuf-c/protobuf-c.h @@ -284,6 +284,12 @@ typedef enum { * preserved. */ PROTOBUF_C_LABEL_REPEATED, + + /** + * This field has no label. This is valid only in proto3 and is + * equivalent to OPTIONAL but no "has" quantifier will be consulted. + */ + PROTOBUF_C_LABEL_NONE, } ProtobufCLabel; /** diff --git a/protoc-c/c_bytes_field.cc b/protoc-c/c_bytes_field.cc index dc3a3eb..603a2ca 100644 --- a/protoc-c/c_bytes_field.cc +++ b/protoc-c/c_bytes_field.cc @@ -101,7 +101,7 @@ void BytesFieldGenerator::GenerateStructMembers(io::Printer* printer) const printer->Print(variables_, "ProtobufCBinaryData $name$$deprecated$;\n"); break; case FieldDescriptor::LABEL_OPTIONAL: - if (descriptor_->containing_oneof() == NULL) + if (descriptor_->containing_oneof() == NULL && FieldSyntax(descriptor_) == 2) printer->Print(variables_, "protobuf_c_boolean has_$name$$deprecated$;\n"); printer->Print(variables_, "ProtobufCBinaryData $name$$deprecated$;\n"); break; @@ -142,7 +142,9 @@ void BytesFieldGenerator::GenerateStaticInit(io::Printer* printer) const printer->Print(variables_, "$default_value$"); break; case FieldDescriptor::LABEL_OPTIONAL: - printer->Print(variables_, "0,$default_value$"); + if (FieldSyntax(descriptor_) == 2) + printer->Print(variables_, "0, "); + printer->Print(variables_, "$default_value$"); break; case FieldDescriptor::LABEL_REPEATED: // no support for default? diff --git a/protoc-c/c_enum_field.cc b/protoc-c/c_enum_field.cc index f151ce7..aabad8d 100644 --- a/protoc-c/c_enum_field.cc +++ b/protoc-c/c_enum_field.cc @@ -103,7 +103,7 @@ void EnumFieldGenerator::GenerateStructMembers(io::Printer* printer) const printer->Print(variables_, "$type$ $name$$deprecated$;\n"); break; case FieldDescriptor::LABEL_OPTIONAL: - if (descriptor_->containing_oneof() == NULL) + if (descriptor_->containing_oneof() == NULL && FieldSyntax(descriptor_) == 2) printer->Print(variables_, "protobuf_c_boolean has_$name$$deprecated$;\n"); printer->Print(variables_, "$type$ $name$$deprecated$;\n"); break; @@ -125,7 +125,9 @@ void EnumFieldGenerator::GenerateStaticInit(io::Printer* printer) const printer->Print(variables_, "$default$"); break; case FieldDescriptor::LABEL_OPTIONAL: - printer->Print(variables_, "0,$default$"); + if (FieldSyntax(descriptor_) == 2) + printer->Print(variables_, "0, "); + printer->Print(variables_, "$default$"); break; case FieldDescriptor::LABEL_REPEATED: // no support for default? diff --git a/protoc-c/c_field.cc b/protoc-c/c_field.cc index cee26d9..8366d37 100644 --- a/protoc-c/c_field.cc +++ b/protoc-c/c_field.cc @@ -107,7 +107,6 @@ void FieldGenerator::GenerateDescriptorInitializerGeneric(io::Printer* printer, const string &descriptor_addr) const { map variables; - variables["LABEL"] = CamelToUpper(GetLabelName(descriptor_->label())); variables["TYPE"] = type_macro; variables["classname"] = FullNameToC(FieldScope(descriptor_)->full_name()); variables["name"] = FieldName(descriptor_); @@ -118,6 +117,14 @@ void FieldGenerator::GenerateDescriptorInitializerGeneric(io::Printer* printer, if (oneof != NULL) variables["oneofname"] = FullNameToLower(oneof->name()); + if (FieldSyntax(descriptor_) == 3 && + descriptor_->label() == FieldDescriptor::LABEL_OPTIONAL) { + variables["LABEL"] = "NONE"; + optional_uses_has = false; + } else { + variables["LABEL"] = CamelToUpper(GetLabelName(descriptor_->label())); + } + if (descriptor_->has_default_value()) { variables["default_value"] = string("&") + FullNameToLower(descriptor_->full_name()) diff --git a/protoc-c/c_helpers.h b/protoc-c/c_helpers.h index 7d7aada..9dc8c5e 100644 --- a/protoc-c/c_helpers.h +++ b/protoc-c/c_helpers.h @@ -179,6 +179,16 @@ struct NameIndex }; int compare_name_indices_by_name(const void*, const void*); +// Return the syntax version of the file containing the field. +// This wrapper is needed to be able to compile against protobuf2. +inline int FieldSyntax(const FieldDescriptor* field) { +#ifdef HAVE_PROTO3 + return field->file()->syntax() == FileDescriptor::SYNTAX_PROTO3 ? 3 : 2; +#else + return 2; +#endif +} + } // namespace c } // namespace compiler } // namespace protobuf diff --git a/protoc-c/c_primitive_field.cc b/protoc-c/c_primitive_field.cc index d834217..a3fb1ae 100644 --- a/protoc-c/c_primitive_field.cc +++ b/protoc-c/c_primitive_field.cc @@ -113,7 +113,7 @@ void PrimitiveFieldGenerator::GenerateStructMembers(io::Printer* printer) const printer->Print(vars, "$c_type$ $name$$deprecated$;\n"); break; case FieldDescriptor::LABEL_OPTIONAL: - if (descriptor_->containing_oneof() == NULL) + if (descriptor_->containing_oneof() == NULL && FieldSyntax(descriptor_) == 2) printer->Print(vars, "protobuf_c_boolean has_$name$$deprecated$;\n"); printer->Print(vars, "$c_type$ $name$$deprecated$;\n"); break; @@ -160,7 +160,9 @@ void PrimitiveFieldGenerator::GenerateStaticInit(io::Printer* printer) const printer->Print(vars, "$default_value$"); break; case FieldDescriptor::LABEL_OPTIONAL: - printer->Print(vars, "0,$default_value$"); + if (FieldSyntax(descriptor_) == 2) + printer->Print(vars, "0, "); + printer->Print(vars, "$default_value$"); break; case FieldDescriptor::LABEL_REPEATED: printer->Print("0,NULL");