/src/google/protobuf-c/protobuf-c.c
C | 2163 lines | 1861 code | 114 blank | 188 comment | 317 complexity | 0d5846277d9eca2be77613833c4b4f92 MD5 | raw file
Possible License(s): BSD-3-Clause
Large files files are truncated, but you can click here to view the full file
- /* --- protobuf-c.c: public protobuf c runtime implementation --- */
- /*
- * Copyright (c) 2008-2011, Dave Benson.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with
- * or without modification, are permitted provided that the
- * following conditions are met:
- *
- * Redistributions of source code must retain the above
- * copyright notice, this list of conditions and the following
- * disclaimer.
- * Redistributions in binary form must reproduce
- * the above copyright notice, this list of conditions and
- * the following disclaimer in the documentation and/or other
- * materials provided with the distribution.
- *
- * Neither the name
- * of "protobuf-c" nor the names of its contributors
- * may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
- * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
- * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
- * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
- /* 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
- */
- #if HAVE_PROTOBUF_C_CONFIG_H
- #include "protobuf-c-config.h"
- #endif
- #include <stdio.h> /* for occasional printf()s */
- #include <stdlib.h> /* for abort(), malloc() etc */
- #include <string.h> /* for strlen(), memcpy(), memmove() */
- #if 0 /* we no longer use alloca. */
- #if HAVE_ALLOCA_H
- #include <alloca.h>
- #elif HAVE_MALLOC_H
- #include <malloc.h>
- #endif
- #endif
- #ifndef PRINT_UNPACK_ERRORS
- #define PRINT_UNPACK_ERRORS 1
- #endif
- #include "protobuf-c.h"
- unsigned protobuf_c_major = PROTOBUF_C_MAJOR;
- unsigned protobuf_c_minor = PROTOBUF_C_MINOR;
- #define MAX_UINT64_ENCODED_SIZE 10
- /* This should be roughly the biggest message you think you'll encounter.
- However, the only point of the hashing is to detect uninitialized required members.
- I doubt many messages have 128 REQUIRED fields, so hopefully this'll be fine.
- TODO: A better solution is to separately in the descriptor index the required fields,
- and use the allcoator if the required field count is too big. */
- #define MAX_MEMBERS_FOR_HASH_SIZE 128
- /* convenience macros */
- #define TMPALLOC(allocator, size) ((allocator)->tmp_alloc ((allocator)->allocator_data, (size)))
- #define FREE(allocator, ptr) \
- do { if ((ptr) != NULL) ((allocator)->free ((allocator)->allocator_data, (ptr))); } while(0)
- #define UNALIGNED_ALLOC(allocator, size) ALLOC (allocator, size) /* placeholder */
- #define STRUCT_MEMBER_P(struct_p, struct_offset) \
- ((void *) ((uint8_t*) (struct_p) + (struct_offset)))
- #define STRUCT_MEMBER(member_type, struct_p, struct_offset) \
- (*(member_type*) STRUCT_MEMBER_P ((struct_p), (struct_offset)))
- #define STRUCT_MEMBER_PTR(member_type, struct_p, struct_offset) \
- ((member_type*) STRUCT_MEMBER_P ((struct_p), (struct_offset)))
- #define TRUE 1
- #define FALSE 0
- static void
- alloc_failed_warning (unsigned size, const char *filename, unsigned line)
- {
- fprintf (stderr,
- "WARNING: out-of-memory allocating a block of size %u (%s:%u)\n",
- size, filename, line);
- }
- /* Try to allocate memory, running some special code if it fails. */
- #define DO_ALLOC(dst, allocator, size, fail_code) \
- { size_t da__allocation_size = (size); \
- if (da__allocation_size == 0) \
- dst = NULL; \
- else if ((dst=((allocator)->alloc ((allocator)->allocator_data, \
- da__allocation_size))) == NULL) \
- { \
- alloc_failed_warning (da__allocation_size, __FILE__, __LINE__); \
- fail_code; \
- } \
- }
- #define DO_UNALIGNED_ALLOC DO_ALLOC /* placeholder */
- #define ASSERT_IS_ENUM_DESCRIPTOR(desc) \
- assert((desc)->magic == PROTOBUF_C_ENUM_DESCRIPTOR_MAGIC)
- #define ASSERT_IS_MESSAGE_DESCRIPTOR(desc) \
- assert((desc)->magic == PROTOBUF_C_MESSAGE_DESCRIPTOR_MAGIC)
- #define ASSERT_IS_MESSAGE(message) \
- ASSERT_IS_MESSAGE_DESCRIPTOR((message)->descriptor)
- #define ASSERT_IS_SERVICE_DESCRIPTOR(desc) \
- assert((desc)->magic == PROTOBUF_C_SERVICE_DESCRIPTOR_MAGIC)
- /* --- allocator --- */
- static void protobuf_c_out_of_memory_default (void)
- {
- fprintf (stderr, "Out Of Memory!!!\n");
- abort ();
- }
- void (*protobuf_c_out_of_memory) (void) = protobuf_c_out_of_memory_default;
- static void *system_alloc(void *allocator_data, size_t size)
- {
- void *rv;
- (void) allocator_data;
- if (size == 0)
- return NULL;
- rv = malloc (size);
- if (rv == NULL)
- protobuf_c_out_of_memory ();
- return rv;
- }
- static void system_free (void *allocator_data, void *data)
- {
- (void) allocator_data;
- if (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,
- system_free,
- NULL, /* tmp_alloc */
- 8192, /* max_alloca */
- NULL /* allocator_data */
- };
- /* 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,
- system_free,
- NULL, /* tmp_alloc */
- 8192, /* max_alloca */
- NULL /* allocator_data */
- };
- /* === buffer-simple === */
- void
- protobuf_c_buffer_simple_append (ProtobufCBuffer *buffer,
- size_t len,
- const uint8_t *data)
- {
- ProtobufCBufferSimple *simp = (ProtobufCBufferSimple *) buffer;
- size_t new_len = simp->len + len;
- if (new_len > simp->alloced)
- {
- size_t new_alloced = simp->alloced * 2;
- uint8_t *new_data;
- while (new_alloced < new_len)
- new_alloced += new_alloced;
- DO_ALLOC (new_data, &protobuf_c_default_allocator, new_alloced, return);
- memcpy (new_data, simp->data, simp->len);
- if (simp->must_free_data)
- FREE (&protobuf_c_default_allocator, simp->data);
- else
- simp->must_free_data = 1;
- simp->data = new_data;
- simp->alloced = new_alloced;
- }
- memcpy (simp->data + simp->len, data, len);
- simp->len = new_len;
- }
- /* === get_packed_size() === */
- /* Return the number of bytes required to store the
- tag for the field (which includes 3 bits for
- the wire-type, and a single bit that denotes the end-of-tag. */
- static inline size_t
- get_tag_size (unsigned number)
- {
- if (number < (1<<4))
- return 1;
- else if (number < (1<<11))
- return 2;
- else if (number < (1<<18))
- return 3;
- else if (number < (1<<25))
- return 4;
- else
- return 5;
- }
- /* Return the number of bytes required to store
- a variable-length unsigned integer that fits in 32-bit uint
- in base-128 encoding. */
- static inline size_t
- uint32_size (uint32_t v)
- {
- if (v < (1<<7))
- return 1;
- else if (v < (1<<14))
- return 2;
- else if (v < (1<<21))
- return 3;
- else if (v < (1<<28))
- return 4;
- else
- return 5;
- }
- /* Return the number of bytes required to store
- a variable-length signed integer that fits in 32-bit int
- in base-128 encoding. */
- static inline size_t
- int32_size (int32_t v)
- {
- if (v < 0)
- return 10;
- else if (v < (1<<7))
- return 1;
- else if (v < (1<<14))
- return 2;
- else if (v < (1<<21))
- return 3;
- else if (v < (1<<28))
- return 4;
- else
- return 5;
- }
- /* return the zigzag-encoded 32-bit unsigned int from a 32-bit signed int */
- static inline uint32_t
- zigzag32 (int32_t v)
- {
- if (v < 0)
- return ((uint32_t)(-v)) * 2 - 1;
- else
- return v * 2;
- }
- /* Return the number of bytes required to store
- a variable-length signed integer that fits in 32-bit int,
- converted to unsigned via the zig-zag algorithm,
- then packed using base-128 encoding. */
- static inline size_t
- sint32_size (int32_t v)
- {
- return uint32_size(zigzag32(v));
- }
- /* Return the number of bytes required to store
- a variable-length unsigned integer that fits in 64-bit uint
- in base-128 encoding. */
- static inline size_t
- uint64_size (uint64_t v)
- {
- uint32_t upper_v = (uint32_t )(v>>32);
- if (upper_v == 0)
- return uint32_size ((uint32_t)v);
- else if (upper_v < (1<<3))
- return 5;
- else if (upper_v < (1<<10))
- return 6;
- else if (upper_v < (1<<17))
- return 7;
- else if (upper_v < (1<<24))
- return 8;
- else if (upper_v < (1U<<31))
- return 9;
- else
- return 10;
- }
- /* return the zigzag-encoded 64-bit unsigned int from a 64-bit signed int */
- static inline uint64_t
- zigzag64 (int64_t v)
- {
- if (v < 0)
- return ((uint64_t)(-v)) * 2 - 1;
- else
- return v * 2;
- }
- /* Return the number of bytes required to store
- a variable-length signed integer that fits in 64-bit int,
- converted to unsigned via the zig-zag algorithm,
- then packed using base-128 encoding. */
- static inline size_t
- sint64_size (int64_t v)
- {
- return uint64_size(zigzag64(v));
- }
- /* Get serialized size of a single field in the message,
- including the space needed by the identifying tag. */
- static size_t
- required_field_get_packed_size (const ProtobufCFieldDescriptor *field,
- const void *member)
- {
- size_t rv = get_tag_size (field->id);
- switch (field->type)
- {
- case PROTOBUF_C_TYPE_SINT32:
- return rv + sint32_size (*(const int32_t *) member);
- case PROTOBUF_C_TYPE_INT32:
- return rv + int32_size (*(const uint32_t *) member);
- case PROTOBUF_C_TYPE_UINT32:
- return rv + uint32_size (*(const uint32_t *) member);
- case PROTOBUF_C_TYPE_SINT64:
- return rv + sint64_size (*(const int64_t *) member);
- case PROTOBUF_C_TYPE_INT64:
- case PROTOBUF_C_TYPE_UINT64:
- return rv + uint64_size (*(const uint64_t *) member);
- case PROTOBUF_C_TYPE_SFIXED32:
- case PROTOBUF_C_TYPE_FIXED32:
- return rv + 4;
- case PROTOBUF_C_TYPE_SFIXED64:
- case PROTOBUF_C_TYPE_FIXED64:
- return rv + 8;
- case PROTOBUF_C_TYPE_BOOL:
- return rv + 1;
- case PROTOBUF_C_TYPE_FLOAT:
- return rv + 4;
- case PROTOBUF_C_TYPE_DOUBLE:
- return rv + 8;
- case PROTOBUF_C_TYPE_ENUM:
- // TODO: is this correct for negative-valued enums?
- return rv + uint32_size (*(const uint32_t *) member);
- case PROTOBUF_C_TYPE_STRING:
- {
- const char *str = *(char * const *) member;
- size_t len = str ? strlen (str) : 0;
- return rv + uint32_size (len) + len;
- }
- case PROTOBUF_C_TYPE_BYTES:
- {
- size_t len = ((const ProtobufCBinaryData*) member)->len;
- return rv + uint32_size (len) + len;
- }
- //case PROTOBUF_C_TYPE_GROUP:
- case PROTOBUF_C_TYPE_MESSAGE:
- {
- const ProtobufCMessage *msg = * (ProtobufCMessage * const *) member;
- size_t subrv = msg ? protobuf_c_message_get_packed_size (msg) : 0;
- return rv + uint32_size (subrv) + subrv;
- }
- }
- PROTOBUF_C_ASSERT_NOT_REACHED ();
- return 0;
- }
- /* Get serialized size of a single optional field in the message,
- including the space needed by the identifying tag.
- Returns 0 if the optional field isn't set. */
- static size_t
- optional_field_get_packed_size (const ProtobufCFieldDescriptor *field,
- const protobuf_c_boolean *has,
- const void *member)
- {
- 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
- {
- if (!*has)
- return 0;
- }
- return required_field_get_packed_size (field, member);
- }
- /* Get serialized size of a repeated field in the message,
- which may consist of any number of values (including 0).
- Includes the space needed by the identifying tags (as needed). */
- static size_t
- repeated_field_get_packed_size (const ProtobufCFieldDescriptor *field,
- size_t count,
- const void *member)
- {
- 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:
- for (i = 0; i < count; i++)
- rv += sint32_size (((int32_t*)array)[i]);
- break;
- case PROTOBUF_C_TYPE_INT32:
- for (i = 0; i < count; i++)
- rv += int32_size (((uint32_t*)array)[i]);
- break;
- case PROTOBUF_C_TYPE_UINT32:
- case PROTOBUF_C_TYPE_ENUM:
- for (i = 0; i < count; i++)
- rv += uint32_size (((uint32_t*)array)[i]);
- break;
- case PROTOBUF_C_TYPE_SINT64:
- for (i = 0; i < count; i++)
- rv += sint64_size (((int64_t*)array)[i]);
- break;
- case PROTOBUF_C_TYPE_INT64:
- case PROTOBUF_C_TYPE_UINT64:
- for (i = 0; i < count; i++)
- rv += uint64_size (((uint64_t*)array)[i]);
- break;
- case PROTOBUF_C_TYPE_SFIXED32:
- case PROTOBUF_C_TYPE_FIXED32:
- case PROTOBUF_C_TYPE_FLOAT:
- rv += 4 * count;
- break;
- case PROTOBUF_C_TYPE_SFIXED64:
- case PROTOBUF_C_TYPE_FIXED64:
- case PROTOBUF_C_TYPE_DOUBLE:
- rv += 8 * count;
- break;
- case PROTOBUF_C_TYPE_BOOL:
- rv += count;
- break;
- case PROTOBUF_C_TYPE_STRING:
- for (i = 0; i < count; i++)
- {
- size_t len = strlen (((char**) array)[i]);
- rv += uint32_size (len) + len;
- }
- break;
-
- case PROTOBUF_C_TYPE_BYTES:
- for (i = 0; i < count; i++)
- {
- size_t len = ((ProtobufCBinaryData*) array)[i].len;
- rv += uint32_size (len) + len;
- }
- break;
- case PROTOBUF_C_TYPE_MESSAGE:
- for (i = 0; i < count; i++)
- {
- size_t len = protobuf_c_message_get_packed_size (((ProtobufCMessage **) array)[i]);
- rv += uint32_size (len) + len;
- }
- break;
- //case PROTOBUF_C_TYPE_GROUP: // NOT SUPPORTED
- }
- if (field->packed)
- header_size += uint32_size (rv);
- return header_size + rv;
- }
- /* Get the packed size of a unknown field (meaning one that
- is passed through mostly uninterpreted... this is done
- for forward compatibilty with the addition of new fields). */
- static inline size_t
- unknown_field_get_packed_size (const ProtobufCMessageUnknownField *field)
- {
- return get_tag_size (field->tag) + field->len;
- }
- /* Get the number of bytes that the message will occupy once serialized. */
- size_t
- protobuf_c_message_get_packed_size(const ProtobufCMessage *message)
- {
- unsigned i;
- size_t rv = 0;
- ASSERT_IS_MESSAGE (message);
- for (i = 0; i < message->descriptor->n_fields; i++)
- {
- const ProtobufCFieldDescriptor *field = message->descriptor->fields + i;
- const void *member = ((const char *) message) + field->offset;
- const void *qmember = ((const char *) message) + field->quantifier_offset;
- 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);
- 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() === */
- /* Pack an unsigned 32-bit integer in base-128 encoding, and return the number of bytes needed:
- this will be 5 or less. */
- static inline size_t
- uint32_pack (uint32_t value, uint8_t *out)
- {
- unsigned rv = 0;
- if (value >= 0x80)
- {
- out[rv++] = value | 0x80;
- value >>= 7;
- if (value >= 0x80)
- {
- out[rv++] = value | 0x80;
- value >>= 7;
- if (value >= 0x80)
- {
- out[rv++] = value | 0x80;
- value >>= 7;
- if (value >= 0x80)
- {
- out[rv++] = value | 0x80;
- value >>= 7;
- }
- }
- }
- }
- /* assert: value<128 */
- out[rv++] = value;
- return rv;
- }
- /* Pack a 32-bit signed integer, returning the number of bytes needed.
- Negative numbers are packed as twos-complement 64-bit integers. */
- static inline size_t
- int32_pack (int32_t value, uint8_t *out)
- {
- if (value < 0)
- {
- out[0] = value | 0x80;
- out[1] = (value>>7) | 0x80;
- out[2] = (value>>14) | 0x80;
- out[3] = (value>>21) | 0x80;
- out[4] = (value>>28) | 0x80;
- out[5] = out[6] = out[7] = out[8] = 0xff;
- out[9] = 0x01;
- return 10;
- }
- else
- return uint32_pack (value, out);
- }
- /* Pack a 32-bit integer in zigzag encoding. */
- static inline size_t
- sint32_pack (int32_t value, uint8_t *out)
- {
- return uint32_pack (zigzag32 (value), out);
- }
- /* Pack a 64-bit unsigned integer that fits in a 64-bit uint,
- using base-128 encoding. */
- static size_t
- uint64_pack (uint64_t value, uint8_t *out)
- {
- uint32_t hi = (uint32_t )(value>>32);
- uint32_t lo = (uint32_t )value;
- unsigned rv;
- if (hi == 0)
- return uint32_pack ((uint32_t)lo, out);
- out[0] = (lo) | 0x80;
- out[1] = (lo>>7) | 0x80;
- out[2] = (lo>>14) | 0x80;
- out[3] = (lo>>21) | 0x80;
- if (hi < 8)
- {
- out[4] = (hi<<4) | (lo>>28);
- return 5;
- }
- else
- {
- out[4] = ((hi&7)<<4) | (lo>>28) | 0x80;
- hi >>= 3;
- }
- rv = 5;
- while (hi >= 128)
- {
- out[rv++] = hi | 0x80;
- hi >>= 7;
- }
- out[rv++] = hi;
- return rv;
- }
- /* Pack a 64-bit signed integer in zigzan encoding,
- return the size of the packed output.
- (Max returned value is 10) */
- static inline size_t
- sint64_pack (int64_t value, uint8_t *out)
- {
- return uint64_pack (zigzag64 (value), out);
- }
- /* Pack a 32-bit value, little-endian.
- Used for fixed32, sfixed32, float) */
- static inline size_t
- fixed32_pack (uint32_t value, void *out)
- {
- #if IS_LITTLE_ENDIAN
- memcpy (out, &value, 4);
- #else
- uint8_t *buf = out;
- buf[0] = value;
- buf[1] = value>>8;
- buf[2] = value>>16;
- buf[3] = value>>24;
- #endif
- 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, void *out)
- {
- #if IS_LITTLE_ENDIAN
- memcpy (out, &value, 8);
- #else
- fixed32_pack (value, out);
- fixed32_pack (value>>32, ((char*)out)+4);
- #endif
- 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)
- {
- *out = value ? 1 : 0;
- 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)
- {
- if (str == NULL)
- {
- out[0] = 0;
- return 1;
- }
- else
- {
- size_t len = strlen (str);
- size_t rv = uint32_pack (len, out);
- memcpy (out + rv, str, len);
- return rv + len;
- }
- }
- static inline size_t
- binary_data_pack (const ProtobufCBinaryData *bd, uint8_t *out)
- {
- size_t len = bd->len;
- size_t rv = uint32_pack (len, out);
- memcpy (out + rv, bd->data, len);
- return rv + len;
- }
- static inline size_t
- prefixed_message_pack (const ProtobufCMessage *message, uint8_t *out)
- {
- if (message == NULL)
- {
- out[0] = 0;
- return 1;
- }
- else
- {
- size_t rv = protobuf_c_message_pack (message, out + 1);
- uint32_t rv_packed_size = uint32_size (rv);
- if (rv_packed_size != 1)
- memmove (out + rv_packed_size, out + 1, rv);
- return uint32_pack (rv, out) + rv;
- }
- }
- /* wire-type will be added in required_field_pack() */
- /* 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,
- uint8_t *out)
- {
- size_t rv = tag_pack (field->id, out);
- switch (field->type)
- {
- case PROTOBUF_C_TYPE_SINT32:
- out[0] |= PROTOBUF_C_WIRE_TYPE_VARINT;
- return rv + sint32_pack (*(const int32_t *) member, out + rv);
- case PROTOBUF_C_TYPE_INT32:
- out[0] |= PROTOBUF_C_WIRE_TYPE_VARINT;
- return rv + int32_pack (*(const uint32_t *) member, out + rv);
- case PROTOBUF_C_TYPE_UINT32:
- case PROTOBUF_C_TYPE_ENUM:
- out[0] |= PROTOBUF_C_WIRE_TYPE_VARINT;
- return rv + uint32_pack (*(const uint32_t *) member, out + rv);
- case PROTOBUF_C_TYPE_SINT64:
- out[0] |= PROTOBUF_C_WIRE_TYPE_VARINT;
- return rv + sint64_pack (*(const int64_t *) member, out + rv);
- case PROTOBUF_C_TYPE_INT64:
- case PROTOBUF_C_TYPE_UINT64:
- out[0] |= PROTOBUF_C_WIRE_TYPE_VARINT;
- return rv + uint64_pack (*(const uint64_t *) member, out + rv);
- case PROTOBUF_C_TYPE_SFIXED32:
- case PROTOBUF_C_TYPE_FIXED32:
- case PROTOBUF_C_TYPE_FLOAT:
- out[0] |= PROTOBUF_C_WIRE_TYPE_32BIT;
- return rv + fixed32_pack (*(const uint32_t *) member, out + rv);
- case PROTOBUF_C_TYPE_SFIXED64:
- case PROTOBUF_C_TYPE_FIXED64:
- case PROTOBUF_C_TYPE_DOUBLE:
- out[0] |= PROTOBUF_C_WIRE_TYPE_64BIT;
- return rv + fixed64_pack (*(const uint64_t *) member, out + rv);
- case PROTOBUF_C_TYPE_BOOL:
- out[0] |= PROTOBUF_C_WIRE_TYPE_VARINT;
- return rv + boolean_pack (*(const protobuf_c_boolean *) member, out + rv);
- case PROTOBUF_C_TYPE_STRING:
- {
- out[0] |= PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED;
- return rv + string_pack (*(char * const *) member, out + rv);
- }
-
- case PROTOBUF_C_TYPE_BYTES:
- {
- const ProtobufCBinaryData * bd = ((const ProtobufCBinaryData*) member);
- out[0] |= PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED;
- return rv + binary_data_pack (bd, out + rv);
- }
- //case PROTOBUF_C_TYPE_GROUP: // NOT SUPPORTED
- case PROTOBUF_C_TYPE_MESSAGE:
- {
- out[0] |= PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED;
- return rv + prefixed_message_pack (*(ProtobufCMessage * const *) member,
- out + rv);
- }
- }
- PROTOBUF_C_ASSERT_NOT_REACHED ();
- return 0;
- }
- static size_t
- optional_field_pack (const ProtobufCFieldDescriptor *field,
- const protobuf_c_boolean *has,
- const void *member,
- uint8_t *out)
- {
- 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
- {
- if (!*has)
- return 0;
- }
- return required_field_pack (field, member, out);
- }
- /* TODO: implement as a table lookup */
- static inline size_t
- sizeof_elt_in_repeated_array (ProtobufCType type)
- {
- switch (type)
- {
- case PROTOBUF_C_TYPE_SINT32:
- case PROTOBUF_C_TYPE_INT32:
- case PROTOBUF_C_TYPE_UINT32:
- case PROTOBUF_C_TYPE_SFIXED32:
- case PROTOBUF_C_TYPE_FIXED32:
- case PROTOBUF_C_TYPE_FLOAT:
- case PROTOBUF_C_TYPE_ENUM:
- return 4;
- case PROTOBUF_C_TYPE_SINT64:
- case PROTOBUF_C_TYPE_INT64:
- case PROTOBUF_C_TYPE_UINT64:
- case PROTOBUF_C_TYPE_SFIXED64:
- case PROTOBUF_C_TYPE_FIXED64:
- case PROTOBUF_C_TYPE_DOUBLE:
- return 8;
- case PROTOBUF_C_TYPE_BOOL:
- return sizeof (protobuf_c_boolean);
- case PROTOBUF_C_TYPE_STRING:
- case PROTOBUF_C_TYPE_MESSAGE:
- return sizeof (void *);
- case PROTOBUF_C_TYPE_BYTES:
- return sizeof (ProtobufCBinaryData);
- }
- 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,
- const void *member,
- uint8_t *out)
- {
- char *array = * (char * const *) member;
- unsigned i;
- if (field->packed)
- {
- 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;
- }
- }
- static size_t
- unknown_field_pack (const ProtobufCMessageUnknownField *field,
- uint8_t *out)
- {
- size_t rv = tag_pack (field->tag, out);
- out[0] |= field->wire_type;
- memcpy (out + rv, field->data, field->len);
- return rv + field->len;
- }
- size_t
- protobuf_c_message_pack (const ProtobufCMessage *message,
- uint8_t *out)
- {
- unsigned i;
- size_t rv = 0;
- ASSERT_IS_MESSAGE (message);
- for (i = 0; i < message->descriptor->n_fields; i++)
- {
- 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);
- }
- for (i = 0; i < message->n_unknown_fields; i++)
- rv += unknown_field_pack (&message->unknown_fields[i], out + rv);
- return rv;
- }
- /* === pack_to_buffer() === */
- static size_t
- required_field_pack_to_buffer (const ProtobufCFieldDescriptor *field,
- const void *member,
- ProtobufCBuffer *buffer)
- {
- size_t rv;
- uint8_t scratch[MAX_UINT64_ENCODED_SIZE * 2];
- rv = tag_pack (field->id, scratch);
- switch (field->type)
- {
- case PROTOBUF_C_TYPE_SINT32:
- scratch[0] |= PROTOBUF_C_WIRE_TYPE_VARINT;
- rv += sint32_pack (*(const int32_t *) member, scratch + rv);
- buffer->append (buffer, rv, scratch);
- break;
- case PROTOBUF_C_TYPE_INT32:
- scratch[0] |= PROTOBUF_C_WIRE_TYPE_VARINT;
- rv += int32_pack (*(const uint32_t *) member, scratch + rv);
- buffer->append (buffer, rv, scratch);
- break;
- case PROTOBUF_C_TYPE_UINT32:
- case PROTOBUF_C_TYPE_ENUM:
- scratch[0] |= PROTOBUF_C_WIRE_TYPE_VARINT;
- rv += uint32_pack (*(const uint32_t *) member, scratch + rv);
- buffer->append (buffer, rv, scratch);
- break;
- case PROTOBUF_C_TYPE_SINT64:
- scratch[0] |= PROTOBUF_C_WIRE_TYPE_VARINT;
- rv += sint64_pack (*(const int64_t *) member, scratch + rv);
- buffer->append (buffer, rv, scratch);
- break;
- case PROTOBUF_C_TYPE_INT64:
- case PROTOBUF_C_TYPE_UINT64:
- scratch[0] |= PROTOBUF_C_WIRE_TYPE_VARINT;
- rv += uint64_pack (*(const uint64_t *) member, scratch + rv);
- buffer->append (buffer, rv, scratch);
- break;
- case PROTOBUF_C_TYPE_SFIXED32:
- case PROTOBUF_C_TYPE_FIXED32:
- case PROTOBUF_C_TYPE_FLOAT:
- scratch[0] |= PROTOBUF_C_WIRE_TYPE_32BIT;
- rv += fixed32_pack (*(const uint32_t *) member, scratch + rv);
- buffer->append (buffer, rv, scratch);
- break;
- case PROTOBUF_C_TYPE_SFIXED64:
- case PROTOBUF_C_TYPE_FIXED64:
- case PROTOBUF_C_TYPE_DOUBLE:
- scratch[0] |= PROTOBUF_C_WIRE_TYPE_64BIT;
- rv += fixed64_pack (*(const uint64_t *) member, scratch + rv);
- buffer->append (buffer, rv, scratch);
- break;
- case PROTOBUF_C_TYPE_BOOL:
- scratch[0] |= PROTOBUF_C_WIRE_TYPE_VARINT;
- rv += boolean_pack (*(const protobuf_c_boolean *) member, scratch + rv);
- buffer->append (buffer, rv, scratch);
- break;
- case PROTOBUF_C_TYPE_STRING:
- {
- const char *str = *(char * const *) member;
- size_t sublen = str ? strlen (str) : 0;
- scratch[0] |= PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED;
- rv += uint32_pack (sublen, scratch + rv);
- buffer->append (buffer, rv, scratch);
- buffer->append (buffer, sublen, (const uint8_t *) str);
- rv += sublen;
- break;
- }
-
- case PROTOBUF_C_TYPE_BYTES:
- {
- const ProtobufCBinaryData * bd = ((const ProtobufCBinaryData*) member);
- size_t sublen = bd->len;
- scratch[0] |= PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED;
- rv += uint32_pack (sublen, scratch + rv);
- buffer->append (buffer, rv, scratch);
- buffer->append (buffer, sublen, bd->data);
- rv += sublen;
- break;
- }
- //PROTOBUF_C_TYPE_GROUP, // NOT SUPPORTED
- case PROTOBUF_C_TYPE_MESSAGE:
- {
- uint8_t simple_buffer_scratch[256];
- size_t sublen;
- ProtobufCBufferSimple simple_buffer
- = PROTOBUF_C_BUFFER_SIMPLE_INIT (simple_buffer_scratch);
- const ProtobufCMessage *msg = *(ProtobufCMessage * const *) member;
- scratch[0] |= PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED;
- if (msg == NULL)
- sublen = 0;
- else
- sublen = protobuf_c_message_pack_to_buffer (msg, &simple_buffer.base);
- rv += uint32_pack (sublen, scratch + rv);
- buffer->append (buffer, rv, scratch);
- buffer->append (buffer, sublen, simple_buffer.data);
- rv += sublen;
- PROTOBUF_C_BUFFER_SIMPLE_CLEAR (&simple_buffer);
- break;
- }
- default:
- PROTOBUF_C_ASSERT_NOT_REACHED ();
- }
- return rv;
- }
- static size_t
- optional_field_pack_to_buffer (const ProtobufCFieldDescriptor *field,
- const protobuf_c_boolean *has,
- const void *member,
- ProtobufCBuffer *buffer)
- {
- 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
- {
- if (!*has)
- return 0;
- }
- 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,
- const void *member,
- ProtobufCBuffer *buffer)
- {
- char *array = * (char * const *) member;
- if (count == 0)
- return 0;
- if (field->packed)
- {
- 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;
- }
- }
- static size_t
- unknown_field_pack_to_buffer (const ProtobufCMessageUnknownField *field,
- ProtobufCBuffer *buffer)
- {
- uint8_t header[MAX_UINT64_ENCODED_SIZE];
- size_t rv = tag_pack (field->tag, header);
- header[0] |= field->wire_type;
- buffer->append (buffer, rv, header);
- buffer->append (buffer, field->len, field->data);
- return rv + field->len;
- }
- size_t
- protobuf_c_message_pack_to_buffer (const ProtobufCMessage *message,
- ProtobufCBuffer *buffer)
- {
- unsigned i;
- size_t rv = 0;
- ASSERT_IS_MESSAGE (message);
- for (i = 0; i < message->descriptor->n_fields; i++)
- {
- const ProtobufCFieldDescriptor *field = message->descriptor->fields + i;
- const void *member = ((const char *) message) + field->offset;
- const void *qmember = ((const char *) message) + field->quantifier_offset;
- 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);
- else
- rv += repeated_field_pack_to_buffer (field, * (const size_t *) qmember, member, buffer);
- }
- for (i = 0; i < message->n_unknown_fields; i++)
- rv += unknown_field_pack_to_buffer (&message->unknown_fields[i], buffer);
- return rv;
- }
- /* === unpacking === */
- #if PRINT_UNPACK_ERRORS
- # define UNPACK_ERROR(args) do { printf args;printf("\n"); }while(0)
- #else
- # define UNPACK_ERROR(args) do { } while (0)
- #endif
- static inline int
- int_range_lookup (unsigned n_ranges,
- const ProtobufCIntRange *ranges,
- int value)
- {
- unsigned start, n;
- if (n_ranges == 0)
- return -1;
- start = 0;
- n = n_ranges;
- while (n > 1)
- {
- unsigned mid = start + n / 2;
- if (value < ranges[mid].start_value)
- {
- n = mid - start;
- }
- else if (value >= ranges[mid].start_value + (int)(ranges[mid+1].orig_index-ranges[mid].orig_index))
- {
- unsigned new_start = mid + 1;
- n = start + n - new_start;
- start = new_start;
- }
- else
- return (value - ranges[mid].start_value) + ranges[mid].orig_index;
- }
- if (n > 0)
- {
- unsigned start_orig_index = ranges[start].orig_index;
- unsigned range_size = ranges[start+1].orig_index - start_orig_index;
- if (ranges[start].start_value <= value
- && value < (int)(ranges[start].start_value + range_size))
- return (value - ranges[start].start_value) + start_orig_index;
- }
- return -1;
- }
- static size_t
- parse_tag_and_wiretype (size_t len,
- const uint8_t *data,
- uint32_t *tag_out,
- ProtobufCWireType *wiretype_out)
- {
- unsigned max_rv = len > 5 ? 5 : len;
- uint32_t tag = (data[0]&0x7f) >> 3;
- unsigned shift = 4;
- unsigned rv;
- *wiretype_out = data[0] & 7;
- if ((data[0] & 0x80) == 0)
- {
- *tag_out = tag;
- return 1;
- }
- for (rv = 1; rv < max_rv; rv++)
- if (data[rv] & 0x80)
- {
- tag |= (data[rv] & 0x7f) << shift;
- shift += 7;
- }
- else
- {
- tag |= data[rv] << shift;
- *tag_out = tag;
- return rv + 1;
- }
- return 0; /* error: bad header */
- }
- /* sizeof(ScannedMember) must be <= (1<<BOUND_SIZEOF_SCANNED_MEMBER_LOG2) */
- #define BOUND_SIZEOF_SCANNED_MEMBER_LOG2 5
- typedef struct _ScannedMember ScannedMember;
- struct _ScannedMember
- {
- uint32_t tag;
- uint8_t wire_type;
- uint8_t length_prefix_len;
- const ProtobufCFieldDescriptor *field;
- size_t len;
- const uint8_t *data;
- };
- static inline uint32_t
- scan_length_prefixed_data (size_t len, const uint8_t *data, size_t *prefix_len_out)
- {
- unsigned hdr_max = len < 5 ? len : 5;
- unsigned hdr_len;
- uint32_t val = 0;
- unsigned i;
- unsigned shift = 0;
- for (i = 0; i < hdr_max; i++)
- {
- val |= (data[i] & 0x7f) << shift;
- shift += 7;
- if ((data[i] & 0x80) == 0)
- break;
- }
- if (i == hdr_max)
- {
- UNPACK_ERROR (("error parsing length for length-prefixed data"));
- return 0;
- }
- hdr_len = i + 1;
- *prefix_len_out = hdr_len;
- if (hdr_len + val > len)
- {
- UNPACK_ERROR (("data too short after length-prefix of %u",
- val));
- return 0;
- }
- 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)
- {
- unsigned rv = data[0] & 0x7f;
- if (len > 1)
- {
- rv |= ((data[1] & 0x7f) << 7);
- if (len > 2)
- {
- rv |= ((data[2] & 0x7f) << 14);
- if (len > 3)
- {
- rv |= ((data[3] & 0x7f) << 21);
- if (len > 4)
- rv |= (data[4] << 28);
- }
- }
- }
- return rv;
- }
- static inline uint32_t
- parse_int32 (unsigned len, const uint8_t *data)
- {
- return parse_uint32 (len, data);
- }
- static inline int32_t
- unzigzag32 (uint32_t v)
- {
- if (v&1)
- return -(v>>1) - 1;
- else
- return v>>1;
- }
- static inline uint32_t
- parse_fixed_uint32 (const uint8_t *data)
- {
- #if IS_LITTLE_ENDIAN
- uint32_t t;
- memcpy (&t, data, 4);
- return t;
- #else
- return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
- #endif
- }
- static uint64_t
- parse_uint64 (unsigned len, const uint8_t *data)
- {
- unsigned shift, i;
- uint64_t rv;
- if (len < 5)
- return parse_uint32 (len, data);
- rv = ((data[0] & 0x7f))
- | ((data[1] & 0x7f)<<7)
- | ((data[2] & 0x7f)<<14)
- | ((data[3] & 0x7f)<<21);
- shift = 28;
- for (i = 4; i < len; i++)
- {
- rv |= (((uint64_t)(data[i]&0x7f)) << shift);
- shift += 7;
- }
- return rv;
- }
- static inline int64_t
- unzigzag64 (uint64_t v)
- {
- if (v&1)
- return -(v>>1) - 1;
- else
- return v>>1;
- }
- static inline uint64_t
- par…
Large files files are truncated, but you can click here to view the full file