Commit 453f39e6 authored by Robert Edmonds's avatar Robert Edmonds

Merge oneof support

parents 8f23163f 6090d9a9
......@@ -47,7 +47,7 @@ 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.5.0])
PKG_CHECK_MODULES([protobuf], [protobuf >= 2.6.0])
save_CPPFLAGS="$CPPFLAGS"
CPPFLAGS="$save_CPPFLAGS $protobuf_CFLAGS"
......
This diff is collapsed.
......@@ -239,6 +239,9 @@ typedef enum {
/** Set if the field is marked with the `deprecated` option. */
PROTOBUF_C_FIELD_FLAG_DEPRECATED = (1 << 1),
/** Set if the field is a member of a oneof (union). */
PROTOBUF_C_FIELD_FLAG_ONEOF = (1 << 2),
} ProtobufCFieldFlag;
/**
......@@ -545,7 +548,7 @@ struct ProtobufCFieldDescriptor {
/**
* The offset in bytes of the message's C structure's quantifier field
* (the `has_MEMBER` field for optional members or the `n_MEMBER` field
* for repeated members.
* for repeated members or the case enum for oneofs).
*/
unsigned quantifier_offset;
......
......@@ -101,7 +101,8 @@ void BytesFieldGenerator::GenerateStructMembers(io::Printer* printer) const
printer->Print(variables_, "ProtobufCBinaryData $name$$deprecated$;\n");
break;
case FieldDescriptor::LABEL_OPTIONAL:
printer->Print(variables_, "protobuf_c_boolean has_$name$$deprecated$;\n");
if (descriptor_->containing_oneof() == NULL)
printer->Print(variables_, "protobuf_c_boolean has_$name$$deprecated$;\n");
printer->Print(variables_, "ProtobufCBinaryData $name$$deprecated$;\n");
break;
case FieldDescriptor::LABEL_REPEATED:
......
......@@ -106,7 +106,8 @@ void EnumFieldGenerator::GenerateStructMembers(io::Printer* printer) const
printer->Print(variables_, "$type$ $name$$deprecated$;\n");
break;
case FieldDescriptor::LABEL_OPTIONAL:
printer->Print(variables_, "protobuf_c_boolean has_$name$$deprecated$;\n");
if (descriptor_->containing_oneof() == NULL)
printer->Print(variables_, "protobuf_c_boolean has_$name$$deprecated$;\n");
printer->Print(variables_, "$type$ $name$$deprecated$;\n");
break;
case FieldDescriptor::LABEL_REPEATED:
......
......@@ -114,6 +114,9 @@ void FieldGenerator::GenerateDescriptorInitializerGeneric(io::Printer* printer,
variables["proto_name"] = descriptor_->name();
variables["descriptor_addr"] = descriptor_addr;
variables["value"] = SimpleItoa(descriptor_->number());
const OneofDescriptor *oneof = descriptor_->containing_oneof();
if (oneof != NULL)
variables["oneofname"] = FullNameToLower(oneof->name());
if (descriptor_->has_default_value()) {
variables["default_value"] = string("&")
......@@ -133,6 +136,9 @@ void FieldGenerator::GenerateDescriptorInitializerGeneric(io::Printer* printer,
if (descriptor_->options().deprecated())
variables["flags"] += " | PROTOBUF_C_FIELD_FLAG_DEPRECATED";
if (oneof != NULL)
variables["flags"] += " | PROTOBUF_C_FIELD_FLAG_ONEOF";
printer->Print(variables,
"{\n"
" \"$proto_name$\",\n"
......@@ -144,7 +150,9 @@ void FieldGenerator::GenerateDescriptorInitializerGeneric(io::Printer* printer,
printer->Print(variables, " 0, /* quantifier_offset */\n");
break;
case FieldDescriptor::LABEL_OPTIONAL:
if (optional_uses_has) {
if (oneof != NULL) {
printer->Print(variables, " offsetof($classname$, $oneofname$_case),\n");
} else if (optional_uses_has) {
printer->Print(variables, " offsetof($classname$, has_$name$),\n");
} else {
printer->Print(variables, " 0, /* quantifier_offset */\n");
......
......@@ -147,6 +147,25 @@ GenerateStructDefinition(io::Printer* printer) {
vars["dllexport"] = dllexport_decl_ + " ";
}
// Generate the case enums for unions
for (int i = 0; i < descriptor_->oneof_decl_count(); i++) {
const OneofDescriptor *oneof = descriptor_->oneof_decl(i);
vars["oneofname"] = FullNameToUpper(oneof->name());
vars["foneofname"] = FullNameToC(oneof->full_name());
printer->Print("typedef enum {\n");
printer->Indent();
printer->Print(vars, "$ucclassname$__$oneofname$__NOT_SET = 0,\n");
for (int j = 0; j < oneof->field_count(); j++) {
const FieldDescriptor *field = oneof->field(j);
vars["fieldname"] = FullNameToUpper(field->name());
vars["fieldnum"] = SimpleItoa(field->number());
printer->Print(vars, "$ucclassname$__$oneofname$_$fieldname$ = $fieldnum$,\n");
}
printer->Outdent();
printer->Print(vars, "} $foneofname$Case;\n\n");
}
printer->Print(vars,
"struct $dllexport$ _$classname$\n"
"{\n"
......@@ -156,7 +175,27 @@ GenerateStructDefinition(io::Printer* printer) {
printer->Indent();
for (int i = 0; i < descriptor_->field_count(); i++) {
const FieldDescriptor *field = descriptor_->field(i);
field_generators_.get(field).GenerateStructMembers(printer);
if (field->containing_oneof() == NULL) {
field_generators_.get(field).GenerateStructMembers(printer);
}
}
// Generate unions from oneofs.
for (int i = 0; i < descriptor_->oneof_decl_count(); i++) {
const OneofDescriptor *oneof = descriptor_->oneof_decl(i);
vars["oneofname"] = FullNameToLower(oneof->name());
vars["foneofname"] = FullNameToC(oneof->full_name());
printer->Print(vars, "$foneofname$Case $oneofname$_case;\n");
printer->Print("union {\n");
printer->Indent();
for (int j = 0; j < oneof->field_count(); j++) {
const FieldDescriptor *field = oneof->field(j);
field_generators_.get(field).GenerateStructMembers(printer);
}
printer->Outdent();
printer->Print(vars, "};\n");
}
printer->Outdent();
......@@ -173,8 +212,18 @@ GenerateStructDefinition(io::Printer* printer) {
" { PROTOBUF_C_MESSAGE_INIT (&$lcclassname$__descriptor) \\\n ");
for (int i = 0; i < descriptor_->field_count(); i++) {
const FieldDescriptor *field = descriptor_->field(i);
printer->Print(", ");
field_generators_.get(field).GenerateStaticInit(printer);
if (field->containing_oneof() == NULL) {
printer->Print(", ");
field_generators_.get(field).GenerateStaticInit(printer);
}
}
for (int i = 0; i < descriptor_->oneof_decl_count(); i++) {
const OneofDescriptor *oneof = descriptor_->oneof_decl(i);
vars["foneofname"] = FullNameToUpper(oneof->full_name());
// Initialize the case enum
printer->Print(vars, ", $foneofname$__NOT_SET");
// Initialize the enum
printer->Print(", {}");
}
printer->Print(" }\n\n\n");
......
......@@ -113,7 +113,8 @@ void PrimitiveFieldGenerator::GenerateStructMembers(io::Printer* printer) const
printer->Print(vars, "$c_type$ $name$$deprecated$;\n");
break;
case FieldDescriptor::LABEL_OPTIONAL:
printer->Print(vars, "protobuf_c_boolean has_$name$$deprecated$;\n");
if (descriptor_->containing_oneof() == NULL)
printer->Print(vars, "protobuf_c_boolean has_$name$$deprecated$;\n");
printer->Print(vars, "$c_type$ $name$$deprecated$;\n");
break;
case FieldDescriptor::LABEL_REPEATED:
......
......@@ -36,25 +36,35 @@ using namespace foo;
#define N_ELEMENTS(arr) (sizeof(arr)/sizeof((arr)[0]))
static void
dump_message_bytes(google::protobuf::Message *message,
const char *label)
{
std::string rv;
unsigned char *bytes;
unsigned len;
if (!message->SerializeToString(&rv))
assert(0);
bytes = (unsigned char *) rv.data();
len = rv.size();
printf ("static const uint8_t %s[%u] = { ", label, len);
for (unsigned i = 0; i < len; i++) {
if (i)
dump_messages_bytes(size_t n_msgs,
google::protobuf::Message **messages,
const char *label)
{
printf ("static const uint8_t %s[] = { ", label);
for (unsigned m = 0; m < n_msgs; m++) {
std::string rv;
google::protobuf::Message *message = messages[m];
if (m)
printf (", ");
printf ("0x%02x", bytes[i]);
if (!message->SerializeToString(&rv))
assert(0);
unsigned char *bytes = (unsigned char *) rv.data();
for (unsigned i = 0; i < rv.size(); i++) {
if (i)
printf (", ");
printf ("0x%02x", bytes[i]);
}
}
printf (" };\n");
}
static void
dump_message_bytes(google::protobuf::Message *message,
const char *label)
{
dump_messages_bytes (1, &message, label);
}
static void
dump_test_enum_small (void)
{
......@@ -564,6 +574,33 @@ static void dump_test_optional_message (void)
opt.mutable_test_message()->set_test(42);
dump_message_bytes (&opt, "test_optional_submess_42");
}
static void dump_test_oneof_merge (void)
{
#define SWAP(a, b) temp = a, a = b, b = temp
google::protobuf::Message *temp;
TestMessOptional opt[6];
google::protobuf::Message *msgs[6] = { &opt[0], &opt[1], &opt[2], &opt[3],
&opt[4], &opt[5] };
opt[0].set_test_bytes ("hello");
opt[1].mutable_test_message()->set_test (42);
opt[2].set_test_string ("");
opt[3].set_test_int32 (666);
opt[4].set_test_float (333);
opt[5].set_test_double (444455555);
dump_messages_bytes (6, msgs, "test_oneof_merge_double");
SWAP (msgs[5], msgs[4]);
dump_messages_bytes (6, msgs, "test_oneof_merge_float");
SWAP (msgs[5], msgs[3]);
dump_messages_bytes (6, msgs, "test_oneof_merge_int32");
SWAP (msgs[5], msgs[2]);
dump_messages_bytes (6, msgs, "test_oneof_merge_string");
SWAP (msgs[5], msgs[1]);
dump_messages_bytes (6, msgs, "test_oneof_merge_submess");
SWAP (msgs[5], msgs[0]);
dump_messages_bytes (6, msgs, "test_oneof_merge_bytes");
#undef SWAP
}
#define DUMP_STATIC_ARRAY_GENERIC(member, static_array, output_array_name) \
do{ \
......@@ -978,6 +1015,65 @@ static void dump_test_unknown_fields (void)
dump_message_bytes (&mess, "test_unknown_fields_1");
}
static void dump_test_submess_merge (void)
{
TestMessSubMess mess1, mess2, merged1, merged2;
/* Repeated merge */
mess1.mutable_rep_mess()->add_test_int32(1);
mess1.mutable_rep_mess()->add_test_int32(2);
mess2.mutable_rep_mess()->add_test_int32(3);
mess2.mutable_rep_mess()->add_test_int32(4);
mess1.mutable_rep_mess()->add_test_string("hello ");
mess2.mutable_rep_mess()->add_test_string("world");
mess1.mutable_rep_mess()->add_test_bytes("\001\002\003");
mess2.mutable_rep_mess()->add_test_bytes("\004\005\006");
mess1.mutable_rep_mess()->add_test_message()->set_test(111);
mess2.mutable_rep_mess()->add_test_message()->set_test(222);
/* Optional merge */
mess1.mutable_opt_mess()->set_test_sint32(-1);
mess2.mutable_opt_mess()->set_test_sint32(-2);
mess1.mutable_opt_mess()->set_test_float(333);
mess2.mutable_opt_mess()->set_test_double(444);
mess1.mutable_opt_mess()->set_test_bytes("\001\002\003");
mess1.mutable_opt_mess()->mutable_test_message()->set_test(111);
mess2.mutable_opt_mess()->set_test_string("hello");
/* Oneof merge */
mess1.mutable_oneof_mess()->set_opt_int (1);
mess2.mutable_oneof_mess()->mutable_test_message()->set_test(111);
/* Required merge */
mess1.mutable_req_mess()->set_test(1);
mess2.mutable_req_mess()->set_test(2);
/* Default value merge */
mess1.mutable_def_mess()->set_v_int32(111);
mess1.mutable_def_mess()->set_v_string("hello");
mess2.mutable_def_mess()->set_v_bytes("\001\002\003");
mess2.mutable_def_mess()->set_v_double(444);
/* Merge both ways and encode the merged and unmerged messages */
merged1.CopyFrom(mess1);
merged1.MergeFrom(mess2);
merged2.CopyFrom(mess2);
merged2.MergeFrom(mess1);
google::protobuf::Message *msgs[] = { &mess1, &mess2 };
dump_messages_bytes (2, msgs, "test_submess_unmerged1");
msgs[0] = &mess2;
msgs[1] = &mess1;
dump_messages_bytes (2, msgs, "test_submess_unmerged2");
dump_message_bytes(&merged1, "test_submess_merged1");
dump_message_bytes(&merged2, "test_submess_merged2");
}
int main()
{
dump_test_enum_small ();
......@@ -1019,6 +1115,7 @@ int main()
dump_test_optional_string ();
dump_test_optional_bytes ();
dump_test_optional_message ();
dump_test_oneof_merge ();
dump_test_repeated_int32 ();
dump_test_repeated_sint32 ();
dump_test_repeated_uint32 ();
......@@ -1053,5 +1150,6 @@ int main()
dump_test_packed_repeated_enum_small ();
dump_test_packed_repeated_enum ();
dump_test_unknown_fields ();
dump_test_submess_merge ();
return 0;
}
This diff is collapsed.
......@@ -132,6 +132,30 @@ message TestMessOptional {
optional SubMess test_message = 18;
}
message TestMessOneof {
oneof test_oneof {
int32 test_int32 = 1;
sint32 test_sint32 = 2;
sfixed32 test_sfixed32 = 3;
int64 test_int64 = 4;
sint64 test_sint64 = 5;
sfixed64 test_sfixed64 = 6;
uint32 test_uint32 = 7;
fixed32 test_fixed32 = 8;
uint64 test_uint64 = 9;
fixed64 test_fixed64 = 10;
float test_float = 11;
double test_double = 12;
bool test_boolean = 13;
TestEnumSmall test_enum_small = 14;
TestEnum test_enum = 15;
string test_string = 16;
bytes test_bytes = 17;
SubMess test_message = 18;
}
optional int32 opt_int = 19;
}
message TestMessRequiredInt32 {
required int32 test = 42;
}
......@@ -379,3 +403,10 @@ message TestMessageCheck {
optional bytes optional_bytes = 9;
}
message TestMessSubMess {
required TestMess rep_mess = 1;
required TestMessOptional opt_mess = 2;
required TestMessOneof oneof_mess = 3;
required SubMess req_mess = 4;
required DefaultOptionalValues def_mess = 5;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment