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"
......
......@@ -460,6 +460,39 @@ required_field_get_packed_size(const ProtobufCFieldDescriptor *field,
return 0;
}
/**
* Calculate the serialized size of a single oneof message field, including
* the space needed by the preceding tag. Returns 0 if the oneof field isn't
* selected or is not set.
*
* \param field
* Field descriptor for member.
* \param oneof_case
* A pointer to the case enum that selects the field in the oneof.
* \param member
* Field to encode.
* \return
* Number of bytes required.
*/
static size_t
oneof_field_get_packed_size(const ProtobufCFieldDescriptor *field,
const uint32_t *oneof_case,
const void *member)
{
if (*oneof_case == field->id) {
if (field->type == PROTOBUF_C_TYPE_MESSAGE ||
field->type == PROTOBUF_C_TYPE_STRING)
{
const void *ptr = *(const void * const *) member;
if (ptr == NULL || ptr == field->default_value)
return 0;
}
} else {
return 0;
}
return required_field_get_packed_size(field, member);
}
/**
* Calculate the serialized size of a single optional message field, including
* the space needed by the preceding tag. Returns 0 if the optional field isn't
......@@ -621,7 +654,10 @@ 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) {
rv += optional_field_get_packed_size(field, qmember, member);
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);
} else {
rv += repeated_field_get_packed_size(
field,
......@@ -1018,6 +1054,40 @@ required_field_pack(const ProtobufCFieldDescriptor *field,
return 0;
}
/**
* Pack a oneof field and return the number of bytes written. Only packs the
* field that is selected by the case enum.
*
* \param field
* Field descriptor.
* \param oneof_case
* A pointer to the case enum that selects the field in the oneof.
* \param member
* The field member.
* \param[out] out
* Packed value.
* \return
* Number of bytes written to `out`.
*/
static size_t
oneof_field_pack(const ProtobufCFieldDescriptor *field,
const uint32_t *oneof_case,
const void *member, uint8_t *out)
{
if (*oneof_case == field->id) {
if (field->type == PROTOBUF_C_TYPE_MESSAGE ||
field->type == PROTOBUF_C_TYPE_STRING)
{
const void *ptr = *(const void * const *) member;
if (ptr == NULL || ptr == field->default_value)
return 0;
}
} else {
return 0;
}
return required_field_pack(field, member, out);
}
/**
* Pack an optional field and return the number of bytes written.
*
......@@ -1312,6 +1382,7 @@ protobuf_c_message_pack(const ProtobufCMessage *message, uint8_t *out)
* quantifier field of the structure), but the pointer is only
* valid if the field is:
* - a repeated field, or
* - a field that is part of a oneof
* - an optional field that isn't a pointer type
* (Meaning: not a message or a string).
*/
......@@ -1321,11 +1392,10 @@ 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) {
/*
* Note that qmember is bogus for strings and messages,
* but it isn't used.
*/
rv += optional_field_pack(field, qmember, member, out + rv);
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);
} else {
rv += repeated_field_pack(field, *(const size_t *) qmember,
member, out + rv);
......@@ -1459,6 +1529,39 @@ required_field_pack_to_buffer(const ProtobufCFieldDescriptor *field,
return rv;
}
/**
* Pack a oneof field to a buffer. Only packs the field that is selected by the case enum.
*
* \param field
* Field descriptor.
* \param oneof_case
* A pointer to the case enum that selects the field in the oneof.
* \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
oneof_field_pack_to_buffer(const ProtobufCFieldDescriptor *field,
const uint32_t *oneof_case,
const void *member, ProtobufCBuffer *buffer)
{
if (*oneof_case == field->id) {
if (field->type == PROTOBUF_C_TYPE_MESSAGE ||
field->type == PROTOBUF_C_TYPE_STRING)
{
const void *ptr = *(const void *const *) member;
if (ptr == NULL || ptr == field->default_value)
return 0;
}
} else {
return 0;
}
return required_field_pack_to_buffer(field, member, buffer);
}
/**
* Pack an optional field to a buffer.
*
......@@ -1735,12 +1838,21 @@ 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) {
rv += optional_field_pack_to_buffer(
field,
qmember,
member,
buffer
);
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
);
}
} else {
rv += repeated_field_pack_to_buffer(
field,
......@@ -1910,7 +2022,7 @@ merge_messages(ProtobufCMessage *earlier_msg,
{
unsigned i;
const ProtobufCFieldDescriptor *fields =
earlier_msg->descriptor->fields;
latter_msg->descriptor->fields;
for (i = 0; i < latter_msg->descriptor->n_fields; i++) {
if (fields[i].label == PROTOBUF_C_LABEL_REPEATED) {
size_t *n_earlier =
......@@ -1958,40 +2070,62 @@ merge_messages(ProtobufCMessage *earlier_msg,
*n_earlier = 0;
*p_earlier = 0;
}
} else if (fields[i].type == PROTOBUF_C_TYPE_MESSAGE) {
ProtobufCMessage **em =
STRUCT_MEMBER_PTR(ProtobufCMessage *,
earlier_msg,
fields[i].offset);
ProtobufCMessage **lm =
STRUCT_MEMBER_PTR(ProtobufCMessage *,
latter_msg,
fields[i].offset);
if (*em != NULL) {
if (*lm != NULL) {
if (!merge_messages
(*em, *lm, allocator))
return FALSE;
} else if (fields[i].label == PROTOBUF_C_LABEL_OPTIONAL) {
const ProtobufCFieldDescriptor *field;
uint32_t *earlier_case_p = STRUCT_MEMBER_PTR(uint32_t,
earlier_msg,
fields[i].
quantifier_offset);
uint32_t *latter_case_p = STRUCT_MEMBER_PTR(uint32_t,
latter_msg,
fields[i].
quantifier_offset);
if (fields[i].flags & PROTOBUF_C_FIELD_FLAG_ONEOF) {
if (*latter_case_p == 0) {
/* lookup correct oneof field */
int field_index =
int_range_lookup(
latter_msg->descriptor
->n_field_ranges,
latter_msg->descriptor
->field_ranges,
*earlier_case_p);
field = latter_msg->descriptor->fields +
field_index;
} else {
/* Zero copy the optional message */
assert(fields[i].label ==
PROTOBUF_C_LABEL_OPTIONAL);
*lm = *em;
*em = NULL;
/* Oneof is present in the latter message, move on */
continue;
}
} else {
field = &fields[i];
}
} else if (fields[i].label == PROTOBUF_C_LABEL_OPTIONAL) {
size_t el_size = 0;
protobuf_c_boolean need_to_merge = FALSE;
void *earlier_elem =
STRUCT_MEMBER_P(earlier_msg, fields[i].offset);
STRUCT_MEMBER_P(earlier_msg, field->offset);
void *latter_elem =
STRUCT_MEMBER_P(latter_msg, fields[i].offset);
const void *def_val = fields[i].default_value;
switch (fields[i].type) {
STRUCT_MEMBER_P(latter_msg, field->offset);
const void *def_val = field->default_value;
switch (field->type) {
case PROTOBUF_C_TYPE_MESSAGE: {
ProtobufCMessage *em = *(ProtobufCMessage **) earlier_elem;
ProtobufCMessage *lm = *(ProtobufCMessage **) latter_elem;
if (em != NULL) {
if (lm != NULL) {
if (!merge_messages(em, lm, allocator))
return FALSE;
/* Already merged */
need_to_merge = FALSE;
} else {
/* Zero copy the message */
need_to_merge = TRUE;
}
}
break;
}
case PROTOBUF_C_TYPE_BYTES: {
el_size = sizeof(ProtobufCBinaryData);
uint8_t *e_data =
((ProtobufCBinaryData *) earlier_elem)->data;
uint8_t *l_data =
......@@ -2009,7 +2143,6 @@ merge_messages(ProtobufCMessage *earlier_msg,
break;
}
case PROTOBUF_C_TYPE_STRING: {
el_size = sizeof(char *);
char *e_str = *(char **) earlier_elem;
char *l_str = *(char **) latter_elem;
const char *d_str = def_val;
......@@ -2018,20 +2151,18 @@ merge_messages(ProtobufCMessage *earlier_msg,
break;
}
default: {
el_size = sizeof_elt_in_repeated_array(fields[i].type);
need_to_merge =
STRUCT_MEMBER(protobuf_c_boolean,
earlier_msg,
fields[i].quantifier_offset) &&
!STRUCT_MEMBER(protobuf_c_boolean,
latter_msg,
fields[i].quantifier_offset);
/* Could be has field or case enum, the logic is
* equivalent, since 0 (FALSE) means not set for
* oneof */
need_to_merge = (*earlier_case_p != 0) &&
(*latter_case_p == 0);
break;
}
}
if (need_to_merge) {
size_t el_size =
sizeof_elt_in_repeated_array(field->type);
memcpy(latter_elem, earlier_elem, el_size);
/*
* Reset the element from the old message to 0
......@@ -2042,16 +2173,11 @@ merge_messages(ProtobufCMessage *earlier_msg,
*/
memset(earlier_elem, 0, el_size);
if (fields[i].quantifier_offset != 0) {
/* Set the has field, if applicable */
STRUCT_MEMBER(protobuf_c_boolean,
latter_msg,
fields[i].
quantifier_offset) = TRUE;
STRUCT_MEMBER(protobuf_c_boolean,
earlier_msg,
fields[i].
quantifier_offset) = FALSE;
if (field->quantifier_offset != 0) {
/* Set the has field or the case enum,
* if applicable */
*latter_case_p = *earlier_case_p;
*earlier_case_p = 0;
}
}
}
......@@ -2349,6 +2475,64 @@ parse_required_member(ScannedMember *scanned_member,
return FALSE;
}
static protobuf_c_boolean
parse_oneof_member (ScannedMember *scanned_member,
void *member,
ProtobufCMessage *message,
ProtobufCAllocator *allocator)
{
uint32_t *oneof_case = STRUCT_MEMBER_PTR(uint32_t, message,
scanned_member->field->quantifier_offset);
/* If we have already parsed a member of this oneof, free it. */
if (*oneof_case != 0) {
/* lookup field */
int field_index =
int_range_lookup(message->descriptor->n_field_ranges,
message->descriptor->field_ranges,
*oneof_case);
const ProtobufCFieldDescriptor *old_field =
message->descriptor->fields + field_index;
switch (old_field->type) {
case PROTOBUF_C_TYPE_STRING: {
char **pstr = member;
const char *def = old_field->default_value;
if (*pstr != NULL && *pstr != def)
do_free(allocator, *pstr);
break;
}
case PROTOBUF_C_TYPE_BYTES: {
ProtobufCBinaryData *bd = member;
const ProtobufCBinaryData *def_bd = old_field->default_value;
if (bd->data != NULL &&
(def_bd == NULL || bd->data != def_bd->data))
{
do_free(allocator, bd->data);
}
break;
}
case PROTOBUF_C_TYPE_MESSAGE: {
ProtobufCMessage **pmessage = member;
const ProtobufCMessage *def_mess = old_field->default_value;
if (*pmessage != NULL && *pmessage != def_mess)
protobuf_c_message_free_unpacked(*pmessage, allocator);
break;
}
default:
break;
}
size_t el_size = sizeof_elt_in_repeated_array(old_field->type);
memset (member, 0, el_size);
}
if (!parse_required_member (scanned_member, member, allocator, TRUE))
return FALSE;
*oneof_case = scanned_member->tag;
return TRUE;
}
static protobuf_c_boolean
parse_optional_member(ScannedMember *scanned_member,
void *member,
......@@ -2562,8 +2746,13 @@ parse_member(ScannedMember *scanned_member,
return parse_required_member(scanned_member, member,
allocator, TRUE);
case PROTOBUF_C_LABEL_OPTIONAL:
return parse_optional_member(scanned_member, member,
message, allocator);
if (0 != (field->flags & PROTOBUF_C_FIELD_FLAG_ONEOF)) {
return parse_oneof_member(scanned_member, member,
message, allocator);
} else {
return parse_optional_member(scanned_member, member,
message, allocator);
}
case PROTOBUF_C_LABEL_REPEATED:
if (scanned_member->wire_type ==
PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED &&
......@@ -2748,9 +2937,9 @@ protobuf_c_message_unpack(const ProtobufCMessageDescriptor *desc,
goto error_cleanup_during_scan;
}
/*
* \todo Consider optimizing for field[1].id == tag, if field[1]
* exists!
*/
* \todo Consider optimizing for field[1].id == tag, if field[1]
* exists!
*/
if (last_field == NULL || last_field->id != tag) {
/* lookup field */
int field_index =
......@@ -2977,6 +3166,14 @@ protobuf_c_message_free_unpacked(ProtobufCMessage *message,
allocator = &protobuf_c__allocator;
message->descriptor = NULL;
for (f = 0; f < desc->n_fields; f++) {
if (0 != (desc->fields[f].flags & PROTOBUF_C_FIELD_FLAG_ONEOF) &&
desc->fields[f].id !=
STRUCT_MEMBER(uint32_t, message, desc->fields[f].quantifier_offset))
{
/* This is not the selected oneof, skip it */
continue;
}
if (desc->fields[f].label == PROTOBUF_C_LABEL_REPEATED) {
size_t n = STRUCT_MEMBER(size_t,
message,
......
......@@ -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;
}
......@@ -82,9 +82,14 @@ test_compare_pack_methods (ProtobufCMessage *message,
return rv;
}
#define GENERIC_ASSIGN(dst,src) ((dst) = (src))
#define NUMERIC_EQUALS(a,b) ((a) == (b))
#define STRING_EQUALS(a,b) (strcmp((a),(b))==0)
#define CHECK_NONE(a)
#define CHECK_NOT_NULL(a) assert( (a) != NULL )
static protobuf_c_boolean
binary_data_equals (ProtobufCBinaryData a, ProtobufCBinaryData b)
{
......@@ -681,6 +686,297 @@ static void test_optional_SubMess (void)
DO_TEST (&submess, test_optional_submess_42);
#undef DO_TEST
}
/* === Oneof type fields === */
static void test_empty_oneof (void)
{
Foo__TestMessOneof mess = FOO__TEST_MESS_ONEOF__INIT;
size_t len;
uint8_t *data;
Foo__TestMessOneof *mess2 = test_compare_pack_methods (&mess.base, &len, &data);
assert (len == 0);
free (data);
foo__test_mess_oneof__free_unpacked (mess2, NULL);
}
#define DO_TEST_GENERIC_ONEOF(type, init, free_unpacked, case_member, case_enum, member, value, example_packed_data, assign, equal_func, result_check) \
do{ \
type opt = init; \
type *mess; \
size_t len; uint8_t *data; \
opt.case_member = case_enum; \
assign(opt.member, value); \
mess = test_compare_pack_methods (&opt.base, &len, &data); \
TEST_VERSUS_STATIC_ARRAY (len, data, example_packed_data); \
assert (mess->case_member == case_enum); \
result_check(mess->member); \
assert (equal_func (mess->member, value)); \
free_unpacked (mess, NULL); \
free (data); \
}while(0)
#define DO_TEST_ONEOF(member, MEMBER, value, example_packed_data, assign, equal_func) \
DO_TEST_GENERIC_ONEOF(Foo__TestMessOneof, \
FOO__TEST_MESS_ONEOF__INIT, \
foo__test_mess_oneof__free_unpacked, \
test_oneof_case, \
FOO__TEST_MESS_ONEOF__TEST_ONEOF_##MEMBER, \
member, \
value, example_packed_data, assign, equal_func, CHECK_NONE)
#define DO_TEST_ONEOF_REF_VAL(member, MEMBER, value, example_packed_data, assign, equal_func) \
DO_TEST_GENERIC_ONEOF(Foo__TestMessOneof, \
FOO__TEST_MESS_ONEOF__INIT, \
foo__test_mess_oneof__free_unpacked, \
test_oneof_case, \
FOO__TEST_MESS_ONEOF__TEST_ONEOF_##MEMBER, \
member, \
value, example_packed_data, assign, equal_func, CHECK_NOT_NULL)
static void test_oneof_int32 (void)
{
#define DO_TEST(value, example_packed_data) \
DO_TEST_ONEOF(test_int32, TEST_INT32, value, example_packed_data, GENERIC_ASSIGN, NUMERIC_EQUALS)
DO_TEST (INT32_MIN, test_optional_int32_min);
DO_TEST (-1, test_optional_int32_m1);
DO_TEST (0, test_optional_int32_0);
DO_TEST (666, test_optional_int32_666);
DO_TEST (INT32_MAX, test_optional_int32_max);
#undef DO_TEST
}
static void test_oneof_sint32 (void)
{
#define DO_TEST(value, example_packed_data) \
DO_TEST_ONEOF(test_sint32, TEST_SINT32, value, example_packed_data, GENERIC_ASSIGN, NUMERIC_EQUALS)
DO_TEST (INT32_MIN, test_optional_sint32_min);
DO_TEST (-1, test_optional_sint32_m1);
DO_TEST (0, test_optional_sint32_0);
DO_TEST (666, test_optional_sint32_666);
DO_TEST (INT32_MAX, test_optional_sint32_max);
#undef DO_TEST
}
static void test_oneof_sfixed32 (void)
{
#define DO_TEST(value, example_packed_data) \
DO_TEST_ONEOF(test_sfixed32, TEST_SFIXED32, value, example_packed_data, GENERIC_ASSIGN, NUMERIC_EQUALS)
DO_TEST (INT32_MIN, test_optional_sfixed32_min);
DO_TEST (-1, test_optional_sfixed32_m1);
DO_TEST (0, test_optional_sfixed32_0);
DO_TEST (666, test_optional_sfixed32_666);
DO_TEST (INT32_MAX, test_optional_sfixed32_max);
#undef DO_TEST
}
static void test_oneof_int64 (void)
{
#define DO_TEST(value, example_packed_data) \
DO_TEST_ONEOF(test_int64, TEST_INT64, value, example_packed_data, GENERIC_ASSIGN, NUMERIC_EQUALS)
DO_TEST (INT64_MIN, test_optional_int64_min);
DO_TEST (-1111111111LL, test_optional_int64_m1111111111LL);
DO_TEST (0, test_optional_int64_0);
DO_TEST (QUINTILLION, test_optional_int64_quintillion);
DO_TEST (INT64_MAX, test_optional_int64_max);
#undef DO_TEST
}
static void test_oneof_sint64 (void)
{
#define DO_TEST(value, example_packed_data) \
DO_TEST_ONEOF(test_sint64, TEST_SINT64, value, example_packed_data, GENERIC_ASSIGN, NUMERIC_EQUALS)
DO_TEST (INT64_MIN, test_optional_sint64_min);
DO_TEST (-1111111111LL, test_optional_sint64_m1111111111LL);
DO_TEST (0, test_optional_sint64_0);
DO_TEST (QUINTILLION, test_optional_sint64_quintillion);
DO_TEST (INT64_MAX, test_optional_sint64_max);
#undef DO_TEST
}
static void test_oneof_sfixed64 (void)
{
#define DO_TEST(value, example_packed_data) \
DO_TEST_ONEOF(test_sfixed64, TEST_SFIXED64, value, example_packed_data, GENERIC_ASSIGN, NUMERIC_EQUALS)
DO_TEST (INT64_MIN, test_optional_sfixed64_min);
DO_TEST (-1111111111LL, test_optional_sfixed64_m1111111111LL);
DO_TEST (0, test_optional_sfixed64_0);
DO_TEST (QUINTILLION, test_optional_sfixed64_quintillion);
DO_TEST (INT64_MAX, test_optional_sfixed64_max);
#undef DO_TEST
}
static void test_oneof_uint32 (void)
{
#define DO_TEST(value, example_packed_data) \
DO_TEST_ONEOF(test_uint32, TEST_UINT32, value, example_packed_data, GENERIC_ASSIGN, NUMERIC_EQUALS)
DO_TEST (0, test_optional_uint32_0);
DO_TEST (669, test_optional_uint32_669);
DO_TEST (UINT32_MAX, test_optional_uint32_max);
#undef DO_TEST
}
static void test_oneof_fixed32 (void)
{
#define DO_TEST(value, example_packed_data) \
DO_TEST_ONEOF(test_fixed32, TEST_FIXED32, value, example_packed_data, GENERIC_ASSIGN, NUMERIC_EQUALS)
DO_TEST (0, test_optional_fixed32_0);
DO_TEST (669, test_optional_fixed32_669);
DO_TEST (UINT32_MAX, test_optional_fixed32_max);
#undef DO_TEST
}
static void test_oneof_uint64 (void)
{
#define DO_TEST(value, example_packed_data) \
DO_TEST_ONEOF(test_uint64, TEST_UINT64, value, example_packed_data, GENERIC_ASSIGN, NUMERIC_EQUALS)
DO_TEST (0, test_optional_uint64_0);
DO_TEST (669669669669669ULL, test_optional_uint64_669669669669669);
DO_TEST (UINT64_MAX, test_optional_uint64_max);
#undef DO_TEST
}
static void test_oneof_fixed64 (void)
{
#define DO_TEST(value, example_packed_data) \
DO_TEST_ONEOF(test_fixed64, TEST_FIXED64, value, example_packed_data, GENERIC_ASSIGN, NUMERIC_EQUALS)
DO_TEST (0, test_optional_fixed64_0);
DO_TEST (669669669669669ULL, test_optional_fixed64_669669669669669);
DO_TEST (UINT64_MAX, test_optional_fixed64_max);
#undef DO_TEST
}
static void test_oneof_float (void)
{
#define DO_TEST(value, example_packed_data) \
DO_TEST_ONEOF(test_float, TEST_FLOAT, value, example_packed_data, GENERIC_ASSIGN, NUMERIC_EQUALS)
DO_TEST (-100, test_optional_float_m100);
DO_TEST (0, test_optional_float_0);
DO_TEST (141243, test_optional_float_141243);
#undef DO_TEST
}
static void test_oneof_double (void)
{
#define DO_TEST(value, example_packed_data) \
DO_TEST_ONEOF(test_double, TEST_DOUBLE, value, example_packed_data, GENERIC_ASSIGN, NUMERIC_EQUALS)
DO_TEST (-100, test_optional_double_m100);
DO_TEST (0, test_optional_double_0);
DO_TEST (141243, test_optional_double_141243);
#undef DO_TEST
}
static void test_oneof_bool (void)
{
#define DO_TEST(value, example_packed_data) \
DO_TEST_ONEOF(test_boolean, TEST_BOOLEAN, value, example_packed_data, GENERIC_ASSIGN, NUMERIC_EQUALS)
DO_TEST (0, test_optional_bool_0);
DO_TEST (1, test_optional_bool_1);
#undef DO_TEST
}
static void test_oneof_TestEnumSmall (void)
{
#define DO_TEST(value, example_packed_data) \
DO_TEST_ONEOF(test_enum_small, TEST_ENUM_SMALL, value, example_packed_data, GENERIC_ASSIGN, NUMERIC_EQUALS)
DO_TEST (0, test_optional_enum_small_0);
DO_TEST (1, test_optional_enum_small_1);
#undef DO_TEST
}
static void test_oneof_TestEnum (void)
{
#define DO_TEST(value, example_packed_data) \
DO_TEST_ONEOF(test_enum, TEST_ENUM, value, example_packed_data, GENERIC_ASSIGN, NUMERIC_EQUALS)
DO_TEST (FOO__TEST_ENUM__VALUE0, test_optional_enum_0);
DO_TEST (FOO__TEST_ENUM__VALUE1, test_optional_enum_1);
DO_TEST (FOO__TEST_ENUM__VALUE127, test_optional_enum_127);
DO_TEST (FOO__TEST_ENUM__VALUE128, test_optional_enum_128);
DO_TEST (FOO__TEST_ENUM__VALUE16383, test_optional_enum_16383);
DO_TEST (FOO__TEST_ENUM__VALUE16384, test_optional_enum_16384);
DO_TEST (FOO__TEST_ENUM__VALUE2097151, test_optional_enum_2097151);
DO_TEST (FOO__TEST_ENUM__VALUE2097152, test_optional_enum_2097152);
DO_TEST (FOO__TEST_ENUM__VALUE268435455, test_optional_enum_268435455);
DO_TEST (FOO__TEST_ENUM__VALUE268435456, test_optional_enum_268435456);
#undef DO_TEST
}
static void test_oneof_string (void)
{
#define DO_TEST(value, example_packed_data) \
DO_TEST_ONEOF_REF_VAL (test_string, TEST_STRING, value, example_packed_data, GENERIC_ASSIGN, STRING_EQUALS)
DO_TEST ("", test_optional_string_empty);
DO_TEST ("hello", test_optional_string_hello);
#undef DO_TEST
}
static void test_oneof_bytes (void)
{
static ProtobufCBinaryData bd_empty = { 0, (uint8_t*)"" };
static ProtobufCBinaryData bd_hello = { 5, (uint8_t*)"hello" };
static ProtobufCBinaryData bd_random = { 5, (uint8_t*)"\1\0\375\2\4" };
#define DO_TEST(value, example_packed_data) \
DO_TEST_ONEOF (test_bytes, TEST_BYTES, value, example_packed_data, GENERIC_ASSIGN, binary_data_equals)
DO_TEST (bd_empty, test_optional_bytes_empty);
DO_TEST (bd_hello, test_optional_bytes_hello);
DO_TEST (bd_random, test_optional_bytes_random);
#undef DO_TEST
}
static void test_oneof_SubMess (void)
{
Foo__SubMess submess = FOO__SUB_MESS__INIT;
#define DO_TEST(value, example_packed_data) \
DO_TEST_ONEOF_REF_VAL (test_message, TEST_MESSAGE, value, example_packed_data, GENERIC_ASSIGN, submesses_equals)
submess.test = 0;
DO_TEST (&submess, test_optional_submess_0);
submess.test = 42;
DO_TEST (&submess, test_optional_submess_42);
#undef DO_TEST
}
static void test_oneof_merge (void)
{
Foo__TestMessOneof *msg;
#define DO_TEST(value, member, MEMBER, equals_func, example_packed_data) \
msg = foo__test_mess_oneof__unpack (NULL, sizeof (example_packed_data), example_packed_data); \
assert (msg); \
assert (msg->test_oneof_case == FOO__TEST_MESS_ONEOF__TEST_ONEOF_##MEMBER); \
assert (equals_func (msg->member, value)); \
foo__test_mess_oneof__free_unpacked (msg, NULL);
DO_TEST (444455555, test_double, TEST_DOUBLE, NUMERIC_EQUALS, test_oneof_merge_double);
DO_TEST (333, test_float, TEST_FLOAT, NUMERIC_EQUALS, test_oneof_merge_float);
DO_TEST (666, test_int32, TEST_INT32, NUMERIC_EQUALS, test_oneof_merge_int32);
DO_TEST ("", test_string, TEST_STRING, STRING_EQUALS, test_oneof_merge_string);
Foo__SubMess submess = FOO__SUB_MESS__INIT;
submess.test = 42;
DO_TEST (&submess, test_message, TEST_MESSAGE, submesses_equals, test_oneof_merge_submess);
ProtobufCBinaryData bd_hello = { 5, (uint8_t*)"hello" };
DO_TEST(bd_hello, test_bytes, TEST_BYTES, binary_data_equals, test_oneof_merge_bytes);
#undef DO_TEST
}
/* === repeated type fields === */
#define DO_TEST_REPEATED(lc_member_name, cast, \
static_array, example_packed_data, \
......@@ -1514,6 +1810,40 @@ test_field_merge (void)
foo__test_mess_optional__free_unpacked (merged, NULL);
}
static void
test_submessage_merge (void)
{
Foo__TestMessSubMess *merged;
size_t size;
uint8_t *packed;
merged = foo__test_mess_sub_mess__unpack
(NULL, sizeof (test_submess_unmerged1), test_submess_unmerged1);
size = foo__test_mess_sub_mess__get_packed_size(merged);
packed = malloc (size);
foo__test_mess_sub_mess__pack (merged, packed);
assert (size == sizeof (test_submess_merged1));
assert (memcmp (packed, test_submess_merged1, size) == 0);
foo__test_mess_sub_mess__free_unpacked (merged, NULL);
free (packed);
merged = foo__test_mess_sub_mess__unpack
(NULL, sizeof (test_submess_unmerged2), test_submess_unmerged2);
size = foo__test_mess_sub_mess__get_packed_size(merged);
packed = malloc (size);
foo__test_mess_sub_mess__pack (merged, packed);
assert (size == sizeof (test_submess_merged2));
assert (memcmp (packed, test_submess_merged2, size) == 0);
foo__test_mess_sub_mess__free_unpacked (merged, NULL);
free (packed);
}
static struct alloc_data {
uint32_t alloc_count;
int32_t allocs_left;
......@@ -1821,6 +2151,27 @@ static Test tests[] =
{ "test optional string", test_optional_string },
{ "test optional bytes", test_optional_bytes },
{ "test optional SubMess", test_optional_SubMess },
{ "test empty oneof" ,test_empty_oneof },
{ "test oneof int32", test_oneof_int32 },
{ "test oneof sint32", test_oneof_sint32 },
{ "test oneof sfixed32", test_oneof_sfixed32 },
{ "test oneof int64", test_oneof_int64 },
{ "test oneof sint64", test_oneof_sint64 },
{ "test oneof sfixed64", test_oneof_sfixed64 },
{ "test oneof uint32", test_oneof_uint32 },
{ "test oneof fixed32", test_oneof_fixed32 },
{ "test oneof uint64", test_oneof_uint64 },
{ "test oneof fixed64", test_oneof_fixed64 },
{ "test oneof float", test_oneof_float },
{ "test oneof double", test_oneof_double },
{ "test oneof bool", test_oneof_bool },
{ "test oneof TestEnumSmall", test_oneof_TestEnumSmall },
{ "test oneof TestEnum", test_oneof_TestEnum },
{ "test oneof string", test_oneof_string },
{ "test oneof bytes", test_oneof_bytes },
{ "test oneof SubMess", test_oneof_SubMess },
{ "test merged oneof unpack", test_oneof_merge },
{ "test empty repeated" ,test_empty_repeated },
{ "test repeated int32" ,test_repeated_int32 },
......@@ -1869,6 +2220,7 @@ static Test tests[] =
{ "test optional lowercase enum default value", test_optional_lowercase_enum_default_value },
{ "test field merge", test_field_merge },
{ "test submessage merge", test_submessage_merge },
{ "test free unpacked", test_alloc_free_all },
{ "test alloc failure", test_alloc_fail },
......
......@@ -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