online compiler and debugger for c/c++

code. compile. run. debug. share.
Source Code   
Language
/****************************************************************************** Click on the green Run button above *******************************************************************************/ #include <stdio.h> #include <stdint.h> #include "pb_encode.h" #include "message.pb.h" #include "pb.h" // Tx buffer of encoded values uint8_t buffer[200]; /* bool pb_callback_string(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) { char str[32]; int index = *((int *)arg[0]); // Get index from array of arguments snprintf(str, sizeof(str), "Input_%d", index); return pb_encode_tag_for_field(stream, field) && pb_encode_string(stream, str, strlen(str)); } bool pb_callback_inputs(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) { for (int i = 0; i < 3; i++) { pb_input_t msg_input; msg_input.state = true; msg_input.name.funcs.encode = pb_callback_string; msg_input.name.arg = &i; // Set index parameter to callback if (pb_encode_tag_for_field(stream, field) == false || pb_encode_submessage(stream, pb_input_t_fields, &msg_input) == false) { return false; } } return true; } */ int main(void) { // Create instance and fill it with default values pb_packet_t msg = pb_packet_t_init_default; // Set our item msg.illuminance = 300; //msg.revision = pb_revision_t_PB_REV_1_2; //msg.has_voltage = true; //msg.voltage.battery = 630; //msg.voltage.external = 1230; //msg.inputs.funcs.encode = pb_callback_inputs; // Create stream and encode data pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer)); pb_encode(&stream, pb_packet_t_fields, &msg); // Print buffer length and content printf("Length: %d\n", (int) stream.bytes_written); for (int i = 0; i < stream.bytes_written; i++) { printf("%02X", buffer[i]); } printf("\n"); return 0; }
syntax = "proto3"; message pb_packet_t { uint32 luminance = 1; }
/* Automatically generated nanopb constant definitions */ /* Generated by nanopb-0.4.5-dev */ #include "message.pb.h" #if PB_PROTO_HEADER_VERSION != 40 #error Regenerate this file with the current version of nanopb generator. #endif PB_BIND(pb_input_t, pb_input_t, AUTO) PB_BIND(pb_voltage_t, pb_voltage_t, AUTO) PB_BIND(pb_packet_t, pb_packet_t, AUTO)
/* Common parts of the nanopb library. Most of these are quite low-level * stuff. For the high-level interface, see pb_encode.h and pb_decode.h. */ #ifndef PB_H_INCLUDED #define PB_H_INCLUDED /***************************************************************** * Nanopb compilation time options. You can change these here by * * uncommenting the lines, or on the compiler command line. * *****************************************************************/ /* Enable support for dynamically allocated fields */ /* #define PB_ENABLE_MALLOC 1 */ /* Define this if your CPU / compiler combination does not support * unaligned memory access to packed structures. */ /* #define PB_NO_PACKED_STRUCTS 1 */ /* Increase the number of required fields that are tracked. * A compiler warning will tell if you need this. */ /* #define PB_MAX_REQUIRED_FIELDS 256 */ /* Add support for tag numbers > 65536 and fields larger than 65536 bytes. */ /* #define PB_FIELD_32BIT 1 */ /* Disable support for error messages in order to save some code space. */ /* #define PB_NO_ERRMSG 1 */ /* Disable support for custom streams (support only memory buffers). */ /* #define PB_BUFFER_ONLY 1 */ /* Disable support for 64-bit datatypes, for compilers without int64_t or to save some code space. */ /* #define PB_WITHOUT_64BIT 1 */ /* Don't encode scalar arrays as packed. This is only to be used when * the decoder on the receiving side cannot process packed scalar arrays. * Such example is older protobuf.js. */ /* #define PB_ENCODE_ARRAYS_UNPACKED 1 */ /* Enable conversion of doubles to floats for platforms that do not * support 64-bit doubles. Most commonly AVR. */ /* #define PB_CONVERT_DOUBLE_FLOAT 1 */ /* Check whether incoming strings are valid UTF-8 sequences. Slows down * the string processing slightly and slightly increases code size. */ /* #define PB_VALIDATE_UTF8 1 */ /****************************************************************** * You usually don't need to change anything below this line. * * Feel free to look around and use the defined macros, though. * ******************************************************************/ /* Version of the nanopb library. Just in case you want to check it in * your own program. */ #define NANOPB_VERSION nanopb-0.4.4-dev /* Include all the system headers needed by nanopb. You will need the * definitions of the following: * - strlen, memcpy, memset functions * - [u]int_least8_t, uint_fast8_t, [u]int_least16_t, [u]int32_t, [u]int64_t * - size_t * - bool * * If you don't have the standard header files, you can instead provide * a custom header that defines or includes all this. In that case, * define PB_SYSTEM_HEADER to the path of this file. */ #ifdef PB_SYSTEM_HEADER #include PB_SYSTEM_HEADER #else #include <stdint.h> #include <stddef.h> #include <stdbool.h> #include <string.h> #include <limits.h> #ifdef PB_ENABLE_MALLOC #include <stdlib.h> #endif #endif #ifdef __cplusplus extern "C" { #endif /* Macro for defining packed structures (compiler dependent). * This just reduces memory requirements, but is not required. */ #if defined(PB_NO_PACKED_STRUCTS) /* Disable struct packing */ # define PB_PACKED_STRUCT_START # define PB_PACKED_STRUCT_END # define pb_packed #elif defined(__GNUC__) || defined(__clang__) /* For GCC and clang */ # define PB_PACKED_STRUCT_START # define PB_PACKED_STRUCT_END # define pb_packed __attribute__((packed)) #elif defined(__ICCARM__) || defined(__CC_ARM) /* For IAR ARM and Keil MDK-ARM compilers */ # define PB_PACKED_STRUCT_START _Pragma("pack(push, 1)") # define PB_PACKED_STRUCT_END _Pragma("pack(pop)") # define pb_packed #elif defined(_MSC_VER) && (_MSC_VER >= 1500) /* For Microsoft Visual C++ */ # define PB_PACKED_STRUCT_START __pragma(pack(push, 1)) # define PB_PACKED_STRUCT_END __pragma(pack(pop)) # define pb_packed #else /* Unknown compiler */ # define PB_PACKED_STRUCT_START # define PB_PACKED_STRUCT_END # define pb_packed #endif /* Handly macro for suppressing unreferenced-parameter compiler warnings. */ #ifndef PB_UNUSED #define PB_UNUSED(x) (void)(x) #endif /* Harvard-architecture processors may need special attributes for storing * field information in program memory. */ #ifndef PB_PROGMEM #ifdef __AVR__ #include <avr/pgmspace.h> #define PB_PROGMEM PROGMEM #define PB_PROGMEM_READU32(x) pgm_read_dword(&x) #else #define PB_PROGMEM #define PB_PROGMEM_READU32(x) (x) #endif #endif /* Compile-time assertion, used for checking compatible compilation options. * If this does not work properly on your compiler, use * #define PB_NO_STATIC_ASSERT to disable it. * * But before doing that, check carefully the error message / place where it * comes from to see if the error has a real cause. Unfortunately the error * message is not always very clear to read, but you can see the reason better * in the place where the PB_STATIC_ASSERT macro was called. */ #ifndef PB_NO_STATIC_ASSERT # ifndef PB_STATIC_ASSERT # if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L /* C11 standard _Static_assert mechanism */ # define PB_STATIC_ASSERT(COND,MSG) _Static_assert(COND,#MSG); # else /* Classic negative-size-array static assert mechanism */ # define PB_STATIC_ASSERT(COND,MSG) typedef char PB_STATIC_ASSERT_MSG(MSG, __LINE__, __COUNTER__)[(COND)?1:-1]; # define PB_STATIC_ASSERT_MSG(MSG, LINE, COUNTER) PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER) # define PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER) pb_static_assertion_##MSG##_##LINE##_##COUNTER # endif # endif #else /* Static asserts disabled by PB_NO_STATIC_ASSERT */ # define PB_STATIC_ASSERT(COND,MSG) #endif /* Number of required fields to keep track of. */ #ifndef PB_MAX_REQUIRED_FIELDS #define PB_MAX_REQUIRED_FIELDS 64 #endif #if PB_MAX_REQUIRED_FIELDS < 64 #error You should not lower PB_MAX_REQUIRED_FIELDS from the default value (64). #endif #ifdef PB_WITHOUT_64BIT #ifdef PB_CONVERT_DOUBLE_FLOAT /* Cannot use doubles without 64-bit types */ #undef PB_CONVERT_DOUBLE_FLOAT #endif #endif /* List of possible field types. These are used in the autogenerated code. * Least-significant 4 bits tell the scalar type * Most-significant 4 bits specify repeated/required/packed etc. */ typedef uint_least8_t pb_type_t; /**** Field data types ****/ /* Numeric types */ #define PB_LTYPE_BOOL 0x00U /* bool */ #define PB_LTYPE_VARINT 0x01U /* int32, int64, enum, bool */ #define PB_LTYPE_UVARINT 0x02U /* uint32, uint64 */ #define PB_LTYPE_SVARINT 0x03U /* sint32, sint64 */ #define PB_LTYPE_FIXED32 0x04U /* fixed32, sfixed32, float */ #define PB_LTYPE_FIXED64 0x05U /* fixed64, sfixed64, double */ /* Marker for last packable field type. */ #define PB_LTYPE_LAST_PACKABLE 0x05U /* Byte array with pre-allocated buffer. * data_size is the length of the allocated PB_BYTES_ARRAY structure. */ #define PB_LTYPE_BYTES 0x06U /* String with pre-allocated buffer. * data_size is the maximum length. */ #define PB_LTYPE_STRING 0x07U /* Submessage * submsg_fields is pointer to field descriptions */ #define PB_LTYPE_SUBMESSAGE 0x08U /* Submessage with pre-decoding callback * The pre-decoding callback is stored as pb_callback_t right before pSize. * submsg_fields is pointer to field descriptions */ #define PB_LTYPE_SUBMSG_W_CB 0x09U /* Extension pseudo-field * The field contains a pointer to pb_extension_t */ #define PB_LTYPE_EXTENSION 0x0AU /* Byte array with inline, pre-allocated byffer. * data_size is the length of the inline, allocated buffer. * This differs from PB_LTYPE_BYTES by defining the element as * pb_byte_t[data_size] rather than pb_bytes_array_t. */ #define PB_LTYPE_FIXED_LENGTH_BYTES 0x0BU /* Number of declared LTYPES */ #define PB_LTYPES_COUNT 0x0CU #define PB_LTYPE_MASK 0x0FU /**** Field repetition rules ****/ #define PB_HTYPE_REQUIRED 0x00U #define PB_HTYPE_OPTIONAL 0x10U #define PB_HTYPE_SINGULAR 0x10U #define PB_HTYPE_REPEATED 0x20U #define PB_HTYPE_FIXARRAY 0x20U #define PB_HTYPE_ONEOF 0x30U #define PB_HTYPE_MASK 0x30U /**** Field allocation types ****/ #define PB_ATYPE_STATIC 0x00U #define PB_ATYPE_POINTER 0x80U #define PB_ATYPE_CALLBACK 0x40U #define PB_ATYPE_MASK 0xC0U #define PB_ATYPE(x) ((x) & PB_ATYPE_MASK) #define PB_HTYPE(x) ((x) & PB_HTYPE_MASK) #define PB_LTYPE(x) ((x) & PB_LTYPE_MASK) #define PB_LTYPE_IS_SUBMSG(x) (PB_LTYPE(x) == PB_LTYPE_SUBMESSAGE || \ PB_LTYPE(x) == PB_LTYPE_SUBMSG_W_CB) /* Data type used for storing sizes of struct fields * and array counts. */ #if defined(PB_FIELD_32BIT) typedef uint32_t pb_size_t; typedef int32_t pb_ssize_t; #else typedef uint_least16_t pb_size_t; typedef int_least16_t pb_ssize_t; #endif #define PB_SIZE_MAX ((pb_size_t)-1) /* Data type for storing encoded data and other byte streams. * This typedef exists to support platforms where uint8_t does not exist. * You can regard it as equivalent on uint8_t on other platforms. */ typedef uint_least8_t pb_byte_t; /* Forward declaration of struct types */ typedef struct pb_istream_s pb_istream_t; typedef struct pb_ostream_s pb_ostream_t; typedef struct pb_field_iter_s pb_field_iter_t; /* This structure is used in auto-generated constants * to specify struct fields. */ typedef struct pb_msgdesc_s pb_msgdesc_t; struct pb_msgdesc_s { const uint32_t *field_info; const pb_msgdesc_t * const * submsg_info; const pb_byte_t *default_value; bool (*field_callback)(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_iter_t *field); pb_size_t field_count; pb_size_t required_field_count; pb_size_t largest_tag; }; /* Iterator for message descriptor */ struct pb_field_iter_s { const pb_msgdesc_t *descriptor; /* Pointer to message descriptor constant */ void *message; /* Pointer to start of the structure */ pb_size_t index; /* Index of the field */ pb_size_t field_info_index; /* Index to descriptor->field_info array */ pb_size_t required_field_index; /* Index that counts only the required fields */ pb_size_t submessage_index; /* Index that counts only submessages */ pb_size_t tag; /* Tag of current field */ pb_size_t data_size; /* sizeof() of a single item */ pb_size_t array_size; /* Number of array entries */ pb_type_t type; /* Type of current field */ void *pField; /* Pointer to current field in struct */ void *pData; /* Pointer to current data contents. Different than pField for arrays and pointers. */ void *pSize; /* Pointer to count/has field */ const pb_msgdesc_t *submsg_desc; /* For submessage fields, pointer to field descriptor for the submessage. */ }; /* For compatibility with legacy code */ typedef pb_field_iter_t pb_field_t; /* Make sure that the standard integer types are of the expected sizes. * Otherwise fixed32/fixed64 fields can break. * * If you get errors here, it probably means that your stdint.h is not * correct for your platform. */ #ifndef PB_WITHOUT_64BIT PB_STATIC_ASSERT(sizeof(int64_t) == 2 * sizeof(int32_t), INT64_T_WRONG_SIZE) PB_STATIC_ASSERT(sizeof(uint64_t) == 2 * sizeof(uint32_t), UINT64_T_WRONG_SIZE) #endif /* This structure is used for 'bytes' arrays. * It has the number of bytes in the beginning, and after that an array. * Note that actual structs used will have a different length of bytes array. */ #define PB_BYTES_ARRAY_T(n) struct { pb_size_t size; pb_byte_t bytes[n]; } #define PB_BYTES_ARRAY_T_ALLOCSIZE(n) ((size_t)n + offsetof(pb_bytes_array_t, bytes)) struct pb_bytes_array_s { pb_size_t size; pb_byte_t bytes[1]; }; typedef struct pb_bytes_array_s pb_bytes_array_t; /* This structure is used for giving the callback function. * It is stored in the message structure and filled in by the method that * calls pb_decode. * * The decoding callback will be given a limited-length stream * If the wire type was string, the length is the length of the string. * If the wire type was a varint/fixed32/fixed64, the length is the length * of the actual value. * The function may be called multiple times (especially for repeated types, * but also otherwise if the message happens to contain the field multiple * times.) * * The encoding callback will receive the actual output stream. * It should write all the data in one call, including the field tag and * wire type. It can write multiple fields. * * The callback can be null if you want to skip a field. */ typedef struct pb_callback_s pb_callback_t; struct pb_callback_s { /* Callback functions receive a pointer to the arg field. * You can access the value of the field as *arg, and modify it if needed. */ union { bool (*decode)(pb_istream_t *stream, const pb_field_t *field, void **arg); bool (*encode)(pb_ostream_t *stream, const pb_field_t *field, void * const *arg); } funcs; /* Free arg for use by callback */ void *arg; }; extern bool pb_default_field_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field); /* Wire types. Library user needs these only in encoder callbacks. */ typedef enum { PB_WT_VARINT = 0, PB_WT_64BIT = 1, PB_WT_STRING = 2, PB_WT_32BIT = 5 } pb_wire_type_t; /* Structure for defining the handling of unknown/extension fields. * Usually the pb_extension_type_t structure is automatically generated, * while the pb_extension_t structure is created by the user. However, * if you want to catch all unknown fields, you can also create a custom * pb_extension_type_t with your own callback. */ typedef struct pb_extension_type_s pb_extension_type_t; typedef struct pb_extension_s pb_extension_t; struct pb_extension_type_s { /* Called for each unknown field in the message. * If you handle the field, read off all of its data and return true. * If you do not handle the field, do not read anything and return true. * If you run into an error, return false. * Set to NULL for default handler. */ bool (*decode)(pb_istream_t *stream, pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type); /* Called once after all regular fields have been encoded. * If you have something to write, do so and return true. * If you do not have anything to write, just return true. * If you run into an error, return false. * Set to NULL for default handler. */ bool (*encode)(pb_ostream_t *stream, const pb_extension_t *extension); /* Free field for use by the callback. */ const void *arg; }; struct pb_extension_s { /* Type describing the extension field. Usually you'll initialize * this to a pointer to the automatically generated structure. */ const pb_extension_type_t *type; /* Destination for the decoded data. This must match the datatype * of the extension field. */ void *dest; /* Pointer to the next extension handler, or NULL. * If this extension does not match a field, the next handler is * automatically called. */ pb_extension_t *next; /* The decoder sets this to true if the extension was found. * Ignored for encoding. */ bool found; }; #define pb_extension_init_zero {NULL,NULL,NULL,false} /* Memory allocation functions to use. You can define pb_realloc and * pb_free to custom functions if you want. */ #ifdef PB_ENABLE_MALLOC # ifndef pb_realloc # define pb_realloc(ptr, size) realloc(ptr, size) # endif # ifndef pb_free # define pb_free(ptr) free(ptr) # endif #endif /* This is used to inform about need to regenerate .pb.h/.pb.c files. */ #define PB_PROTO_HEADER_VERSION 40 /* These macros are used to declare pb_field_t's in the constant array. */ /* Size of a structure member, in bytes. */ #define pb_membersize(st, m) (sizeof ((st*)0)->m) /* Number of entries in an array. */ #define pb_arraysize(st, m) (pb_membersize(st, m) / pb_membersize(st, m[0])) /* Delta from start of one member to the start of another member. */ #define pb_delta(st, m1, m2) ((int)offsetof(st, m1) - (int)offsetof(st, m2)) /* Force expansion of macro value */ #define PB_EXPAND(x) x /* Binding of a message field set into a specific structure */ #define PB_BIND(msgname, structname, width) \ const uint32_t structname ## _field_info[] PB_PROGMEM = \ { \ msgname ## _FIELDLIST(PB_GEN_FIELD_INFO_ ## width, structname) \ 0 \ }; \ const pb_msgdesc_t* const structname ## _submsg_info[] = \ { \ msgname ## _FIELDLIST(PB_GEN_SUBMSG_INFO, structname) \ NULL \ }; \ const pb_msgdesc_t structname ## _msg = \ { \ structname ## _field_info, \ structname ## _submsg_info, \ msgname ## _DEFAULT, \ msgname ## _CALLBACK, \ 0 msgname ## _FIELDLIST(PB_GEN_FIELD_COUNT, structname), \ 0 msgname ## _FIELDLIST(PB_GEN_REQ_FIELD_COUNT, structname), \ 0 msgname ## _FIELDLIST(PB_GEN_LARGEST_TAG, structname), \ }; \ msgname ## _FIELDLIST(PB_GEN_FIELD_INFO_ASSERT_ ## width, structname) #define PB_GEN_FIELD_COUNT(structname, atype, htype, ltype, fieldname, tag) +1 #define PB_GEN_REQ_FIELD_COUNT(structname, atype, htype, ltype, fieldname, tag) \ + (PB_HTYPE_ ## htype == PB_HTYPE_REQUIRED) #define PB_GEN_LARGEST_TAG(structname, atype, htype, ltype, fieldname, tag) \ * 0 + tag /* X-macro for generating the entries in struct_field_info[] array. */ #define PB_GEN_FIELD_INFO_1(structname, atype, htype, ltype, fieldname, tag) \ PB_FIELDINFO_1(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) #define PB_GEN_FIELD_INFO_2(structname, atype, htype, ltype, fieldname, tag) \ PB_FIELDINFO_2(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) #define PB_GEN_FIELD_INFO_4(structname, atype, htype, ltype, fieldname, tag) \ PB_FIELDINFO_4(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) #define PB_GEN_FIELD_INFO_8(structname, atype, htype, ltype, fieldname, tag) \ PB_FIELDINFO_8(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) #define PB_GEN_FIELD_INFO_AUTO(structname, atype, htype, ltype, fieldname, tag) \ PB_FIELDINFO_AUTO2(PB_FIELDINFO_WIDTH_AUTO(_PB_ATYPE_ ## atype, _PB_HTYPE_ ## htype, _PB_LTYPE_ ## ltype), \ tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) #define PB_FIELDINFO_AUTO2(width, tag, type, data_offset, data_size, size_offset, array_size) \ PB_FIELDINFO_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) #define PB_FIELDINFO_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) \ PB_FIELDINFO_ ## width(tag, type, data_offset, data_size, size_offset, array_size) /* X-macro for generating asserts that entries fit in struct_field_info[] array. * The structure of macros here must match the structure above in PB_GEN_FIELD_INFO_x(), * but it is not easily reused because of how macro substitutions work. */ #define PB_GEN_FIELD_INFO_ASSERT_1(structname, atype, htype, ltype, fieldname, tag) \ PB_FIELDINFO_ASSERT_1(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) #define PB_GEN_FIELD_INFO_ASSERT_2(structname, atype, htype, ltype, fieldname, tag) \ PB_FIELDINFO_ASSERT_2(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) #define PB_GEN_FIELD_INFO_ASSERT_4(structname, atype, htype, ltype, fieldname, tag) \ PB_FIELDINFO_ASSERT_4(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) #define PB_GEN_FIELD_INFO_ASSERT_8(structname, atype, htype, ltype, fieldname, tag) \ PB_FIELDINFO_ASSERT_8(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) #define PB_GEN_FIELD_INFO_ASSERT_AUTO(structname, atype, htype, ltype, fieldname, tag) \ PB_FIELDINFO_ASSERT_AUTO2(PB_FIELDINFO_WIDTH_AUTO(_PB_ATYPE_ ## atype, _PB_HTYPE_ ## htype, _PB_LTYPE_ ## ltype), \ tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) #define PB_FIELDINFO_ASSERT_AUTO2(width, tag, type, data_offset, data_size, size_offset, array_size) \ PB_FIELDINFO_ASSERT_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) #define PB_FIELDINFO_ASSERT_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) \ PB_FIELDINFO_ASSERT_ ## width(tag, type, data_offset, data_size, size_offset, array_size) #define PB_DATA_OFFSET_STATIC(htype, structname, fieldname) PB_DO ## htype(structname, fieldname) #define PB_DATA_OFFSET_POINTER(htype, structname, fieldname) PB_DO ## htype(structname, fieldname) #define PB_DATA_OFFSET_CALLBACK(htype, structname, fieldname) PB_DO ## htype(structname, fieldname) #define PB_DO_PB_HTYPE_REQUIRED(structname, fieldname) offsetof(structname, fieldname) #define PB_DO_PB_HTYPE_SINGULAR(structname, fieldname) offsetof(structname, fieldname) #define PB_DO_PB_HTYPE_ONEOF(structname, fieldname) offsetof(structname, PB_ONEOF_NAME(FULL, fieldname)) #define PB_DO_PB_HTYPE_OPTIONAL(structname, fieldname) offsetof(structname, fieldname) #define PB_DO_PB_HTYPE_REPEATED(structname, fieldname) offsetof(structname, fieldname) #define PB_DO_PB_HTYPE_FIXARRAY(structname, fieldname) offsetof(structname, fieldname) #define PB_SIZE_OFFSET_STATIC(htype, structname, fieldname) PB_SO ## htype(structname, fieldname) #define PB_SIZE_OFFSET_POINTER(htype, structname, fieldname) PB_SO_PTR ## htype(structname, fieldname) #define PB_SIZE_OFFSET_CALLBACK(htype, structname, fieldname) PB_SO_CB ## htype(structname, fieldname) #define PB_SO_PB_HTYPE_REQUIRED(structname, fieldname) 0 #define PB_SO_PB_HTYPE_SINGULAR(structname, fieldname) 0 #define PB_SO_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF2(structname, PB_ONEOF_NAME(FULL, fieldname), PB_ONEOF_NAME(UNION, fieldname)) #define PB_SO_PB_HTYPE_ONEOF2(structname, fullname, unionname) PB_SO_PB_HTYPE_ONEOF3(structname, fullname, unionname) #define PB_SO_PB_HTYPE_ONEOF3(structname, fullname, unionname) pb_delta(structname, fullname, which_ ## unionname) #define PB_SO_PB_HTYPE_OPTIONAL(structname, fieldname) pb_delta(structname, fieldname, has_ ## fieldname) #define PB_SO_PB_HTYPE_REPEATED(structname, fieldname) pb_delta(structname, fieldname, fieldname ## _count) #define PB_SO_PB_HTYPE_FIXARRAY(structname, fieldname) 0 #define PB_SO_PTR_PB_HTYPE_REQUIRED(structname, fieldname) 0 #define PB_SO_PTR_PB_HTYPE_SINGULAR(structname, fieldname) 0 #define PB_SO_PTR_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF(structname, fieldname) #define PB_SO_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) 0 #define PB_SO_PTR_PB_HTYPE_REPEATED(structname, fieldname) PB_SO_PB_HTYPE_REPEATED(structname, fieldname) #define PB_SO_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) 0 #define PB_SO_CB_PB_HTYPE_REQUIRED(structname, fieldname) 0 #define PB_SO_CB_PB_HTYPE_SINGULAR(structname, fieldname) 0 #define PB_SO_CB_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF(structname, fieldname) #define PB_SO_CB_PB_HTYPE_OPTIONAL(structname, fieldname) 0 #define PB_SO_CB_PB_HTYPE_REPEATED(structname, fieldname) 0 #define PB_SO_CB_PB_HTYPE_FIXARRAY(structname, fieldname) 0 #define PB_ARRAY_SIZE_STATIC(htype, structname, fieldname) PB_AS ## htype(structname, fieldname) #define PB_ARRAY_SIZE_POINTER(htype, structname, fieldname) PB_AS_PTR ## htype(structname, fieldname) #define PB_ARRAY_SIZE_CALLBACK(htype, structname, fieldname) 1 #define PB_AS_PB_HTYPE_REQUIRED(structname, fieldname) 1 #define PB_AS_PB_HTYPE_SINGULAR(structname, fieldname) 1 #define PB_AS_PB_HTYPE_OPTIONAL(structname, fieldname) 1 #define PB_AS_PB_HTYPE_ONEOF(structname, fieldname) 1 #define PB_AS_PB_HTYPE_REPEATED(structname, fieldname) pb_arraysize(structname, fieldname) #define PB_AS_PB_HTYPE_FIXARRAY(structname, fieldname) pb_arraysize(structname, fieldname) #define PB_AS_PTR_PB_HTYPE_REQUIRED(structname, fieldname) 1 #define PB_AS_PTR_PB_HTYPE_SINGULAR(structname, fieldname) 1 #define PB_AS_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) 1 #define PB_AS_PTR_PB_HTYPE_ONEOF(structname, fieldname) 1 #define PB_AS_PTR_PB_HTYPE_REPEATED(structname, fieldname) 1 #define PB_AS_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) pb_arraysize(structname, fieldname[0]) #define PB_DATA_SIZE_STATIC(htype, structname, fieldname) PB_DS ## htype(structname, fieldname) #define PB_DATA_SIZE_POINTER(htype, structname, fieldname) PB_DS_PTR ## htype(structname, fieldname) #define PB_DATA_SIZE_CALLBACK(htype, structname, fieldname) PB_DS_CB ## htype(structname, fieldname) #define PB_DS_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname) #define PB_DS_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname) #define PB_DS_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname) #define PB_DS_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname)) #define PB_DS_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname[0]) #define PB_DS_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname[0]) #define PB_DS_PTR_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname[0]) #define PB_DS_PTR_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname[0]) #define PB_DS_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname[0]) #define PB_DS_PTR_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname)[0]) #define PB_DS_PTR_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname[0]) #define PB_DS_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname[0][0]) #define PB_DS_CB_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname) #define PB_DS_CB_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname) #define PB_DS_CB_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname) #define PB_DS_CB_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname)) #define PB_DS_CB_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname) #define PB_DS_CB_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname) #define PB_ONEOF_NAME(type, tuple) PB_EXPAND(PB_ONEOF_NAME_ ## type tuple) #define PB_ONEOF_NAME_UNION(unionname,membername,fullname) unionname #define PB_ONEOF_NAME_MEMBER(unionname,membername,fullname) membername #define PB_ONEOF_NAME_FULL(unionname,membername,fullname) fullname #define PB_GEN_SUBMSG_INFO(structname, atype, htype, ltype, fieldname, tag) \ PB_SUBMSG_INFO_ ## htype(_PB_LTYPE_ ## ltype, structname, fieldname) #define PB_SUBMSG_INFO_REQUIRED(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) #define PB_SUBMSG_INFO_SINGULAR(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) #define PB_SUBMSG_INFO_OPTIONAL(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) #define PB_SUBMSG_INFO_ONEOF(ltype, structname, fieldname) PB_SUBMSG_INFO_ONEOF2(ltype, structname, PB_ONEOF_NAME(UNION, fieldname), PB_ONEOF_NAME(MEMBER, fieldname)) #define PB_SUBMSG_INFO_ONEOF2(ltype, structname, unionname, membername) PB_SUBMSG_INFO_ONEOF3(ltype, structname, unionname, membername) #define PB_SUBMSG_INFO_ONEOF3(ltype, structname, unionname, membername) PB_SI ## ltype(structname ## _ ## unionname ## _ ## membername ## _MSGTYPE) #define PB_SUBMSG_INFO_REPEATED(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) #define PB_SUBMSG_INFO_FIXARRAY(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) #define PB_SI_PB_LTYPE_BOOL(t) #define PB_SI_PB_LTYPE_BYTES(t) #define PB_SI_PB_LTYPE_DOUBLE(t) #define PB_SI_PB_LTYPE_ENUM(t) #define PB_SI_PB_LTYPE_UENUM(t) #define PB_SI_PB_LTYPE_FIXED32(t) #define PB_SI_PB_LTYPE_FIXED64(t) #define PB_SI_PB_LTYPE_FLOAT(t) #define PB_SI_PB_LTYPE_INT32(t) #define PB_SI_PB_LTYPE_INT64(t) #define PB_SI_PB_LTYPE_MESSAGE(t) PB_SUBMSG_DESCRIPTOR(t) #define PB_SI_PB_LTYPE_MSG_W_CB(t) PB_SUBMSG_DESCRIPTOR(t) #define PB_SI_PB_LTYPE_SFIXED32(t) #define PB_SI_PB_LTYPE_SFIXED64(t) #define PB_SI_PB_LTYPE_SINT32(t) #define PB_SI_PB_LTYPE_SINT64(t) #define PB_SI_PB_LTYPE_STRING(t) #define PB_SI_PB_LTYPE_UINT32(t) #define PB_SI_PB_LTYPE_UINT64(t) #define PB_SI_PB_LTYPE_EXTENSION(t) #define PB_SI_PB_LTYPE_FIXED_LENGTH_BYTES(t) #define PB_SUBMSG_DESCRIPTOR(t) &(t ## _msg), /* The field descriptors use a variable width format, with width of either * 1, 2, 4 or 8 of 32-bit words. The two lowest bytes of the first byte always * encode the descriptor size, 6 lowest bits of field tag number, and 8 bits * of the field type. * * Descriptor size is encoded as 0 = 1 word, 1 = 2 words, 2 = 4 words, 3 = 8 words. * * Formats, listed starting with the least significant bit of the first word. * 1 word: [2-bit len] [6-bit tag] [8-bit type] [8-bit data_offset] [4-bit size_offset] [4-bit data_size] * * 2 words: [2-bit len] [6-bit tag] [8-bit type] [12-bit array_size] [4-bit size_offset] * [16-bit data_offset] [12-bit data_size] [4-bit tag>>6] * * 4 words: [2-bit len] [6-bit tag] [8-bit type] [16-bit array_size] * [8-bit size_offset] [24-bit tag>>6] * [32-bit data_offset] * [32-bit data_size] * * 8 words: [2-bit len] [6-bit tag] [8-bit type] [16-bit reserved] * [8-bit size_offset] [24-bit tag>>6] * [32-bit data_offset] * [32-bit data_size] * [32-bit array_size] * [32-bit reserved] * [32-bit reserved] * [32-bit reserved] */ #define PB_FIELDINFO_1(tag, type, data_offset, data_size, size_offset, array_size) \ (0 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(data_offset) & 0xFF) << 16) | \ (((uint32_t)(size_offset) & 0x0F) << 24) | (((uint32_t)(data_size) & 0x0F) << 28)), #define PB_FIELDINFO_2(tag, type, data_offset, data_size, size_offset, array_size) \ (1 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(array_size) & 0xFFF) << 16) | (((uint32_t)(size_offset) & 0x0F) << 28)), \ (((uint32_t)(data_offset) & 0xFFFF) | (((uint32_t)(data_size) & 0xFFF) << 16) | (((uint32_t)(tag) & 0x3c0) << 22)), #define PB_FIELDINFO_4(tag, type, data_offset, data_size, size_offset, array_size) \ (2 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(array_size) & 0xFFFF) << 16)), \ ((uint32_t)(int_least8_t)(size_offset) | (((uint32_t)(tag) << 2) & 0xFFFFFF00)), \ (data_offset), (data_size), #define PB_FIELDINFO_8(tag, type, data_offset, data_size, size_offset, array_size) \ (3 | (((tag) << 2) & 0xFF) | ((type) << 8)), \ ((uint32_t)(int_least8_t)(size_offset) | (((uint32_t)(tag) << 2) & 0xFFFFFF00)), \ (data_offset), (data_size), (array_size), 0, 0, 0, /* These assertions verify that the field information fits in the allocated space. * The generator tries to automatically determine the correct width that can fit all * data associated with a message. These asserts will fail only if there has been a * problem in the automatic logic - this may be worth reporting as a bug. As a workaround, * you can increase the descriptor width by defining PB_FIELDINFO_WIDTH or by setting * descriptorsize option in .options file. */ #define PB_FITS(value,bits) ((uint32_t)(value) < ((uint32_t)1<<bits)) #define PB_FIELDINFO_ASSERT_1(tag, type, data_offset, data_size, size_offset, array_size) \ PB_STATIC_ASSERT(PB_FITS(tag,6) && PB_FITS(data_offset,8) && PB_FITS(size_offset,4) && PB_FITS(data_size,4) && PB_FITS(array_size,1), FIELDINFO_DOES_NOT_FIT_width1_field ## tag) #define PB_FIELDINFO_ASSERT_2(tag, type, data_offset, data_size, size_offset, array_size) \ PB_STATIC_ASSERT(PB_FITS(tag,10) && PB_FITS(data_offset,16) && PB_FITS(size_offset,4) && PB_FITS(data_size,12) && PB_FITS(array_size,12), FIELDINFO_DOES_NOT_FIT_width2_field ## tag) #ifndef PB_FIELD_32BIT /* Maximum field sizes are still 16-bit if pb_size_t is 16-bit */ #define PB_FIELDINFO_ASSERT_4(tag, type, data_offset, data_size, size_offset, array_size) \ PB_STATIC_ASSERT(PB_FITS(tag,16) && PB_FITS(data_offset,16) && PB_FITS((int_least8_t)size_offset,8) && PB_FITS(data_size,16) && PB_FITS(array_size,16), FIELDINFO_DOES_NOT_FIT_width4_field ## tag) #define PB_FIELDINFO_ASSERT_8(tag, type, data_offset, data_size, size_offset, array_size) \ PB_STATIC_ASSERT(PB_FITS(tag,16) && PB_FITS(data_offset,16) && PB_FITS((int_least8_t)size_offset,8) && PB_FITS(data_size,16) && PB_FITS(array_size,16), FIELDINFO_DOES_NOT_FIT_width8_field ## tag) #else /* Up to 32-bit fields supported. * Note that the checks are against 31 bits to avoid compiler warnings about shift wider than type in the test. * I expect that there is no reasonable use for >2GB messages with nanopb anyway. */ #define PB_FIELDINFO_ASSERT_4(tag, type, data_offset, data_size, size_offset, array_size) \ PB_STATIC_ASSERT(PB_FITS(tag,30) && PB_FITS(data_offset,31) && PB_FITS(size_offset,8) && PB_FITS(data_size,31) && PB_FITS(array_size,16), FIELDINFO_DOES_NOT_FIT_width4_field ## tag) #define PB_FIELDINFO_ASSERT_8(tag, type, data_offset, data_size, size_offset, array_size) \ PB_STATIC_ASSERT(PB_FITS(tag,30) && PB_FITS(data_offset,31) && PB_FITS(size_offset,8) && PB_FITS(data_size,31) && PB_FITS(array_size,31), FIELDINFO_DOES_NOT_FIT_width8_field ## tag) #endif /* Automatic picking of FIELDINFO width: * Uses width 1 when possible, otherwise resorts to width 2. * This is used when PB_BIND() is called with "AUTO" as the argument. * The generator will give explicit size argument when it knows that a message * structure grows beyond 1-word format limits. */ #define PB_FIELDINFO_WIDTH_AUTO(atype, htype, ltype) PB_FI_WIDTH ## atype(htype, ltype) #define PB_FI_WIDTH_PB_ATYPE_STATIC(htype, ltype) PB_FI_WIDTH ## htype(ltype) #define PB_FI_WIDTH_PB_ATYPE_POINTER(htype, ltype) PB_FI_WIDTH ## htype(ltype) #define PB_FI_WIDTH_PB_ATYPE_CALLBACK(htype, ltype) 2 #define PB_FI_WIDTH_PB_HTYPE_REQUIRED(ltype) PB_FI_WIDTH ## ltype #define PB_FI_WIDTH_PB_HTYPE_SINGULAR(ltype) PB_FI_WIDTH ## ltype #define PB_FI_WIDTH_PB_HTYPE_OPTIONAL(ltype) PB_FI_WIDTH ## ltype #define PB_FI_WIDTH_PB_HTYPE_ONEOF(ltype) PB_FI_WIDTH ## ltype #define PB_FI_WIDTH_PB_HTYPE_REPEATED(ltype) 2 #define PB_FI_WIDTH_PB_HTYPE_FIXARRAY(ltype) 2 #define PB_FI_WIDTH_PB_LTYPE_BOOL 1 #define PB_FI_WIDTH_PB_LTYPE_BYTES 2 #define PB_FI_WIDTH_PB_LTYPE_DOUBLE 1 #define PB_FI_WIDTH_PB_LTYPE_ENUM 1 #define PB_FI_WIDTH_PB_LTYPE_UENUM 1 #define PB_FI_WIDTH_PB_LTYPE_FIXED32 1 #define PB_FI_WIDTH_PB_LTYPE_FIXED64 1 #define PB_FI_WIDTH_PB_LTYPE_FLOAT 1 #define PB_FI_WIDTH_PB_LTYPE_INT32 1 #define PB_FI_WIDTH_PB_LTYPE_INT64 1 #define PB_FI_WIDTH_PB_LTYPE_MESSAGE 2 #define PB_FI_WIDTH_PB_LTYPE_MSG_W_CB 2 #define PB_FI_WIDTH_PB_LTYPE_SFIXED32 1 #define PB_FI_WIDTH_PB_LTYPE_SFIXED64 1 #define PB_FI_WIDTH_PB_LTYPE_SINT32 1 #define PB_FI_WIDTH_PB_LTYPE_SINT64 1 #define PB_FI_WIDTH_PB_LTYPE_STRING 2 #define PB_FI_WIDTH_PB_LTYPE_UINT32 1 #define PB_FI_WIDTH_PB_LTYPE_UINT64 1 #define PB_FI_WIDTH_PB_LTYPE_EXTENSION 1 #define PB_FI_WIDTH_PB_LTYPE_FIXED_LENGTH_BYTES 2 /* The mapping from protobuf types to LTYPEs is done using these macros. */ #define PB_LTYPE_MAP_BOOL PB_LTYPE_BOOL #define PB_LTYPE_MAP_BYTES PB_LTYPE_BYTES #define PB_LTYPE_MAP_DOUBLE PB_LTYPE_FIXED64 #define PB_LTYPE_MAP_ENUM PB_LTYPE_VARINT #define PB_LTYPE_MAP_UENUM PB_LTYPE_UVARINT #define PB_LTYPE_MAP_FIXED32 PB_LTYPE_FIXED32 #define PB_LTYPE_MAP_FIXED64 PB_LTYPE_FIXED64 #define PB_LTYPE_MAP_FLOAT PB_LTYPE_FIXED32 #define PB_LTYPE_MAP_INT32 PB_LTYPE_VARINT #define PB_LTYPE_MAP_INT64 PB_LTYPE_VARINT #define PB_LTYPE_MAP_MESSAGE PB_LTYPE_SUBMESSAGE #define PB_LTYPE_MAP_MSG_W_CB PB_LTYPE_SUBMSG_W_CB #define PB_LTYPE_MAP_SFIXED32 PB_LTYPE_FIXED32 #define PB_LTYPE_MAP_SFIXED64 PB_LTYPE_FIXED64 #define PB_LTYPE_MAP_SINT32 PB_LTYPE_SVARINT #define PB_LTYPE_MAP_SINT64 PB_LTYPE_SVARINT #define PB_LTYPE_MAP_STRING PB_LTYPE_STRING #define PB_LTYPE_MAP_UINT32 PB_LTYPE_UVARINT #define PB_LTYPE_MAP_UINT64 PB_LTYPE_UVARINT #define PB_LTYPE_MAP_EXTENSION PB_LTYPE_EXTENSION #define PB_LTYPE_MAP_FIXED_LENGTH_BYTES PB_LTYPE_FIXED_LENGTH_BYTES /* These macros are used for giving out error messages. * They are mostly a debugging aid; the main error information * is the true/false return value from functions. * Some code space can be saved by disabling the error * messages if not used. * * PB_SET_ERROR() sets the error message if none has been set yet. * msg must be a constant string literal. * PB_GET_ERROR() always returns a pointer to a string. * PB_RETURN_ERROR() sets the error and returns false from current * function. */ #ifdef PB_NO_ERRMSG #define PB_SET_ERROR(stream, msg) PB_UNUSED(stream) #define PB_GET_ERROR(stream) "(errmsg disabled)" #else #define PB_SET_ERROR(stream, msg) (stream->errmsg = (stream)->errmsg ? (stream)->errmsg : (msg)) #define PB_GET_ERROR(stream) ((stream)->errmsg ? (stream)->errmsg : "(none)") #endif #define PB_RETURN_ERROR(stream, msg) return PB_SET_ERROR(stream, msg), false #ifdef __cplusplus } /* extern "C" */ #endif #ifdef __cplusplus #if __cplusplus >= 201103L #define PB_CONSTEXPR constexpr #else // __cplusplus >= 201103L #define PB_CONSTEXPR #endif // __cplusplus >= 201103L #if __cplusplus >= 201703L #define PB_INLINE_CONSTEXPR inline constexpr #else // __cplusplus >= 201703L #define PB_INLINE_CONSTEXPR PB_CONSTEXPR #endif // __cplusplus >= 201703L namespace nanopb { // Each type will be partially specialized by the generator. template <typename GenMessageT> struct MessageDescriptor; } // namespace nanopb #endif /* __cplusplus */ #endif
/* pb_common.c: Common support functions for pb_encode.c and pb_decode.c. * * 2014 Petteri Aimonen <[email protected]> */ #include "pb_common.h" static bool load_descriptor_values(pb_field_iter_t *iter) { uint32_t word0; uint32_t data_offset; int_least8_t size_offset; if (iter->index >= iter->descriptor->field_count) return false; word0 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); iter->type = (pb_type_t)((word0 >> 8) & 0xFF); switch(word0 & 3) { case 0: { /* 1-word format */ iter->array_size = 1; iter->tag = (pb_size_t)((word0 >> 2) & 0x3F); size_offset = (int_least8_t)((word0 >> 24) & 0x0F); data_offset = (word0 >> 16) & 0xFF; iter->data_size = (pb_size_t)((word0 >> 28) & 0x0F); break; } case 1: { /* 2-word format */ uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]); iter->array_size = (pb_size_t)((word0 >> 16) & 0x0FFF); iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 28) << 6)); size_offset = (int_least8_t)((word0 >> 28) & 0x0F); data_offset = word1 & 0xFFFF; iter->data_size = (pb_size_t)((word1 >> 16) & 0x0FFF); break; } case 2: { /* 4-word format */ uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]); uint32_t word2 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 2]); uint32_t word3 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 3]); iter->array_size = (pb_size_t)(word0 >> 16); iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 8) << 6)); size_offset = (int_least8_t)(word1 & 0xFF); data_offset = word2; iter->data_size = (pb_size_t)word3; break; } default: { /* 8-word format */ uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]); uint32_t word2 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 2]); uint32_t word3 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 3]); uint32_t word4 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 4]); iter->array_size = (pb_size_t)word4; iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 8) << 6)); size_offset = (int_least8_t)(word1 & 0xFF); data_offset = word2; iter->data_size = (pb_size_t)word3; break; } } if (!iter->message) { /* Avoid doing arithmetic on null pointers, it is undefined */ iter->pField = NULL; iter->pSize = NULL; } else { iter->pField = (char*)iter->message + data_offset; if (size_offset) { iter->pSize = (char*)iter->pField - size_offset; } else if (PB_HTYPE(iter->type) == PB_HTYPE_REPEATED && (PB_ATYPE(iter->type) == PB_ATYPE_STATIC || PB_ATYPE(iter->type) == PB_ATYPE_POINTER)) { /* Fixed count array */ iter->pSize = &iter->array_size; } else { iter->pSize = NULL; } if (PB_ATYPE(iter->type) == PB_ATYPE_POINTER && iter->pField != NULL) { iter->pData = *(void**)iter->pField; } else { iter->pData = iter->pField; } } if (PB_LTYPE_IS_SUBMSG(iter->type)) { iter->submsg_desc = iter->descriptor->submsg_info[iter->submessage_index]; } else { iter->submsg_desc = NULL; } return true; } static void advance_iterator(pb_field_iter_t *iter) { iter->index++; if (iter->index >= iter->descriptor->field_count) { /* Restart */ iter->index = 0; iter->field_info_index = 0; iter->submessage_index = 0; iter->required_field_index = 0; } else { /* Increment indexes based on previous field type. * All field info formats have the following fields: * - lowest 2 bits tell the amount of words in the descriptor (2^n words) * - bits 2..7 give the lowest bits of tag number. * - bits 8..15 give the field type. */ uint32_t prev_descriptor = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); pb_type_t prev_type = (prev_descriptor >> 8) & 0xFF; pb_size_t descriptor_len = (pb_size_t)(1 << (prev_descriptor & 3)); /* Add to fields. * The cast to pb_size_t is needed to avoid -Wconversion warning. * Because the data is is constants from generator, there is no danger of overflow. */ iter->field_info_index = (pb_size_t)(iter->field_info_index + descriptor_len); iter->required_field_index = (pb_size_t)(iter->required_field_index + (PB_HTYPE(prev_type) == PB_HTYPE_REQUIRED)); iter->submessage_index = (pb_size_t)(iter->submessage_index + PB_LTYPE_IS_SUBMSG(prev_type)); } } bool pb_field_iter_begin(pb_field_iter_t *iter, const pb_msgdesc_t *desc, void *message) { memset(iter, 0, sizeof(*iter)); iter->descriptor = desc; iter->message = message; return load_descriptor_values(iter); } bool pb_field_iter_begin_extension(pb_field_iter_t *iter, pb_extension_t *extension) { const pb_msgdesc_t *msg = (const pb_msgdesc_t*)extension->type->arg; bool status; uint32_t word0 = PB_PROGMEM_READU32(msg->field_info[0]); if (PB_ATYPE(word0 >> 8) == PB_ATYPE_POINTER) { /* For pointer extensions, the pointer is stored directly * in the extension structure. This avoids having an extra * indirection. */ status = pb_field_iter_begin(iter, msg, &extension->dest); } else { status = pb_field_iter_begin(iter, msg, extension->dest); } iter->pSize = &extension->found; return status; } bool pb_field_iter_next(pb_field_iter_t *iter) { advance_iterator(iter); (void)load_descriptor_values(iter); return iter->index != 0; } bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag) { if (iter->tag == tag) { return true; /* Nothing to do, correct field already. */ } else if (tag > iter->descriptor->largest_tag) { return false; } else { pb_size_t start = iter->index; uint32_t fieldinfo; if (tag < iter->tag) { /* Fields are in tag number order, so we know that tag is between * 0 and our start position. Setting index to end forces * advance_iterator() call below to restart from beginning. */ iter->index = iter->descriptor->field_count; } do { /* Advance iterator but don't load values yet */ advance_iterator(iter); /* Do fast check for tag number match */ fieldinfo = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); if (((fieldinfo >> 2) & 0x3F) == (tag & 0x3F)) { /* Good candidate, check further */ (void)load_descriptor_values(iter); if (iter->tag == tag && PB_LTYPE(iter->type) != PB_LTYPE_EXTENSION) { /* Found it */ return true; } } } while (iter->index != start); /* Searched all the way back to start, and found nothing. */ (void)load_descriptor_values(iter); return false; } } bool pb_field_iter_find_extension(pb_field_iter_t *iter) { if (PB_LTYPE(iter->type) == PB_LTYPE_EXTENSION) { return true; } else { pb_size_t start = iter->index; uint32_t fieldinfo; do { /* Advance iterator but don't load values yet */ advance_iterator(iter); /* Do fast check for field type */ fieldinfo = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); if (PB_LTYPE((fieldinfo >> 8) & 0xFF) == PB_LTYPE_EXTENSION) { return load_descriptor_values(iter); } } while (iter->index != start); /* Searched all the way back to start, and found nothing. */ (void)load_descriptor_values(iter); return false; } } static void *pb_const_cast(const void *p) { /* Note: this casts away const, in order to use the common field iterator * logic for both encoding and decoding. The cast is done using union * to avoid spurious compiler warnings. */ union { void *p1; const void *p2; } t; t.p2 = p; return t.p1; } bool pb_field_iter_begin_const(pb_field_iter_t *iter, const pb_msgdesc_t *desc, const void *message) { return pb_field_iter_begin(iter, desc, pb_const_cast(message)); } bool pb_field_iter_begin_extension_const(pb_field_iter_t *iter, const pb_extension_t *extension) { return pb_field_iter_begin_extension(iter, (pb_extension_t*)pb_const_cast(extension)); } bool pb_default_field_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field) { if (field->data_size == sizeof(pb_callback_t)) { pb_callback_t *pCallback = (pb_callback_t*)field->pData; if (pCallback != NULL) { if (istream != NULL && pCallback->funcs.decode != NULL) { return pCallback->funcs.decode(istream, field, &pCallback->arg); } if (ostream != NULL && pCallback->funcs.encode != NULL) { return pCallback->funcs.encode(ostream, field, &pCallback->arg); } } } return true; /* Success, but didn't do anything */ } #ifdef PB_VALIDATE_UTF8 /* This function checks whether a string is valid UTF-8 text. * * Algorithm is adapted from https://www.cl.cam.ac.uk/~mgk25/ucs/utf8_check.c * Original copyright: Markus Kuhn <http://www.cl.cam.ac.uk/~mgk25/> 2005-03-30 * Licensed under "Short code license", which allows use under MIT license or * any compatible with it. */ bool pb_validate_utf8(const char *str) { const pb_byte_t *s = (const pb_byte_t*)str; while (*s) { if (*s < 0x80) { /* 0xxxxxxx */ s++; } else if ((s[0] & 0xe0) == 0xc0) { /* 110XXXXx 10xxxxxx */ if ((s[1] & 0xc0) != 0x80 || (s[0] & 0xfe) == 0xc0) /* overlong? */ return false; else s += 2; } else if ((s[0] & 0xf0) == 0xe0) { /* 1110XXXX 10Xxxxxx 10xxxxxx */ if ((s[1] & 0xc0) != 0x80 || (s[2] & 0xc0) != 0x80 || (s[0] == 0xe0 && (s[1] & 0xe0) == 0x80) || /* overlong? */ (s[0] == 0xed && (s[1] & 0xe0) == 0xa0) || /* surrogate? */ (s[0] == 0xef && s[1] == 0xbf && (s[2] & 0xfe) == 0xbe)) /* U+FFFE or U+FFFF? */ return false; else s += 3; } else if ((s[0] & 0xf8) == 0xf0) { /* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */ if ((s[1] & 0xc0) != 0x80 || (s[2] & 0xc0) != 0x80 || (s[3] & 0xc0) != 0x80 || (s[0] == 0xf0 && (s[1] & 0xf0) == 0x80) || /* overlong? */ (s[0] == 0xf4 && s[1] > 0x8f) || s[0] > 0xf4) /* > U+10FFFF? */ return false; else s += 4; } else { return false; } } return true; } #endif
/* pb_common.h: Common support functions for pb_encode.c and pb_decode.c. * These functions are rarely needed by applications directly. */ #ifndef PB_COMMON_H_INCLUDED #define PB_COMMON_H_INCLUDED #include "pb.h" #ifdef __cplusplus extern "C" { #endif /* Initialize the field iterator structure to beginning. * Returns false if the message type is empty. */ bool pb_field_iter_begin(pb_field_iter_t *iter, const pb_msgdesc_t *desc, void *message); /* Get a field iterator for extension field. */ bool pb_field_iter_begin_extension(pb_field_iter_t *iter, pb_extension_t *extension); /* Same as pb_field_iter_begin(), but for const message pointer. * Note that the pointers in pb_field_iter_t will be non-const but shouldn't * be written to when using these functions. */ bool pb_field_iter_begin_const(pb_field_iter_t *iter, const pb_msgdesc_t *desc, const void *message); bool pb_field_iter_begin_extension_const(pb_field_iter_t *iter, const pb_extension_t *extension); /* Advance the iterator to the next field. * Returns false when the iterator wraps back to the first field. */ bool pb_field_iter_next(pb_field_iter_t *iter); /* Advance the iterator until it points at a field with the given tag. * Returns false if no such field exists. */ bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag); /* Find a field with type PB_LTYPE_EXTENSION, or return false if not found. * There can be only one extension range field per message. */ bool pb_field_iter_find_extension(pb_field_iter_t *iter); #ifdef PB_VALIDATE_UTF8 /* Validate UTF-8 text string */ bool pb_validate_utf8(const char *s); #endif #ifdef __cplusplus } /* extern "C" */ #endif #endif
/* pb_decode.c -- decode a protobuf using minimal resources * * 2011 Petteri Aimonen <[email protected]> */ /* Use the GCC warn_unused_result attribute to check that all return values * are propagated correctly. On other compilers and gcc before 3.4.0 just * ignore the annotation. */ #if !defined(__GNUC__) || ( __GNUC__ < 3) || (__GNUC__ == 3 && __GNUC_MINOR__ < 4) #define checkreturn #else #define checkreturn __attribute__((warn_unused_result)) #endif #include "pb.h" #include "pb_decode.h" #include "pb_common.h" /************************************** * Declarations internal to this file * **************************************/ static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count); static bool checkreturn pb_decode_varint32_eof(pb_istream_t *stream, uint32_t *dest, bool *eof); static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, pb_byte_t *buf, size_t *size); static bool checkreturn decode_basic_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); static bool checkreturn default_extension_decoder(pb_istream_t *stream, pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type); static bool checkreturn decode_extension(pb_istream_t *stream, uint32_t tag, pb_wire_type_t wire_type, pb_extension_t *extension); static bool pb_message_set_to_defaults(pb_field_iter_t *iter); static bool checkreturn pb_dec_bool(pb_istream_t *stream, const pb_field_iter_t *field); static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_iter_t *field); static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_iter_t *field); static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_iter_t *field); static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_iter_t *field); static bool checkreturn pb_dec_fixed_length_bytes(pb_istream_t *stream, const pb_field_iter_t *field); static bool checkreturn pb_skip_varint(pb_istream_t *stream); static bool checkreturn pb_skip_string(pb_istream_t *stream); #ifdef PB_ENABLE_MALLOC static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, size_t data_size, size_t array_size); static void initialize_pointer_field(void *pItem, pb_field_iter_t *field); static bool checkreturn pb_release_union_field(pb_istream_t *stream, pb_field_iter_t *field); static void pb_release_single_field(pb_field_iter_t *field); #endif #ifdef PB_WITHOUT_64BIT #define pb_int64_t int32_t #define pb_uint64_t uint32_t #else #define pb_int64_t int64_t #define pb_uint64_t uint64_t #endif #define PB_WT_PACKED ((pb_wire_type_t)0xFF) typedef struct { uint32_t bitfield[(PB_MAX_REQUIRED_FIELDS + 31) / 32]; } pb_fields_seen_t; /******************************* * pb_istream_t implementation * *******************************/ static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count) { size_t i; const pb_byte_t *source = (const pb_byte_t*)stream->state; stream->state = (pb_byte_t*)stream->state + count; if (buf != NULL) { for (i = 0; i < count; i++) buf[i] = source[i]; } return true; } bool checkreturn pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count) { if (count == 0) return true; #ifndef PB_BUFFER_ONLY if (buf == NULL && stream->callback != buf_read) { /* Skip input bytes */ pb_byte_t tmp[16]; while (count > 16) { if (!pb_read(stream, tmp, 16)) return false; count -= 16; } return pb_read(stream, tmp, count); } #endif if (stream->bytes_left < count) PB_RETURN_ERROR(stream, "end-of-stream"); #ifndef PB_BUFFER_ONLY if (!stream->callback(stream, buf, count)) PB_RETURN_ERROR(stream, "io error"); #else if (!buf_read(stream, buf, count)) return false; #endif stream->bytes_left -= count; return true; } /* Read a single byte from input stream. buf may not be NULL. * This is an optimization for the varint decoding. */ static bool checkreturn pb_readbyte(pb_istream_t *stream, pb_byte_t *buf) { if (stream->bytes_left == 0) PB_RETURN_ERROR(stream, "end-of-stream"); #ifndef PB_BUFFER_ONLY if (!stream->callback(stream, buf, 1)) PB_RETURN_ERROR(stream, "io error"); #else *buf = *(const pb_byte_t*)stream->state; stream->state = (pb_byte_t*)stream->state + 1; #endif stream->bytes_left--; return true; } pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t msglen) { pb_istream_t stream; /* Cast away the const from buf without a compiler error. We are * careful to use it only in a const manner in the callbacks. */ union { void *state; const void *c_state; } state; #ifdef PB_BUFFER_ONLY stream.callback = NULL; #else stream.callback = &buf_read; #endif state.c_state = buf; stream.state = state.state; stream.bytes_left = msglen; #ifndef PB_NO_ERRMSG stream.errmsg = NULL; #endif return stream; } /******************** * Helper functions * ********************/ static bool checkreturn pb_decode_varint32_eof(pb_istream_t *stream, uint32_t *dest, bool *eof) { pb_byte_t byte; uint32_t result; if (!pb_readbyte(stream, &byte)) { if (stream->bytes_left == 0) { if (eof) { *eof = true; } } return false; } if ((byte & 0x80) == 0) { /* Quick case, 1 byte value */ result = byte; } else { /* Multibyte case */ uint_fast8_t bitpos = 7; result = byte & 0x7F; do { if (!pb_readbyte(stream, &byte)) return false; if (bitpos >= 32) { /* Note: The varint could have trailing 0x80 bytes, or 0xFF for negative. */ pb_byte_t sign_extension = (bitpos < 63) ? 0xFF : 0x01; bool valid_extension = ((byte & 0x7F) == 0x00 || ((result >> 31) != 0 && byte == sign_extension)); if (bitpos >= 64 || !valid_extension) { PB_RETURN_ERROR(stream, "varint overflow"); } } else { result |= (uint32_t)(byte & 0x7F) << bitpos; } bitpos = (uint_fast8_t)(bitpos + 7); } while (byte & 0x80); if (bitpos == 35 && (byte & 0x70) != 0) { /* The last byte was at bitpos=28, so only bottom 4 bits fit. */ PB_RETURN_ERROR(stream, "varint overflow"); } } *dest = result; return true; } bool checkreturn pb_decode_varint32(pb_istream_t *stream, uint32_t *dest) { return pb_decode_varint32_eof(stream, dest, NULL); } #ifndef PB_WITHOUT_64BIT bool checkreturn pb_decode_varint(pb_istream_t *stream, uint64_t *dest) { pb_byte_t byte; uint_fast8_t bitpos = 0; uint64_t result = 0; do { if (bitpos >= 64) PB_RETURN_ERROR(stream, "varint overflow"); if (!pb_readbyte(stream, &byte)) return false; result |= (uint64_t)(byte & 0x7F) << bitpos; bitpos = (uint_fast8_t)(bitpos + 7); } while (byte & 0x80); *dest = result; return true; } #endif bool checkreturn pb_skip_varint(pb_istream_t *stream) { pb_byte_t byte; do { if (!pb_read(stream, &byte, 1)) return false; } while (byte & 0x80); return true; } bool checkreturn pb_skip_string(pb_istream_t *stream) { uint32_t length; if (!pb_decode_varint32(stream, &length)) return false; if ((size_t)length != length) { PB_RETURN_ERROR(stream, "size too large"); } return pb_read(stream, NULL, (size_t)length); } bool checkreturn pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof) { uint32_t temp; *eof = false; *wire_type = (pb_wire_type_t) 0; *tag = 0; if (!pb_decode_varint32_eof(stream, &temp, eof)) { return false; } *tag = temp >> 3; *wire_type = (pb_wire_type_t)(temp & 7); return true; } bool checkreturn pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type) { switch (wire_type) { case PB_WT_VARINT: return pb_skip_varint(stream); case PB_WT_64BIT: return pb_read(stream, NULL, 8); case PB_WT_STRING: return pb_skip_string(stream); case PB_WT_32BIT: return pb_read(stream, NULL, 4); default: PB_RETURN_ERROR(stream, "invalid wire_type"); } } /* Read a raw value to buffer, for the purpose of passing it to callback as * a substream. Size is maximum size on call, and actual size on return. */ static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, pb_byte_t *buf, size_t *size) { size_t max_size = *size; switch (wire_type) { case PB_WT_VARINT: *size = 0; do { (*size)++; if (*size > max_size) PB_RETURN_ERROR(stream, "varint overflow"); if (!pb_read(stream, buf, 1)) return false; } while (*buf++ & 0x80); return true; case PB_WT_64BIT: *size = 8; return pb_read(stream, buf, 8); case PB_WT_32BIT: *size = 4; return pb_read(stream, buf, 4); case PB_WT_STRING: /* Calling read_raw_value with a PB_WT_STRING is an error. * Explicitly handle this case and fallthrough to default to avoid * compiler warnings. */ default: PB_RETURN_ERROR(stream, "invalid wire_type"); } } /* Decode string length from stream and return a substream with limited length. * Remember to close the substream using pb_close_string_substream(). */ bool checkreturn pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream) { uint32_t size; if (!pb_decode_varint32(stream, &size)) return false; *substream = *stream; if (substream->bytes_left < size) PB_RETURN_ERROR(stream, "parent stream too short"); substream->bytes_left = (size_t)size; stream->bytes_left -= (size_t)size; return true; } bool checkreturn pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream) { if (substream->bytes_left) { if (!pb_read(substream, NULL, substream->bytes_left)) return false; } stream->state = substream->state; #ifndef PB_NO_ERRMSG stream->errmsg = substream->errmsg; #endif return true; } /************************* * Decode a single field * *************************/ static bool checkreturn decode_basic_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) { switch (PB_LTYPE(field->type)) { case PB_LTYPE_BOOL: if (wire_type != PB_WT_VARINT && wire_type != PB_WT_PACKED) PB_RETURN_ERROR(stream, "wrong wire type"); return pb_dec_bool(stream, field); case PB_LTYPE_VARINT: case PB_LTYPE_UVARINT: case PB_LTYPE_SVARINT: if (wire_type != PB_WT_VARINT && wire_type != PB_WT_PACKED) PB_RETURN_ERROR(stream, "wrong wire type"); return pb_dec_varint(stream, field); case PB_LTYPE_FIXED32: if (wire_type != PB_WT_32BIT && wire_type != PB_WT_PACKED) PB_RETURN_ERROR(stream, "wrong wire type"); return pb_decode_fixed32(stream, field->pData); case PB_LTYPE_FIXED64: if (wire_type != PB_WT_64BIT && wire_type != PB_WT_PACKED) PB_RETURN_ERROR(stream, "wrong wire type"); #ifdef PB_CONVERT_DOUBLE_FLOAT if (field->data_size == sizeof(float)) { return pb_decode_double_as_float(stream, (float*)field->pData); } #endif #ifdef PB_WITHOUT_64BIT PB_RETURN_ERROR(stream, "invalid data_size"); #else return pb_decode_fixed64(stream, field->pData); #endif case PB_LTYPE_BYTES: if (wire_type != PB_WT_STRING) PB_RETURN_ERROR(stream, "wrong wire type"); return pb_dec_bytes(stream, field); case PB_LTYPE_STRING: if (wire_type != PB_WT_STRING) PB_RETURN_ERROR(stream, "wrong wire type"); return pb_dec_string(stream, field); case PB_LTYPE_SUBMESSAGE: case PB_LTYPE_SUBMSG_W_CB: if (wire_type != PB_WT_STRING) PB_RETURN_ERROR(stream, "wrong wire type"); return pb_dec_submessage(stream, field); case PB_LTYPE_FIXED_LENGTH_BYTES: if (wire_type != PB_WT_STRING) PB_RETURN_ERROR(stream, "wrong wire type"); return pb_dec_fixed_length_bytes(stream, field); default: PB_RETURN_ERROR(stream, "invalid field type"); } } static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) { switch (PB_HTYPE(field->type)) { case PB_HTYPE_REQUIRED: return decode_basic_field(stream, wire_type, field); case PB_HTYPE_OPTIONAL: if (field->pSize != NULL) *(bool*)field->pSize = true; return decode_basic_field(stream, wire_type, field); case PB_HTYPE_REPEATED: if (wire_type == PB_WT_STRING && PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE) { /* Packed array */ bool status = true; pb_istream_t substream; pb_size_t *size = (pb_size_t*)field->pSize; field->pData = (char*)field->pField + field->data_size * (*size); if (!pb_make_string_substream(stream, &substream)) return false; while (substream.bytes_left > 0 && *size < field->array_size) { if (!decode_basic_field(&substream, PB_WT_PACKED, field)) { status = false; break; } (*size)++; field->pData = (char*)field->pData + field->data_size; } if (substream.bytes_left != 0) PB_RETURN_ERROR(stream, "array overflow"); if (!pb_close_string_substream(stream, &substream)) return false; return status; } else { /* Repeated field */ pb_size_t *size = (pb_size_t*)field->pSize; field->pData = (char*)field->pField + field->data_size * (*size); if ((*size)++ >= field->array_size) PB_RETURN_ERROR(stream, "array overflow"); return decode_basic_field(stream, wire_type, field); } case PB_HTYPE_ONEOF: *(pb_size_t*)field->pSize = field->tag; if (PB_LTYPE_IS_SUBMSG(field->type)) { /* We memset to zero so that any callbacks are set to NULL. * This is because the callbacks might otherwise have values * from some other union field. * If callbacks are needed inside oneof field, use .proto * option submsg_callback to have a separate callback function * that can set the fields before submessage is decoded. * pb_dec_submessage() will set any default values. */ memset(field->pData, 0, (size_t)field->data_size); } return decode_basic_field(stream, wire_type, field); default: PB_RETURN_ERROR(stream, "invalid field type"); } } #ifdef PB_ENABLE_MALLOC /* Allocate storage for the field and store the pointer at iter->pData. * array_size is the number of entries to reserve in an array. * Zero size is not allowed, use pb_free() for releasing. */ static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, size_t data_size, size_t array_size) { void *ptr = *(void**)pData; if (data_size == 0 || array_size == 0) PB_RETURN_ERROR(stream, "invalid size"); #ifdef __AVR__ /* Workaround for AVR libc bug 53284: http://savannah.nongnu.org/bugs/?53284 * Realloc to size of 1 byte can cause corruption of the malloc structures. */ if (data_size == 1 && array_size == 1) { data_size = 2; } #endif /* Check for multiplication overflows. * This code avoids the costly division if the sizes are small enough. * Multiplication is safe as long as only half of bits are set * in either multiplicand. */ { const size_t check_limit = (size_t)1 << (sizeof(size_t) * 4); if (data_size >= check_limit || array_size >= check_limit) { const size_t size_max = (size_t)-1; if (size_max / array_size < data_size) { PB_RETURN_ERROR(stream, "size too large"); } } } /* Allocate new or expand previous allocation */ /* Note: on failure the old pointer will remain in the structure, * the message must be freed by caller also on error return. */ ptr = pb_realloc(ptr, array_size * data_size); if (ptr == NULL) PB_RETURN_ERROR(stream, "realloc failed"); *(void**)pData = ptr; return true; } /* Clear a newly allocated item in case it contains a pointer, or is a submessage. */ static void initialize_pointer_field(void *pItem, pb_field_iter_t *field) { if (PB_LTYPE(field->type) == PB_LTYPE_STRING || PB_LTYPE(field->type) == PB_LTYPE_BYTES) { *(void**)pItem = NULL; } else if (PB_LTYPE_IS_SUBMSG(field->type)) { /* We memset to zero so that any callbacks are set to NULL. * Default values will be set by pb_dec_submessage(). */ memset(pItem, 0, field->data_size); } } #endif static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) { #ifndef PB_ENABLE_MALLOC PB_UNUSED(wire_type); PB_UNUSED(field); PB_RETURN_ERROR(stream, "no malloc support"); #else switch (PB_HTYPE(field->type)) { case PB_HTYPE_REQUIRED: case PB_HTYPE_OPTIONAL: case PB_HTYPE_ONEOF: if (PB_LTYPE_IS_SUBMSG(field->type) && *(void**)field->pField != NULL) { /* Duplicate field, have to release the old allocation first. */ /* FIXME: Does this work correctly for oneofs? */ pb_release_single_field(field); } if (PB_HTYPE(field->type) == PB_HTYPE_ONEOF) { *(pb_size_t*)field->pSize = field->tag; } if (PB_LTYPE(field->type) == PB_LTYPE_STRING || PB_LTYPE(field->type) == PB_LTYPE_BYTES) { /* pb_dec_string and pb_dec_bytes handle allocation themselves */ field->pData = field->pField; return decode_basic_field(stream, wire_type, field); } else { if (!allocate_field(stream, field->pField, field->data_size, 1)) return false; field->pData = *(void**)field->pField; initialize_pointer_field(field->pData, field); return decode_basic_field(stream, wire_type, field); } case PB_HTYPE_REPEATED: if (wire_type == PB_WT_STRING && PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE) { /* Packed array, multiple items come in at once. */ bool status = true; pb_size_t *size = (pb_size_t*)field->pSize; size_t allocated_size = *size; pb_istream_t substream; if (!pb_make_string_substream(stream, &substream)) return false; while (substream.bytes_left) { if (*size == PB_SIZE_MAX) { #ifndef PB_NO_ERRMSG stream->errmsg = "too many array entries"; #endif status = false; break; } if ((size_t)*size + 1 > allocated_size) { /* Allocate more storage. This tries to guess the * number of remaining entries. Round the division * upwards. */ size_t remain = (substream.bytes_left - 1) / field->data_size + 1; if (remain < PB_SIZE_MAX - allocated_size) allocated_size += remain; else allocated_size += 1; if (!allocate_field(&substream, field->pField, field->data_size, allocated_size)) { status = false; break; } } /* Decode the array entry */ field->pData = *(char**)field->pField + field->data_size * (*size); initialize_pointer_field(field->pData, field); if (!decode_basic_field(&substream, PB_WT_PACKED, field)) { status = false; break; } (*size)++; } if (!pb_close_string_substream(stream, &substream)) return false; return status; } else { /* Normal repeated field, i.e. only one item at a time. */ pb_size_t *size = (pb_size_t*)field->pSize; if (*size == PB_SIZE_MAX) PB_RETURN_ERROR(stream, "too many array entries"); if (!allocate_field(stream, field->pField, field->data_size, (size_t)(*size + 1))) return false; field->pData = *(char**)field->pField + field->data_size * (*size); (*size)++; initialize_pointer_field(field->pData, field); return decode_basic_field(stream, wire_type, field); } default: PB_RETURN_ERROR(stream, "invalid field type"); } #endif } static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) { if (!field->descriptor->field_callback) return pb_skip_field(stream, wire_type); if (wire_type == PB_WT_STRING) { pb_istream_t substream; size_t prev_bytes_left; if (!pb_make_string_substream(stream, &substream)) return false; do { prev_bytes_left = substream.bytes_left; if (!field->descriptor->field_callback(&substream, NULL, field)) PB_RETURN_ERROR(stream, "callback failed"); } while (substream.bytes_left > 0 && substream.bytes_left < prev_bytes_left); if (!pb_close_string_substream(stream, &substream)) return false; return true; } else { /* Copy the single scalar value to stack. * This is required so that we can limit the stream length, * which in turn allows to use same callback for packed and * not-packed fields. */ pb_istream_t substream; pb_byte_t buffer[10]; size_t size = sizeof(buffer); if (!read_raw_value(stream, wire_type, buffer, &size)) return false; substream = pb_istream_from_buffer(buffer, size); return field->descriptor->field_callback(&substream, NULL, field); } } static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) { #ifdef PB_ENABLE_MALLOC /* When decoding an oneof field, check if there is old data that must be * released first. */ if (PB_HTYPE(field->type) == PB_HTYPE_ONEOF) { if (!pb_release_union_field(stream, field)) return false; } #endif switch (PB_ATYPE(field->type)) { case PB_ATYPE_STATIC: return decode_static_field(stream, wire_type, field); case PB_ATYPE_POINTER: return decode_pointer_field(stream, wire_type, field); case PB_ATYPE_CALLBACK: return decode_callback_field(stream, wire_type, field); default: PB_RETURN_ERROR(stream, "invalid field type"); } } /* Default handler for extension fields. Expects to have a pb_msgdesc_t * pointer in the extension->type->arg field, pointing to a message with * only one field in it. */ static bool checkreturn default_extension_decoder(pb_istream_t *stream, pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type) { pb_field_iter_t iter; if (!pb_field_iter_begin_extension(&iter, extension)) PB_RETURN_ERROR(stream, "invalid extension"); if (iter.tag != tag || !iter.message) return true; extension->found = true; return decode_field(stream, wire_type, &iter); } /* Try to decode an unknown field as an extension field. Tries each extension * decoder in turn, until one of them handles the field or loop ends. */ static bool checkreturn decode_extension(pb_istream_t *stream, uint32_t tag, pb_wire_type_t wire_type, pb_extension_t *extension) { size_t pos = stream->bytes_left; while (extension != NULL && pos == stream->bytes_left) { bool status; if (extension->type->decode) status = extension->type->decode(stream, extension, tag, wire_type); else status = default_extension_decoder(stream, extension, tag, wire_type); if (!status) return false; extension = extension->next; } return true; } /* Initialize message fields to default values, recursively */ static bool pb_field_set_to_default(pb_field_iter_t *field) { pb_type_t type; type = field->type; if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) { pb_extension_t *ext = *(pb_extension_t* const *)field->pData; while (ext != NULL) { pb_field_iter_t ext_iter; if (pb_field_iter_begin_extension(&ext_iter, ext)) { ext->found = false; if (!pb_message_set_to_defaults(&ext_iter)) return false; } ext = ext->next; } } else if (PB_ATYPE(type) == PB_ATYPE_STATIC) { bool init_data = true; if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL && field->pSize != NULL) { /* Set has_field to false. Still initialize the optional field * itself also. */ *(bool*)field->pSize = false; } else if (PB_HTYPE(type) == PB_HTYPE_REPEATED || PB_HTYPE(type) == PB_HTYPE_ONEOF) { /* REPEATED: Set array count to 0, no need to initialize contents. ONEOF: Set which_field to 0. */ *(pb_size_t*)field->pSize = 0; init_data = false; } if (init_data) { if (PB_LTYPE_IS_SUBMSG(field->type) && (field->submsg_desc->default_value != NULL || field->submsg_desc->field_callback != NULL || field->submsg_desc->submsg_info[0] != NULL)) { /* Initialize submessage to defaults. * Only needed if it has default values * or callback/submessage fields. */ pb_field_iter_t submsg_iter; if (pb_field_iter_begin(&submsg_iter, field->submsg_desc, field->pData)) { if (!pb_message_set_to_defaults(&submsg_iter)) return false; } } else { /* Initialize to zeros */ memset(field->pData, 0, (size_t)field->data_size); } } } else if (PB_ATYPE(type) == PB_ATYPE_POINTER) { /* Initialize the pointer to NULL. */ *(void**)field->pField = NULL; /* Initialize array count to 0. */ if (PB_HTYPE(type) == PB_HTYPE_REPEATED || PB_HTYPE(type) == PB_HTYPE_ONEOF) { *(pb_size_t*)field->pSize = 0; } } else if (PB_ATYPE(type) == PB_ATYPE_CALLBACK) { /* Don't overwrite callback */ } return true; } static bool pb_message_set_to_defaults(pb_field_iter_t *iter) { pb_istream_t defstream = PB_ISTREAM_EMPTY; uint32_t tag = 0; pb_wire_type_t wire_type = PB_WT_VARINT; bool eof; if (iter->descriptor->default_value) { defstream = pb_istream_from_buffer(iter->descriptor->default_value, (size_t)-1); if (!pb_decode_tag(&defstream, &wire_type, &tag, &eof)) return false; } do { if (!pb_field_set_to_default(iter)) return false; if (tag != 0 && iter->tag == tag) { /* We have a default value for this field in the defstream */ if (!decode_field(&defstream, wire_type, iter)) return false; if (!pb_decode_tag(&defstream, &wire_type, &tag, &eof)) return false; if (iter->pSize) *(bool*)iter->pSize = false; } } while (pb_field_iter_next(iter)); return true; } /********************* * Decode all fields * *********************/ static bool checkreturn pb_decode_inner(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct, unsigned int flags) { uint32_t extension_range_start = 0; pb_extension_t *extensions = NULL; /* 'fixed_count_field' and 'fixed_count_size' track position of a repeated fixed * count field. This can only handle _one_ repeated fixed count field that * is unpacked and unordered among other (non repeated fixed count) fields. */ pb_size_t fixed_count_field = PB_SIZE_MAX; pb_size_t fixed_count_size = 0; pb_size_t fixed_count_total_size = 0; pb_fields_seen_t fields_seen = {{0, 0}}; const uint32_t allbits = ~(uint32_t)0; pb_field_iter_t iter; if (pb_field_iter_begin(&iter, fields, dest_struct)) { if ((flags & PB_DECODE_NOINIT) == 0) { if (!pb_message_set_to_defaults(&iter)) PB_RETURN_ERROR(stream, "failed to set defaults"); } } while (stream->bytes_left) { uint32_t tag; pb_wire_type_t wire_type; bool eof; if (!pb_decode_tag(stream, &wire_type, &tag, &eof)) { if (eof) break; else return false; } if (tag == 0) { if (flags & PB_DECODE_NULLTERMINATED) { break; } else { PB_RETURN_ERROR(stream, "zero tag"); } } if (!pb_field_iter_find(&iter, tag) || PB_LTYPE(iter.type) == PB_LTYPE_EXTENSION) { /* No match found, check if it matches an extension. */ if (extension_range_start == 0) { if (pb_field_iter_find_extension(&iter)) { extensions = *(pb_extension_t* const *)iter.pData; extension_range_start = iter.tag; } if (!extensions) { extension_range_start = (uint32_t)-1; } } if (tag >= extension_range_start) { size_t pos = stream->bytes_left; if (!decode_extension(stream, tag, wire_type, extensions)) return false; if (pos != stream->bytes_left) { /* The field was handled */ continue; } } /* No match found, skip data */ if (!pb_skip_field(stream, wire_type)) return false; continue; } /* If a repeated fixed count field was found, get size from * 'fixed_count_field' as there is no counter contained in the struct. */ if (PB_HTYPE(iter.type) == PB_HTYPE_REPEATED && iter.pSize == &iter.array_size) { if (fixed_count_field != iter.index) { /* If the new fixed count field does not match the previous one, * check that the previous one is NULL or that it finished * receiving all the expected data. */ if (fixed_count_field != PB_SIZE_MAX && fixed_count_size != fixed_count_total_size) { PB_RETURN_ERROR(stream, "wrong size for fixed count field"); } fixed_count_field = iter.index; fixed_count_size = 0; fixed_count_total_size = iter.array_size; } iter.pSize = &fixed_count_size; } if (PB_HTYPE(iter.type) == PB_HTYPE_REQUIRED && iter.required_field_index < PB_MAX_REQUIRED_FIELDS) { uint32_t tmp = ((uint32_t)1 << (iter.required_field_index & 31)); fields_seen.bitfield[iter.required_field_index >> 5] |= tmp; } if (!decode_field(stream, wire_type, &iter)) return false; } /* Check that all elements of the last decoded fixed count field were present. */ if (fixed_count_field != PB_SIZE_MAX && fixed_count_size != fixed_count_total_size) { PB_RETURN_ERROR(stream, "wrong size for fixed count field"); } /* Check that all required fields were present. */ { pb_size_t req_field_count = iter.descriptor->required_field_count; if (req_field_count > 0) { pb_size_t i; if (req_field_count > PB_MAX_REQUIRED_FIELDS) req_field_count = PB_MAX_REQUIRED_FIELDS; /* Check the whole words */ for (i = 0; i < (req_field_count >> 5); i++) { if (fields_seen.bitfield[i] != allbits) PB_RETURN_ERROR(stream, "missing required field"); } /* Check the remaining bits (if any) */ if ((req_field_count & 31) != 0) { if (fields_seen.bitfield[req_field_count >> 5] != (allbits >> (uint_least8_t)(32 - (req_field_count & 31)))) { PB_RETURN_ERROR(stream, "missing required field"); } } } } return true; } bool checkreturn pb_decode_ex(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct, unsigned int flags) { bool status; if ((flags & PB_DECODE_DELIMITED) == 0) { status = pb_decode_inner(stream, fields, dest_struct, flags); } else { pb_istream_t substream; if (!pb_make_string_substream(stream, &substream)) return false; status = pb_decode_inner(&substream, fields, dest_struct, flags); if (!pb_close_string_substream(stream, &substream)) return false; } #ifdef PB_ENABLE_MALLOC if (!status) pb_release(fields, dest_struct); #endif return status; } bool checkreturn pb_decode(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct) { bool status; status = pb_decode_inner(stream, fields, dest_struct, 0); #ifdef PB_ENABLE_MALLOC if (!status) pb_release(fields, dest_struct); #endif return status; } #ifdef PB_ENABLE_MALLOC /* Given an oneof field, if there has already been a field inside this oneof, * release it before overwriting with a different one. */ static bool pb_release_union_field(pb_istream_t *stream, pb_field_iter_t *field) { pb_field_iter_t old_field = *field; pb_size_t old_tag = *(pb_size_t*)field->pSize; /* Previous which_ value */ pb_size_t new_tag = field->tag; /* New which_ value */ if (old_tag == 0) return true; /* Ok, no old data in union */ if (old_tag == new_tag) return true; /* Ok, old data is of same type => merge */ /* Release old data. The find can fail if the message struct contains * invalid data. */ if (!pb_field_iter_find(&old_field, old_tag)) PB_RETURN_ERROR(stream, "invalid union tag"); pb_release_single_field(&old_field); return true; } static void pb_release_single_field(pb_field_iter_t *field) { pb_type_t type; type = field->type; if (PB_HTYPE(type) == PB_HTYPE_ONEOF) { if (*(pb_size_t*)field->pSize != field->tag) return; /* This is not the current field in the union */ } /* Release anything contained inside an extension or submsg. * This has to be done even if the submsg itself is statically * allocated. */ if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) { /* Release fields from all extensions in the linked list */ pb_extension_t *ext = *(pb_extension_t**)field->pData; while (ext != NULL) { pb_field_iter_t ext_iter; if (pb_field_iter_begin_extension(&ext_iter, ext)) { pb_release_single_field(&ext_iter); } ext = ext->next; } } else if (PB_LTYPE_IS_SUBMSG(type) && PB_ATYPE(type) != PB_ATYPE_CALLBACK) { /* Release fields in submessage or submsg array */ pb_size_t count = 1; if (PB_ATYPE(type) == PB_ATYPE_POINTER) { field->pData = *(void**)field->pField; } else { field->pData = field->pField; } if (PB_HTYPE(type) == PB_HTYPE_REPEATED) { count = *(pb_size_t*)field->pSize; if (PB_ATYPE(type) == PB_ATYPE_STATIC && count > field->array_size) { /* Protect against corrupted _count fields */ count = field->array_size; } } if (field->pData) { for (; count > 0; count--) { pb_release(field->submsg_desc, field->pData); field->pData = (char*)field->pData + field->data_size; } } } if (PB_ATYPE(type) == PB_ATYPE_POINTER) { if (PB_HTYPE(type) == PB_HTYPE_REPEATED && (PB_LTYPE(type) == PB_LTYPE_STRING || PB_LTYPE(type) == PB_LTYPE_BYTES)) { /* Release entries in repeated string or bytes array */ void **pItem = *(void***)field->pField; pb_size_t count = *(pb_size_t*)field->pSize; for (; count > 0; count--) { pb_free(*pItem); *pItem++ = NULL; } } if (PB_HTYPE(type) == PB_HTYPE_REPEATED) { /* We are going to release the array, so set the size to 0 */ *(pb_size_t*)field->pSize = 0; } /* Release main pointer */ pb_free(*(void**)field->pField); *(void**)field->pField = NULL; } } void pb_release(const pb_msgdesc_t *fields, void *dest_struct) { pb_field_iter_t iter; if (!dest_struct) return; /* Ignore NULL pointers, similar to free() */ if (!pb_field_iter_begin(&iter, fields, dest_struct)) return; /* Empty message type */ do { pb_release_single_field(&iter); } while (pb_field_iter_next(&iter)); } #endif /* Field decoders */ bool pb_decode_bool(pb_istream_t *stream, bool *dest) { uint32_t value; if (!pb_decode_varint32(stream, &value)) return false; *(bool*)dest = (value != 0); return true; } bool pb_decode_svarint(pb_istream_t *stream, pb_int64_t *dest) { pb_uint64_t value; if (!pb_decode_varint(stream, &value)) return false; if (value & 1) *dest = (pb_int64_t)(~(value >> 1)); else *dest = (pb_int64_t)(value >> 1); return true; } bool pb_decode_fixed32(pb_istream_t *stream, void *dest) { union { uint32_t fixed32; pb_byte_t bytes[4]; } u; if (!pb_read(stream, u.bytes, 4)) return false; #if defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN && CHAR_BIT == 8 /* fast path - if we know that we're on little endian, assign directly */ *(uint32_t*)dest = u.fixed32; #else *(uint32_t*)dest = ((uint32_t)u.bytes[0] << 0) | ((uint32_t)u.bytes[1] << 8) | ((uint32_t)u.bytes[2] << 16) | ((uint32_t)u.bytes[3] << 24); #endif return true; } #ifndef PB_WITHOUT_64BIT bool pb_decode_fixed64(pb_istream_t *stream, void *dest) { union { uint64_t fixed64; pb_byte_t bytes[8]; } u; if (!pb_read(stream, u.bytes, 8)) return false; #if defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN && CHAR_BIT == 8 /* fast path - if we know that we're on little endian, assign directly */ *(uint64_t*)dest = u.fixed64; #else *(uint64_t*)dest = ((uint64_t)u.bytes[0] << 0) | ((uint64_t)u.bytes[1] << 8) | ((uint64_t)u.bytes[2] << 16) | ((uint64_t)u.bytes[3] << 24) | ((uint64_t)u.bytes[4] << 32) | ((uint64_t)u.bytes[5] << 40) | ((uint64_t)u.bytes[6] << 48) | ((uint64_t)u.bytes[7] << 56); #endif return true; } #endif static bool checkreturn pb_dec_bool(pb_istream_t *stream, const pb_field_iter_t *field) { return pb_decode_bool(stream, (bool*)field->pData); } static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_iter_t *field) { if (PB_LTYPE(field->type) == PB_LTYPE_UVARINT) { pb_uint64_t value, clamped; if (!pb_decode_varint(stream, &value)) return false; /* Cast to the proper field size, while checking for overflows */ if (field->data_size == sizeof(pb_uint64_t)) clamped = *(pb_uint64_t*)field->pData = value; else if (field->data_size == sizeof(uint32_t)) clamped = *(uint32_t*)field->pData = (uint32_t)value; else if (field->data_size == sizeof(uint_least16_t)) clamped = *(uint_least16_t*)field->pData = (uint_least16_t)value; else if (field->data_size == sizeof(uint_least8_t)) clamped = *(uint_least8_t*)field->pData = (uint_least8_t)value; else PB_RETURN_ERROR(stream, "invalid data_size"); if (clamped != value) PB_RETURN_ERROR(stream, "integer too large"); return true; } else { pb_uint64_t value; pb_int64_t svalue; pb_int64_t clamped; if (PB_LTYPE(field->type) == PB_LTYPE_SVARINT) { if (!pb_decode_svarint(stream, &svalue)) return false; } else { if (!pb_decode_varint(stream, &value)) return false; /* See issue 97: Google's C++ protobuf allows negative varint values to * be cast as int32_t, instead of the int64_t that should be used when * encoding. Nanopb versions before 0.2.5 had a bug in encoding. In order to * not break decoding of such messages, we cast <=32 bit fields to * int32_t first to get the sign correct. */ if (field->data_size == sizeof(pb_int64_t)) svalue = (pb_int64_t)value; else svalue = (int32_t)value; } /* Cast to the proper field size, while checking for overflows */ if (field->data_size == sizeof(pb_int64_t)) clamped = *(pb_int64_t*)field->pData = svalue; else if (field->data_size == sizeof(int32_t)) clamped = *(int32_t*)field->pData = (int32_t)svalue; else if (field->data_size == sizeof(int_least16_t)) clamped = *(int_least16_t*)field->pData = (int_least16_t)svalue; else if (field->data_size == sizeof(int_least8_t)) clamped = *(int_least8_t*)field->pData = (int_least8_t)svalue; else PB_RETURN_ERROR(stream, "invalid data_size"); if (clamped != svalue) PB_RETURN_ERROR(stream, "integer too large"); return true; } } static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_iter_t *field) { uint32_t size; size_t alloc_size; pb_bytes_array_t *dest; if (!pb_decode_varint32(stream, &size)) return false; if (size > PB_SIZE_MAX) PB_RETURN_ERROR(stream, "bytes overflow"); alloc_size = PB_BYTES_ARRAY_T_ALLOCSIZE(size); if (size > alloc_size) PB_RETURN_ERROR(stream, "size too large"); if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) { #ifndef PB_ENABLE_MALLOC PB_RETURN_ERROR(stream, "no malloc support"); #else if (stream->bytes_left < size) PB_RETURN_ERROR(stream, "end-of-stream"); if (!allocate_field(stream, field->pData, alloc_size, 1)) return false; dest = *(pb_bytes_array_t**)field->pData; #endif } else { if (alloc_size > field->data_size) PB_RETURN_ERROR(stream, "bytes overflow"); dest = (pb_bytes_array_t*)field->pData; } dest->size = (pb_size_t)size; return pb_read(stream, dest->bytes, (size_t)size); } static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_iter_t *field) { uint32_t size; size_t alloc_size; pb_byte_t *dest = (pb_byte_t*)field->pData; if (!pb_decode_varint32(stream, &size)) return false; if (size == (uint32_t)-1) PB_RETURN_ERROR(stream, "size too large"); /* Space for null terminator */ alloc_size = (size_t)(size + 1); if (alloc_size < size) PB_RETURN_ERROR(stream, "size too large"); if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) { #ifndef PB_ENABLE_MALLOC PB_RETURN_ERROR(stream, "no malloc support"); #else if (stream->bytes_left < size) PB_RETURN_ERROR(stream, "end-of-stream"); if (!allocate_field(stream, field->pData, alloc_size, 1)) return false; dest = *(pb_byte_t**)field->pData; #endif } else { if (alloc_size > field->data_size) PB_RETURN_ERROR(stream, "string overflow"); } dest[size] = 0; if (!pb_read(stream, dest, (size_t)size)) return false; #ifdef PB_VALIDATE_UTF8 if (!pb_validate_utf8((const char*)dest)) PB_RETURN_ERROR(stream, "invalid utf8"); #endif return true; } static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_iter_t *field) { bool status = true; bool submsg_consumed = false; pb_istream_t substream; if (!pb_make_string_substream(stream, &substream)) return false; if (field->submsg_desc == NULL) PB_RETURN_ERROR(stream, "invalid field descriptor"); /* Submessages can have a separate message-level callback that is called * before decoding the message. Typically it is used to set callback fields * inside oneofs. */ if (PB_LTYPE(field->type) == PB_LTYPE_SUBMSG_W_CB && field->pSize != NULL) { /* Message callback is stored right before pSize. */ pb_callback_t *callback = (pb_callback_t*)field->pSize - 1; if (callback->funcs.decode) { status = callback->funcs.decode(&substream, field, &callback->arg); if (substream.bytes_left == 0) { submsg_consumed = true; } } } /* Now decode the submessage contents */ if (status && !submsg_consumed) { unsigned int flags = 0; /* Static required/optional fields are already initialized by top-level * pb_decode(), no need to initialize them again. */ if (PB_ATYPE(field->type) == PB_ATYPE_STATIC && PB_HTYPE(field->type) != PB_HTYPE_REPEATED && PB_HTYPE(field->type) != PB_HTYPE_ONEOF) { flags = PB_DECODE_NOINIT; } status = pb_decode_inner(&substream, field->submsg_desc, field->pData, flags); } if (!pb_close_string_substream(stream, &substream)) return false; return status; } static bool checkreturn pb_dec_fixed_length_bytes(pb_istream_t *stream, const pb_field_iter_t *field) { uint32_t size; if (!pb_decode_varint32(stream, &size)) return false; if (size > PB_SIZE_MAX) PB_RETURN_ERROR(stream, "bytes overflow"); if (size == 0) { /* As a special case, treat empty bytes string as all zeros for fixed_length_bytes. */ memset(field->pData, 0, (size_t)field->data_size); return true; } if (size != field->data_size) PB_RETURN_ERROR(stream, "incorrect fixed length bytes size"); return pb_read(stream, (pb_byte_t*)field->pData, (size_t)field->data_size); } #ifdef PB_CONVERT_DOUBLE_FLOAT bool pb_decode_double_as_float(pb_istream_t *stream, float *dest) { uint_least8_t sign; int exponent; uint32_t mantissa; uint64_t value; union { float f; uint32_t i; } out; if (!pb_decode_fixed64(stream, &value)) return false; /* Decompose input value */ sign = (uint_least8_t)((value >> 63) & 1); exponent = (int)((value >> 52) & 0x7FF) - 1023; mantissa = (value >> 28) & 0xFFFFFF; /* Highest 24 bits */ /* Figure if value is in range representable by floats. */ if (exponent == 1024) { /* Special value */ exponent = 128; mantissa >>= 1; } else { if (exponent > 127) { /* Too large, convert to infinity */ exponent = 128; mantissa = 0; } else if (exponent < -150) { /* Too small, convert to zero */ exponent = -127; mantissa = 0; } else if (exponent < -126) { /* Denormalized */ mantissa |= 0x1000000; mantissa >>= (-126 - exponent); exponent = -127; } /* Round off mantissa */ mantissa = (mantissa + 1) >> 1; /* Check if mantissa went over 2.0 */ if (mantissa & 0x800000) { exponent += 1; mantissa &= 0x7FFFFF; mantissa >>= 1; } } /* Combine fields */ out.i = mantissa; out.i |= (uint32_t)(exponent + 127) << 23; out.i |= (uint32_t)sign << 31; *dest = out.f; return true; } #endif
/* pb_decode.h: Functions to decode protocol buffers. Depends on pb_decode.c. * The main function is pb_decode. You also need an input stream, and the * field descriptions created by nanopb_generator.py. */ #ifndef PB_DECODE_H_INCLUDED #define PB_DECODE_H_INCLUDED #include "pb.h" #ifdef __cplusplus extern "C" { #endif /* Structure for defining custom input streams. You will need to provide * a callback function to read the bytes from your storage, which can be * for example a file or a network socket. * * The callback must conform to these rules: * * 1) Return false on IO errors. This will cause decoding to abort. * 2) You can use state to store your own data (e.g. buffer pointer), * and rely on pb_read to verify that no-body reads past bytes_left. * 3) Your callback may be used with substreams, in which case bytes_left * is different than from the main stream. Don't use bytes_left to compute * any pointers. */ struct pb_istream_s { #ifdef PB_BUFFER_ONLY /* Callback pointer is not used in buffer-only configuration. * Having an int pointer here allows binary compatibility but * gives an error if someone tries to assign callback function. */ int *callback; #else bool (*callback)(pb_istream_t *stream, pb_byte_t *buf, size_t count); #endif void *state; /* Free field for use by callback implementation */ size_t bytes_left; #ifndef PB_NO_ERRMSG const char *errmsg; #endif }; #ifndef PB_NO_ERRMSG #define PB_ISTREAM_EMPTY {0,0,0,0} #else #define PB_ISTREAM_EMPTY {0,0,0} #endif /*************************** * Main decoding functions * ***************************/ /* Decode a single protocol buffers message from input stream into a C structure. * Returns true on success, false on any failure. * The actual struct pointed to by dest must match the description in fields. * Callback fields of the destination structure must be initialized by caller. * All other fields will be initialized by this function. * * Example usage: * MyMessage msg = {}; * uint8_t buffer[64]; * pb_istream_t stream; * * // ... read some data into buffer ... * * stream = pb_istream_from_buffer(buffer, count); * pb_decode(&stream, MyMessage_fields, &msg); */ bool pb_decode(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct); /* Extended version of pb_decode, with several options to control * the decoding process: * * PB_DECODE_NOINIT: Do not initialize the fields to default values. * This is slightly faster if you do not need the default * values and instead initialize the structure to 0 using * e.g. memset(). This can also be used for merging two * messages, i.e. combine already existing data with new * values. * * PB_DECODE_DELIMITED: Input message starts with the message size as varint. * Corresponds to parseDelimitedFrom() in Google's * protobuf API. * * PB_DECODE_NULLTERMINATED: Stop reading when field tag is read as 0. This allows * reading null terminated messages. * NOTE: Until nanopb-0.4.0, pb_decode() also allows * null-termination. This behaviour is not supported in * most other protobuf implementations, so PB_DECODE_DELIMITED * is a better option for compatibility. * * Multiple flags can be combined with bitwise or (| operator) */ #define PB_DECODE_NOINIT 0x01U #define PB_DECODE_DELIMITED 0x02U #define PB_DECODE_NULLTERMINATED 0x04U bool pb_decode_ex(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct, unsigned int flags); /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ #define pb_decode_noinit(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_NOINIT) #define pb_decode_delimited(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_DELIMITED) #define pb_decode_delimited_noinit(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_DELIMITED | PB_DECODE_NOINIT) #define pb_decode_nullterminated(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_NULLTERMINATED) #ifdef PB_ENABLE_MALLOC /* Release any allocated pointer fields. If you use dynamic allocation, you should * call this for any successfully decoded message when you are done with it. If * pb_decode() returns with an error, the message is already released. */ void pb_release(const pb_msgdesc_t *fields, void *dest_struct); #else /* Allocation is not supported, so release is no-op */ #define pb_release(fields, dest_struct) PB_UNUSED(fields); PB_UNUSED(dest_struct); #endif /************************************** * Functions for manipulating streams * **************************************/ /* Create an input stream for reading from a memory buffer. * * msglen should be the actual length of the message, not the full size of * allocated buffer. * * Alternatively, you can use a custom stream that reads directly from e.g. * a file or a network socket. */ pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t msglen); /* Function to read from a pb_istream_t. You can use this if you need to * read some custom header data, or to read data in field callbacks. */ bool pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count); /************************************************ * Helper functions for writing field callbacks * ************************************************/ /* Decode the tag for the next field in the stream. Gives the wire type and * field tag. At end of the message, returns false and sets eof to true. */ bool pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof); /* Skip the field payload data, given the wire type. */ bool pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type); /* Decode an integer in the varint format. This works for enum, int32, * int64, uint32 and uint64 field types. */ #ifndef PB_WITHOUT_64BIT bool pb_decode_varint(pb_istream_t *stream, uint64_t *dest); #else #define pb_decode_varint pb_decode_varint32 #endif /* Decode an integer in the varint format. This works for enum, int32, * and uint32 field types. */ bool pb_decode_varint32(pb_istream_t *stream, uint32_t *dest); /* Decode a bool value in varint format. */ bool pb_decode_bool(pb_istream_t *stream, bool *dest); /* Decode an integer in the zig-zagged svarint format. This works for sint32 * and sint64. */ #ifndef PB_WITHOUT_64BIT bool pb_decode_svarint(pb_istream_t *stream, int64_t *dest); #else bool pb_decode_svarint(pb_istream_t *stream, int32_t *dest); #endif /* Decode a fixed32, sfixed32 or float value. You need to pass a pointer to * a 4-byte wide C variable. */ bool pb_decode_fixed32(pb_istream_t *stream, void *dest); #ifndef PB_WITHOUT_64BIT /* Decode a fixed64, sfixed64 or double value. You need to pass a pointer to * a 8-byte wide C variable. */ bool pb_decode_fixed64(pb_istream_t *stream, void *dest); #endif #ifdef PB_CONVERT_DOUBLE_FLOAT /* Decode a double value into float variable. */ bool pb_decode_double_as_float(pb_istream_t *stream, float *dest); #endif /* Make a limited-length substream for reading a PB_WT_STRING field. */ bool pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream); bool pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream); #ifdef __cplusplus } /* extern "C" */ #endif #endif
/* pb_encode.c -- encode a protobuf using minimal resources * * 2011 Petteri Aimonen <[email protected]> */ #include "pb.h" #include "pb_encode.h" #include "pb_common.h" /* Use the GCC warn_unused_result attribute to check that all return values * are propagated correctly. On other compilers and gcc before 3.4.0 just * ignore the annotation. */ #if !defined(__GNUC__) || ( __GNUC__ < 3) || (__GNUC__ == 3 && __GNUC_MINOR__ < 4) #define checkreturn #else #define checkreturn __attribute__((warn_unused_result)) #endif /************************************** * Declarations internal to this file * **************************************/ static bool checkreturn buf_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); static bool checkreturn encode_array(pb_ostream_t *stream, pb_field_iter_t *field); static bool checkreturn pb_check_proto3_default_value(const pb_field_iter_t *field); static bool checkreturn encode_basic_field(pb_ostream_t *stream, const pb_field_iter_t *field); static bool checkreturn encode_callback_field(pb_ostream_t *stream, const pb_field_iter_t *field); static bool checkreturn encode_field(pb_ostream_t *stream, pb_field_iter_t *field); static bool checkreturn encode_extension_field(pb_ostream_t *stream, const pb_field_iter_t *field); static bool checkreturn default_extension_encoder(pb_ostream_t *stream, const pb_extension_t *extension); static bool checkreturn pb_encode_varint_32(pb_ostream_t *stream, uint32_t low, uint32_t high); static bool checkreturn pb_enc_bool(pb_ostream_t *stream, const pb_field_iter_t *field); static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_iter_t *field); static bool checkreturn pb_enc_fixed(pb_ostream_t *stream, const pb_field_iter_t *field); static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_iter_t *field); static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_iter_t *field); static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_iter_t *field); static bool checkreturn pb_enc_fixed_length_bytes(pb_ostream_t *stream, const pb_field_iter_t *field); #ifdef PB_WITHOUT_64BIT #define pb_int64_t int32_t #define pb_uint64_t uint32_t #else #define pb_int64_t int64_t #define pb_uint64_t uint64_t #endif /******************************* * pb_ostream_t implementation * *******************************/ static bool checkreturn buf_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count) { size_t i; pb_byte_t *dest = (pb_byte_t*)stream->state; stream->state = dest + count; for (i = 0; i < count; i++) dest[i] = buf[i]; return true; } pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize) { pb_ostream_t stream; #ifdef PB_BUFFER_ONLY stream.callback = (void*)1; /* Just a marker value */ #else stream.callback = &buf_write; #endif stream.state = buf; stream.max_size = bufsize; stream.bytes_written = 0; #ifndef PB_NO_ERRMSG stream.errmsg = NULL; #endif return stream; } bool checkreturn pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count) { if (count > 0 && stream->callback != NULL) { if (stream->bytes_written + count < stream->bytes_written || stream->bytes_written + count > stream->max_size) { PB_RETURN_ERROR(stream, "stream full"); } #ifdef PB_BUFFER_ONLY if (!buf_write(stream, buf, count)) PB_RETURN_ERROR(stream, "io error"); #else if (!stream->callback(stream, buf, count)) PB_RETURN_ERROR(stream, "io error"); #endif } stream->bytes_written += count; return true; } /************************* * Encode a single field * *************************/ /* Read a bool value without causing undefined behavior even if the value * is invalid. See issue #434 and * https://stackoverflow.com/questions/27661768/weird-results-for-conditional */ static bool safe_read_bool(const void *pSize) { const char *p = (const char *)pSize; size_t i; for (i = 0; i < sizeof(bool); i++) { if (p[i] != 0) return true; } return false; } /* Encode a static array. Handles the size calculations and possible packing. */ static bool checkreturn encode_array(pb_ostream_t *stream, pb_field_iter_t *field) { pb_size_t i; pb_size_t count; #ifndef PB_ENCODE_ARRAYS_UNPACKED size_t size; #endif count = *(pb_size_t*)field->pSize; if (count == 0) return true; if (PB_ATYPE(field->type) != PB_ATYPE_POINTER && count > field->array_size) PB_RETURN_ERROR(stream, "array max size exceeded"); #ifndef PB_ENCODE_ARRAYS_UNPACKED /* We always pack arrays if the datatype allows it. */ if (PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE) { if (!pb_encode_tag(stream, PB_WT_STRING, field->tag)) return false; /* Determine the total size of packed array. */ if (PB_LTYPE(field->type) == PB_LTYPE_FIXED32) { size = 4 * (size_t)count; } else if (PB_LTYPE(field->type) == PB_LTYPE_FIXED64) { size = 8 * (size_t)count; } else { pb_ostream_t sizestream = PB_OSTREAM_SIZING; void *pData_orig = field->pData; for (i = 0; i < count; i++) { if (!pb_enc_varint(&sizestream, field)) PB_RETURN_ERROR(stream, PB_GET_ERROR(&sizestream)); field->pData = (char*)field->pData + field->data_size; } field->pData = pData_orig; size = sizestream.bytes_written; } if (!pb_encode_varint(stream, (pb_uint64_t)size)) return false; if (stream->callback == NULL) return pb_write(stream, NULL, size); /* Just sizing.. */ /* Write the data */ for (i = 0; i < count; i++) { if (PB_LTYPE(field->type) == PB_LTYPE_FIXED32 || PB_LTYPE(field->type) == PB_LTYPE_FIXED64) { if (!pb_enc_fixed(stream, field)) return false; } else { if (!pb_enc_varint(stream, field)) return false; } field->pData = (char*)field->pData + field->data_size; } } else /* Unpacked fields */ #endif { for (i = 0; i < count; i++) { /* Normally the data is stored directly in the array entries, but * for pointer-type string and bytes fields, the array entries are * actually pointers themselves also. So we have to dereference once * more to get to the actual data. */ if (PB_ATYPE(field->type) == PB_ATYPE_POINTER && (PB_LTYPE(field->type) == PB_LTYPE_STRING || PB_LTYPE(field->type) == PB_LTYPE_BYTES)) { bool status; void *pData_orig = field->pData; field->pData = *(void* const*)field->pData; if (!field->pData) { /* Null pointer in array is treated as empty string / bytes */ status = pb_encode_tag_for_field(stream, field) && pb_encode_varint(stream, 0); } else { status = encode_basic_field(stream, field); } field->pData = pData_orig; if (!status) return false; } else { if (!encode_basic_field(stream, field)) return false; } field->pData = (char*)field->pData + field->data_size; } } return true; } /* In proto3, all fields are optional and are only encoded if their value is "non-zero". * This function implements the check for the zero value. */ static bool checkreturn pb_check_proto3_default_value(const pb_field_iter_t *field) { pb_type_t type = field->type; if (PB_ATYPE(type) == PB_ATYPE_STATIC) { if (PB_HTYPE(type) == PB_HTYPE_REQUIRED) { /* Required proto2 fields inside proto3 submessage, pretty rare case */ return false; } else if (PB_HTYPE(type) == PB_HTYPE_REPEATED) { /* Repeated fields inside proto3 submessage: present if count != 0 */ return *(const pb_size_t*)field->pSize == 0; } else if (PB_HTYPE(type) == PB_HTYPE_ONEOF) { /* Oneof fields */ return *(const pb_size_t*)field->pSize == 0; } else if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL && field->pSize != NULL) { /* Proto2 optional fields inside proto3 message, or proto3 * submessage fields. */ return safe_read_bool(field->pSize) == false; } else if (field->descriptor->default_value) { /* Proto3 messages do not have default values, but proto2 messages * can contain optional fields without has_fields (generator option 'proto3'). * In this case they must always be encoded, to make sure that the * non-zero default value is overwritten. */ return false; } /* Rest is proto3 singular fields */ if (PB_LTYPE(type) <= PB_LTYPE_LAST_PACKABLE) { /* Simple integer / float fields */ pb_size_t i; const char *p = (const char*)field->pData; for (i = 0; i < field->data_size; i++) { if (p[i] != 0) { return false; } } return true; } else if (PB_LTYPE(type) == PB_LTYPE_BYTES) { const pb_bytes_array_t *bytes = (const pb_bytes_array_t*)field->pData; return bytes->size == 0; } else if (PB_LTYPE(type) == PB_LTYPE_STRING) { return *(const char*)field->pData == '\0'; } else if (PB_LTYPE(type) == PB_LTYPE_FIXED_LENGTH_BYTES) { /* Fixed length bytes is only empty if its length is fixed * as 0. Which would be pretty strange, but we can check * it anyway. */ return field->data_size == 0; } else if (PB_LTYPE_IS_SUBMSG(type)) { /* Check all fields in the submessage to find if any of them * are non-zero. The comparison cannot be done byte-per-byte * because the C struct may contain padding bytes that must * be skipped. Note that usually proto3 submessages have * a separate has_field that is checked earlier in this if. */ pb_field_iter_t iter; if (pb_field_iter_begin(&iter, field->submsg_desc, field->pData)) { do { if (!pb_check_proto3_default_value(&iter)) { return false; } } while (pb_field_iter_next(&iter)); } return true; } } else if (PB_ATYPE(type) == PB_ATYPE_POINTER) { return field->pData == NULL; } else if (PB_ATYPE(type) == PB_ATYPE_CALLBACK) { if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) { const pb_extension_t *extension = *(const pb_extension_t* const *)field->pData; return extension == NULL; } else if (field->descriptor->field_callback == pb_default_field_callback) { pb_callback_t *pCallback = (pb_callback_t*)field->pData; return pCallback->funcs.encode == NULL; } else { return field->descriptor->field_callback == NULL; } } return false; /* Not typically reached, safe default for weird special cases. */ } /* Encode a field with static or pointer allocation, i.e. one whose data * is available to the encoder directly. */ static bool checkreturn encode_basic_field(pb_ostream_t *stream, const pb_field_iter_t *field) { if (!field->pData) { /* Missing pointer field */ return true; } if (!pb_encode_tag_for_field(stream, field)) return false; switch (PB_LTYPE(field->type)) { case PB_LTYPE_BOOL: return pb_enc_bool(stream, field); case PB_LTYPE_VARINT: case PB_LTYPE_UVARINT: case PB_LTYPE_SVARINT: return pb_enc_varint(stream, field); case PB_LTYPE_FIXED32: case PB_LTYPE_FIXED64: return pb_enc_fixed(stream, field); case PB_LTYPE_BYTES: return pb_enc_bytes(stream, field); case PB_LTYPE_STRING: return pb_enc_string(stream, field); case PB_LTYPE_SUBMESSAGE: case PB_LTYPE_SUBMSG_W_CB: return pb_enc_submessage(stream, field); case PB_LTYPE_FIXED_LENGTH_BYTES: return pb_enc_fixed_length_bytes(stream, field); default: PB_RETURN_ERROR(stream, "invalid field type"); } } /* Encode a field with callback semantics. This means that a user function is * called to provide and encode the actual data. */ static bool checkreturn encode_callback_field(pb_ostream_t *stream, const pb_field_iter_t *field) { if (field->descriptor->field_callback != NULL) { if (!field->descriptor->field_callback(NULL, stream, field)) PB_RETURN_ERROR(stream, "callback error"); } return true; } /* Encode a single field of any callback, pointer or static type. */ static bool checkreturn encode_field(pb_ostream_t *stream, pb_field_iter_t *field) { /* Check field presence */ if (PB_HTYPE(field->type) == PB_HTYPE_ONEOF) { if (*(const pb_size_t*)field->pSize != field->tag) { /* Different type oneof field */ return true; } } else if (PB_HTYPE(field->type) == PB_HTYPE_OPTIONAL) { if (field->pSize) { if (safe_read_bool(field->pSize) == false) { /* Missing optional field */ return true; } } else if (PB_ATYPE(field->type) == PB_ATYPE_STATIC) { /* Proto3 singular field */ if (pb_check_proto3_default_value(field)) return true; } } if (!field->pData) { if (PB_HTYPE(field->type) == PB_HTYPE_REQUIRED) PB_RETURN_ERROR(stream, "missing required field"); /* Pointer field set to NULL */ return true; } /* Then encode field contents */ if (PB_ATYPE(field->type) == PB_ATYPE_CALLBACK) { return encode_callback_field(stream, field); } else if (PB_HTYPE(field->type) == PB_HTYPE_REPEATED) { return encode_array(stream, field); } else { return encode_basic_field(stream, field); } } /* Default handler for extension fields. Expects to have a pb_msgdesc_t * pointer in the extension->type->arg field, pointing to a message with * only one field in it. */ static bool checkreturn default_extension_encoder(pb_ostream_t *stream, const pb_extension_t *extension) { pb_field_iter_t iter; if (!pb_field_iter_begin_extension_const(&iter, extension)) PB_RETURN_ERROR(stream, "invalid extension"); return encode_field(stream, &iter); } /* Walk through all the registered extensions and give them a chance * to encode themselves. */ static bool checkreturn encode_extension_field(pb_ostream_t *stream, const pb_field_iter_t *field) { const pb_extension_t *extension = *(const pb_extension_t* const *)field->pData; while (extension) { bool status; if (extension->type->encode) status = extension->type->encode(stream, extension); else status = default_extension_encoder(stream, extension); if (!status) return false; extension = extension->next; } return true; } /********************* * Encode all fields * *********************/ bool checkreturn pb_encode(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct) { pb_field_iter_t iter; if (!pb_field_iter_begin_const(&iter, fields, src_struct)) return true; /* Empty message type */ do { if (PB_LTYPE(iter.type) == PB_LTYPE_EXTENSION) { /* Special case for the extension field placeholder */ if (!encode_extension_field(stream, &iter)) return false; } else { /* Regular field */ if (!encode_field(stream, &iter)) return false; } } while (pb_field_iter_next(&iter)); return true; } bool checkreturn pb_encode_ex(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct, unsigned int flags) { if ((flags & PB_ENCODE_DELIMITED) != 0) { return pb_encode_submessage(stream, fields, src_struct); } else if ((flags & PB_ENCODE_NULLTERMINATED) != 0) { const pb_byte_t zero = 0; if (!pb_encode(stream, fields, src_struct)) return false; return pb_write(stream, &zero, 1); } else { return pb_encode(stream, fields, src_struct); } } bool pb_get_encoded_size(size_t *size, const pb_msgdesc_t *fields, const void *src_struct) { pb_ostream_t stream = PB_OSTREAM_SIZING; if (!pb_encode(&stream, fields, src_struct)) return false; *size = stream.bytes_written; return true; } /******************** * Helper functions * ********************/ /* This function avoids 64-bit shifts as they are quite slow on many platforms. */ static bool checkreturn pb_encode_varint_32(pb_ostream_t *stream, uint32_t low, uint32_t high) { size_t i = 0; pb_byte_t buffer[10]; pb_byte_t byte = (pb_byte_t)(low & 0x7F); low >>= 7; while (i < 4 && (low != 0 || high != 0)) { byte |= 0x80; buffer[i++] = byte; byte = (pb_byte_t)(low & 0x7F); low >>= 7; } if (high) { byte = (pb_byte_t)(byte | ((high & 0x07) << 4)); high >>= 3; while (high) { byte |= 0x80; buffer[i++] = byte; byte = (pb_byte_t)(high & 0x7F); high >>= 7; } } buffer[i++] = byte; return pb_write(stream, buffer, i); } bool checkreturn pb_encode_varint(pb_ostream_t *stream, pb_uint64_t value) { if (value <= 0x7F) { /* Fast path: single byte */ pb_byte_t byte = (pb_byte_t)value; return pb_write(stream, &byte, 1); } else { #ifdef PB_WITHOUT_64BIT return pb_encode_varint_32(stream, value, 0); #else return pb_encode_varint_32(stream, (uint32_t)value, (uint32_t)(value >> 32)); #endif } } bool checkreturn pb_encode_svarint(pb_ostream_t *stream, pb_int64_t value) { pb_uint64_t zigzagged; if (value < 0) zigzagged = ~((pb_uint64_t)value << 1); else zigzagged = (pb_uint64_t)value << 1; return pb_encode_varint(stream, zigzagged); } bool checkreturn pb_encode_fixed32(pb_ostream_t *stream, const void *value) { uint32_t val = *(const uint32_t*)value; pb_byte_t bytes[4]; bytes[0] = (pb_byte_t)(val & 0xFF); bytes[1] = (pb_byte_t)((val >> 8) & 0xFF); bytes[2] = (pb_byte_t)((val >> 16) & 0xFF); bytes[3] = (pb_byte_t)((val >> 24) & 0xFF); return pb_write(stream, bytes, 4); } #ifndef PB_WITHOUT_64BIT bool checkreturn pb_encode_fixed64(pb_ostream_t *stream, const void *value) { uint64_t val = *(const uint64_t*)value; pb_byte_t bytes[8]; bytes[0] = (pb_byte_t)(val & 0xFF); bytes[1] = (pb_byte_t)((val >> 8) & 0xFF); bytes[2] = (pb_byte_t)((val >> 16) & 0xFF); bytes[3] = (pb_byte_t)((val >> 24) & 0xFF); bytes[4] = (pb_byte_t)((val >> 32) & 0xFF); bytes[5] = (pb_byte_t)((val >> 40) & 0xFF); bytes[6] = (pb_byte_t)((val >> 48) & 0xFF); bytes[7] = (pb_byte_t)((val >> 56) & 0xFF); return pb_write(stream, bytes, 8); } #endif bool checkreturn pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number) { pb_uint64_t tag = ((pb_uint64_t)field_number << 3) | wiretype; return pb_encode_varint(stream, tag); } bool pb_encode_tag_for_field ( pb_ostream_t* stream, const pb_field_iter_t* field ) { pb_wire_type_t wiretype; switch (PB_LTYPE(field->type)) { case PB_LTYPE_BOOL: case PB_LTYPE_VARINT: case PB_LTYPE_UVARINT: case PB_LTYPE_SVARINT: wiretype = PB_WT_VARINT; break; case PB_LTYPE_FIXED32: wiretype = PB_WT_32BIT; break; case PB_LTYPE_FIXED64: wiretype = PB_WT_64BIT; break; case PB_LTYPE_BYTES: case PB_LTYPE_STRING: case PB_LTYPE_SUBMESSAGE: case PB_LTYPE_SUBMSG_W_CB: case PB_LTYPE_FIXED_LENGTH_BYTES: wiretype = PB_WT_STRING; break; default: PB_RETURN_ERROR(stream, "invalid field type"); } return pb_encode_tag(stream, wiretype, field->tag); } bool checkreturn pb_encode_string(pb_ostream_t *stream, const pb_byte_t *buffer, size_t size) { if (!pb_encode_varint(stream, (pb_uint64_t)size)) return false; return pb_write(stream, buffer, size); } bool checkreturn pb_encode_submessage(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct) { /* First calculate the message size using a non-writing substream. */ pb_ostream_t substream = PB_OSTREAM_SIZING; size_t size; bool status; if (!pb_encode(&substream, fields, src_struct)) { #ifndef PB_NO_ERRMSG stream->errmsg = substream.errmsg; #endif return false; } size = substream.bytes_written; if (!pb_encode_varint(stream, (pb_uint64_t)size)) return false; if (stream->callback == NULL) return pb_write(stream, NULL, size); /* Just sizing */ if (stream->bytes_written + size > stream->max_size) PB_RETURN_ERROR(stream, "stream full"); /* Use a substream to verify that a callback doesn't write more than * what it did the first time. */ substream.callback = stream->callback; substream.state = stream->state; substream.max_size = size; substream.bytes_written = 0; #ifndef PB_NO_ERRMSG substream.errmsg = NULL; #endif status = pb_encode(&substream, fields, src_struct); stream->bytes_written += substream.bytes_written; stream->state = substream.state; #ifndef PB_NO_ERRMSG stream->errmsg = substream.errmsg; #endif if (substream.bytes_written != size) PB_RETURN_ERROR(stream, "submsg size changed"); return status; } /* Field encoders */ static bool checkreturn pb_enc_bool(pb_ostream_t *stream, const pb_field_iter_t *field) { uint32_t value = safe_read_bool(field->pData) ? 1 : 0; PB_UNUSED(field); return pb_encode_varint(stream, value); } static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_iter_t *field) { if (PB_LTYPE(field->type) == PB_LTYPE_UVARINT) { /* Perform unsigned integer extension */ pb_uint64_t value = 0; if (field->data_size == sizeof(uint_least8_t)) value = *(const uint_least8_t*)field->pData; else if (field->data_size == sizeof(uint_least16_t)) value = *(const uint_least16_t*)field->pData; else if (field->data_size == sizeof(uint32_t)) value = *(const uint32_t*)field->pData; else if (field->data_size == sizeof(pb_uint64_t)) value = *(const pb_uint64_t*)field->pData; else PB_RETURN_ERROR(stream, "invalid data_size"); return pb_encode_varint(stream, value); } else { /* Perform signed integer extension */ pb_int64_t value = 0; if (field->data_size == sizeof(int_least8_t)) value = *(const int_least8_t*)field->pData; else if (field->data_size == sizeof(int_least16_t)) value = *(const int_least16_t*)field->pData; else if (field->data_size == sizeof(int32_t)) value = *(const int32_t*)field->pData; else if (field->data_size == sizeof(pb_int64_t)) value = *(const pb_int64_t*)field->pData; else PB_RETURN_ERROR(stream, "invalid data_size"); if (PB_LTYPE(field->type) == PB_LTYPE_SVARINT) return pb_encode_svarint(stream, value); #ifdef PB_WITHOUT_64BIT else if (value < 0) return pb_encode_varint_32(stream, (uint32_t)value, (uint32_t)-1); #endif else return pb_encode_varint(stream, (pb_uint64_t)value); } } static bool checkreturn pb_enc_fixed(pb_ostream_t *stream, const pb_field_iter_t *field) { #ifdef PB_CONVERT_DOUBLE_FLOAT if (field->data_size == sizeof(float) && PB_LTYPE(field->type) == PB_LTYPE_FIXED64) { return pb_encode_float_as_double(stream, *(float*)field->pData); } #endif if (field->data_size == sizeof(uint32_t)) { return pb_encode_fixed32(stream, field->pData); } #ifndef PB_WITHOUT_64BIT else if (field->data_size == sizeof(uint64_t)) { return pb_encode_fixed64(stream, field->pData); } #endif else { PB_RETURN_ERROR(stream, "invalid data_size"); } } static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_iter_t *field) { const pb_bytes_array_t *bytes = NULL; bytes = (const pb_bytes_array_t*)field->pData; if (bytes == NULL) { /* Treat null pointer as an empty bytes field */ return pb_encode_string(stream, NULL, 0); } if (PB_ATYPE(field->type) == PB_ATYPE_STATIC && bytes->size > field->data_size - offsetof(pb_bytes_array_t, bytes)) { PB_RETURN_ERROR(stream, "bytes size exceeded"); } return pb_encode_string(stream, bytes->bytes, (size_t)bytes->size); } static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_iter_t *field) { size_t size = 0; size_t max_size = (size_t)field->data_size; const char *str = (const char*)field->pData; if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) { max_size = (size_t)-1; } else { /* pb_dec_string() assumes string fields end with a null * terminator when the type isn't PB_ATYPE_POINTER, so we * shouldn't allow more than max-1 bytes to be written to * allow space for the null terminator. */ if (max_size == 0) PB_RETURN_ERROR(stream, "zero-length string"); max_size -= 1; } if (str == NULL) { size = 0; /* Treat null pointer as an empty string */ } else { const char *p = str; /* strnlen() is not always available, so just use a loop */ while (size < max_size && *p != '\0') { size++; p++; } if (*p != '\0') { PB_RETURN_ERROR(stream, "unterminated string"); } } #ifdef PB_VALIDATE_UTF8 if (!pb_validate_utf8(str)) PB_RETURN_ERROR(stream, "invalid utf8"); #endif return pb_encode_string(stream, (const pb_byte_t*)str, size); } static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_iter_t *field) { if (field->submsg_desc == NULL) PB_RETURN_ERROR(stream, "invalid field descriptor"); if (PB_LTYPE(field->type) == PB_LTYPE_SUBMSG_W_CB && field->pSize != NULL) { /* Message callback is stored right before pSize. */ pb_callback_t *callback = (pb_callback_t*)field->pSize - 1; if (callback->funcs.encode) { if (!callback->funcs.encode(stream, field, &callback->arg)) return false; } } return pb_encode_submessage(stream, field->submsg_desc, field->pData); } static bool checkreturn pb_enc_fixed_length_bytes(pb_ostream_t *stream, const pb_field_iter_t *field) { return pb_encode_string(stream, (const pb_byte_t*)field->pData, (size_t)field->data_size); } #ifdef PB_CONVERT_DOUBLE_FLOAT bool pb_encode_float_as_double(pb_ostream_t *stream, float value) { union { float f; uint32_t i; } in; uint_least8_t sign; int exponent; uint64_t mantissa; in.f = value; /* Decompose input value */ sign = (uint_least8_t)((in.i >> 31) & 1); exponent = (int)((in.i >> 23) & 0xFF) - 127; mantissa = in.i & 0x7FFFFF; if (exponent == 128) { /* Special value (NaN etc.) */ exponent = 1024; } else if (exponent == -127) { if (!mantissa) { /* Zero */ exponent = -1023; } else { /* Denormalized */ mantissa <<= 1; while (!(mantissa & 0x800000)) { mantissa <<= 1; exponent--; } mantissa &= 0x7FFFFF; } } /* Combine fields */ mantissa <<= 29; mantissa |= (uint64_t)(exponent + 1023) << 52; mantissa |= (uint64_t)sign << 63; return pb_encode_fixed64(stream, &mantissa); } #endif
/* pb_encode.h: Functions to encode protocol buffers. Depends on pb_encode.c. * The main function is pb_encode. You also need an output stream, and the * field descriptions created by nanopb_generator.py. */ #ifndef PB_ENCODE_H_INCLUDED #define PB_ENCODE_H_INCLUDED #include "pb.h" #ifdef __cplusplus extern "C" { #endif /* Structure for defining custom output streams. You will need to provide * a callback function to write the bytes to your storage, which can be * for example a file or a network socket. * * The callback must conform to these rules: * * 1) Return false on IO errors. This will cause encoding to abort. * 2) You can use state to store your own data (e.g. buffer pointer). * 3) pb_write will update bytes_written after your callback runs. * 4) Substreams will modify max_size and bytes_written. Don't use them * to calculate any pointers. */ struct pb_ostream_s { #ifdef PB_BUFFER_ONLY /* Callback pointer is not used in buffer-only configuration. * Having an int pointer here allows binary compatibility but * gives an error if someone tries to assign callback function. * Also, NULL pointer marks a 'sizing stream' that does not * write anything. */ int *callback; #else bool (*callback)(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); #endif void *state; /* Free field for use by callback implementation. */ size_t max_size; /* Limit number of output bytes written (or use SIZE_MAX). */ size_t bytes_written; /* Number of bytes written so far. */ #ifndef PB_NO_ERRMSG const char *errmsg; #endif }; /*************************** * Main encoding functions * ***************************/ /* Encode a single protocol buffers message from C structure into a stream. * Returns true on success, false on any failure. * The actual struct pointed to by src_struct must match the description in fields. * All required fields in the struct are assumed to have been filled in. * * Example usage: * MyMessage msg = {}; * uint8_t buffer[64]; * pb_ostream_t stream; * * msg.field1 = 42; * stream = pb_ostream_from_buffer(buffer, sizeof(buffer)); * pb_encode(&stream, MyMessage_fields, &msg); */ bool pb_encode(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct); /* Extended version of pb_encode, with several options to control the * encoding process: * * PB_ENCODE_DELIMITED: Prepend the length of message as a varint. * Corresponds to writeDelimitedTo() in Google's * protobuf API. * * PB_ENCODE_NULLTERMINATED: Append a null byte to the message for termination. * NOTE: This behaviour is not supported in most other * protobuf implementations, so PB_ENCODE_DELIMITED * is a better option for compatibility. */ #define PB_ENCODE_DELIMITED 0x02U #define PB_ENCODE_NULLTERMINATED 0x04U bool pb_encode_ex(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct, unsigned int flags); /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ #define pb_encode_delimited(s,f,d) pb_encode_ex(s,f,d, PB_ENCODE_DELIMITED) #define pb_encode_nullterminated(s,f,d) pb_encode_ex(s,f,d, PB_ENCODE_NULLTERMINATED) /* Encode the message to get the size of the encoded data, but do not store * the data. */ bool pb_get_encoded_size(size_t *size, const pb_msgdesc_t *fields, const void *src_struct); /************************************** * Functions for manipulating streams * **************************************/ /* Create an output stream for writing into a memory buffer. * The number of bytes written can be found in stream.bytes_written after * encoding the message. * * Alternatively, you can use a custom stream that writes directly to e.g. * a file or a network socket. */ pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize); /* Pseudo-stream for measuring the size of a message without actually storing * the encoded data. * * Example usage: * MyMessage msg = {}; * pb_ostream_t stream = PB_OSTREAM_SIZING; * pb_encode(&stream, MyMessage_fields, &msg); * printf("Message size is %d\n", stream.bytes_written); */ #ifndef PB_NO_ERRMSG #define PB_OSTREAM_SIZING {0,0,0,0,0} #else #define PB_OSTREAM_SIZING {0,0,0,0} #endif /* Function to write into a pb_ostream_t stream. You can use this if you need * to append or prepend some custom headers to the message. */ bool pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); /************************************************ * Helper functions for writing field callbacks * ************************************************/ /* Encode field header based on type and field number defined in the field * structure. Call this from the callback before writing out field contents. */ bool pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_iter_t *field); /* Encode field header by manually specifing wire type. You need to use this * if you want to write out packed arrays from a callback field. */ bool pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number); /* Encode an integer in the varint format. * This works for bool, enum, int32, int64, uint32 and uint64 field types. */ #ifndef PB_WITHOUT_64BIT bool pb_encode_varint(pb_ostream_t *stream, uint64_t value); #else bool pb_encode_varint(pb_ostream_t *stream, uint32_t value); #endif /* Encode an integer in the zig-zagged svarint format. * This works for sint32 and sint64. */ #ifndef PB_WITHOUT_64BIT bool pb_encode_svarint(pb_ostream_t *stream, int64_t value); #else bool pb_encode_svarint(pb_ostream_t *stream, int32_t value); #endif /* Encode a string or bytes type field. For strings, pass strlen(s) as size. */ bool pb_encode_string(pb_ostream_t *stream, const pb_byte_t *buffer, size_t size); /* Encode a fixed32, sfixed32 or float value. * You need to pass a pointer to a 4-byte wide C variable. */ bool pb_encode_fixed32(pb_ostream_t *stream, const void *value); #ifndef PB_WITHOUT_64BIT /* Encode a fixed64, sfixed64 or double value. * You need to pass a pointer to a 8-byte wide C variable. */ bool pb_encode_fixed64(pb_ostream_t *stream, const void *value); #endif #ifdef PB_CONVERT_DOUBLE_FLOAT /* Encode a float value so that it appears like a double in the encoded * message. */ bool pb_encode_float_as_double(pb_ostream_t *stream, float value); #endif /* Encode a submessage field. * You need to pass the pb_field_t array and pointer to struct, just like * with pb_encode(). This internally encodes the submessage twice, first to * calculate message size and then to actually write it out. */ bool pb_encode_submessage(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct); #ifdef __cplusplus } /* extern "C" */ #endif #endif
/* Automatically generated nanopb header */ /* Generated by nanopb-0.4.5-dev */ #ifndef PB_MESSAGE_PB_H_INCLUDED #define PB_MESSAGE_PB_H_INCLUDED #include "pb.h" #if PB_PROTO_HEADER_VERSION != 40 #error Regenerate this file with the current version of nanopb generator. #endif /* Enum definitions */ typedef enum _pb_revision_t { pb_revision_t_PB_REV_NONE = 0, pb_revision_t_PB_REV_1_0 = 1, pb_revision_t_PB_REV_1_1 = 2, pb_revision_t_PB_REV_1_2 = 3, pb_revision_t_PB_REV_2_0 = 4, pb_revision_t_PB_REV_2_1 = 5 } pb_revision_t; /* Struct definitions */ typedef struct _pb_input_t { bool state; pb_callback_t name; } pb_input_t; typedef struct _pb_voltage_t { uint32_t battery; uint32_t external; } pb_voltage_t; typedef struct _pb_packet_t { uint32_t illuminance; pb_revision_t revision; pb_callback_t inputs; bool has_voltage; pb_voltage_t voltage; } pb_packet_t; /* Helper constants for enums */ #define _pb_revision_t_MIN pb_revision_t_PB_REV_NONE #define _pb_revision_t_MAX pb_revision_t_PB_REV_2_1 #define _pb_revision_t_ARRAYSIZE ((pb_revision_t)(pb_revision_t_PB_REV_2_1+1)) #ifdef __cplusplus extern "C" { #endif /* Initializer values for message structs */ #define pb_input_t_init_default {0, {{NULL}, NULL}} #define pb_voltage_t_init_default {0, 0} #define pb_packet_t_init_default {0, _pb_revision_t_MIN, {{NULL}, NULL}, false, pb_voltage_t_init_default} #define pb_input_t_init_zero {0, {{NULL}, NULL}} #define pb_voltage_t_init_zero {0, 0} #define pb_packet_t_init_zero {0, _pb_revision_t_MIN, {{NULL}, NULL}, false, pb_voltage_t_init_zero} /* Field tags (for use in manual encoding/decoding) */ #define pb_input_t_state_tag 1 #define pb_input_t_name_tag 2 #define pb_voltage_t_battery_tag 1 #define pb_voltage_t_external_tag 2 #define pb_packet_t_illuminance_tag 1 #define pb_packet_t_revision_tag 2 #define pb_packet_t_inputs_tag 3 #define pb_packet_t_voltage_tag 4 /* Struct field encoding specification for nanopb */ #define pb_input_t_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, BOOL, state, 1) \ X(a, CALLBACK, SINGULAR, STRING, name, 2) #define pb_input_t_CALLBACK pb_default_field_callback #define pb_input_t_DEFAULT NULL #define pb_voltage_t_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, UINT32, battery, 1) \ X(a, STATIC, SINGULAR, UINT32, external, 2) #define pb_voltage_t_CALLBACK NULL #define pb_voltage_t_DEFAULT NULL #define pb_packet_t_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, UINT32, illuminance, 1) \ X(a, STATIC, SINGULAR, UENUM, revision, 2) \ X(a, CALLBACK, REPEATED, MESSAGE, inputs, 3) \ X(a, STATIC, OPTIONAL, MESSAGE, voltage, 4) #define pb_packet_t_CALLBACK pb_default_field_callback #define pb_packet_t_DEFAULT NULL #define pb_packet_t_inputs_MSGTYPE pb_input_t #define pb_packet_t_voltage_MSGTYPE pb_voltage_t extern const pb_msgdesc_t pb_input_t_msg; extern const pb_msgdesc_t pb_voltage_t_msg; extern const pb_msgdesc_t pb_packet_t_msg; /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ #define pb_input_t_fields &pb_input_t_msg #define pb_voltage_t_fields &pb_voltage_t_msg #define pb_packet_t_fields &pb_packet_t_msg /* Maximum encoded size of messages (where known) */ /* pb_input_t_size depends on runtime parameters */ #define pb_voltage_t_size 12 /* pb_packet_t_size depends on runtime parameters */ #ifdef __cplusplus } /* extern "C" */ #endif #endif

Compiling Program...

Command line arguments:
Standard Input: Interactive Console Text
×

                

                

Program is not being debugged. Click "Debug" button to start program in debug mode.

#FunctionFile:Line
VariableValue
RegisterValue
ExpressionValue