proto3 support

This is a first cut at adding proto3 support.

As far as I understand protobuf-c already has pretty much everything
needed once it is built using a new version of protobuf itself.
The only missing thing is that in proto3 all fields are optional and
having to manually set has_foo is inconvenient.

This patch special cases the proto3 syntax files so that structs for the
bytes, enum and primitive fields do not emit the has_ field.

It also adds PROTOBUF_C_LABEL_NONE to the label enum that is used for
proto3 fields. When a fields has this label, the quantifier is not
consulted and instead the field is packed/unpacked depending on
whether it has a value different from NULL/0.
This commit is contained in:
Paolo Borelli 2016-07-24 17:21:20 +02:00
parent 9c4c8f6366
commit 8194f4d91a
8 changed files with 175 additions and 38 deletions

View File

@ -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

View File

@ -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);

View File

@ -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;
/**

View File

@ -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?

View File

@ -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?

View File

@ -107,7 +107,6 @@ void FieldGenerator::GenerateDescriptorInitializerGeneric(io::Printer* printer,
const string &descriptor_addr) const
{
map<string, string> 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())

View File

@ -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

View File

@ -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");