From 7d08a2778842a70bded9c2bc7951eca4168bc179 Mon Sep 17 00:00:00 2001 From: fatbob313 Date: Tue, 2 Nov 2010 21:16:03 +0000 Subject: [PATCH] Update Protobuf-C to 0.14 --- src/Mumble.pb-c.h | 15 + .../protobuf-c/protobuf-c-data-buffer.c | 7 +- src/google/protobuf-c/protobuf-c.c | 774 ++++++++++++++++-- src/google/protobuf-c/protobuf-c.h | 51 +- 4 files changed, 780 insertions(+), 67 deletions(-) diff --git a/src/Mumble.pb-c.h b/src/Mumble.pb-c.h index 55bad75..55da17f 100644 --- a/src/Mumble.pb-c.h +++ b/src/Mumble.pb-c.h @@ -704,6 +704,9 @@ MumbleProto__UserState * void mumble_proto__user_state__free_unpacked (MumbleProto__UserState *message, ProtobufCAllocator *allocator); +/* MumbleProto__BanList__BanEntry methods */ +void mumble_proto__ban_list__ban_entry__init + (MumbleProto__BanList__BanEntry *message); /* MumbleProto__BanList methods */ void mumble_proto__ban_list__init (MumbleProto__BanList *message); @@ -761,6 +764,12 @@ MumbleProto__PermissionDenied * void mumble_proto__permission_denied__free_unpacked (MumbleProto__PermissionDenied *message, ProtobufCAllocator *allocator); +/* MumbleProto__ACL__ChanGroup methods */ +void mumble_proto__acl__chan_group__init + (MumbleProto__ACL__ChanGroup *message); +/* MumbleProto__ACL__ChanACL methods */ +void mumble_proto__acl__chan_acl__init + (MumbleProto__ACL__ChanACL *message); /* MumbleProto__ACL methods */ void mumble_proto__acl__init (MumbleProto__ACL *message); @@ -856,6 +865,9 @@ MumbleProto__ContextAction * void mumble_proto__context_action__free_unpacked (MumbleProto__ContextAction *message, ProtobufCAllocator *allocator); +/* MumbleProto__UserList__User methods */ +void mumble_proto__user_list__user__init + (MumbleProto__UserList__User *message); /* MumbleProto__UserList methods */ void mumble_proto__user_list__init (MumbleProto__UserList *message); @@ -875,6 +887,9 @@ MumbleProto__UserList * void mumble_proto__user_list__free_unpacked (MumbleProto__UserList *message, ProtobufCAllocator *allocator); +/* MumbleProto__VoiceTarget__Target methods */ +void mumble_proto__voice_target__target__init + (MumbleProto__VoiceTarget__Target *message); /* MumbleProto__VoiceTarget methods */ void mumble_proto__voice_target__init (MumbleProto__VoiceTarget *message); diff --git a/src/google/protobuf-c/protobuf-c-data-buffer.c b/src/google/protobuf-c/protobuf-c-data-buffer.c index 4ed46ce..a13236b 100644 --- a/src/google/protobuf-c/protobuf-c-data-buffer.c +++ b/src/google/protobuf-c/protobuf-c-data-buffer.c @@ -23,7 +23,12 @@ #include #include #include -#include +#if HAVE_ALLOCA_H +# include +#elif HAVE_MALLOC_H +# include +#endif +#include #include "protobuf-c-data-buffer.h" #undef TRUE diff --git a/src/google/protobuf-c/protobuf-c.c b/src/google/protobuf-c/protobuf-c.c index 89e3cb7..4e4094f 100644 --- a/src/google/protobuf-c/protobuf-c.c +++ b/src/google/protobuf-c/protobuf-c.c @@ -15,15 +15,21 @@ * under the License. */ -/* TODO: certain implementations use 32-bit math even for - (uint64_size, uint64_pack, parse_uint64) */ - -/* TODO: get_packed_size and pack seem to use type-prefixed names, - whereas parse uses type-suffixed names. pick one and stick with it. - Decision: go with type-suffixed, since the type (or its instance) - is typically the object of the verb. - NOTE: perhaps the "parse" methods should be reanemd to "unpack" - at the same time. +/* TODO items: + + * 64-BIT OPTIMIZATION: certain implementations use 32-bit math even on 64-bit platforms + (uint64_size, uint64_pack, parse_uint64) + + * get_packed_size and pack seem to use type-prefixed names, + whereas parse uses type-suffixed names. pick one and stick with it. + Decision: go with type-suffixed, since the type (or its instance) + is typically the object of the verb. + NOTE: perhaps the "parse" methods should be reanemd to "unpack" + at the same time. (this only affects internal (static) functions) + + * use TRUE and FALSE instead of 1 and 0 as appropriate + + * use size_t consistently */ #include /* for occasional printf()s */ @@ -113,6 +119,9 @@ static void system_free (void *allocator_data, void *data) free (data); } +/* Some users may configure the default allocator; + providing your own allocator to unpack() is prefered. + this allocator is still used for packing nested messages. */ ProtobufCAllocator protobuf_c_default_allocator = { system_alloc, @@ -122,6 +131,10 @@ ProtobufCAllocator protobuf_c_default_allocator = NULL }; +/* Users should NOT modify this structure, + but it's difficult to prevent. + + please modify protobuf_c_default_allocator instead. */ ProtobufCAllocator protobuf_c_system_allocator = { system_alloc, @@ -366,9 +379,15 @@ repeated_field_get_packed_size (const ProtobufCFieldDescriptor *field, size_t count, const void *member) { - size_t rv = get_tag_size (field->id) * count; + size_t header_size; + size_t rv = 0; unsigned i; void *array = * (void * const *) member; + if (count == 0) + return 0; + header_size = get_tag_size (field->id); + if (!field->packed) + header_size *= count; switch (field->type) { case PROTOBUF_C_TYPE_SINT32: @@ -430,7 +449,9 @@ repeated_field_get_packed_size (const ProtobufCFieldDescriptor *field, break; //case PROTOBUF_C_TYPE_GROUP: // NOT SUPPORTED } - return rv; + if (field->packed) + header_size += uint32_size (rv); + return header_size + rv; } /* Get the packed size of a unknown field (meaning one that @@ -459,12 +480,11 @@ protobuf_c_message_get_packed_size(const ProtobufCMessage *message) 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); - else + else rv += repeated_field_get_packed_size (field, * (const size_t *) qmember, member); } for (i = 0; i < message->n_unknown_fields; i++) rv += unknown_field_get_packed_size (&message->unknown_fields[i]); - return rv; } /* === pack() === */ @@ -585,6 +605,11 @@ fixed32_pack (uint32_t value, uint8_t *out) return 4; } +/* Pack a 64-bit fixed-length value. + (Used for fixed64, sfixed64, double) */ +/* XXX: the big-endian impl is really only good for 32-bit machines, + a 64-bit version would be appreciated, plus a way + to decide to use 64-bit math where convenient. */ static inline size_t fixed64_pack (uint64_t value, uint8_t *out) { @@ -597,6 +622,11 @@ fixed64_pack (uint64_t value, uint8_t *out) return 8; } + +/* Pack a boolean as 0 or 1, even though the protobuf_c_boolean + can really assume any integer value. */ +/* XXX: perhaps on some platforms "*out = !!value" would be + a better impl, b/c that is idiotmatic c++ in some stl impls. */ static inline size_t boolean_pack (protobuf_c_boolean value, uint8_t *out) { @@ -604,6 +634,15 @@ boolean_pack (protobuf_c_boolean value, uint8_t *out) return 1; } +/* Pack a length-prefixed string. + The input string is NUL-terminated. + + The NULL pointer is treated as an empty string. + This isn't really necessary, but it allows people + to leave required strings blank. + (See Issue 13 in the bug tracker for a + little more explanation). + */ static inline size_t string_pack (const char * str, uint8_t *out) { @@ -649,13 +688,16 @@ prefixed_message_pack (const ProtobufCMessage *message, uint8_t *out) } /* wire-type will be added in required_field_pack() */ -static size_t tag_pack (uint32_t id, uint8_t *out) +/* XXX: just call uint64_pack on 64-bit platforms. */ +static size_t +tag_pack (uint32_t id, uint8_t *out) { if (id < (1<<(32-3))) return uint32_pack (id<<3, out); else return uint64_pack (((uint64_t)id) << 3, out); } + static size_t required_field_pack (const ProtobufCFieldDescriptor *field, const void *member, @@ -740,7 +782,8 @@ optional_field_pack (const ProtobufCFieldDescriptor *field, } /* TODO: implement as a table lookup */ -static inline size_t sizeof_elt_in_repeated_array (ProtobufCType type) +static inline size_t +sizeof_elt_in_repeated_array (ProtobufCType type) { switch (type) { @@ -770,6 +813,46 @@ static inline size_t sizeof_elt_in_repeated_array (ProtobufCType type) PROTOBUF_C_ASSERT_NOT_REACHED (); return 0; } + +static void +copy_to_little_endian_32 (void *out, const void *in, unsigned N) +{ +#if IS_LITTLE_ENDIAN + memcpy (out, in, N * 4); +#else + unsigned i; + const uint32_t *ini = in; + for (i = 0; i < N; i++) + fixed32_pack (ini[i], (uint32_t*)out + i); +#endif +} +static void +copy_to_little_endian_64 (void *out, const void *in, unsigned N) +{ +#if IS_LITTLE_ENDIAN + memcpy (out, in, N * 8); +#else + unsigned i; + const uint64_t *ini = in; + for (i = 0; i < N; i++) + fixed64_pack (ini[i], (uint64_t*)out + i); +#endif +} + +static unsigned +get_type_min_size (ProtobufCType type) +{ + if (type == PROTOBUF_C_TYPE_SFIXED32 + || type == PROTOBUF_C_TYPE_FIXED32 + || type == PROTOBUF_C_TYPE_FLOAT) + return 4; + if (type == PROTOBUF_C_TYPE_SFIXED64 + || type == PROTOBUF_C_TYPE_FIXED64 + || type == PROTOBUF_C_TYPE_DOUBLE) + return 8; + return 1; +} + static size_t repeated_field_pack (const ProtobufCFieldDescriptor *field, size_t count, @@ -777,20 +860,118 @@ repeated_field_pack (const ProtobufCFieldDescriptor *field, uint8_t *out) { char *array = * (char * const *) member; - size_t siz; unsigned i; - size_t rv = 0; - /* CONSIDER: optimize this case a bit (by putting the loop inside the switch) */ - siz = sizeof_elt_in_repeated_array (field->type); - for (i = 0; i < count; i++) + if (field->packed) { - rv += required_field_pack (field, array, out + rv); - array += siz; + unsigned header_len; + unsigned len_start; + unsigned min_length; + unsigned payload_len; + unsigned length_size_min; + unsigned actual_length_size; + uint8_t *payload_at; + if (count == 0) + return 0; + header_len = tag_pack (field->id, out); + out[0] |= PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED; + len_start = header_len; + min_length = get_type_min_size (field->type) * count; + length_size_min = uint32_size (min_length); + header_len += length_size_min; + payload_at = out + header_len; + switch (field->type) + { + case PROTOBUF_C_TYPE_SFIXED32: + case PROTOBUF_C_TYPE_FIXED32: + case PROTOBUF_C_TYPE_FLOAT: + copy_to_little_endian_32 (payload_at, array, count); + payload_at += count * 4; + break; + + case PROTOBUF_C_TYPE_SFIXED64: + case PROTOBUF_C_TYPE_FIXED64: + case PROTOBUF_C_TYPE_DOUBLE: + copy_to_little_endian_64 (payload_at, array, count); + payload_at += count * 8; + break; + + case PROTOBUF_C_TYPE_INT32: + { + const int32_t *arr = (const int32_t *) array; + for (i = 0; i < count; i++) + payload_at += int32_pack (arr[i], payload_at); + } + break; + + case PROTOBUF_C_TYPE_SINT32: + { + const int32_t *arr = (const int32_t *) array; + for (i = 0; i < count; i++) + payload_at += sint32_pack (arr[i], payload_at); + } + break; + + case PROTOBUF_C_TYPE_SINT64: + { + const int64_t *arr = (const int64_t *) array; + for (i = 0; i < count; i++) + payload_at += sint64_pack (arr[i], payload_at); + } + break; + case PROTOBUF_C_TYPE_ENUM: + case PROTOBUF_C_TYPE_UINT32: + { + const uint32_t *arr = (const uint32_t *) array; + for (i = 0; i < count; i++) + payload_at += uint32_pack (arr[i], payload_at); + } + break; + case PROTOBUF_C_TYPE_INT64: + case PROTOBUF_C_TYPE_UINT64: + { + const uint64_t *arr = (const uint64_t *) array; + for (i = 0; i < count; i++) + payload_at += uint64_pack (arr[i], payload_at); + } + break; + case PROTOBUF_C_TYPE_BOOL: + { + const protobuf_c_boolean *arr = (const protobuf_c_boolean *) array; + for (i = 0; i < count; i++) + payload_at += boolean_pack (arr[i], payload_at); + } + break; + + default: + assert (0); + } + payload_len = payload_at - (out + header_len); + actual_length_size = uint32_size (payload_len); + if (length_size_min != actual_length_size) + { + assert (actual_length_size == length_size_min + 1); + memmove (out + header_len + 1, out + header_len, payload_len); + header_len++; + } + uint32_pack (payload_len, out + len_start); + return header_len + payload_len; + } + else + { + /* CONSIDER: optimize this case a bit (by putting the loop inside the switch) */ + size_t rv = 0; + unsigned siz = sizeof_elt_in_repeated_array (field->type); + for (i = 0; i < count; i++) + { + rv += required_field_pack (field, array, out + rv); + array += siz; + } + return rv; } - return rv; } static size_t -unknown_field_pack (const ProtobufCMessageUnknownField *field, uint8_t *out) +unknown_field_pack (const ProtobufCMessageUnknownField *field, + uint8_t *out) { size_t rv = tag_pack (field->tag, out); out[0] |= field->wire_type; @@ -798,8 +979,9 @@ unknown_field_pack (const ProtobufCMessageUnknownField *field, uint8_t *out) return rv + field->len; } -size_t protobuf_c_message_pack (const ProtobufCMessage *message, - uint8_t *out) +size_t +protobuf_c_message_pack (const ProtobufCMessage *message, + uint8_t *out) { unsigned i; size_t rv = 0; @@ -808,11 +990,20 @@ size_t protobuf_c_message_pack (const ProtobufCMessage *message, { const ProtobufCFieldDescriptor *field = message->descriptor->fields + i; const void *member = ((const char *) message) + field->offset; + + /* it doesn't hurt to compute qmember (a pointer to the quantifier + field of the structure), but the pointer is only valid if + the field is one of: + - a repeated field + - an optional field that isn't a pointer type + (meaning: not a message or a string) */ const void *qmember = ((const char *) message) + field->quantifier_offset; 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); else rv += repeated_field_pack (field, * (const size_t *) qmember, member, out + rv); @@ -949,6 +1140,174 @@ optional_field_pack_to_buffer (const ProtobufCFieldDescriptor *field, return required_field_pack_to_buffer (field, member, buffer); } +static size_t +get_packed_payload_length (const ProtobufCFieldDescriptor *field, + unsigned count, + const void *array) +{ + unsigned rv = 0; + unsigned i; + switch (field->type) + { + case PROTOBUF_C_TYPE_SFIXED32: + case PROTOBUF_C_TYPE_FIXED32: + case PROTOBUF_C_TYPE_FLOAT: + return count * 4; + + case PROTOBUF_C_TYPE_SFIXED64: + case PROTOBUF_C_TYPE_FIXED64: + case PROTOBUF_C_TYPE_DOUBLE: + return count * 8; + + case PROTOBUF_C_TYPE_INT32: + { + const int32_t *arr = (const int32_t *) array; + for (i = 0; i < count; i++) + rv += int32_size (arr[i]); + } + break; + + case PROTOBUF_C_TYPE_SINT32: + { + const int32_t *arr = (const int32_t *) array; + for (i = 0; i < count; i++) + rv += sint32_size (arr[i]); + } + break; + case PROTOBUF_C_TYPE_ENUM: + case PROTOBUF_C_TYPE_UINT32: + { + const uint32_t *arr = (const uint32_t *) array; + for (i = 0; i < count; i++) + rv += uint32_size (arr[i]); + } + break; + + case PROTOBUF_C_TYPE_SINT64: + { + const int64_t *arr = (const int64_t *) array; + for (i = 0; i < count; i++) + rv += sint64_size (arr[i]); + } + break; + case PROTOBUF_C_TYPE_INT64: + case PROTOBUF_C_TYPE_UINT64: + { + const uint64_t *arr = (const uint64_t *) array; + for (i = 0; i < count; i++) + rv += uint64_size (arr[i]); + } + break; + case PROTOBUF_C_TYPE_BOOL: + return count; + default: + assert (0); + } + return rv; +} +static size_t +pack_buffer_packed_payload (const ProtobufCFieldDescriptor *field, + unsigned count, + const void *array, + ProtobufCBuffer *buffer) +{ + uint8_t scratch[16]; + size_t rv = 0; + unsigned i; + switch (field->type) + { + case PROTOBUF_C_TYPE_SFIXED32: + case PROTOBUF_C_TYPE_FIXED32: + case PROTOBUF_C_TYPE_FLOAT: +#if IS_LITTLE_ENDIAN + rv = count * 4; + goto no_packing_needed; +#else + for (i = 0; i < count; i++) + { + unsigned len = fixed32_pack (((uint32_t*)array)[i], scratch); + buffer->append (buffer, len, scratch); + rv += len; + } +#endif + break; + case PROTOBUF_C_TYPE_SFIXED64: + case PROTOBUF_C_TYPE_FIXED64: + case PROTOBUF_C_TYPE_DOUBLE: +#if IS_LITTLE_ENDIAN + rv = count * 8; + goto no_packing_needed; +#else + for (i = 0; i < count; i++) + { + unsigned len = fixed64_pack (((uint64_t*)array)[i], scratch); + buffer->append (buffer, len, scratch); + rv += len; + } + break; +#endif + case PROTOBUF_C_TYPE_INT32: + for (i = 0; i < count; i++) + { + unsigned len = int32_pack (((int32_t*)array)[i], scratch); + buffer->append (buffer, len, scratch); + rv += len; + } + break; + + case PROTOBUF_C_TYPE_SINT32: + for (i = 0; i < count; i++) + { + unsigned len = sint32_pack (((int32_t*)array)[i], scratch); + buffer->append (buffer, len, scratch); + rv += len; + } + break; + case PROTOBUF_C_TYPE_ENUM: + case PROTOBUF_C_TYPE_UINT32: + for (i = 0; i < count; i++) + { + unsigned len = uint32_pack (((uint32_t*)array)[i], scratch); + buffer->append (buffer, len, scratch); + rv += len; + } + break; + + case PROTOBUF_C_TYPE_SINT64: + for (i = 0; i < count; i++) + { + unsigned len = sint64_pack (((int64_t*)array)[i], scratch); + buffer->append (buffer, len, scratch); + rv += len; + } + break; + case PROTOBUF_C_TYPE_INT64: + case PROTOBUF_C_TYPE_UINT64: + for (i = 0; i < count; i++) + { + unsigned len = uint64_pack (((uint64_t*)array)[i], scratch); + buffer->append (buffer, len, scratch); + rv += len; + } + break; + case PROTOBUF_C_TYPE_BOOL: + for (i = 0; i < count; i++) + { + unsigned len = boolean_pack (((protobuf_c_boolean*)array)[i], scratch); + buffer->append (buffer, len, scratch); + rv += len; + } + return count; + default: + assert(0); + } + return rv; + +no_packing_needed: + buffer->append (buffer, rv, array); + return rv; +} + static size_t repeated_field_pack_to_buffer (const ProtobufCFieldDescriptor *field, unsigned count, @@ -956,17 +1315,35 @@ repeated_field_pack_to_buffer (const ProtobufCFieldDescriptor *field, ProtobufCBuffer *buffer) { char *array = * (char * const *) member; - size_t siz; - unsigned i; - /* CONSIDER: optimize this case a bit (by putting the loop inside the switch) */ - unsigned rv = 0; - siz = sizeof_elt_in_repeated_array (field->type); - for (i = 0; i < count; i++) + if (count == 0) + return 0; + if (field->packed) { - rv += required_field_pack_to_buffer (field, array, buffer); - array += siz; + uint8_t scratch[MAX_UINT64_ENCODED_SIZE * 2]; + size_t rv = tag_pack (field->id, scratch); + size_t payload_len = get_packed_payload_length (field, count, array); + size_t tmp; + scratch[0] |= PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED; + rv += uint32_pack (payload_len, scratch + rv); + buffer->append (buffer, rv, scratch); + tmp = pack_buffer_packed_payload (field, count, array, buffer); + assert (tmp == payload_len); + return rv + payload_len; + } + else + { + size_t siz; + unsigned i; + /* CONSIDER: optimize this case a bit (by putting the loop inside the switch) */ + unsigned rv = 0; + siz = sizeof_elt_in_repeated_array (field->type); + for (i = 0; i < count; i++) + { + rv += required_field_pack_to_buffer (field, array, buffer); + array += siz; + } + return rv; } - return rv; } static size_t @@ -1096,10 +1473,6 @@ struct _ScannedMember const uint8_t *data; }; -#define MESSAGE_GET_UNKNOWNS(message) \ - STRUCT_MEMBER_PTR (ProtobufCMessageUnknownFieldArray, \ - (message), (message)->descriptor->unknown_field_array_offset) - static inline uint32_t scan_length_prefixed_data (size_t len, const uint8_t *data, size_t *prefix_len_out) { @@ -1131,6 +1504,74 @@ scan_length_prefixed_data (size_t len, const uint8_t *data, size_t *prefix_len_o return hdr_len + val; } +static size_t +max_b128_numbers (size_t len, const uint8_t *data) +{ + size_t rv = 0; + while (len--) + if ((*data++ & 0x80) == 0) + ++rv; + return rv; +} + + +/* Given a raw slab of packed-repeated values, + determine the number of elements. + This function detects certain kinds of errors + but not others; the remaining error checking is done by + parse_packed_repeated_member() */ +static protobuf_c_boolean +count_packed_elements (ProtobufCType type, + size_t len, + const uint8_t *data, + size_t *count_out) +{ + switch (type) + { + case PROTOBUF_C_TYPE_SFIXED32: + case PROTOBUF_C_TYPE_FIXED32: + case PROTOBUF_C_TYPE_FLOAT: + if (len % 4 != 0) + { + UNPACK_ERROR (("length must be a multiple of 4 for fixed-length 32-bit types")); + return FALSE; + } + *count_out = len / 4; + return TRUE; + + case PROTOBUF_C_TYPE_SFIXED64: + case PROTOBUF_C_TYPE_FIXED64: + case PROTOBUF_C_TYPE_DOUBLE: + if (len % 8 != 0) + { + UNPACK_ERROR (("length must be a multiple of 8 for fixed-length 64-bit types")); + return FALSE; + } + *count_out = len / 8; + return TRUE; + + case PROTOBUF_C_TYPE_INT32: + case PROTOBUF_C_TYPE_SINT32: + case PROTOBUF_C_TYPE_ENUM: + case PROTOBUF_C_TYPE_UINT32: + case PROTOBUF_C_TYPE_INT64: + case PROTOBUF_C_TYPE_SINT64: + case PROTOBUF_C_TYPE_UINT64: + *count_out = max_b128_numbers (len, data); + return TRUE; + case PROTOBUF_C_TYPE_BOOL: + *count_out = len; + return TRUE; + + case PROTOBUF_C_TYPE_STRING: + case PROTOBUF_C_TYPE_BYTES: + case PROTOBUF_C_TYPE_MESSAGE: + default: + UNPACK_ERROR (("bad protobuf-c type %u for packed-repeated", type)); + return FALSE; + } +} + static inline uint32_t parse_uint32 (unsigned len, const uint8_t *data) { @@ -1375,6 +1816,160 @@ parse_repeated_member (ScannedMember *scanned_member, return 1; } +static unsigned scan_varint (unsigned len, const uint8_t *data) +{ + unsigned i; + if (len > 10) + len = 10; + for (i = 0; i < len; i++) + if ((data[i] & 0x80) == 0) + break; + if (i == len) + return 0; + return i + 1; +} + +static protobuf_c_boolean +parse_packed_repeated_member (ScannedMember *scanned_member, + void *member, + ProtobufCMessage *message) +{ + const ProtobufCFieldDescriptor *field = scanned_member->field; + size_t *p_n = STRUCT_MEMBER_PTR(size_t, message, field->quantifier_offset); + size_t siz = sizeof_elt_in_repeated_array (field->type); + char *array = *(char**)member + siz * (*p_n); + const uint8_t *at = scanned_member->data + scanned_member->length_prefix_len; + size_t rem = scanned_member->len - scanned_member->length_prefix_len; + size_t count = 0; + unsigned i; + switch (field->type) + { + case PROTOBUF_C_TYPE_SFIXED32: + case PROTOBUF_C_TYPE_FIXED32: + case PROTOBUF_C_TYPE_FLOAT: + count = (scanned_member->len - scanned_member->length_prefix_len) / 4; +#if IS_LITTLE_ENDIAN + goto no_unpacking_needed; +#else + for (i = 0; i < count; i++) + { + ((uint32_t*)array)[i] = parse_fixed_uint32 (at); + at += 4; + } +#endif + break; + case PROTOBUF_C_TYPE_SFIXED64: + case PROTOBUF_C_TYPE_FIXED64: + case PROTOBUF_C_TYPE_DOUBLE: + count = (scanned_member->len - scanned_member->length_prefix_len) / 8; +#if IS_LITTLE_ENDIAN + goto no_unpacking_needed; +#else + for (i = 0; i < count; i++) + { + ((uint64_t*)array)[i] = parse_fixed_uint64 (at); + at += 8; + } + break; +#endif + case PROTOBUF_C_TYPE_INT32: + while (rem > 0) + { + unsigned s = scan_varint (rem, at); + if (s == 0) + { + UNPACK_ERROR (("bad packed-repeated int32 value")); + return FALSE; + } + ((int32_t*)array)[count++] = parse_int32 (s, at); + at += s; + rem -= s; + } + break; + + case PROTOBUF_C_TYPE_SINT32: + while (rem > 0) + { + unsigned s = scan_varint (rem, at); + if (s == 0) + { + UNPACK_ERROR (("bad packed-repeated sint32 value")); + return FALSE; + } + ((int32_t*)array)[count++] = unzigzag32 (parse_uint32 (s, at)); + at += s; + rem -= s; + } + break; + case PROTOBUF_C_TYPE_ENUM: + case PROTOBUF_C_TYPE_UINT32: + while (rem > 0) + { + unsigned s = scan_varint (rem, at); + if (s == 0) + { + UNPACK_ERROR (("bad packed-repeated enum or uint32 value")); + return FALSE; + } + ((uint32_t*)array)[count++] = parse_uint32 (s, at); + at += s; + rem -= s; + } + break; + + case PROTOBUF_C_TYPE_SINT64: + while (rem > 0) + { + unsigned s = scan_varint (rem, at); + if (s == 0) + { + UNPACK_ERROR (("bad packed-repeated sint64 value")); + return FALSE; + } + ((int64_t*)array)[count++] = unzigzag64 (parse_uint64 (s, at)); + at += s; + rem -= s; + } + break; + case PROTOBUF_C_TYPE_INT64: + case PROTOBUF_C_TYPE_UINT64: + while (rem > 0) + { + unsigned s = scan_varint (rem, at); + if (s == 0) + { + UNPACK_ERROR (("bad packed-repeated int64/uint64 value")); + return FALSE; + } + ((int64_t*)array)[count++] = parse_uint64 (s, at); + at += s; + rem -= s; + } + break; + case PROTOBUF_C_TYPE_BOOL: + count = rem; + for (i = 0; i < count; i++) + { + if (at[i] > 1) + { + UNPACK_ERROR (("bad packed-repeated boolean value")); + return FALSE; + } + ((protobuf_c_boolean*)array)[i] = at[i]; + } + break; + default: + assert(0); + } + *p_n += count; + return TRUE; + +no_unpacking_needed: + memcpy (array, at, count * siz); + *p_n += count; + return TRUE; +} + static protobuf_c_boolean parse_member (ScannedMember *scanned_member, ProtobufCMessage *message, @@ -1400,17 +1995,27 @@ parse_member (ScannedMember *scanned_member, case PROTOBUF_C_LABEL_OPTIONAL: return parse_optional_member (scanned_member, member, message, allocator); case PROTOBUF_C_LABEL_REPEATED: - return parse_repeated_member (scanned_member, member, message, allocator); + if (field->packed + && scanned_member->wire_type == PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED) + return parse_packed_repeated_member (scanned_member, member, message); + else + return parse_repeated_member (scanned_member, member, message, allocator); } PROTOBUF_C_ASSERT_NOT_REACHED (); return 0; } -static inline void -setup_default_values (ProtobufCMessage *message) + +/* TODO: expose/use this function if desc->message_init==NULL + (which occurs for old code, and may be useful for certain + programatic techniques for generating descriptors). */ +void +protobuf_c_message_init_generic (const ProtobufCMessageDescriptor *desc, + ProtobufCMessage *message) { - const ProtobufCMessageDescriptor *desc = message->descriptor; unsigned i; + memset (message, 0, desc->sizeof_message); + message->descriptor = desc; for (i = 0; i < desc->n_fields; i++) if (desc->fields[i].default_value != NULL && desc->fields[i].label != PROTOBUF_C_LABEL_REPEATED) @@ -1498,18 +2103,32 @@ protobuf_c_message_unpack (const ProtobufCMessageDescriptor *desc, size_t n_unknown = 0; unsigned f; unsigned i_slab; + unsigned last_field_index = 0; + unsigned long *required_fields_bitmap; + unsigned required_fields_bitmap_len; + static const unsigned word_bits = sizeof(long) * 8; ASSERT_IS_MESSAGE_DESCRIPTOR (desc); if (allocator == NULL) allocator = &protobuf_c_default_allocator; + + required_fields_bitmap_len = (desc->n_fields + word_bits - 1) / word_bits; + required_fields_bitmap = alloca(required_fields_bitmap_len * sizeof(long)); + memset(required_fields_bitmap, 0, required_fields_bitmap_len * sizeof(long)); + DO_ALLOC (rv, allocator, desc->sizeof_message, return NULL); scanned_member_slabs[0] = first_member_slab; - memset (rv, 0, desc->sizeof_message); - rv->descriptor = desc; - - setup_default_values (rv); + /* Generated code always defines "message_init". + However, we provide a fallback for (1) users of old protobuf-c + generated-code that do not provide the function, + and (2) descriptors constructed from some other source + (most likely, direct construction from the .proto file) */ + if (desc->message_init != NULL) + protobuf_c_message_init (desc, rv); + else + protobuf_c_message_init_generic (desc, rv); while (rem > 0) { @@ -1540,11 +2159,15 @@ protobuf_c_message_unpack (const ProtobufCMessageDescriptor *desc, { field = desc->fields + field_index; last_field = field; + last_field_index = field_index; } } else field = last_field; + if (field != NULL && field->label == PROTOBUF_C_LABEL_REQUIRED) + required_fields_bitmap[last_field_index / word_bits] |= (1UL << (last_field_index % word_bits)); + at += used; rem -= used; tmp.tag = tag; @@ -1625,18 +2248,35 @@ protobuf_c_message_unpack (const ProtobufCMessageDescriptor *desc, if (field != NULL && field->label == PROTOBUF_C_LABEL_REPEATED) { - STRUCT_MEMBER (size_t, rv, field->quantifier_offset) += 1; + size_t *n = STRUCT_MEMBER_PTR (size_t, rv, field->quantifier_offset); + if (field->packed + && wire_type == PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED) + { + size_t count; + if (!count_packed_elements (field->type, + tmp.len - tmp.length_prefix_len, + tmp.data + tmp.length_prefix_len, + &count)) + { + UNPACK_ERROR (("counting packed elements")); + goto error_cleanup_during_scan; + } + *n += count; + } + else + *n += 1; } at += tmp.len; rem -= tmp.len; } - /* allocate space for repeated fields */ + /* allocate space for repeated fields, also check that all required fields have been set */ for (f = 0; f < desc->n_fields; f++) - if (desc->fields[f].label == PROTOBUF_C_LABEL_REPEATED) - { - const ProtobufCFieldDescriptor *field = desc->fields + f; + { + const ProtobufCFieldDescriptor *field = desc->fields + f; + if (field->label == PROTOBUF_C_LABEL_REPEATED) + { size_t siz = sizeof_elt_in_repeated_array (field->type); size_t *n_ptr = STRUCT_MEMBER_PTR (size_t, rv, field->quantifier_offset); if (*n_ptr != 0) @@ -1656,7 +2296,16 @@ protobuf_c_message_unpack (const ProtobufCMessageDescriptor *desc, CLEAR_REMAINING_N_PTRS (); goto error_cleanup); #undef CLEAR_REMAINING_N_PTRS } + } + else if (field->label == PROTOBUF_C_LABEL_REQUIRED) + { + if (field->default_value == NULL && 0 == (required_fields_bitmap[f / word_bits] & (1UL << (f % word_bits)))) + { + UNPACK_ERROR (("message '%s': missing required field '%s'", desc->name, field->name)); + goto error_cleanup; } + } + } /* allocate space for unknown fields */ if (n_unknown) @@ -1785,7 +2434,6 @@ protobuf_c_message_free_unpacked (ProtobufCMessage *message, } /* === services === */ -typedef void (*DestroyHandler)(void *service); typedef void (*GenericHandler)(void *service, const ProtobufCMessage *input, ProtobufCClosure closure, @@ -1799,8 +2447,20 @@ protobuf_c_service_invoke_internal(ProtobufCService *service, { GenericHandler *handlers; GenericHandler handler; + + /* Verify that method_index is within range. + If this fails, you are likely invoking a newly added + method on an old service. (Although other memory corruption + bugs can cause this assertion too) */ PROTOBUF_C_ASSERT (method_index < service->descriptor->n_methods); + + /* Get the array of virtual methods (which are enumerated by + the generated code) */ handlers = (GenericHandler *) (service + 1); + + /* get our method and invoke it */ + /* TODO: seems like handler==NULL is a situation that + needs handling */ handler = handlers[method_index]; (*handler) (service, input, closure, closure_data); } @@ -1837,7 +2497,7 @@ protobuf_c_enum_descriptor_get_value_by_name return desc->values + desc->values_by_name[mid].index; else if (rv < 0) { - count = start + count - (mid - 1); + count = start + count - (mid + 1); start = mid + 1; } else @@ -1913,12 +2573,14 @@ protobuf_c_service_descriptor_get_method_by_name while (count > 1) { unsigned mid = start + count / 2; - int rv = strcmp (desc->methods[desc->method_indices_by_name[mid]].name, name); + unsigned mid_index = desc->method_indices_by_name[mid]; + const char *mid_name = desc->methods[mid_index].name; + int rv = strcmp (mid_name, name); if (rv == 0) return desc->methods + desc->method_indices_by_name[mid]; if (rv < 0) { - count = start + count - (mid - 1); + count = start + count - (mid + 1); start = mid + 1; } else diff --git a/src/google/protobuf-c/protobuf-c.h b/src/google/protobuf-c/protobuf-c.h index a476961..98bd77b 100644 --- a/src/google/protobuf-c/protobuf-c.h +++ b/src/google/protobuf-c/protobuf-c.h @@ -18,7 +18,6 @@ #ifndef __PROTOBUF_C_RUNTIME_H_ #define __PROTOBUF_C_RUNTIME_H_ -#include #include #include @@ -30,6 +29,31 @@ # define PROTOBUF_C_END_DECLS #endif +#if !defined(PROTOBUF_C_NO_DEPRECATED) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) +#define PROTOBUF_C_DEPRECATED __attribute__((__deprecated__)) +#else +#define PROTOBUF_C_DEPRECATED +#endif + +/* Define int32_t, int64_t, uint32_t, uint64_t, uint8_t. + + Usually, just include to do the work. + XXX: should we use stdint.h? + */ +#ifndef PROTOBUF_C_SKIP_INTTYPES_H +# if defined(_MSC_VER) + /* On windows, in ms visual studio, define the types ourselves */ +# define int32_t signed __int32 +# define uint32_t unsigned __int32 +# define int64_t signed __int64 +# define uint64_t unsigned __int64 +# define uint8_t unsigned char +# else + /* Use the system inttypes.h */ +# include +# endif +#endif + PROTOBUF_C_BEGIN_DECLS typedef enum @@ -89,12 +113,17 @@ struct _ProtobufCAllocator /* This is a configurable allocator. * By default, it uses the system allocator (meaning malloc() and free()). - * This is typically done to incorporate into frameworks that provide + * This is typically changed to adapt to frameworks that provide * some nonstandard allocation functions. + * + * NOTE: you may modify this allocator. */ extern ProtobufCAllocator protobuf_c_default_allocator; /* settable */ -/* This is the system allocator, meaning it uses malloc() and free() */ +/* This is the system allocator, meaning it uses malloc() and free(). + * + * NOTE: please do NOT modify this allocator. + */ extern ProtobufCAllocator protobuf_c_system_allocator; /* use malloc, free etc */ /* This is the function that our default allocators call when they @@ -175,6 +204,8 @@ struct _ProtobufCEnumDescriptor /* --- messages --- */ typedef struct _ProtobufCMessageDescriptor ProtobufCMessageDescriptor; typedef struct _ProtobufCFieldDescriptor ProtobufCFieldDescriptor; +typedef struct _ProtobufCMessage ProtobufCMessage; +typedef void (*ProtobufCMessageInit)(ProtobufCMessage *); /* ProtobufCFieldDescriptor: description of a single field * in a message. * 'name' is the name of the field, as given in the .proto file. @@ -202,9 +233,11 @@ struct _ProtobufCFieldDescriptor unsigned offset; const void *descriptor; /* for MESSAGE and ENUM types */ const void *default_value; /* or NULL if no default-value */ + protobuf_c_boolean packed; - void *reserved1; + unsigned reserved_flags; void *reserved2; + void *reserved3; }; /* ProtobufCMessageDescriptor: description of a message. * @@ -240,10 +273,10 @@ struct _ProtobufCMessageDescriptor unsigned n_field_ranges; const ProtobufCIntRange *field_ranges; + ProtobufCMessageInit message_init; void *reserved1; void *reserved2; void *reserved3; - void *reserved4; }; @@ -267,7 +300,6 @@ struct _ProtobufCMessageDescriptor * 'n_unknown_fields' is the number of fields we didn't recognize. * 'unknown_fields' are fields we didn't recognize. */ -typedef struct _ProtobufCMessage ProtobufCMessage; typedef struct _ProtobufCMessageUnknownField ProtobufCMessageUnknownField; struct _ProtobufCMessage { @@ -299,10 +331,9 @@ ProtobufCMessage * void protobuf_c_message_free_unpacked (ProtobufCMessage *message, ProtobufCAllocator *allocator); -/* WARNING: 'to_init' must be a block of memory - of size description->sizeof_message. */ -size_t protobuf_c_message_init (const ProtobufCMessageDescriptor *, - ProtobufCMessage *to_init); +/* WARNING: 'message' must be a block of memory + of size descriptor->sizeof_message. */ +#define protobuf_c_message_init(descriptor, message) ((descriptor)->message_init((ProtobufCMessage*) (message))) /* --- services --- */ typedef struct _ProtobufCMethodDescriptor ProtobufCMethodDescriptor; -- 2.30.2