1. I'm not using git-subrepo for this because most of the protobuf-c git repository is for the protocol buffers compiler integration, which we do not need because nats.c already has the generated files in its source tree. 2. The protobuf-c runtime library is pretty simple, so maintaining its build process should be quite straightforward. It seems stable as well, which should simplify the maintenance burden. 3. Not having streaming support is the last major nats.c feature missing from this project (as far as I am aware). Building this functionality into the library, even if a nice zig API is missing (as it is with jetStream as well), can still allow a determined user to wrap and use the functionality while benefitting from the zig build system and package manager, which is a win.
3673 lines
94 KiB
C
3673 lines
94 KiB
C
/*
|
|
* Copyright (c) 2008-2022, Dave Benson and the protobuf-c authors.
|
|
* 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.
|
|
*
|
|
* 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
|
|
* OWNER 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.
|
|
*/
|
|
|
|
/*! \file
|
|
* Support library for `protoc-c` generated code.
|
|
*
|
|
* This file implements the public API used by the code generated
|
|
* by `protoc-c`.
|
|
*
|
|
* \authors Dave Benson and the protobuf-c authors
|
|
*
|
|
* \copyright 2008-2014. Licensed under the terms of the [BSD-2-Clause] license.
|
|
*/
|
|
|
|
/**
|
|
* \todo 64-BIT OPTIMIZATION: certain implementations use 32-bit math
|
|
* even on 64-bit platforms (uint64_size, uint64_pack, parse_uint64).
|
|
*
|
|
* \todo Use size_t consistently.
|
|
*/
|
|
|
|
#include <stdlib.h> /* for malloc, free */
|
|
#include <string.h> /* for strcmp, strlen, memcpy, memmove, memset */
|
|
|
|
#include "protobuf-c.h"
|
|
|
|
#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
|
|
*
|
|
* These are not exported by the library but are useful to developers working
|
|
* on `libprotobuf-c` itself.
|
|
*/
|
|
|
|
/**
|
|
* \defgroup macros Utility macros for manipulating structures
|
|
*
|
|
* Macros and constants used to manipulate the base "classes" generated by
|
|
* `protobuf-c`. They also define limits and check correctness.
|
|
*
|
|
* \ingroup internal
|
|
* @{
|
|
*/
|
|
|
|
/** The maximum length of a 64-bit integer in varint encoding. */
|
|
#define MAX_UINT64_ENCODED_SIZE 10
|
|
|
|
#ifndef PROTOBUF_C_UNPACK_ERROR
|
|
# define PROTOBUF_C_UNPACK_ERROR(...)
|
|
#endif
|
|
|
|
#if !defined(_WIN32) || !defined(PROTOBUF_C_USE_SHARED_LIB)
|
|
const char protobuf_c_empty_string[] = "";
|
|
#endif
|
|
|
|
/**
|
|
* Internal `ProtobufCMessage` manipulation macro.
|
|
*
|
|
* Base macro for manipulating a `ProtobufCMessage`. Used by STRUCT_MEMBER() and
|
|
* STRUCT_MEMBER_PTR().
|
|
*/
|
|
#define STRUCT_MEMBER_P(struct_p, struct_offset) \
|
|
((void *) ((uint8_t *) (struct_p) + (struct_offset)))
|
|
|
|
/**
|
|
* Return field in a `ProtobufCMessage` based on offset.
|
|
*
|
|
* Take a pointer to a `ProtobufCMessage` and find the field at the offset.
|
|
* Cast it to the passed type.
|
|
*/
|
|
#define STRUCT_MEMBER(member_type, struct_p, struct_offset) \
|
|
(*(member_type *) STRUCT_MEMBER_P((struct_p), (struct_offset)))
|
|
|
|
/**
|
|
* Return field in a `ProtobufCMessage` based on offset.
|
|
*
|
|
* Take a pointer to a `ProtobufCMessage` and find the field at the offset. Cast
|
|
* it to a pointer to the passed type.
|
|
*/
|
|
#define STRUCT_MEMBER_PTR(member_type, struct_p, struct_offset) \
|
|
((member_type *) STRUCT_MEMBER_P((struct_p), (struct_offset)))
|
|
|
|
/* Assertions for magic numbers. */
|
|
|
|
#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)
|
|
|
|
/**@}*/
|
|
|
|
/* --- version --- */
|
|
|
|
const char *
|
|
protobuf_c_version(void)
|
|
{
|
|
return PROTOBUF_C_VERSION;
|
|
}
|
|
|
|
uint32_t
|
|
protobuf_c_version_number(void)
|
|
{
|
|
return PROTOBUF_C_VERSION_NUMBER;
|
|
}
|
|
|
|
/* --- allocator --- */
|
|
|
|
static void *
|
|
system_alloc(void *allocator_data, size_t size)
|
|
{
|
|
(void)allocator_data;
|
|
return malloc(size);
|
|
}
|
|
|
|
static void
|
|
system_free(void *allocator_data, void *data)
|
|
{
|
|
(void)allocator_data;
|
|
free(data);
|
|
}
|
|
|
|
static inline void *
|
|
do_alloc(ProtobufCAllocator *allocator, size_t size)
|
|
{
|
|
return allocator->alloc(allocator->allocator_data, size);
|
|
}
|
|
|
|
static inline void
|
|
do_free(ProtobufCAllocator *allocator, void *data)
|
|
{
|
|
if (data != NULL)
|
|
allocator->free(allocator->allocator_data, data);
|
|
}
|
|
|
|
/*
|
|
* This allocator uses the system's malloc() and free(). It is the default
|
|
* allocator used if NULL is passed as the ProtobufCAllocator to an exported
|
|
* function.
|
|
*/
|
|
static ProtobufCAllocator protobuf_c__allocator = {
|
|
.alloc = &system_alloc,
|
|
.free = &system_free,
|
|
.allocator_data = NULL,
|
|
};
|
|
|
|
/* === 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) {
|
|
ProtobufCAllocator *allocator = simp->allocator;
|
|
size_t new_alloced = simp->alloced * 2;
|
|
uint8_t *new_data;
|
|
|
|
if (allocator == NULL)
|
|
allocator = &protobuf_c__allocator;
|
|
while (new_alloced < new_len)
|
|
new_alloced += new_alloced;
|
|
new_data = do_alloc(allocator, new_alloced);
|
|
if (!new_data)
|
|
return;
|
|
memcpy(new_data, simp->data, simp->len);
|
|
if (simp->must_free_data)
|
|
do_free(allocator, simp->data);
|
|
else
|
|
simp->must_free_data = TRUE;
|
|
simp->data = new_data;
|
|
simp->alloced = new_alloced;
|
|
}
|
|
memcpy(simp->data + simp->len, data, len);
|
|
simp->len = new_len;
|
|
}
|
|
|
|
/**
|
|
* \defgroup packedsz protobuf_c_message_get_packed_size() implementation
|
|
*
|
|
* Routines mainly used by protobuf_c_message_get_packed_size().
|
|
*
|
|
* \ingroup internal
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* Return the number of bytes required to store the tag for the field. Includes
|
|
* 3 bits for the wire-type, and a single bit that denotes the end-of-tag.
|
|
*
|
|
* \param number
|
|
* Field tag to encode.
|
|
* \return
|
|
* Number of bytes required.
|
|
*/
|
|
static inline size_t
|
|
get_tag_size(uint32_t number)
|
|
{
|
|
if (number < (1UL << 4)) {
|
|
return 1;
|
|
} else if (number < (1UL << 11)) {
|
|
return 2;
|
|
} else if (number < (1UL << 18)) {
|
|
return 3;
|
|
} else if (number < (1UL << 25)) {
|
|
return 4;
|
|
} else {
|
|
return 5;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return the number of bytes required to store a variable-length unsigned
|
|
* 32-bit integer in base-128 varint encoding.
|
|
*
|
|
* \param v
|
|
* Value to encode.
|
|
* \return
|
|
* Number of bytes required.
|
|
*/
|
|
static inline size_t
|
|
uint32_size(uint32_t v)
|
|
{
|
|
if (v < (1UL << 7)) {
|
|
return 1;
|
|
} else if (v < (1UL << 14)) {
|
|
return 2;
|
|
} else if (v < (1UL << 21)) {
|
|
return 3;
|
|
} else if (v < (1UL << 28)) {
|
|
return 4;
|
|
} else {
|
|
return 5;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return the number of bytes required to store a variable-length signed 32-bit
|
|
* integer in base-128 varint encoding.
|
|
*
|
|
* \param v
|
|
* Value to encode.
|
|
* \return
|
|
* Number of bytes required.
|
|
*/
|
|
static inline size_t
|
|
int32_size(int32_t v)
|
|
{
|
|
if (v < 0) {
|
|
return 10;
|
|
} else if (v < (1L << 7)) {
|
|
return 1;
|
|
} else if (v < (1L << 14)) {
|
|
return 2;
|
|
} else if (v < (1L << 21)) {
|
|
return 3;
|
|
} else if (v < (1L << 28)) {
|
|
return 4;
|
|
} else {
|
|
return 5;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return the ZigZag-encoded 32-bit unsigned integer form of a 32-bit signed
|
|
* integer.
|
|
*
|
|
* \param v
|
|
* Value to encode.
|
|
* \return
|
|
* ZigZag encoded integer.
|
|
*/
|
|
static inline uint32_t
|
|
zigzag32(int32_t v)
|
|
{
|
|
// Note: Using unsigned types prevents undefined behavior
|
|
return ((uint32_t)v << 1) ^ -((uint32_t)v >> 31);
|
|
}
|
|
|
|
/**
|
|
* Return the number of bytes required to store a signed 32-bit integer,
|
|
* converted to an unsigned 32-bit integer with ZigZag encoding, using base-128
|
|
* varint encoding.
|
|
*
|
|
* \param v
|
|
* Value to encode.
|
|
* \return
|
|
* Number of bytes required.
|
|
*/
|
|
static inline size_t
|
|
sint32_size(int32_t v)
|
|
{
|
|
return uint32_size(zigzag32(v));
|
|
}
|
|
|
|
/**
|
|
* Return the number of bytes required to store a 64-bit unsigned integer in
|
|
* base-128 varint encoding.
|
|
*
|
|
* \param v
|
|
* Value to encode.
|
|
* \return
|
|
* Number of bytes required.
|
|
*/
|
|
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 < (1UL << 3)) {
|
|
return 5;
|
|
} else if (upper_v < (1UL << 10)) {
|
|
return 6;
|
|
} else if (upper_v < (1UL << 17)) {
|
|
return 7;
|
|
} else if (upper_v < (1UL << 24)) {
|
|
return 8;
|
|
} else if (upper_v < (1UL << 31)) {
|
|
return 9;
|
|
} else {
|
|
return 10;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return the ZigZag-encoded 64-bit unsigned integer form of a 64-bit signed
|
|
* integer.
|
|
*
|
|
* \param v
|
|
* Value to encode.
|
|
* \return
|
|
* ZigZag encoded integer.
|
|
*/
|
|
static inline uint64_t
|
|
zigzag64(int64_t v)
|
|
{
|
|
// Note: Using unsigned types prevents undefined behavior
|
|
return ((uint64_t)v << 1) ^ -((uint64_t)v >> 63);
|
|
}
|
|
|
|
/**
|
|
* Return the number of bytes required to store a signed 64-bit integer,
|
|
* converted to an unsigned 64-bit integer with ZigZag encoding, using base-128
|
|
* varint encoding.
|
|
*
|
|
* \param v
|
|
* Value to encode.
|
|
* \return
|
|
* Number of bytes required.
|
|
*/
|
|
static inline size_t
|
|
sint64_size(int64_t v)
|
|
{
|
|
return uint64_size(zigzag64(v));
|
|
}
|
|
|
|
/**
|
|
* Calculate the serialized size of a single required message field, including
|
|
* the space needed by the preceding tag.
|
|
*
|
|
* \param field
|
|
* Field descriptor for member.
|
|
* \param member
|
|
* Field to encode.
|
|
* \return
|
|
* Number of bytes required.
|
|
*/
|
|
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_ENUM:
|
|
case PROTOBUF_C_TYPE_INT32:
|
|
return rv + int32_size(*(const int32_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_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_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;
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
* Enum value 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,
|
|
uint32_t oneof_case,
|
|
const void *member)
|
|
{
|
|
if (oneof_case != field->id) {
|
|
return 0;
|
|
}
|
|
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;
|
|
}
|
|
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.
|
|
*
|
|
* \param field
|
|
* Field descriptor for member.
|
|
* \param has
|
|
* True if the field exists, false if not.
|
|
* \param member
|
|
* Field to encode.
|
|
* \return
|
|
* Number of bytes required.
|
|
*/
|
|
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);
|
|
}
|
|
|
|
static protobuf_c_boolean
|
|
field_is_zeroish(const ProtobufCFieldDescriptor *field,
|
|
const void *member)
|
|
{
|
|
protobuf_c_boolean ret = FALSE;
|
|
|
|
switch (field->type) {
|
|
case PROTOBUF_C_TYPE_BOOL:
|
|
ret = (0 == *(const protobuf_c_boolean *) member);
|
|
break;
|
|
case PROTOBUF_C_TYPE_ENUM:
|
|
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:
|
|
ret = (0 == *(const uint32_t *) member);
|
|
break;
|
|
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:
|
|
ret = (0 == *(const uint64_t *) member);
|
|
break;
|
|
case PROTOBUF_C_TYPE_FLOAT:
|
|
ret = (0 == *(const float *) member);
|
|
break;
|
|
case PROTOBUF_C_TYPE_DOUBLE:
|
|
ret = (0 == *(const double *) member);
|
|
break;
|
|
case PROTOBUF_C_TYPE_STRING:
|
|
ret = (NULL == *(const char * const *) member) ||
|
|
('\0' == **(const char * const *) member);
|
|
break;
|
|
case PROTOBUF_C_TYPE_BYTES:
|
|
case PROTOBUF_C_TYPE_MESSAGE:
|
|
ret = (NULL == *(const void * const *) member);
|
|
break;
|
|
default:
|
|
ret = TRUE;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Calculate the serialized size of a single unlabeled message field, including
|
|
* the space needed by the preceding tag. Returns 0 if the field isn't set or
|
|
* if it is set to a "zeroish" value (null pointer or 0 for numerical values).
|
|
* Unlabeled fields are supported only in proto3.
|
|
*
|
|
* \param field
|
|
* Field descriptor for member.
|
|
* \param member
|
|
* Field to encode.
|
|
* \return
|
|
* Number of bytes required.
|
|
*/
|
|
static size_t
|
|
unlabeled_field_get_packed_size(const ProtobufCFieldDescriptor *field,
|
|
const void *member)
|
|
{
|
|
if (field_is_zeroish(field, member))
|
|
return 0;
|
|
return required_field_get_packed_size(field, member);
|
|
}
|
|
|
|
/**
|
|
* Calculate the serialized size of repeated message fields, which may consist
|
|
* of any number of values (including 0). Includes the space needed by the
|
|
* preceding tags (as needed).
|
|
*
|
|
* \param field
|
|
* Field descriptor for member.
|
|
* \param count
|
|
* Number of repeated field members.
|
|
* \param member
|
|
* Field to encode.
|
|
* \return
|
|
* Number of bytes required.
|
|
*/
|
|
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 (0 == (field->flags & PROTOBUF_C_FIELD_FLAG_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_ENUM:
|
|
case PROTOBUF_C_TYPE_INT32:
|
|
for (i = 0; i < count; i++)
|
|
rv += int32_size(((int32_t *) array)[i]);
|
|
break;
|
|
case PROTOBUF_C_TYPE_UINT32:
|
|
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;
|
|
}
|
|
|
|
if (0 != (field->flags & PROTOBUF_C_FIELD_FLAG_PACKED))
|
|
header_size += uint32_size(rv);
|
|
return header_size + rv;
|
|
}
|
|
|
|
/**
|
|
* Calculate the serialized size of an unknown field, i.e. one that is passed
|
|
* through mostly uninterpreted. This is required for forward compatibility if
|
|
* new fields are added to the message descriptor.
|
|
*
|
|
* \param field
|
|
* Unknown field type.
|
|
* \return
|
|
* Number of bytes required.
|
|
*/
|
|
static inline size_t
|
|
unknown_field_get_packed_size(const ProtobufCMessageUnknownField *field)
|
|
{
|
|
return get_tag_size(field->tag) + field->len;
|
|
}
|
|
|
|
/**@}*/
|
|
|
|
/*
|
|
* Calculate the serialized size of the message.
|
|
*/
|
|
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 ||
|
|
field->label == PROTOBUF_C_LABEL_NONE) &&
|
|
(0 != (field->flags & PROTOBUF_C_FIELD_FLAG_ONEOF))) {
|
|
rv += oneof_field_get_packed_size(
|
|
field,
|
|
*(const uint32_t *) qmember,
|
|
member
|
|
);
|
|
} else if (field->label == PROTOBUF_C_LABEL_OPTIONAL) {
|
|
rv += optional_field_get_packed_size(
|
|
field,
|
|
*(protobuf_c_boolean *) qmember,
|
|
member
|
|
);
|
|
} else if (field->label == PROTOBUF_C_LABEL_NONE) {
|
|
rv += unlabeled_field_get_packed_size(
|
|
field,
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* \defgroup pack protobuf_c_message_pack() implementation
|
|
*
|
|
* Routines mainly used by protobuf_c_message_pack().
|
|
*
|
|
* \ingroup internal
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* Pack an unsigned 32-bit integer in base-128 varint encoding and return the
|
|
* number of bytes written, which must be 5 or less.
|
|
*
|
|
* \param value
|
|
* Value to encode.
|
|
* \param[out] out
|
|
* Packed value.
|
|
* \return
|
|
* Number of bytes written to `out`.
|
|
*/
|
|
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 signed 32-bit integer and return the number of bytes written,
|
|
* passed as unsigned to avoid implementation-specific behavior.
|
|
* Negative numbers are encoded as two's complement 64-bit integers.
|
|
*
|
|
* \param value
|
|
* Value to encode.
|
|
* \param[out] out
|
|
* Packed value.
|
|
* \return
|
|
* Number of bytes written to `out`.
|
|
*/
|
|
static inline size_t
|
|
int32_pack(uint32_t value, uint8_t *out)
|
|
{
|
|
if ((int32_t)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) | 0xf0;
|
|
out[5] = out[6] = out[7] = out[8] = 0xff;
|
|
out[9] = 0x01;
|
|
return 10;
|
|
} else {
|
|
return uint32_pack(value, out);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Pack a signed 32-bit integer using ZigZag encoding and return the number of
|
|
* bytes written.
|
|
*
|
|
* \param value
|
|
* Value to encode.
|
|
* \param[out] out
|
|
* Packed value.
|
|
* \return
|
|
* Number of bytes written to `out`.
|
|
*/
|
|
static inline size_t
|
|
sint32_pack(int32_t value, uint8_t *out)
|
|
{
|
|
return uint32_pack(zigzag32(value), out);
|
|
}
|
|
|
|
/**
|
|
* Pack a 64-bit unsigned integer using base-128 varint encoding and return the
|
|
* number of bytes written.
|
|
*
|
|
* \param value
|
|
* Value to encode.
|
|
* \param[out] out
|
|
* Packed value.
|
|
* \return
|
|
* Number of bytes written to `out`.
|
|
*/
|
|
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 ZigZag encoding and return the number of
|
|
* bytes written.
|
|
*
|
|
* \param value
|
|
* Value to encode.
|
|
* \param[out] out
|
|
* Packed value.
|
|
* \return
|
|
* Number of bytes written to `out`.
|
|
*/
|
|
static inline size_t
|
|
sint64_pack(int64_t value, uint8_t *out)
|
|
{
|
|
return uint64_pack(zigzag64(value), out);
|
|
}
|
|
|
|
/**
|
|
* Pack a 32-bit quantity in little-endian byte order. Used for protobuf wire
|
|
* types fixed32, sfixed32, float. Similar to "htole32".
|
|
*
|
|
* \param value
|
|
* Value to encode.
|
|
* \param[out] out
|
|
* Packed value.
|
|
* \return
|
|
* Number of bytes written to `out`.
|
|
*/
|
|
static inline size_t
|
|
fixed32_pack(uint32_t value, void *out)
|
|
{
|
|
#if !defined(WORDS_BIGENDIAN)
|
|
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 quantity in little-endian byte order. Used for protobuf wire
|
|
* types fixed64, sfixed64, double. Similar to "htole64".
|
|
*
|
|
* \todo 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.
|
|
*
|
|
* \param value
|
|
* Value to encode.
|
|
* \param[out] out
|
|
* Packed value.
|
|
* \return
|
|
* Number of bytes written to `out`.
|
|
*/
|
|
static inline size_t
|
|
fixed64_pack(uint64_t value, void *out)
|
|
{
|
|
#if !defined(WORDS_BIGENDIAN)
|
|
memcpy(out, &value, 8);
|
|
#else
|
|
fixed32_pack(value, out);
|
|
fixed32_pack(value >> 32, ((char *) out) + 4);
|
|
#endif
|
|
return 8;
|
|
}
|
|
|
|
/**
|
|
* Pack a boolean value as an integer and return the number of bytes written.
|
|
*
|
|
* \todo Perhaps on some platforms *out = !!value would be a better impl, b/c
|
|
* that is idiomatic C++ in some STL implementations.
|
|
*
|
|
* \param value
|
|
* Value to encode.
|
|
* \param[out] out
|
|
* Packed value.
|
|
* \return
|
|
* Number of bytes written to `out`.
|
|
*/
|
|
static inline size_t
|
|
boolean_pack(protobuf_c_boolean value, uint8_t *out)
|
|
{
|
|
*out = value ? TRUE : FALSE;
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* Pack a NUL-terminated C string and return the number of bytes written. The
|
|
* output includes a length delimiter.
|
|
*
|
|
* 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).
|
|
*
|
|
* \param str
|
|
* String to encode.
|
|
* \param[out] out
|
|
* Packed value.
|
|
* \return
|
|
* Number of bytes written to `out`.
|
|
*/
|
|
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;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Pack a ProtobufCBinaryData and return the number of bytes written. The output
|
|
* includes a length delimiter.
|
|
*
|
|
* \param bd
|
|
* ProtobufCBinaryData to encode.
|
|
* \param[out] out
|
|
* Packed value.
|
|
* \return
|
|
* Number of bytes written to `out`.
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* Pack a ProtobufCMessage and return the number of bytes written. The output
|
|
* includes a length delimiter.
|
|
*
|
|
* \param message
|
|
* ProtobufCMessage object to pack.
|
|
* \param[out] out
|
|
* Packed message.
|
|
* \return
|
|
* Number of bytes written to `out`.
|
|
*/
|
|
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;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Pack a field tag.
|
|
*
|
|
* Wire-type will be added in required_field_pack().
|
|
*
|
|
* \todo Just call uint64_pack on 64-bit platforms.
|
|
*
|
|
* \param id
|
|
* Tag value to encode.
|
|
* \param[out] out
|
|
* Packed value.
|
|
* \return
|
|
* Number of bytes written to `out`.
|
|
*/
|
|
static size_t
|
|
tag_pack(uint32_t id, uint8_t *out)
|
|
{
|
|
if (id < (1UL << (32 - 3)))
|
|
return uint32_pack(id << 3, out);
|
|
else
|
|
return uint64_pack(((uint64_t) id) << 3, out);
|
|
}
|
|
|
|
/**
|
|
* Pack a required field and return the number of bytes written.
|
|
*
|
|
* \param field
|
|
* Field descriptor.
|
|
* \param member
|
|
* The field member.
|
|
* \param[out] out
|
|
* Packed value.
|
|
* \return
|
|
* Number of bytes written to `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_ENUM:
|
|
case PROTOBUF_C_TYPE_INT32:
|
|
out[0] |= PROTOBUF_C_WIRE_TYPE_VARINT;
|
|
return rv + int32_pack(*(const int32_t *) member, out + rv);
|
|
case PROTOBUF_C_TYPE_UINT32:
|
|
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:
|
|
out[0] |= PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED;
|
|
return rv + binary_data_pack((const ProtobufCBinaryData *) member, out + rv);
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
* Enum value 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,
|
|
uint32_t oneof_case,
|
|
const void *member, uint8_t *out)
|
|
{
|
|
if (oneof_case != field->id) {
|
|
return 0;
|
|
}
|
|
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;
|
|
}
|
|
return required_field_pack(field, member, out);
|
|
}
|
|
|
|
/**
|
|
* Pack an optional field and return the number of bytes written.
|
|
*
|
|
* \param field
|
|
* Field descriptor.
|
|
* \param has
|
|
* Whether the field is set.
|
|
* \param member
|
|
* The field member.
|
|
* \param[out] out
|
|
* Packed value.
|
|
* \return
|
|
* Number of bytes written to `out`.
|
|
*/
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* Pack an unlabeled field and return the number of bytes written.
|
|
*
|
|
* \param field
|
|
* Field descriptor.
|
|
* \param member
|
|
* The field member.
|
|
* \param[out] out
|
|
* Packed value.
|
|
* \return
|
|
* Number of bytes written to `out`.
|
|
*/
|
|
static size_t
|
|
unlabeled_field_pack(const ProtobufCFieldDescriptor *field,
|
|
const void *member, uint8_t *out)
|
|
{
|
|
if (field_is_zeroish(field, member))
|
|
return 0;
|
|
return required_field_pack(field, member, out);
|
|
}
|
|
|
|
/**
|
|
* Given a field type, return the in-memory size.
|
|
*
|
|
* \todo Implement as a table lookup.
|
|
*
|
|
* \param type
|
|
* Field type.
|
|
* \return
|
|
* Size of the field.
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* Pack an array of 32-bit quantities.
|
|
*
|
|
* \param[out] out
|
|
* Destination.
|
|
* \param[in] in
|
|
* Source.
|
|
* \param[in] n
|
|
* Number of elements in the source array.
|
|
*/
|
|
static void
|
|
copy_to_little_endian_32(void *out, const void *in, const unsigned n)
|
|
{
|
|
#if !defined(WORDS_BIGENDIAN)
|
|
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
|
|
}
|
|
|
|
/**
|
|
* Pack an array of 64-bit quantities.
|
|
*
|
|
* \param[out] out
|
|
* Destination.
|
|
* \param[in] in
|
|
* Source.
|
|
* \param[in] n
|
|
* Number of elements in the source array.
|
|
*/
|
|
static void
|
|
copy_to_little_endian_64(void *out, const void *in, const unsigned n)
|
|
{
|
|
#if !defined(WORDS_BIGENDIAN)
|
|
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
|
|
}
|
|
|
|
/**
|
|
* Get the minimum number of bytes required to pack a field value of a
|
|
* particular type.
|
|
*
|
|
* \param type
|
|
* Field type.
|
|
* \return
|
|
* Number of bytes.
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* Packs the elements of a repeated field and returns the serialised field and
|
|
* its length.
|
|
*
|
|
* \param field
|
|
* Field descriptor.
|
|
* \param count
|
|
* Number of elements in the repeated field array.
|
|
* \param member
|
|
* Pointer to the elements for this repeated field.
|
|
* \param[out] out
|
|
* Serialised representation of the repeated field.
|
|
* \return
|
|
* Number of bytes serialised to `out`.
|
|
*/
|
|
static size_t
|
|
repeated_field_pack(const ProtobufCFieldDescriptor *field,
|
|
size_t count, const void *member, uint8_t *out)
|
|
{
|
|
void *array = *(void * const *) member;
|
|
unsigned i;
|
|
|
|
if (0 != (field->flags & PROTOBUF_C_FIELD_FLAG_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_ENUM:
|
|
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_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:
|
|
PROTOBUF_C__ASSERT_NOT_REACHED();
|
|
}
|
|
|
|
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 {
|
|
/* not "packed" cased */
|
|
/* 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 = (char *)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:
|
|
* - 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).
|
|
*/
|
|
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 ||
|
|
field->label == PROTOBUF_C_LABEL_NONE) &&
|
|
(0 != (field->flags & PROTOBUF_C_FIELD_FLAG_ONEOF))) {
|
|
rv += oneof_field_pack(
|
|
field,
|
|
*(const uint32_t *) qmember,
|
|
member,
|
|
out + rv
|
|
);
|
|
} else if (field->label == PROTOBUF_C_LABEL_OPTIONAL) {
|
|
rv += optional_field_pack(
|
|
field,
|
|
*(const protobuf_c_boolean *) qmember,
|
|
member,
|
|
out + rv
|
|
);
|
|
} else if (field->label == PROTOBUF_C_LABEL_NONE) {
|
|
rv += unlabeled_field_pack(field, 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;
|
|
}
|
|
|
|
/**
|
|
* \defgroup packbuf protobuf_c_message_pack_to_buffer() implementation
|
|
*
|
|
* Routines mainly used by protobuf_c_message_pack_to_buffer().
|
|
*
|
|
* \ingroup internal
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* Pack a required field to a virtual buffer.
|
|
*
|
|
* \param field
|
|
* Field descriptor.
|
|
* \param member
|
|
* The element to be packed.
|
|
* \param[out] buffer
|
|
* Virtual buffer to append data to.
|
|
* \return
|
|
* Number of bytes packed.
|
|
*/
|
|
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_ENUM:
|
|
case PROTOBUF_C_TYPE_INT32:
|
|
scratch[0] |= PROTOBUF_C_WIRE_TYPE_VARINT;
|
|
rv += int32_pack(*(const int32_t *) member, scratch + rv);
|
|
buffer->append(buffer, rv, scratch);
|
|
break;
|
|
case PROTOBUF_C_TYPE_UINT32:
|
|
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;
|
|
}
|
|
case PROTOBUF_C_TYPE_MESSAGE: {
|
|
const ProtobufCMessage *msg = *(ProtobufCMessage * const *) member;
|
|
|
|
scratch[0] |= PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED;
|
|
if (msg == NULL) {
|
|
rv += uint32_pack(0, scratch + rv);
|
|
buffer->append(buffer, rv, scratch);
|
|
} else {
|
|
size_t sublen = protobuf_c_message_get_packed_size(msg);
|
|
rv += uint32_pack(sublen, scratch + rv);
|
|
buffer->append(buffer, rv, scratch);
|
|
protobuf_c_message_pack_to_buffer(msg, buffer);
|
|
rv += sublen;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
PROTOBUF_C__ASSERT_NOT_REACHED();
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
/**
|
|
* Pack a oneof field to a buffer. Only packs the field that is selected by the case enum.
|
|
*
|
|
* \param field
|
|
* Field descriptor.
|
|
* \param oneof_case
|
|
* Enum value 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,
|
|
uint32_t oneof_case,
|
|
const void *member, ProtobufCBuffer *buffer)
|
|
{
|
|
if (oneof_case != field->id) {
|
|
return 0;
|
|
}
|
|
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;
|
|
}
|
|
return required_field_pack_to_buffer(field, member, buffer);
|
|
}
|
|
|
|
/**
|
|
* Pack an optional field to a buffer.
|
|
*
|
|
* \param field
|
|
* Field descriptor.
|
|
* \param has
|
|
* Whether the field is set.
|
|
* \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
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* Pack an unlabeled field to a buffer.
|
|
*
|
|
* \param field
|
|
* Field descriptor.
|
|
* \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
|
|
unlabeled_field_pack_to_buffer(const ProtobufCFieldDescriptor *field,
|
|
const void *member, ProtobufCBuffer *buffer)
|
|
{
|
|
if (field_is_zeroish(field, member))
|
|
return 0;
|
|
return required_field_pack_to_buffer(field, member, buffer);
|
|
}
|
|
|
|
/**
|
|
* Get the packed size of an array of same field type.
|
|
*
|
|
* \param field
|
|
* Field descriptor.
|
|
* \param count
|
|
* Number of elements of this type.
|
|
* \param array
|
|
* The elements to get the size of.
|
|
* \return
|
|
* Number of bytes required.
|
|
*/
|
|
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_ENUM:
|
|
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_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:
|
|
PROTOBUF_C__ASSERT_NOT_REACHED();
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
/**
|
|
* Pack an array of same field type to a virtual buffer.
|
|
*
|
|
* \param field
|
|
* Field descriptor.
|
|
* \param count
|
|
* Number of elements of this type.
|
|
* \param array
|
|
* The elements to get the size of.
|
|
* \param[out] buffer
|
|
* Virtual buffer to append data to.
|
|
* \return
|
|
* Number of bytes packed.
|
|
*/
|
|
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 !defined(WORDS_BIGENDIAN)
|
|
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;
|
|
}
|
|
break;
|
|
#endif
|
|
case PROTOBUF_C_TYPE_SFIXED64:
|
|
case PROTOBUF_C_TYPE_FIXED64:
|
|
case PROTOBUF_C_TYPE_DOUBLE:
|
|
#if !defined(WORDS_BIGENDIAN)
|
|
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_ENUM:
|
|
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_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:
|
|
PROTOBUF_C__ASSERT_NOT_REACHED();
|
|
}
|
|
return rv;
|
|
|
|
#if !defined(WORDS_BIGENDIAN)
|
|
no_packing_needed:
|
|
buffer->append(buffer, rv, array);
|
|
return rv;
|
|
#endif
|
|
}
|
|
|
|
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 (0 != (field->flags & PROTOBUF_C_FIELD_FLAG_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 ||
|
|
field->label == PROTOBUF_C_LABEL_NONE) &&
|
|
(0 != (field->flags & PROTOBUF_C_FIELD_FLAG_ONEOF))) {
|
|
rv += oneof_field_pack_to_buffer(
|
|
field,
|
|
*(const uint32_t *) qmember,
|
|
member,
|
|
buffer
|
|
);
|
|
} else if (field->label == PROTOBUF_C_LABEL_OPTIONAL) {
|
|
rv += optional_field_pack_to_buffer(
|
|
field,
|
|
*(const protobuf_c_boolean *) qmember,
|
|
member,
|
|
buffer
|
|
);
|
|
} else if (field->label == PROTOBUF_C_LABEL_NONE) {
|
|
rv += unlabeled_field_pack_to_buffer(
|
|
field,
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* \defgroup unpack unpacking implementation
|
|
*
|
|
* Routines mainly used by the unpacking functions.
|
|
*
|
|
* \ingroup internal
|
|
* @{
|
|
*/
|
|
|
|
static inline int
|
|
int_range_lookup(unsigned n_ranges, const ProtobufCIntRange *ranges, int value)
|
|
{
|
|
unsigned n;
|
|
unsigned start;
|
|
|
|
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,
|
|
uint8_t *wiretype_out)
|
|
{
|
|
unsigned max_rv = len > 5 ? 5 : len;
|
|
uint32_t tag = (data[0] & 0x7f) >> 3;
|
|
unsigned shift = 4;
|
|
unsigned rv;
|
|
|
|
/* 0 is not a valid tag value */
|
|
if ((data[0] & 0xf8) == 0) {
|
|
return 0;
|
|
}
|
|
|
|
*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 <= (1UL<<BOUND_SIZEOF_SCANNED_MEMBER_LOG2) */
|
|
#define BOUND_SIZEOF_SCANNED_MEMBER_LOG2 5
|
|
typedef struct ScannedMember ScannedMember;
|
|
/** Field as it's being read. */
|
|
struct ScannedMember {
|
|
uint32_t tag; /**< Field tag. */
|
|
uint8_t wire_type; /**< Field type. */
|
|
uint8_t length_prefix_len; /**< Prefix length. */
|
|
const ProtobufCFieldDescriptor *field; /**< Field descriptor. */
|
|
size_t len; /**< Field length. */
|
|
const uint8_t *data; /**< Pointer to field data. */
|
|
};
|
|
|
|
static inline size_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;
|
|
size_t val = 0;
|
|
unsigned i;
|
|
unsigned shift = 0;
|
|
|
|
for (i = 0; i < hdr_max; i++) {
|
|
val |= ((size_t)data[i] & 0x7f) << shift;
|
|
shift += 7;
|
|
if ((data[i] & 0x80) == 0)
|
|
break;
|
|
}
|
|
if (i == hdr_max) {
|
|
PROTOBUF_C_UNPACK_ERROR("error parsing length for length-prefixed data");
|
|
return 0;
|
|
}
|
|
hdr_len = i + 1;
|
|
*prefix_len_out = hdr_len;
|
|
if (val > INT_MAX) {
|
|
// Protobuf messages should always be less than 2 GiB in size.
|
|
// We also want to return early here so that hdr_len + val does
|
|
// not overflow on 32-bit systems.
|
|
PROTOBUF_C_UNPACK_ERROR("length prefix of %lu is too large",
|
|
(unsigned long int)val);
|
|
return 0;
|
|
}
|
|
if (hdr_len + val > len) {
|
|
PROTOBUF_C_UNPACK_ERROR("data too short after length-prefix of %lu",
|
|
(unsigned long int)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;
|
|
}
|
|
|
|
/**@}*/
|
|
|
|
/**
|
|
* Merge earlier message into a latter message.
|
|
*
|
|
* For numeric types and strings, if the same value appears multiple
|
|
* times, the parser accepts the last value it sees. For embedded
|
|
* message fields, the parser merges multiple instances of the same
|
|
* field. That is, all singular scalar fields in the latter instance
|
|
* replace those in the former, singular embedded messages are merged,
|
|
* and repeated fields are concatenated.
|
|
*
|
|
* The earlier message should be freed after calling this function, as
|
|
* some of its fields may have been reused and changed to their default
|
|
* values during the merge.
|
|
*/
|
|
static protobuf_c_boolean
|
|
merge_messages(ProtobufCMessage *earlier_msg,
|
|
ProtobufCMessage *latter_msg,
|
|
ProtobufCAllocator *allocator)
|
|
{
|
|
unsigned i;
|
|
const ProtobufCFieldDescriptor *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 =
|
|
STRUCT_MEMBER_PTR(size_t, earlier_msg,
|
|
fields[i].quantifier_offset);
|
|
uint8_t **p_earlier =
|
|
STRUCT_MEMBER_PTR(uint8_t *, earlier_msg,
|
|
fields[i].offset);
|
|
size_t *n_latter =
|
|
STRUCT_MEMBER_PTR(size_t, latter_msg,
|
|
fields[i].quantifier_offset);
|
|
uint8_t **p_latter =
|
|
STRUCT_MEMBER_PTR(uint8_t *, latter_msg,
|
|
fields[i].offset);
|
|
|
|
if (*n_earlier > 0) {
|
|
if (*n_latter > 0) {
|
|
/* Concatenate the repeated field */
|
|
size_t el_size =
|
|
sizeof_elt_in_repeated_array(fields[i].type);
|
|
uint8_t *new_field;
|
|
|
|
new_field = do_alloc(allocator,
|
|
(*n_earlier + *n_latter) * el_size);
|
|
if (!new_field)
|
|
return FALSE;
|
|
|
|
memcpy(new_field, *p_earlier,
|
|
*n_earlier * el_size);
|
|
memcpy(new_field +
|
|
*n_earlier * el_size,
|
|
*p_latter,
|
|
*n_latter * el_size);
|
|
|
|
do_free(allocator, *p_latter);
|
|
do_free(allocator, *p_earlier);
|
|
*p_latter = new_field;
|
|
*n_latter = *n_earlier + *n_latter;
|
|
} else {
|
|
/* Zero copy the repeated field from the earlier message */
|
|
*n_latter = *n_earlier;
|
|
*p_latter = *p_earlier;
|
|
}
|
|
/* Make sure the field does not get double freed */
|
|
*n_earlier = 0;
|
|
*p_earlier = 0;
|
|
}
|
|
} else if (fields[i].label == PROTOBUF_C_LABEL_OPTIONAL ||
|
|
fields[i].label == PROTOBUF_C_LABEL_NONE) {
|
|
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);
|
|
if (field_index < 0)
|
|
return FALSE;
|
|
field = latter_msg->descriptor->fields +
|
|
field_index;
|
|
} else {
|
|
/* Oneof is present in the latter message, move on */
|
|
continue;
|
|
}
|
|
} else {
|
|
field = &fields[i];
|
|
}
|
|
|
|
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;
|
|
uint8_t *l_data =
|
|
((ProtobufCBinaryData *) latter_elem)->data;
|
|
const ProtobufCBinaryData *d_bd =
|
|
(ProtobufCBinaryData *) def_val;
|
|
|
|
need_to_merge =
|
|
(e_data != NULL &&
|
|
(d_bd == NULL ||
|
|
e_data != d_bd->data)) &&
|
|
(l_data == NULL ||
|
|
(d_bd != NULL &&
|
|
l_data == d_bd->data));
|
|
break;
|
|
}
|
|
case PROTOBUF_C_TYPE_STRING: {
|
|
char *e_str = *(char **) earlier_elem;
|
|
char *l_str = *(char **) latter_elem;
|
|
const char *d_str = def_val;
|
|
|
|
need_to_merge = e_str != d_str && l_str == d_str;
|
|
break;
|
|
}
|
|
default: {
|
|
/* 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
|
|
* to make sure earlier message deallocation
|
|
* doesn't corrupt zero-copied data in the new
|
|
* message, earlier message will be freed after
|
|
* this function is called anyway
|
|
*/
|
|
memset(earlier_elem, 0, el_size);
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Count packed elements.
|
|
*
|
|
* 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) {
|
|
PROTOBUF_C_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) {
|
|
PROTOBUF_C_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_ENUM:
|
|
case PROTOBUF_C_TYPE_INT32:
|
|
case PROTOBUF_C_TYPE_SINT32:
|
|
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:
|
|
PROTOBUF_C_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)
|
|
{
|
|
uint32_t rv = data[0] & 0x7f;
|
|
if (len > 1) {
|
|
rv |= ((uint32_t) (data[1] & 0x7f) << 7);
|
|
if (len > 2) {
|
|
rv |= ((uint32_t) (data[2] & 0x7f) << 14);
|
|
if (len > 3) {
|
|
rv |= ((uint32_t) (data[3] & 0x7f) << 21);
|
|
if (len > 4)
|
|
rv |= ((uint32_t) (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)
|
|
{
|
|
// Note: Using unsigned types prevents undefined behavior
|
|
return (int32_t)((v >> 1) ^ -(v & 1));
|
|
}
|
|
|
|
static inline uint32_t
|
|
parse_fixed_uint32(const uint8_t *data)
|
|
{
|
|
#if !defined(WORDS_BIGENDIAN)
|
|
uint32_t t;
|
|
memcpy(&t, data, 4);
|
|
return t;
|
|
#else
|
|
return data[0] |
|
|
((uint32_t) (data[1]) << 8) |
|
|
((uint32_t) (data[2]) << 16) |
|
|
((uint32_t) (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 = ((uint64_t) (data[0] & 0x7f)) |
|
|
((uint64_t) (data[1] & 0x7f) << 7) |
|
|
((uint64_t) (data[2] & 0x7f) << 14) |
|
|
((uint64_t) (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)
|
|
{
|
|
// Note: Using unsigned types prevents undefined behavior
|
|
return (int64_t)((v >> 1) ^ -(v & 1));
|
|
}
|
|
|
|
static inline uint64_t
|
|
parse_fixed_uint64(const uint8_t *data)
|
|
{
|
|
#if !defined(WORDS_BIGENDIAN)
|
|
uint64_t t;
|
|
memcpy(&t, data, 8);
|
|
return t;
|
|
#else
|
|
return (uint64_t) parse_fixed_uint32(data) |
|
|
(((uint64_t) parse_fixed_uint32(data + 4)) << 32);
|
|
#endif
|
|
}
|
|
|
|
static protobuf_c_boolean
|
|
parse_boolean(unsigned len, const uint8_t *data)
|
|
{
|
|
unsigned i;
|
|
for (i = 0; i < len; i++)
|
|
if (data[i] & 0x7f)
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
static protobuf_c_boolean
|
|
parse_required_member(ScannedMember *scanned_member,
|
|
void *member,
|
|
ProtobufCAllocator *allocator,
|
|
protobuf_c_boolean maybe_clear)
|
|
{
|
|
unsigned len = scanned_member->len;
|
|
const uint8_t *data = scanned_member->data;
|
|
uint8_t wire_type = scanned_member->wire_type;
|
|
|
|
switch (scanned_member->field->type) {
|
|
case PROTOBUF_C_TYPE_ENUM:
|
|
case PROTOBUF_C_TYPE_INT32:
|
|
if (wire_type != PROTOBUF_C_WIRE_TYPE_VARINT)
|
|
return FALSE;
|
|
*(int32_t *) member = parse_int32(len, data);
|
|
return TRUE;
|
|
case PROTOBUF_C_TYPE_UINT32:
|
|
if (wire_type != PROTOBUF_C_WIRE_TYPE_VARINT)
|
|
return FALSE;
|
|
*(uint32_t *) member = parse_uint32(len, data);
|
|
return TRUE;
|
|
case PROTOBUF_C_TYPE_SINT32:
|
|
if (wire_type != PROTOBUF_C_WIRE_TYPE_VARINT)
|
|
return FALSE;
|
|
*(int32_t *) member = unzigzag32(parse_uint32(len, data));
|
|
return TRUE;
|
|
case PROTOBUF_C_TYPE_SFIXED32:
|
|
case PROTOBUF_C_TYPE_FIXED32:
|
|
case PROTOBUF_C_TYPE_FLOAT:
|
|
if (wire_type != PROTOBUF_C_WIRE_TYPE_32BIT)
|
|
return FALSE;
|
|
*(uint32_t *) member = parse_fixed_uint32(data);
|
|
return TRUE;
|
|
case PROTOBUF_C_TYPE_INT64:
|
|
case PROTOBUF_C_TYPE_UINT64:
|
|
if (wire_type != PROTOBUF_C_WIRE_TYPE_VARINT)
|
|
return FALSE;
|
|
*(uint64_t *) member = parse_uint64(len, data);
|
|
return TRUE;
|
|
case PROTOBUF_C_TYPE_SINT64:
|
|
if (wire_type != PROTOBUF_C_WIRE_TYPE_VARINT)
|
|
return FALSE;
|
|
*(int64_t *) member = unzigzag64(parse_uint64(len, data));
|
|
return TRUE;
|
|
case PROTOBUF_C_TYPE_SFIXED64:
|
|
case PROTOBUF_C_TYPE_FIXED64:
|
|
case PROTOBUF_C_TYPE_DOUBLE:
|
|
if (wire_type != PROTOBUF_C_WIRE_TYPE_64BIT)
|
|
return FALSE;
|
|
*(uint64_t *) member = parse_fixed_uint64(data);
|
|
return TRUE;
|
|
case PROTOBUF_C_TYPE_BOOL:
|
|
*(protobuf_c_boolean *) member = parse_boolean(len, data);
|
|
return TRUE;
|
|
case PROTOBUF_C_TYPE_STRING: {
|
|
char **pstr = member;
|
|
unsigned pref_len = scanned_member->length_prefix_len;
|
|
|
|
if (wire_type != PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED)
|
|
return FALSE;
|
|
|
|
if (maybe_clear && *pstr != NULL) {
|
|
const char *def = scanned_member->field->default_value;
|
|
if (*pstr != NULL && *pstr != def)
|
|
do_free(allocator, *pstr);
|
|
}
|
|
*pstr = do_alloc(allocator, len - pref_len + 1);
|
|
if (*pstr == NULL)
|
|
return FALSE;
|
|
memcpy(*pstr, data + pref_len, len - pref_len);
|
|
(*pstr)[len - pref_len] = 0;
|
|
return TRUE;
|
|
}
|
|
case PROTOBUF_C_TYPE_BYTES: {
|
|
ProtobufCBinaryData *bd = member;
|
|
const ProtobufCBinaryData *def_bd;
|
|
unsigned pref_len = scanned_member->length_prefix_len;
|
|
|
|
if (wire_type != PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED)
|
|
return FALSE;
|
|
|
|
def_bd = scanned_member->field->default_value;
|
|
if (maybe_clear &&
|
|
bd->data != NULL &&
|
|
(def_bd == NULL || bd->data != def_bd->data))
|
|
{
|
|
do_free(allocator, bd->data);
|
|
}
|
|
if (len > pref_len) {
|
|
bd->data = do_alloc(allocator, len - pref_len);
|
|
if (bd->data == NULL)
|
|
return FALSE;
|
|
memcpy(bd->data, data + pref_len, len - pref_len);
|
|
} else {
|
|
bd->data = NULL;
|
|
}
|
|
bd->len = len - pref_len;
|
|
return TRUE;
|
|
}
|
|
case PROTOBUF_C_TYPE_MESSAGE: {
|
|
ProtobufCMessage **pmessage = member;
|
|
ProtobufCMessage *subm;
|
|
const ProtobufCMessage *def_mess;
|
|
protobuf_c_boolean merge_successful = TRUE;
|
|
unsigned pref_len = scanned_member->length_prefix_len;
|
|
|
|
if (wire_type != PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED)
|
|
return FALSE;
|
|
|
|
def_mess = scanned_member->field->default_value;
|
|
if (len >= pref_len)
|
|
subm = protobuf_c_message_unpack(scanned_member->field->descriptor,
|
|
allocator,
|
|
len - pref_len,
|
|
data + pref_len);
|
|
else
|
|
subm = NULL;
|
|
|
|
if (maybe_clear &&
|
|
*pmessage != NULL &&
|
|
*pmessage != def_mess)
|
|
{
|
|
if (subm != NULL)
|
|
merge_successful = merge_messages(*pmessage, subm, allocator);
|
|
/* Delete the previous message */
|
|
protobuf_c_message_free_unpacked(*pmessage, allocator);
|
|
}
|
|
*pmessage = subm;
|
|
if (subm == NULL || !merge_successful)
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static protobuf_c_boolean
|
|
parse_oneof_member (ScannedMember *scanned_member,
|
|
void *member,
|
|
ProtobufCMessage *message,
|
|
ProtobufCAllocator *allocator)
|
|
{
|
|
uint32_t *oneof_case = STRUCT_MEMBER_PTR(uint32_t, message,
|
|
scanned_member->field->quantifier_offset);
|
|
|
|
/* If we have already parsed a member of this oneof, free it. */
|
|
if (*oneof_case != 0) {
|
|
const ProtobufCFieldDescriptor *old_field;
|
|
size_t el_size;
|
|
/* lookup field */
|
|
int field_index =
|
|
int_range_lookup(message->descriptor->n_field_ranges,
|
|
message->descriptor->field_ranges,
|
|
*oneof_case);
|
|
if (field_index < 0)
|
|
return FALSE;
|
|
old_field = message->descriptor->fields + field_index;
|
|
el_size = sizeof_elt_in_repeated_array(old_field->type);
|
|
|
|
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;
|
|
}
|
|
|
|
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,
|
|
ProtobufCAllocator *allocator)
|
|
{
|
|
if (!parse_required_member(scanned_member, member, allocator, TRUE))
|
|
return FALSE;
|
|
if (scanned_member->field->quantifier_offset != 0)
|
|
STRUCT_MEMBER(protobuf_c_boolean,
|
|
message,
|
|
scanned_member->field->quantifier_offset) = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
static protobuf_c_boolean
|
|
parse_repeated_member(ScannedMember *scanned_member,
|
|
void *member,
|
|
ProtobufCMessage *message,
|
|
ProtobufCAllocator *allocator)
|
|
{
|
|
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;
|
|
|
|
if (!parse_required_member(scanned_member, array + siz * (*p_n),
|
|
allocator, FALSE))
|
|
{
|
|
return FALSE;
|
|
}
|
|
*p_n += 1;
|
|
return TRUE;
|
|
}
|
|
|
|
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);
|
|
void *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;
|
|
#if defined(WORDS_BIGENDIAN)
|
|
unsigned i;
|
|
#endif
|
|
|
|
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 !defined(WORDS_BIGENDIAN)
|
|
goto no_unpacking_needed;
|
|
#else
|
|
for (i = 0; i < count; i++) {
|
|
((uint32_t *) array)[i] = parse_fixed_uint32(at);
|
|
at += 4;
|
|
}
|
|
break;
|
|
#endif
|
|
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 !defined(WORDS_BIGENDIAN)
|
|
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_ENUM:
|
|
case PROTOBUF_C_TYPE_INT32:
|
|
while (rem > 0) {
|
|
unsigned s = scan_varint(rem, at);
|
|
if (s == 0) {
|
|
PROTOBUF_C_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) {
|
|
PROTOBUF_C_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_UINT32:
|
|
while (rem > 0) {
|
|
unsigned s = scan_varint(rem, at);
|
|
if (s == 0) {
|
|
PROTOBUF_C_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) {
|
|
PROTOBUF_C_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) {
|
|
PROTOBUF_C_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:
|
|
while (rem > 0) {
|
|
unsigned s = scan_varint(rem, at);
|
|
if (s == 0) {
|
|
PROTOBUF_C_UNPACK_ERROR("bad packed-repeated boolean value");
|
|
return FALSE;
|
|
}
|
|
((protobuf_c_boolean *) array)[count++] = parse_boolean(s, at);
|
|
at += s;
|
|
rem -= s;
|
|
}
|
|
break;
|
|
default:
|
|
PROTOBUF_C__ASSERT_NOT_REACHED();
|
|
}
|
|
*p_n += count;
|
|
return TRUE;
|
|
|
|
#if !defined(WORDS_BIGENDIAN)
|
|
no_unpacking_needed:
|
|
memcpy(array, at, count * siz);
|
|
*p_n += count;
|
|
return TRUE;
|
|
#endif
|
|
}
|
|
|
|
static protobuf_c_boolean
|
|
is_packable_type(ProtobufCType type)
|
|
{
|
|
return
|
|
type != PROTOBUF_C_TYPE_STRING &&
|
|
type != PROTOBUF_C_TYPE_BYTES &&
|
|
type != PROTOBUF_C_TYPE_MESSAGE;
|
|
}
|
|
|
|
static protobuf_c_boolean
|
|
parse_member(ScannedMember *scanned_member,
|
|
ProtobufCMessage *message,
|
|
ProtobufCAllocator *allocator)
|
|
{
|
|
const ProtobufCFieldDescriptor *field = scanned_member->field;
|
|
void *member;
|
|
|
|
if (field == NULL) {
|
|
ProtobufCMessageUnknownField *ufield =
|
|
message->unknown_fields +
|
|
(message->n_unknown_fields++);
|
|
ufield->tag = scanned_member->tag;
|
|
ufield->wire_type = scanned_member->wire_type;
|
|
ufield->len = scanned_member->len;
|
|
ufield->data = do_alloc(allocator, scanned_member->len);
|
|
if (ufield->data == NULL)
|
|
return FALSE;
|
|
memcpy(ufield->data, scanned_member->data, ufield->len);
|
|
return TRUE;
|
|
}
|
|
member = (char *) message + field->offset;
|
|
switch (field->label) {
|
|
case PROTOBUF_C_LABEL_REQUIRED:
|
|
return parse_required_member(scanned_member, member,
|
|
allocator, TRUE);
|
|
case PROTOBUF_C_LABEL_OPTIONAL:
|
|
case PROTOBUF_C_LABEL_NONE:
|
|
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 &&
|
|
(0 != (field->flags & PROTOBUF_C_FIELD_FLAG_PACKED) ||
|
|
is_packable_type(field->type)))
|
|
{
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* Initialise messages generated by old code.
|
|
*
|
|
* This function is used if desc->message_init == NULL (which occurs
|
|
* for old code, and which would be useful to support allocating
|
|
* descriptors dynamically).
|
|
*/
|
|
static void
|
|
message_init_generic(const ProtobufCMessageDescriptor *desc,
|
|
ProtobufCMessage *message)
|
|
{
|
|
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)
|
|
{
|
|
void *field =
|
|
STRUCT_MEMBER_P(message, desc->fields[i].offset);
|
|
const void *dv = desc->fields[i].default_value;
|
|
|
|
switch (desc->fields[i].type) {
|
|
case PROTOBUF_C_TYPE_INT32:
|
|
case PROTOBUF_C_TYPE_SINT32:
|
|
case PROTOBUF_C_TYPE_SFIXED32:
|
|
case PROTOBUF_C_TYPE_UINT32:
|
|
case PROTOBUF_C_TYPE_FIXED32:
|
|
case PROTOBUF_C_TYPE_FLOAT:
|
|
case PROTOBUF_C_TYPE_ENUM:
|
|
memcpy(field, dv, 4);
|
|
break;
|
|
case PROTOBUF_C_TYPE_INT64:
|
|
case PROTOBUF_C_TYPE_SINT64:
|
|
case PROTOBUF_C_TYPE_SFIXED64:
|
|
case PROTOBUF_C_TYPE_UINT64:
|
|
case PROTOBUF_C_TYPE_FIXED64:
|
|
case PROTOBUF_C_TYPE_DOUBLE:
|
|
memcpy(field, dv, 8);
|
|
break;
|
|
case PROTOBUF_C_TYPE_BOOL:
|
|
memcpy(field, dv, sizeof(protobuf_c_boolean));
|
|
break;
|
|
case PROTOBUF_C_TYPE_BYTES:
|
|
memcpy(field, dv, sizeof(ProtobufCBinaryData));
|
|
break;
|
|
|
|
case PROTOBUF_C_TYPE_STRING:
|
|
case PROTOBUF_C_TYPE_MESSAGE:
|
|
/*
|
|
* The next line essentially implements a cast
|
|
* from const, which is totally unavoidable.
|
|
*/
|
|
*(const void **) field = dv;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**@}*/
|
|
|
|
/*
|
|
* ScannedMember slabs (an unpacking implementation detail). Before doing real
|
|
* unpacking, we first scan through the elements to see how many there are (for
|
|
* repeated fields), and which field to use (for non-repeated fields given
|
|
* twice).
|
|
*
|
|
* In order to avoid allocations for small messages, we keep a stack-allocated
|
|
* slab of ScannedMembers of size FIRST_SCANNED_MEMBER_SLAB_SIZE (16). After we
|
|
* fill that up, we allocate each slab twice as large as the previous one.
|
|
*/
|
|
#define FIRST_SCANNED_MEMBER_SLAB_SIZE_LOG2 4
|
|
|
|
/*
|
|
* The number of slabs, including the stack-allocated ones; choose the number so
|
|
* that we would overflow if we needed a slab larger than provided.
|
|
*/
|
|
#define MAX_SCANNED_MEMBER_SLAB \
|
|
(sizeof(unsigned int)*8 - 1 \
|
|
- BOUND_SIZEOF_SCANNED_MEMBER_LOG2 \
|
|
- FIRST_SCANNED_MEMBER_SLAB_SIZE_LOG2)
|
|
|
|
#define REQUIRED_FIELD_BITMAP_SET(index) \
|
|
(required_fields_bitmap[(index)/8] |= (1UL<<((index)%8)))
|
|
|
|
#define REQUIRED_FIELD_BITMAP_IS_SET(index) \
|
|
(required_fields_bitmap[(index)/8] & (1UL<<((index)%8)))
|
|
|
|
ProtobufCMessage *
|
|
protobuf_c_message_unpack(const ProtobufCMessageDescriptor *desc,
|
|
ProtobufCAllocator *allocator,
|
|
size_t len, const uint8_t *data)
|
|
{
|
|
ProtobufCMessage *rv;
|
|
size_t rem = len;
|
|
const uint8_t *at = data;
|
|
const ProtobufCFieldDescriptor *last_field = desc->fields + 0;
|
|
ScannedMember first_member_slab[1UL <<
|
|
FIRST_SCANNED_MEMBER_SLAB_SIZE_LOG2];
|
|
|
|
/*
|
|
* scanned_member_slabs[i] is an array of arrays of ScannedMember.
|
|
* The first slab (scanned_member_slabs[0] is just a pointer to
|
|
* first_member_slab), above. All subsequent slabs will be allocated
|
|
* using the allocator.
|
|
*/
|
|
ScannedMember *scanned_member_slabs[MAX_SCANNED_MEMBER_SLAB + 1];
|
|
unsigned which_slab = 0; /* the slab we are currently populating */
|
|
unsigned in_slab_index = 0; /* number of members in the slab */
|
|
size_t n_unknown = 0;
|
|
unsigned f;
|
|
unsigned j;
|
|
unsigned i_slab;
|
|
unsigned last_field_index = 0;
|
|
unsigned required_fields_bitmap_len;
|
|
unsigned char required_fields_bitmap_stack[16];
|
|
unsigned char *required_fields_bitmap = required_fields_bitmap_stack;
|
|
protobuf_c_boolean required_fields_bitmap_alloced = FALSE;
|
|
|
|
ASSERT_IS_MESSAGE_DESCRIPTOR(desc);
|
|
|
|
if (allocator == NULL)
|
|
allocator = &protobuf_c__allocator;
|
|
|
|
rv = do_alloc(allocator, desc->sizeof_message);
|
|
if (!rv)
|
|
return (NULL);
|
|
scanned_member_slabs[0] = first_member_slab;
|
|
|
|
required_fields_bitmap_len = (desc->n_fields + 7) / 8;
|
|
if (required_fields_bitmap_len > sizeof(required_fields_bitmap_stack)) {
|
|
required_fields_bitmap = do_alloc(allocator, required_fields_bitmap_len);
|
|
if (!required_fields_bitmap) {
|
|
do_free(allocator, rv);
|
|
return (NULL);
|
|
}
|
|
required_fields_bitmap_alloced = TRUE;
|
|
}
|
|
memset(required_fields_bitmap, 0, required_fields_bitmap_len);
|
|
|
|
/*
|
|
* 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
|
|
message_init_generic(desc, rv);
|
|
|
|
while (rem > 0) {
|
|
uint32_t tag;
|
|
uint8_t wire_type;
|
|
size_t used = parse_tag_and_wiretype(rem, at, &tag, &wire_type);
|
|
const ProtobufCFieldDescriptor *field;
|
|
ScannedMember tmp;
|
|
|
|
if (used == 0) {
|
|
PROTOBUF_C_UNPACK_ERROR("error parsing tag/wiretype at offset %u",
|
|
(unsigned) (at - data));
|
|
goto error_cleanup_during_scan;
|
|
}
|
|
/*
|
|
* \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 =
|
|
int_range_lookup(desc->n_field_ranges,
|
|
desc->field_ranges,
|
|
tag);
|
|
if (field_index < 0) {
|
|
field = NULL;
|
|
n_unknown++;
|
|
} else {
|
|
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_FIELD_BITMAP_SET(last_field_index);
|
|
|
|
at += used;
|
|
rem -= used;
|
|
tmp.tag = tag;
|
|
tmp.wire_type = wire_type;
|
|
tmp.field = field;
|
|
tmp.data = at;
|
|
tmp.length_prefix_len = 0;
|
|
|
|
switch (wire_type) {
|
|
case PROTOBUF_C_WIRE_TYPE_VARINT: {
|
|
unsigned max_len = rem < 10 ? rem : 10;
|
|
unsigned i;
|
|
|
|
for (i = 0; i < max_len; i++)
|
|
if ((at[i] & 0x80) == 0)
|
|
break;
|
|
if (i == max_len) {
|
|
PROTOBUF_C_UNPACK_ERROR("unterminated varint at offset %u",
|
|
(unsigned) (at - data));
|
|
goto error_cleanup_during_scan;
|
|
}
|
|
tmp.len = i + 1;
|
|
break;
|
|
}
|
|
case PROTOBUF_C_WIRE_TYPE_64BIT:
|
|
if (rem < 8) {
|
|
PROTOBUF_C_UNPACK_ERROR("too short after 64bit wiretype at offset %u",
|
|
(unsigned) (at - data));
|
|
goto error_cleanup_during_scan;
|
|
}
|
|
tmp.len = 8;
|
|
break;
|
|
case PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED: {
|
|
size_t pref_len;
|
|
|
|
tmp.len = scan_length_prefixed_data(rem, at, &pref_len);
|
|
if (tmp.len == 0) {
|
|
/* NOTE: scan_length_prefixed_data calls UNPACK_ERROR */
|
|
goto error_cleanup_during_scan;
|
|
}
|
|
tmp.length_prefix_len = pref_len;
|
|
break;
|
|
}
|
|
case PROTOBUF_C_WIRE_TYPE_32BIT:
|
|
if (rem < 4) {
|
|
PROTOBUF_C_UNPACK_ERROR("too short after 32bit wiretype at offset %u",
|
|
(unsigned) (at - data));
|
|
goto error_cleanup_during_scan;
|
|
}
|
|
tmp.len = 4;
|
|
break;
|
|
default:
|
|
PROTOBUF_C_UNPACK_ERROR("unsupported tag %u at offset %u",
|
|
wire_type, (unsigned) (at - data));
|
|
goto error_cleanup_during_scan;
|
|
}
|
|
|
|
if (in_slab_index == (1UL <<
|
|
(which_slab + FIRST_SCANNED_MEMBER_SLAB_SIZE_LOG2)))
|
|
{
|
|
size_t size;
|
|
|
|
in_slab_index = 0;
|
|
if (which_slab == MAX_SCANNED_MEMBER_SLAB) {
|
|
PROTOBUF_C_UNPACK_ERROR("too many fields");
|
|
goto error_cleanup_during_scan;
|
|
}
|
|
which_slab++;
|
|
size = sizeof(ScannedMember)
|
|
<< (which_slab + FIRST_SCANNED_MEMBER_SLAB_SIZE_LOG2);
|
|
scanned_member_slabs[which_slab] = do_alloc(allocator, size);
|
|
if (scanned_member_slabs[which_slab] == NULL)
|
|
goto error_cleanup_during_scan;
|
|
}
|
|
scanned_member_slabs[which_slab][in_slab_index++] = tmp;
|
|
|
|
if (field != NULL && field->label == PROTOBUF_C_LABEL_REPEATED) {
|
|
size_t *n = STRUCT_MEMBER_PTR(size_t, rv,
|
|
field->quantifier_offset);
|
|
if (wire_type == PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED &&
|
|
(0 != (field->flags & PROTOBUF_C_FIELD_FLAG_PACKED) ||
|
|
is_packable_type(field->type)))
|
|
{
|
|
size_t count;
|
|
if (!count_packed_elements(field->type,
|
|
tmp.len -
|
|
tmp.length_prefix_len,
|
|
tmp.data +
|
|
tmp.length_prefix_len,
|
|
&count))
|
|
{
|
|
PROTOBUF_C_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, also check that all required fields have been set */
|
|
for (f = 0; f < desc->n_fields; f++) {
|
|
const ProtobufCFieldDescriptor *field = desc->fields + f;
|
|
if (field == NULL) {
|
|
continue;
|
|
}
|
|
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) {
|
|
unsigned n = *n_ptr;
|
|
void *a;
|
|
*n_ptr = 0;
|
|
assert(rv->descriptor != NULL);
|
|
#define CLEAR_REMAINING_N_PTRS() \
|
|
for(f++;f < desc->n_fields; f++) \
|
|
{ \
|
|
field = desc->fields + f; \
|
|
if (field->label == PROTOBUF_C_LABEL_REPEATED) \
|
|
STRUCT_MEMBER (size_t, rv, field->quantifier_offset) = 0; \
|
|
}
|
|
a = do_alloc(allocator, siz * n);
|
|
if (!a) {
|
|
CLEAR_REMAINING_N_PTRS();
|
|
goto error_cleanup;
|
|
}
|
|
STRUCT_MEMBER(void *, rv, field->offset) = a;
|
|
}
|
|
} else if (field->label == PROTOBUF_C_LABEL_REQUIRED) {
|
|
if (field->default_value == NULL &&
|
|
!REQUIRED_FIELD_BITMAP_IS_SET(f))
|
|
{
|
|
CLEAR_REMAINING_N_PTRS();
|
|
PROTOBUF_C_UNPACK_ERROR("message '%s': missing required field '%s'",
|
|
desc->name, field->name);
|
|
goto error_cleanup;
|
|
}
|
|
}
|
|
}
|
|
#undef CLEAR_REMAINING_N_PTRS
|
|
|
|
/* allocate space for unknown fields */
|
|
if (n_unknown) {
|
|
rv->unknown_fields = do_alloc(allocator,
|
|
n_unknown * sizeof(ProtobufCMessageUnknownField));
|
|
if (rv->unknown_fields == NULL)
|
|
goto error_cleanup;
|
|
}
|
|
|
|
/* do real parsing */
|
|
for (i_slab = 0; i_slab <= which_slab; i_slab++) {
|
|
unsigned max = (i_slab == which_slab) ?
|
|
in_slab_index : (1UL << (i_slab + 4));
|
|
ScannedMember *slab = scanned_member_slabs[i_slab];
|
|
|
|
for (j = 0; j < max; j++) {
|
|
if (!parse_member(slab + j, rv, allocator)) {
|
|
PROTOBUF_C_UNPACK_ERROR("error parsing member %s of %s",
|
|
slab->field ? slab->field->name : "*unknown-field*",
|
|
desc->name);
|
|
goto error_cleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* cleanup */
|
|
for (j = 1; j <= which_slab; j++)
|
|
do_free(allocator, scanned_member_slabs[j]);
|
|
if (required_fields_bitmap_alloced)
|
|
do_free(allocator, required_fields_bitmap);
|
|
return rv;
|
|
|
|
error_cleanup:
|
|
protobuf_c_message_free_unpacked(rv, allocator);
|
|
for (j = 1; j <= which_slab; j++)
|
|
do_free(allocator, scanned_member_slabs[j]);
|
|
if (required_fields_bitmap_alloced)
|
|
do_free(allocator, required_fields_bitmap);
|
|
return NULL;
|
|
|
|
error_cleanup_during_scan:
|
|
do_free(allocator, rv);
|
|
for (j = 1; j <= which_slab; j++)
|
|
do_free(allocator, scanned_member_slabs[j]);
|
|
if (required_fields_bitmap_alloced)
|
|
do_free(allocator, required_fields_bitmap);
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
protobuf_c_message_free_unpacked(ProtobufCMessage *message,
|
|
ProtobufCAllocator *allocator)
|
|
{
|
|
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,
|
|
desc->fields[f].quantifier_offset);
|
|
void *arr = STRUCT_MEMBER(void *,
|
|
message,
|
|
desc->fields[f].offset);
|
|
|
|
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);
|
|
|
|
if (str && str != desc->fields[f].default_value)
|
|
do_free(allocator, str);
|
|
} else if (desc->fields[f].type == PROTOBUF_C_TYPE_BYTES) {
|
|
void *data = STRUCT_MEMBER(ProtobufCBinaryData, message,
|
|
desc->fields[f].offset).data;
|
|
const ProtobufCBinaryData *default_bd;
|
|
|
|
default_bd = desc->fields[f].default_value;
|
|
if (data != NULL &&
|
|
(default_bd == NULL ||
|
|
default_bd->data != data))
|
|
{
|
|
do_free(allocator, data);
|
|
}
|
|
} else if (desc->fields[f].type == PROTOBUF_C_TYPE_MESSAGE) {
|
|
ProtobufCMessage *sm;
|
|
|
|
sm = STRUCT_MEMBER(ProtobufCMessage *, message,
|
|
desc->fields[f].offset);
|
|
if (sm && sm != desc->fields[f].default_value)
|
|
protobuf_c_message_free_unpacked(sm, allocator);
|
|
}
|
|
}
|
|
|
|
for (f = 0; f < message->n_unknown_fields; f++)
|
|
do_free(allocator, message->unknown_fields[f].data);
|
|
if (message->unknown_fields != NULL)
|
|
do_free(allocator, message->unknown_fields);
|
|
|
|
do_free(allocator, message);
|
|
}
|
|
|
|
void
|
|
protobuf_c_message_init(const ProtobufCMessageDescriptor * descriptor,
|
|
void *message)
|
|
{
|
|
descriptor->message_init((ProtobufCMessage *) (message));
|
|
}
|
|
|
|
protobuf_c_boolean
|
|
protobuf_c_message_check(const ProtobufCMessage *message)
|
|
{
|
|
unsigned i;
|
|
|
|
if (!message ||
|
|
!message->descriptor ||
|
|
message->descriptor->magic != PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
for (i = 0; i < message->descriptor->n_fields; i++) {
|
|
const ProtobufCFieldDescriptor *f = message->descriptor->fields + i;
|
|
ProtobufCType type = f->type;
|
|
ProtobufCLabel label = f->label;
|
|
void *field = STRUCT_MEMBER_P (message, f->offset);
|
|
|
|
if (f->flags & PROTOBUF_C_FIELD_FLAG_ONEOF) {
|
|
const uint32_t *oneof_case = STRUCT_MEMBER_P (message, f->quantifier_offset);
|
|
if (f->id != *oneof_case) {
|
|
continue; //Do not check if it is an unpopulated oneof member.
|
|
}
|
|
}
|
|
|
|
if (label == PROTOBUF_C_LABEL_REPEATED) {
|
|
size_t *quantity = STRUCT_MEMBER_P (message, f->quantifier_offset);
|
|
|
|
if (*quantity > 0 && *(void **) field == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (type == PROTOBUF_C_TYPE_MESSAGE) {
|
|
ProtobufCMessage **submessage = *(ProtobufCMessage ***) field;
|
|
unsigned j;
|
|
for (j = 0; j < *quantity; j++) {
|
|
if (!protobuf_c_message_check(submessage[j]))
|
|
return FALSE;
|
|
}
|
|
} else if (type == PROTOBUF_C_TYPE_STRING) {
|
|
char **string = *(char ***) field;
|
|
unsigned j;
|
|
for (j = 0; j < *quantity; j++) {
|
|
if (!string[j])
|
|
return FALSE;
|
|
}
|
|
} else if (type == PROTOBUF_C_TYPE_BYTES) {
|
|
ProtobufCBinaryData *bd = *(ProtobufCBinaryData **) field;
|
|
unsigned j;
|
|
for (j = 0; j < *quantity; j++) {
|
|
if (bd[j].len > 0 && bd[j].data == NULL)
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
} else { /* PROTOBUF_C_LABEL_REQUIRED or PROTOBUF_C_LABEL_OPTIONAL */
|
|
|
|
if (type == PROTOBUF_C_TYPE_MESSAGE) {
|
|
ProtobufCMessage *submessage = *(ProtobufCMessage **) field;
|
|
if (label == PROTOBUF_C_LABEL_REQUIRED || submessage != NULL) {
|
|
if (!protobuf_c_message_check(submessage))
|
|
return FALSE;
|
|
}
|
|
} else if (type == PROTOBUF_C_TYPE_STRING) {
|
|
char *string = *(char **) field;
|
|
if (label == PROTOBUF_C_LABEL_REQUIRED && string == NULL)
|
|
return FALSE;
|
|
} else if (type == PROTOBUF_C_TYPE_BYTES) {
|
|
protobuf_c_boolean *has = STRUCT_MEMBER_P (message, f->quantifier_offset);
|
|
ProtobufCBinaryData *bd = field;
|
|
if (label == PROTOBUF_C_LABEL_REQUIRED || *has == TRUE) {
|
|
if (bd->len > 0 && bd->data == NULL)
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* === services === */
|
|
|
|
typedef void (*GenericHandler) (void *service,
|
|
const ProtobufCMessage *input,
|
|
ProtobufCClosure closure,
|
|
void *closure_data);
|
|
void
|
|
protobuf_c_service_invoke_internal(ProtobufCService *service,
|
|
unsigned method_index,
|
|
const ProtobufCMessage *input,
|
|
ProtobufCClosure closure,
|
|
void *closure_data)
|
|
{
|
|
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.)
|
|
*/
|
|
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);
|
|
}
|
|
|
|
void
|
|
protobuf_c_service_generated_init(ProtobufCService *service,
|
|
const ProtobufCServiceDescriptor *descriptor,
|
|
ProtobufCServiceDestroy destroy)
|
|
{
|
|
ASSERT_IS_SERVICE_DESCRIPTOR(descriptor);
|
|
service->descriptor = descriptor;
|
|
service->destroy = destroy;
|
|
service->invoke = protobuf_c_service_invoke_internal;
|
|
memset(service + 1, 0, descriptor->n_methods * sizeof(GenericHandler));
|
|
}
|
|
|
|
void protobuf_c_service_destroy(ProtobufCService *service)
|
|
{
|
|
service->destroy(service);
|
|
}
|
|
|
|
/* --- querying the descriptors --- */
|
|
|
|
const ProtobufCEnumValue *
|
|
protobuf_c_enum_descriptor_get_value_by_name(const ProtobufCEnumDescriptor *desc,
|
|
const char *name)
|
|
{
|
|
unsigned start = 0;
|
|
unsigned count;
|
|
|
|
if (desc == NULL || desc->values_by_name == NULL)
|
|
return NULL;
|
|
|
|
count = desc->n_value_names;
|
|
|
|
while (count > 1) {
|
|
unsigned mid = start + count / 2;
|
|
int rv = strcmp(desc->values_by_name[mid].name, name);
|
|
if (rv == 0)
|
|
return desc->values + desc->values_by_name[mid].index;
|
|
else if (rv < 0) {
|
|
count = start + count - (mid + 1);
|
|
start = mid + 1;
|
|
} else
|
|
count = mid - start;
|
|
}
|
|
if (count == 0)
|
|
return NULL;
|
|
if (strcmp(desc->values_by_name[start].name, name) == 0)
|
|
return desc->values + desc->values_by_name[start].index;
|
|
return NULL;
|
|
}
|
|
|
|
const ProtobufCEnumValue *
|
|
protobuf_c_enum_descriptor_get_value(const ProtobufCEnumDescriptor *desc,
|
|
int value)
|
|
{
|
|
int rv = int_range_lookup(desc->n_value_ranges, desc->value_ranges, value);
|
|
if (rv < 0)
|
|
return NULL;
|
|
return desc->values + rv;
|
|
}
|
|
|
|
const ProtobufCFieldDescriptor *
|
|
protobuf_c_message_descriptor_get_field_by_name(const ProtobufCMessageDescriptor *desc,
|
|
const char *name)
|
|
{
|
|
unsigned start = 0;
|
|
unsigned count;
|
|
const ProtobufCFieldDescriptor *field;
|
|
|
|
if (desc == NULL || desc->fields_sorted_by_name == NULL)
|
|
return NULL;
|
|
|
|
count = desc->n_fields;
|
|
|
|
while (count > 1) {
|
|
unsigned mid = start + count / 2;
|
|
int rv;
|
|
field = desc->fields + desc->fields_sorted_by_name[mid];
|
|
rv = strcmp(field->name, name);
|
|
if (rv == 0)
|
|
return field;
|
|
else if (rv < 0) {
|
|
count = start + count - (mid + 1);
|
|
start = mid + 1;
|
|
} else
|
|
count = mid - start;
|
|
}
|
|
if (count == 0)
|
|
return NULL;
|
|
field = desc->fields + desc->fields_sorted_by_name[start];
|
|
if (strcmp(field->name, name) == 0)
|
|
return field;
|
|
return NULL;
|
|
}
|
|
|
|
const ProtobufCFieldDescriptor *
|
|
protobuf_c_message_descriptor_get_field(const ProtobufCMessageDescriptor *desc,
|
|
unsigned value)
|
|
{
|
|
int rv = int_range_lookup(desc->n_field_ranges,desc->field_ranges, value);
|
|
if (rv < 0)
|
|
return NULL;
|
|
return desc->fields + rv;
|
|
}
|
|
|
|
const ProtobufCMethodDescriptor *
|
|
protobuf_c_service_descriptor_get_method_by_name(const ProtobufCServiceDescriptor *desc,
|
|
const char *name)
|
|
{
|
|
unsigned start = 0;
|
|
unsigned count;
|
|
|
|
if (desc == NULL || desc->method_indices_by_name == NULL)
|
|
return NULL;
|
|
|
|
count = desc->n_methods;
|
|
|
|
while (count > 1) {
|
|
unsigned mid = start + count / 2;
|
|
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);
|
|
start = mid + 1;
|
|
} else {
|
|
count = mid - start;
|
|
}
|
|
}
|
|
if (count == 0)
|
|
return NULL;
|
|
if (strcmp(desc->methods[desc->method_indices_by_name[start]].name, name) == 0)
|
|
return desc->methods + desc->method_indices_by_name[start];
|
|
return NULL;
|
|
}
|