diff options
Diffstat (limited to 'libs/libaxolotl/src/protobuf-c/protobuf-c.c')
-rw-r--r-- | libs/libaxolotl/src/protobuf-c/protobuf-c.c | 398 |
1 files changed, 304 insertions, 94 deletions
diff --git a/libs/libaxolotl/src/protobuf-c/protobuf-c.c b/libs/libaxolotl/src/protobuf-c/protobuf-c.c index 4c1438b44a..4f2f5bcc73 100644 --- a/libs/libaxolotl/src/protobuf-c/protobuf-c.c +++ b/libs/libaxolotl/src/protobuf-c/protobuf-c.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008-2014, Dave Benson and the protobuf-c authors. + * Copyright (c) 2008-2015, Dave Benson and the protobuf-c authors. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -50,15 +50,16 @@ #include "protobuf-c.h" -#if defined(_MSC_VER) && !defined(__cplusplus) -#define inline __inline -#endif - #define TRUE 1 #define FALSE 0 #define PROTOBUF_C__ASSERT_NOT_REACHED() assert(0) +/* Workaround for Microsoft compilers. */ +#ifdef _MSC_VER +# define inline __inline +#endif + /** * \defgroup internal Internal functions and macros * @@ -460,6 +461,39 @@ required_field_get_packed_size(const ProtobufCFieldDescriptor *field, } /** + * 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 * set. @@ -620,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 +1055,40 @@ required_field_pack(const ProtobufCFieldDescriptor *field, } /** + * 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. * * \param field @@ -1311,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). */ @@ -1320,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 +1530,39 @@ required_field_pack_to_buffer(const ProtobufCFieldDescriptor *field, } /** + * 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. * * \param field @@ -1734,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, @@ -1909,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 = @@ -1957,38 +2070,65 @@ 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); + protobuf_c_boolean need_to_merge = FALSE; + void *earlier_elem; + void *latter_elem; + const void *def_val; + + 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); - void *latter_elem = - STRUCT_MEMBER_P(latter_msg, fields[i].offset); - const void *def_val = fields[i].default_value; - switch (fields[i].type) { + + earlier_elem = + STRUCT_MEMBER_P(earlier_msg, field->offset); + latter_elem = + STRUCT_MEMBER_P(latter_msg, field->offset); + 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: { uint8_t *e_data = ((ProtobufCBinaryData *) earlier_elem)->data; @@ -1996,11 +2136,10 @@ merge_messages(ProtobufCMessage *earlier_msg, ((ProtobufCBinaryData *) latter_elem)->data; const ProtobufCBinaryData *d_bd = (ProtobufCBinaryData *) def_val; - el_size = sizeof(ProtobufCBinaryData); need_to_merge = (e_data != NULL && - (d_bd != NULL && + (d_bd == NULL || e_data != d_bd->data)) && (l_data == NULL || (d_bd != NULL && @@ -2011,26 +2150,23 @@ merge_messages(ProtobufCMessage *earlier_msg, char *e_str = *(char **) earlier_elem; char *l_str = *(char **) latter_elem; const char *d_str = def_val; - el_size = sizeof(char *); need_to_merge = e_str != d_str && l_str == d_str; 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 @@ -2041,16 +2177,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 +2480,66 @@ parse_required_member(ScannedMember *scanned_member, } 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; + size_t el_size; + + 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; + } + + 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, ProtobufCMessage *message, @@ -2561,8 +2752,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 && @@ -2747,9 +2943,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 = @@ -2969,14 +3165,28 @@ void protobuf_c_message_free_unpacked(ProtobufCMessage *message, ProtobufCAllocator *allocator) { - const ProtobufCMessageDescriptor *desc = message->descriptor; + const ProtobufCMessageDescriptor *desc; unsigned f; + + if (message == NULL) + return; + desc = message->descriptor; + ASSERT_IS_MESSAGE(message); + if (allocator == NULL) 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, @@ -2985,24 +3195,25 @@ protobuf_c_message_free_unpacked(ProtobufCMessage *message, message, desc->fields[f].offset); - if (desc->fields[f].type == PROTOBUF_C_TYPE_STRING) { - unsigned i; - for (i = 0; i < n; i++) - do_free(allocator, ((char **) arr)[i]); - } else if (desc->fields[f].type == PROTOBUF_C_TYPE_BYTES) { - unsigned i; - for (i = 0; i < n; i++) - do_free(allocator, ((ProtobufCBinaryData *) arr)[i].data); - } else if (desc->fields[f].type == PROTOBUF_C_TYPE_MESSAGE) { - unsigned i; - for (i = 0; i < n; i++) - protobuf_c_message_free_unpacked( - ((ProtobufCMessage **) arr)[i], - allocator - ); - } - if (arr != NULL) + if (arr != NULL) { + if (desc->fields[f].type == PROTOBUF_C_TYPE_STRING) { + unsigned i; + for (i = 0; i < n; i++) + do_free(allocator, ((char **) arr)[i]); + } else if (desc->fields[f].type == PROTOBUF_C_TYPE_BYTES) { + unsigned i; + for (i = 0; i < n; i++) + do_free(allocator, ((ProtobufCBinaryData *) arr)[i].data); + } else if (desc->fields[f].type == PROTOBUF_C_TYPE_MESSAGE) { + unsigned i; + for (i = 0; i < n; i++) + protobuf_c_message_free_unpacked( + ((ProtobufCMessage **) arr)[i], + allocator + ); + } do_free(allocator, arr); + } } else if (desc->fields[f].type == PROTOBUF_C_TYPE_STRING) { char *str = STRUCT_MEMBER(char *, message, desc->fields[f].offset); @@ -3049,9 +3260,8 @@ protobuf_c_message_init(const ProtobufCMessageDescriptor * descriptor, protobuf_c_boolean protobuf_c_message_check(const ProtobufCMessage *message) { - unsigned i; - - if (!message || + unsigned i; + if (!message || !message->descriptor || message->descriptor->magic != PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC) { |