mirror of
https://github.com/protobuf-c/protobuf-c.git
synced 2024-12-27 13:31:02 +08:00
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:
parent
9c4c8f6366
commit
8194f4d91a
14
configure.ac
14
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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
/**
|
||||
|
@ -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?
|
||||
|
@ -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?
|
||||
|
@ -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())
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
|
Loading…
x
Reference in New Issue
Block a user