Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: mbed
pb_encode.c
00001 /* pb_encode.c -- encode a protobuf using minimal resources 00002 * 00003 * 2011 Petteri Aimonen <jpa@kapsi.fi> 00004 */ 00005 00006 #include "pb.h" 00007 #include "pb_encode.h" 00008 #include "pb_common.h" 00009 00010 /* Use the GCC warn_unused_result attribute to check that all return values 00011 * are propagated correctly. On other compilers and gcc before 3.4.0 just 00012 * ignore the annotation. 00013 */ 00014 #if !defined(__GNUC__) || ( __GNUC__ < 3) || (__GNUC__ == 3 && __GNUC_MINOR__ < 4) 00015 #define checkreturn 00016 #else 00017 #define checkreturn __attribute__((warn_unused_result)) 00018 #endif 00019 00020 /************************************** 00021 * Declarations internal to this file * 00022 **************************************/ 00023 typedef bool (*pb_encoder_t)(pb_ostream_t *stream, const pb_field_t *field, const void *src) checkreturn; 00024 00025 static bool checkreturn buf_write(pb_ostream_t *stream, const uint8_t *buf, size_t count); 00026 static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *field, const void *pData, size_t count, pb_encoder_t func); 00027 static bool checkreturn encode_field(pb_ostream_t *stream, const pb_field_t *field, const void *pData); 00028 static bool checkreturn default_extension_encoder(pb_ostream_t *stream, const pb_extension_t *extension); 00029 static bool checkreturn encode_extension_field(pb_ostream_t *stream, const pb_field_t *field, const void *pData); 00030 static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_t *field, const void *src); 00031 static bool checkreturn pb_enc_uvarint(pb_ostream_t *stream, const pb_field_t *field, const void *src); 00032 static bool checkreturn pb_enc_svarint(pb_ostream_t *stream, const pb_field_t *field, const void *src); 00033 static bool checkreturn pb_enc_fixed32(pb_ostream_t *stream, const pb_field_t *field, const void *src); 00034 static bool checkreturn pb_enc_fixed64(pb_ostream_t *stream, const pb_field_t *field, const void *src); 00035 static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src); 00036 static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_t *field, const void *src); 00037 static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_t *field, const void *src); 00038 00039 /* --- Function pointers to field encoders --- 00040 * Order in the array must match pb_action_t LTYPE numbering. 00041 */ 00042 static const pb_encoder_t PB_ENCODERS[PB_LTYPES_COUNT] = { 00043 &pb_enc_varint, 00044 &pb_enc_uvarint, 00045 &pb_enc_svarint, 00046 &pb_enc_fixed32, 00047 &pb_enc_fixed64, 00048 00049 &pb_enc_bytes, 00050 &pb_enc_string, 00051 &pb_enc_submessage, 00052 NULL /* extensions */ 00053 }; 00054 00055 /******************************* 00056 * pb_ostream_t implementation * 00057 *******************************/ 00058 00059 static bool checkreturn buf_write(pb_ostream_t *stream, const uint8_t *buf, size_t count) 00060 { 00061 uint8_t *dest = (uint8_t*)stream->state; 00062 stream->state = dest + count; 00063 00064 while (count--) 00065 *dest++ = *buf++; 00066 00067 return true; 00068 } 00069 00070 pb_ostream_t pb_ostream_from_buffer(uint8_t *buf, size_t bufsize) 00071 { 00072 pb_ostream_t stream; 00073 #ifdef PB_BUFFER_ONLY 00074 stream.callback = (void*)1; /* Just a marker value */ 00075 #else 00076 stream.callback = &buf_write; 00077 #endif 00078 stream.state = buf; 00079 stream.max_size = bufsize; 00080 stream.bytes_written = 0; 00081 #ifndef PB_NO_ERRMSG 00082 stream.errmsg = NULL; 00083 #endif 00084 return stream; 00085 } 00086 00087 bool checkreturn pb_write(pb_ostream_t *stream, const uint8_t *buf, size_t count) 00088 { 00089 if (stream->callback != NULL) 00090 { 00091 if (stream->bytes_written + count > stream->max_size) 00092 PB_RETURN_ERROR(stream, "stream full"); 00093 00094 #ifdef PB_BUFFER_ONLY 00095 if (!buf_write(stream, buf, count)) 00096 PB_RETURN_ERROR(stream, "io error"); 00097 #else 00098 if (!stream->callback(stream, buf, count)) 00099 PB_RETURN_ERROR(stream, "io error"); 00100 #endif 00101 } 00102 00103 stream->bytes_written += count; 00104 return true; 00105 } 00106 00107 /************************* 00108 * Encode a single field * 00109 *************************/ 00110 00111 /* Encode a static array. Handles the size calculations and possible packing. */ 00112 static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *field, 00113 const void *pData, size_t count, pb_encoder_t func) 00114 { 00115 size_t i; 00116 const void *p; 00117 size_t size; 00118 00119 if (count == 0) 00120 return true; 00121 00122 if (PB_ATYPE(field->type) != PB_ATYPE_POINTER && count > field->array_size) 00123 PB_RETURN_ERROR(stream, "array max size exceeded"); 00124 00125 /* We always pack arrays if the datatype allows it. */ 00126 if (PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE) 00127 { 00128 if (!pb_encode_tag(stream, PB_WT_STRING, field->tag)) 00129 return false; 00130 00131 /* Determine the total size of packed array. */ 00132 if (PB_LTYPE(field->type) == PB_LTYPE_FIXED32) 00133 { 00134 size = 4 * count; 00135 } 00136 else if (PB_LTYPE(field->type) == PB_LTYPE_FIXED64) 00137 { 00138 size = 8 * count; 00139 } 00140 else 00141 { 00142 pb_ostream_t sizestream = PB_OSTREAM_SIZING; 00143 p = pData; 00144 for (i = 0; i < count; i++) 00145 { 00146 if (!func(&sizestream, field, p)) 00147 return false; 00148 p = (const char*)p + field->data_size; 00149 } 00150 size = sizestream.bytes_written; 00151 } 00152 00153 if (!pb_encode_varint(stream, (uint64_t)size)) 00154 return false; 00155 00156 if (stream->callback == NULL) 00157 return pb_write(stream, NULL, size); /* Just sizing.. */ 00158 00159 /* Write the data */ 00160 p = pData; 00161 for (i = 0; i < count; i++) 00162 { 00163 if (!func(stream, field, p)) 00164 return false; 00165 p = (const char*)p + field->data_size; 00166 } 00167 } 00168 else 00169 { 00170 p = pData; 00171 for (i = 0; i < count; i++) 00172 { 00173 if (!pb_encode_tag_for_field(stream, field)) 00174 return false; 00175 00176 /* Normally the data is stored directly in the array entries, but 00177 * for pointer-type string and bytes fields, the array entries are 00178 * actually pointers themselves also. So we have to dereference once 00179 * more to get to the actual data. */ 00180 if (PB_ATYPE(field->type) == PB_ATYPE_POINTER && 00181 (PB_LTYPE(field->type) == PB_LTYPE_STRING || 00182 PB_LTYPE(field->type) == PB_LTYPE_BYTES)) 00183 { 00184 if (!func(stream, field, *(const void* const*)p)) 00185 return false; 00186 } 00187 else 00188 { 00189 if (!func(stream, field, p)) 00190 return false; 00191 } 00192 p = (const char*)p + field->data_size; 00193 } 00194 } 00195 00196 return true; 00197 } 00198 00199 /* Encode a field with static or pointer allocation, i.e. one whose data 00200 * is available to the encoder directly. */ 00201 static bool checkreturn encode_basic_field(pb_ostream_t *stream, 00202 const pb_field_t *field, const void *pData) 00203 { 00204 pb_encoder_t func; 00205 const void *pSize; 00206 bool implicit_has = true; 00207 00208 func = PB_ENCODERS[PB_LTYPE(field->type)]; 00209 00210 if (field->size_offset) 00211 pSize = (const char*)pData + field->size_offset; 00212 else 00213 pSize = &implicit_has; 00214 00215 if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) 00216 { 00217 /* pData is a pointer to the field, which contains pointer to 00218 * the data. If the 2nd pointer is NULL, it is interpreted as if 00219 * the has_field was false. 00220 */ 00221 00222 pData = *(const void* const*)pData; 00223 implicit_has = (pData != NULL); 00224 } 00225 00226 switch (PB_HTYPE(field->type)) 00227 { 00228 case PB_HTYPE_REQUIRED: 00229 if (!pData) 00230 PB_RETURN_ERROR(stream, "missing required field"); 00231 if (!pb_encode_tag_for_field(stream, field)) 00232 return false; 00233 if (!func(stream, field, pData)) 00234 return false; 00235 break; 00236 00237 case PB_HTYPE_OPTIONAL: 00238 if (*(const bool*)pSize) 00239 { 00240 if (!pb_encode_tag_for_field(stream, field)) 00241 return false; 00242 00243 if (!func(stream, field, pData)) 00244 return false; 00245 } 00246 break; 00247 00248 case PB_HTYPE_REPEATED: 00249 if (!encode_array(stream, field, pData, *(const pb_size_t*)pSize, func)) 00250 return false; 00251 break; 00252 00253 case PB_HTYPE_ONEOF: 00254 if (*(const pb_size_t*)pSize == field->tag) 00255 { 00256 if (!pb_encode_tag_for_field(stream, field)) 00257 return false; 00258 00259 if (!func(stream, field, pData)) 00260 return false; 00261 } 00262 break; 00263 00264 default: 00265 PB_RETURN_ERROR(stream, "invalid field type"); 00266 } 00267 00268 return true; 00269 } 00270 00271 /* Encode a field with callback semantics. This means that a user function is 00272 * called to provide and encode the actual data. */ 00273 static bool checkreturn encode_callback_field(pb_ostream_t *stream, 00274 const pb_field_t *field, const void *pData) 00275 { 00276 const pb_callback_t *callback = (const pb_callback_t*)pData; 00277 00278 #ifdef PB_OLD_CALLBACK_STYLE 00279 const void *arg = callback->arg; 00280 #else 00281 void * const *arg = &(callback->arg); 00282 #endif 00283 00284 if (callback->funcs.encode != NULL) 00285 { 00286 if (!callback->funcs.encode(stream, field, arg)) 00287 PB_RETURN_ERROR(stream, "callback error"); 00288 } 00289 return true; 00290 } 00291 00292 /* Encode a single field of any callback or static type. */ 00293 static bool checkreturn encode_field(pb_ostream_t *stream, 00294 const pb_field_t *field, const void *pData) 00295 { 00296 switch (PB_ATYPE(field->type)) 00297 { 00298 case PB_ATYPE_STATIC: 00299 case PB_ATYPE_POINTER: 00300 return encode_basic_field(stream, field, pData); 00301 00302 case PB_ATYPE_CALLBACK: 00303 return encode_callback_field(stream, field, pData); 00304 00305 default: 00306 PB_RETURN_ERROR(stream, "invalid field type"); 00307 } 00308 } 00309 00310 /* Default handler for extension fields. Expects to have a pb_field_t 00311 * pointer in the extension->type->arg field. */ 00312 static bool checkreturn default_extension_encoder(pb_ostream_t *stream, 00313 const pb_extension_t *extension) 00314 { 00315 const pb_field_t *field = (const pb_field_t*)extension->type->arg; 00316 00317 if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) 00318 { 00319 /* For pointer extensions, the pointer is stored directly 00320 * in the extension structure. This avoids having an extra 00321 * indirection. */ 00322 return encode_field(stream, field, &extension->dest); 00323 } 00324 else 00325 { 00326 return encode_field(stream, field, extension->dest); 00327 } 00328 } 00329 00330 /* Walk through all the registered extensions and give them a chance 00331 * to encode themselves. */ 00332 static bool checkreturn encode_extension_field(pb_ostream_t *stream, 00333 const pb_field_t *field, const void *pData) 00334 { 00335 const pb_extension_t *extension = *(const pb_extension_t* const *)pData; 00336 PB_UNUSED(field); 00337 00338 while (extension) 00339 { 00340 bool status; 00341 if (extension->type->encode) 00342 status = extension->type->encode(stream, extension); 00343 else 00344 status = default_extension_encoder(stream, extension); 00345 00346 if (!status) 00347 return false; 00348 00349 extension = extension->next; 00350 } 00351 00352 return true; 00353 } 00354 00355 /********************* 00356 * Encode all fields * 00357 *********************/ 00358 00359 static void *remove_const(const void *p) 00360 { 00361 /* Note: this casts away const, in order to use the common field iterator 00362 * logic for both encoding and decoding. */ 00363 union { 00364 void *p1; 00365 const void *p2; 00366 } t; 00367 t.p2 = p; 00368 return t.p1; 00369 } 00370 00371 bool checkreturn pb_encode(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct) 00372 { 00373 pb_field_iter_t iter; 00374 if (!pb_field_iter_begin(&iter, fields, remove_const(src_struct))) 00375 return true; /* Empty message type */ 00376 00377 do { 00378 if (PB_LTYPE(iter.pos->type) == PB_LTYPE_EXTENSION) 00379 { 00380 /* Special case for the extension field placeholder */ 00381 if (!encode_extension_field(stream, iter.pos, iter.pData)) 00382 return false; 00383 } 00384 else 00385 { 00386 /* Regular field */ 00387 if (!encode_field(stream, iter.pos, iter.pData)) 00388 return false; 00389 } 00390 } while (pb_field_iter_next(&iter)); 00391 00392 return true; 00393 } 00394 00395 bool pb_encode_delimited(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct) 00396 { 00397 return pb_encode_submessage(stream, fields, src_struct); 00398 } 00399 00400 bool pb_get_encoded_size(size_t *size, const pb_field_t fields[], const void *src_struct) 00401 { 00402 pb_ostream_t stream = PB_OSTREAM_SIZING; 00403 00404 if (!pb_encode(&stream, fields, src_struct)) 00405 return false; 00406 00407 *size = stream.bytes_written; 00408 return true; 00409 } 00410 00411 /******************** 00412 * Helper functions * 00413 ********************/ 00414 bool checkreturn pb_encode_varint(pb_ostream_t *stream, uint64_t value) 00415 { 00416 uint8_t buffer[10]; 00417 size_t i = 0; 00418 00419 if (value == 0) 00420 return pb_write(stream, (uint8_t*)&value, 1); 00421 00422 while (value) 00423 { 00424 buffer[i] = (uint8_t)((value & 0x7F) | 0x80); 00425 value >>= 7; 00426 i++; 00427 } 00428 buffer[i-1] &= 0x7F; /* Unset top bit on last byte */ 00429 00430 return pb_write(stream, buffer, i); 00431 } 00432 00433 bool checkreturn pb_encode_svarint(pb_ostream_t *stream, int64_t value) 00434 { 00435 uint64_t zigzagged; 00436 if (value < 0) 00437 zigzagged = ~((uint64_t)value << 1); 00438 else 00439 zigzagged = (uint64_t)value << 1; 00440 00441 return pb_encode_varint(stream, zigzagged); 00442 } 00443 00444 bool checkreturn pb_encode_fixed32(pb_ostream_t *stream, const void *value) 00445 { 00446 #ifdef __BIG_ENDIAN__ 00447 const uint8_t *bytes = value; 00448 uint8_t lebytes[4]; 00449 lebytes[0] = bytes[3]; 00450 lebytes[1] = bytes[2]; 00451 lebytes[2] = bytes[1]; 00452 lebytes[3] = bytes[0]; 00453 return pb_write(stream, lebytes, 4); 00454 #else 00455 return pb_write(stream, (const uint8_t*)value, 4); 00456 #endif 00457 } 00458 00459 bool checkreturn pb_encode_fixed64(pb_ostream_t *stream, const void *value) 00460 { 00461 #ifdef __BIG_ENDIAN__ 00462 const uint8_t *bytes = value; 00463 uint8_t lebytes[8]; 00464 lebytes[0] = bytes[7]; 00465 lebytes[1] = bytes[6]; 00466 lebytes[2] = bytes[5]; 00467 lebytes[3] = bytes[4]; 00468 lebytes[4] = bytes[3]; 00469 lebytes[5] = bytes[2]; 00470 lebytes[6] = bytes[1]; 00471 lebytes[7] = bytes[0]; 00472 return pb_write(stream, lebytes, 8); 00473 #else 00474 return pb_write(stream, (const uint8_t*)value, 8); 00475 #endif 00476 } 00477 00478 bool checkreturn pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number) 00479 { 00480 uint64_t tag = ((uint64_t)field_number << 3) | wiretype; 00481 return pb_encode_varint(stream, tag); 00482 } 00483 00484 bool checkreturn pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_t *field) 00485 { 00486 pb_wire_type_t wiretype; 00487 switch (PB_LTYPE(field->type)) 00488 { 00489 case PB_LTYPE_VARINT: 00490 case PB_LTYPE_UVARINT: 00491 case PB_LTYPE_SVARINT: 00492 wiretype = PB_WT_VARINT; 00493 break; 00494 00495 case PB_LTYPE_FIXED32: 00496 wiretype = PB_WT_32BIT; 00497 break; 00498 00499 case PB_LTYPE_FIXED64: 00500 wiretype = PB_WT_64BIT; 00501 break; 00502 00503 case PB_LTYPE_BYTES: 00504 case PB_LTYPE_STRING: 00505 case PB_LTYPE_SUBMESSAGE: 00506 wiretype = PB_WT_STRING; 00507 break; 00508 00509 default: 00510 PB_RETURN_ERROR(stream, "invalid field type"); 00511 } 00512 00513 return pb_encode_tag(stream, wiretype, field->tag); 00514 } 00515 00516 bool checkreturn pb_encode_string(pb_ostream_t *stream, const uint8_t *buffer, size_t size) 00517 { 00518 if (!pb_encode_varint(stream, (uint64_t)size)) 00519 return false; 00520 00521 return pb_write(stream, buffer, size); 00522 } 00523 00524 bool checkreturn pb_encode_submessage(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct) 00525 { 00526 /* First calculate the message size using a non-writing substream. */ 00527 pb_ostream_t substream = PB_OSTREAM_SIZING; 00528 size_t size; 00529 bool status; 00530 00531 if (!pb_encode(&substream, fields, src_struct)) 00532 { 00533 #ifndef PB_NO_ERRMSG 00534 stream->errmsg = substream.errmsg; 00535 #endif 00536 return false; 00537 } 00538 00539 size = substream.bytes_written; 00540 00541 if (!pb_encode_varint(stream, (uint64_t)size)) 00542 return false; 00543 00544 if (stream->callback == NULL) 00545 return pb_write(stream, NULL, size); /* Just sizing */ 00546 00547 if (stream->bytes_written + size > stream->max_size) 00548 PB_RETURN_ERROR(stream, "stream full"); 00549 00550 /* Use a substream to verify that a callback doesn't write more than 00551 * what it did the first time. */ 00552 substream.callback = stream->callback; 00553 substream.state = stream->state; 00554 substream.max_size = size; 00555 substream.bytes_written = 0; 00556 #ifndef PB_NO_ERRMSG 00557 substream.errmsg = NULL; 00558 #endif 00559 00560 status = pb_encode(&substream, fields, src_struct); 00561 00562 stream->bytes_written += substream.bytes_written; 00563 stream->state = substream.state; 00564 #ifndef PB_NO_ERRMSG 00565 stream->errmsg = substream.errmsg; 00566 #endif 00567 00568 if (substream.bytes_written != size) 00569 PB_RETURN_ERROR(stream, "submsg size changed"); 00570 00571 return status; 00572 } 00573 00574 /* Field encoders */ 00575 00576 static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_t *field, const void *src) 00577 { 00578 int64_t value = 0; 00579 00580 /* Cases 1 and 2 are for compilers that have smaller types for bool 00581 * or enums, and for int_size option. */ 00582 switch (field->data_size) 00583 { 00584 case 1: value = *(const int8_t*)src; break; 00585 case 2: value = *(const int16_t*)src; break; 00586 case 4: value = *(const int32_t*)src; break; 00587 case 8: value = *(const int64_t*)src; break; 00588 default: PB_RETURN_ERROR(stream, "invalid data_size"); 00589 } 00590 00591 return pb_encode_varint(stream, (uint64_t)value); 00592 } 00593 00594 static bool checkreturn pb_enc_uvarint(pb_ostream_t *stream, const pb_field_t *field, const void *src) 00595 { 00596 uint64_t value = 0; 00597 00598 switch (field->data_size) 00599 { 00600 case 1: value = *(const uint8_t*)src; break; 00601 case 2: value = *(const uint16_t*)src; break; 00602 case 4: value = *(const uint32_t*)src; break; 00603 case 8: value = *(const uint64_t*)src; break; 00604 default: PB_RETURN_ERROR(stream, "invalid data_size"); 00605 } 00606 00607 return pb_encode_varint(stream, value); 00608 } 00609 00610 static bool checkreturn pb_enc_svarint(pb_ostream_t *stream, const pb_field_t *field, const void *src) 00611 { 00612 int64_t value = 0; 00613 00614 switch (field->data_size) 00615 { 00616 case 1: value = *(const int8_t*)src; break; 00617 case 2: value = *(const int16_t*)src; break; 00618 case 4: value = *(const int32_t*)src; break; 00619 case 8: value = *(const int64_t*)src; break; 00620 default: PB_RETURN_ERROR(stream, "invalid data_size"); 00621 } 00622 00623 return pb_encode_svarint(stream, value); 00624 } 00625 00626 static bool checkreturn pb_enc_fixed64(pb_ostream_t *stream, const pb_field_t *field, const void *src) 00627 { 00628 PB_UNUSED(field); 00629 return pb_encode_fixed64(stream, src); 00630 } 00631 00632 static bool checkreturn pb_enc_fixed32(pb_ostream_t *stream, const pb_field_t *field, const void *src) 00633 { 00634 PB_UNUSED(field); 00635 return pb_encode_fixed32(stream, src); 00636 } 00637 00638 static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src) 00639 { 00640 const pb_bytes_array_t *bytes = (const pb_bytes_array_t*)src; 00641 00642 if (src == NULL) 00643 { 00644 /* Threat null pointer as an empty bytes field */ 00645 return pb_encode_string(stream, NULL, 0); 00646 } 00647 00648 if (PB_ATYPE(field->type) == PB_ATYPE_STATIC && 00649 PB_BYTES_ARRAY_T_ALLOCSIZE(bytes->size) > field->data_size) 00650 { 00651 PB_RETURN_ERROR(stream, "bytes size exceeded"); 00652 } 00653 00654 return pb_encode_string(stream, bytes->bytes, bytes->size); 00655 } 00656 00657 static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_t *field, const void *src) 00658 { 00659 size_t size = 0; 00660 size_t max_size = field->data_size; 00661 const char *p = (const char*)src; 00662 00663 if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) 00664 max_size = (size_t)-1; 00665 00666 if (src == NULL) 00667 { 00668 size = 0; /* Threat null pointer as an empty string */ 00669 } 00670 else 00671 { 00672 /* strnlen() is not always available, so just use a loop */ 00673 while (size < max_size && *p != '\0') 00674 { 00675 size++; 00676 p++; 00677 } 00678 } 00679 00680 return pb_encode_string(stream, (const uint8_t*)src, size); 00681 } 00682 00683 static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_t *field, const void *src) 00684 { 00685 if (field->ptr == NULL) 00686 PB_RETURN_ERROR(stream, "invalid field descriptor"); 00687 00688 return pb_encode_submessage(stream, (const pb_field_t*)field->ptr, src); 00689 } 00690 00691
Generated on Fri Sep 23 2022 19:29:17 by
1.7.2