Nanopb is a plain-C implementation of Google's Protocol Buffers data format. It is targeted at 32 bit microcontrollers, but is also fit for other embedded systems with tight (2-10 kB ROM, <1 kB RAM) memory constraints.

Dependents:   FBRLogger Dumb_box_rev2

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers pb_encode.c Source File

pb_encode.c

00001 /* pb_encode.c -- encode a protobuf using minimal resources
00002  *
00003  * 2011 Petteri Aimonen <jpa@kapsi.fi>
00004  */
00005 
00006 #define NANOPB_INTERNALS
00007 #include "pb.h"
00008 #include "pb_encode.h"
00009 #include <string.h>
00010 #include <stdio.h>
00011 
00012 /* The warn_unused_result attribute appeared first in gcc-3.4.0 */
00013 #if !defined(__GNUC__) || ( __GNUC__ < 3) || (__GNUC__ == 3 && __GNUC_MINOR__ < 4)
00014     #define checkreturn
00015 #else
00016     /* Verify that we remember to check all return values for proper error propagation */
00017     #define checkreturn __attribute__((warn_unused_result))
00018 #endif
00019 
00020 typedef bool (*pb_encoder_t)(pb_ostream_t *stream, const pb_field_t *field, const void *src) checkreturn;
00021 
00022 /* --- Function pointers to field encoders ---
00023  * Order in the array must match pb_action_t LTYPE numbering.
00024  */
00025 static const pb_encoder_t PB_ENCODERS[PB_LTYPES_COUNT] = {
00026     &pb_enc_varint,
00027     &pb_enc_svarint,
00028     &pb_enc_fixed32,
00029     &pb_enc_fixed64,
00030     
00031     &pb_enc_bytes,
00032     &pb_enc_string,
00033     &pb_enc_submessage
00034 };
00035 
00036 /* pb_ostream_t implementation */
00037 
00038 static bool checkreturn buf_write(pb_ostream_t *stream, const uint8_t *buf, size_t count)
00039 {
00040     uint8_t *dest = (uint8_t*)stream->state;
00041     stream->state = dest + count;
00042     
00043     while (count--)
00044         *dest++ = *buf++;
00045     
00046     return true;
00047 }
00048 
00049 pb_ostream_t pb_ostream_from_buffer(uint8_t *buf, size_t bufsize)
00050 {
00051     pb_ostream_t stream;
00052 #ifdef PB_BUFFER_ONLY
00053     stream.callback = (void*)1; /* Just some marker value */
00054 #else
00055     stream.callback = &buf_write;
00056 #endif
00057     stream.state = buf;
00058     stream.max_size = bufsize;
00059     stream.bytes_written = 0;
00060     return stream;
00061 }
00062 
00063 bool checkreturn pb_write(pb_ostream_t *stream, const uint8_t *buf, size_t count)
00064 {
00065     if (stream->callback != NULL)
00066     {
00067         if (stream->bytes_written + count > stream->max_size)
00068             return false;
00069 
00070 #ifdef PB_BUFFER_ONLY
00071         if (!buf_write(stream, buf, count))
00072             return false;
00073 #else        
00074         if (!stream->callback(stream, buf, count))
00075             return false;
00076 #endif
00077     }
00078     
00079     stream->bytes_written += count;
00080     return true;
00081 }
00082 
00083 /* Main encoding stuff */
00084 
00085 /* Callbacks don't need this function because they usually know the data type
00086  * without examining the field structure.
00087  * Therefore it is static for now.
00088  */
00089 static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *field,
00090                          const void *pData, size_t count, pb_encoder_t func)
00091 {
00092     size_t i;
00093     const void *p;
00094     size_t size;
00095     
00096     if (count == 0)
00097         return true;
00098     
00099     if (PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE)
00100     {
00101         if (!pb_encode_tag(stream, PB_WT_STRING, field->tag))
00102             return false;
00103         
00104         /* Determine the total size of packed array. */
00105         if (PB_LTYPE(field->type) == PB_LTYPE_FIXED32)
00106         {
00107             size = 4 * count;
00108         }
00109         else if (PB_LTYPE(field->type) == PB_LTYPE_FIXED64)
00110         {
00111             size = 8 * count;
00112         }
00113         else
00114         {
00115             pb_ostream_t sizestream = {0,0,0,0};
00116             p = pData;
00117             for (i = 0; i < count; i++)
00118             {
00119                 if (!func(&sizestream, field, p))
00120                     return false;
00121                 p = (const char*)p + field->data_size;
00122             }
00123             size = sizestream.bytes_written;
00124         }
00125         
00126         if (!pb_encode_varint(stream, (uint64_t)size))
00127             return false;
00128         
00129         if (stream->callback == NULL)
00130             return pb_write(stream, NULL, size); /* Just sizing.. */
00131         
00132         /* Write the data */
00133         p = pData;
00134         for (i = 0; i < count; i++)
00135         {
00136             if (!func(stream, field, p))
00137                 return false;
00138             p = (const char*)p + field->data_size;
00139         }
00140     }
00141     else
00142     {
00143         p = pData;
00144         for (i = 0; i < count; i++)
00145         {
00146             if (!pb_encode_tag_for_field(stream, field))
00147                 return false;
00148             if (!func(stream, field, p))
00149                 return false;
00150             p = (const char*)p + field->data_size;
00151         }
00152     }
00153     
00154     return true;
00155 }
00156 
00157 bool checkreturn pb_encode(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct)
00158 {
00159     const pb_field_t *field = fields;
00160     const void *pData = src_struct;
00161     const void *pSize;
00162     size_t prev_size = 0;
00163     
00164     while (field->tag != 0)
00165     {
00166         pb_encoder_t func = PB_ENCODERS[PB_LTYPE(field->type)];
00167         pData = (const char*)pData + prev_size + field->data_offset;
00168         pSize = (const char*)pData + field->size_offset;
00169         
00170         prev_size = field->data_size;
00171         if (PB_HTYPE(field->type) == PB_HTYPE_ARRAY)
00172             prev_size *= field->array_size;
00173                 
00174         switch (PB_HTYPE(field->type))
00175         {
00176             case PB_HTYPE_REQUIRED:
00177                 if (!pb_encode_tag_for_field(stream, field))
00178                     return false;
00179                 if (!func(stream, field, pData))
00180                     return false;
00181                 break;
00182             
00183             case PB_HTYPE_OPTIONAL:
00184                 if (*(const bool*)pSize)
00185                 {
00186                     if (!pb_encode_tag_for_field(stream, field))
00187                         return false;
00188                 
00189                     if (!func(stream, field, pData))
00190                         return false;
00191                 }
00192                 break;
00193             
00194             case PB_HTYPE_ARRAY:
00195                 if (!encode_array(stream, field, pData, *(const size_t*)pSize, func))
00196                     return false;
00197                 break;
00198             
00199             case PB_HTYPE_CALLBACK:
00200             {
00201                 const pb_callback_t *callback = (const pb_callback_t*)pData;
00202                 if (callback->funcs.encode != NULL)
00203                 {
00204                     if (!callback->funcs.encode(stream, field, callback->arg))
00205                         return false;
00206                 }
00207                 break;
00208             }
00209         }
00210     
00211         field++;
00212     }
00213     
00214     return true;
00215 }
00216 
00217 /* Helper functions */
00218 bool checkreturn pb_encode_varint(pb_ostream_t *stream, uint64_t value)
00219 {
00220     uint8_t buffer[10];
00221     size_t i = 0;
00222     
00223     if (value == 0)
00224         return pb_write(stream, (uint8_t*)&value, 1);
00225     
00226     while (value)
00227     {
00228         buffer[i] = (uint8_t)((value & 0x7F) | 0x80);
00229         value >>= 7;
00230         i++;
00231     }
00232     buffer[i-1] &= 0x7F; /* Unset top bit on last byte */
00233     
00234     return pb_write(stream, buffer, i);
00235 }
00236 
00237 bool checkreturn pb_encode_svarint(pb_ostream_t *stream, int64_t value)
00238 {
00239     uint64_t zigzagged;
00240     if (value < 0)
00241         zigzagged = (uint64_t)(~(value << 1));
00242     else
00243         zigzagged = (uint64_t)(value << 1);
00244     
00245     return pb_encode_varint(stream, zigzagged);
00246 }
00247 
00248 bool checkreturn pb_encode_fixed32(pb_ostream_t *stream, const void *value)
00249 {
00250     #ifdef __BIG_ENDIAN__
00251     const uint8_t *bytes = value;
00252     uint8_t lebytes[4];
00253     lebytes[0] = bytes[3];
00254     lebytes[1] = bytes[2];
00255     lebytes[2] = bytes[1];
00256     lebytes[3] = bytes[0];
00257     return pb_write(stream, lebytes, 4);
00258     #else
00259     return pb_write(stream, (const uint8_t*)value, 4);
00260     #endif
00261 }
00262 
00263 bool checkreturn pb_encode_fixed64(pb_ostream_t *stream, const void *value)
00264 {
00265     #ifdef __BIG_ENDIAN__
00266     const uint8_t *bytes = value;
00267     uint8_t lebytes[8];
00268     lebytes[0] = bytes[7];
00269     lebytes[1] = bytes[6];
00270     lebytes[2] = bytes[5];
00271     lebytes[3] = bytes[4];
00272     lebytes[4] = bytes[3];
00273     lebytes[5] = bytes[2];
00274     lebytes[6] = bytes[1];
00275     lebytes[7] = bytes[0];
00276     return pb_write(stream, lebytes, 8);
00277     #else
00278     return pb_write(stream, (const uint8_t*)value, 8);
00279     #endif
00280 }
00281 
00282 bool checkreturn pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number)
00283 {
00284     uint64_t tag = wiretype | (field_number << 3);
00285     return pb_encode_varint(stream, tag);
00286 }
00287 
00288 bool checkreturn pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_t *field)
00289 {
00290     pb_wire_type_t wiretype;
00291     switch (PB_LTYPE(field->type))
00292     {
00293         case PB_LTYPE_VARINT:
00294         case PB_LTYPE_SVARINT:
00295             wiretype = PB_WT_VARINT;
00296             break;
00297         
00298         case PB_LTYPE_FIXED32:
00299             wiretype = PB_WT_32BIT;
00300             break;
00301         
00302         case PB_LTYPE_FIXED64:
00303             wiretype = PB_WT_64BIT;
00304             break;
00305         
00306         case PB_LTYPE_BYTES:
00307         case PB_LTYPE_STRING:
00308         case PB_LTYPE_SUBMESSAGE:
00309             wiretype = PB_WT_STRING;
00310             break;
00311         
00312         default:
00313             return false;
00314     }
00315     
00316     return pb_encode_tag(stream, wiretype, field->tag);
00317 }
00318 
00319 bool checkreturn pb_encode_string(pb_ostream_t *stream, const uint8_t *buffer, size_t size)
00320 {
00321     if (!pb_encode_varint(stream, (uint64_t)size))
00322         return false;
00323     
00324     return pb_write(stream, buffer, size);
00325 }
00326 
00327 bool checkreturn pb_encode_submessage(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct)
00328 {
00329     /* First calculate the message size using a non-writing substream. */
00330     pb_ostream_t substream = {0,0,0,0};
00331     size_t size;
00332     bool status;
00333     
00334     if (!pb_encode(&substream, fields, src_struct))
00335         return false;
00336     
00337     size = substream.bytes_written;
00338     
00339     if (!pb_encode_varint(stream, (uint64_t)size))
00340         return false;
00341     
00342     if (stream->callback == NULL)
00343         return pb_write(stream, NULL, size); /* Just sizing */
00344     
00345     if (stream->bytes_written + size > stream->max_size)
00346         return false;
00347         
00348     /* Use a substream to verify that a callback doesn't write more than
00349      * what it did the first time. */
00350     substream.callback = stream->callback;
00351     substream.state = stream->state;
00352     substream.max_size = size;
00353     substream.bytes_written = 0;
00354     
00355     status = pb_encode(&substream, fields, src_struct);
00356     
00357     stream->bytes_written += substream.bytes_written;
00358     stream->state = substream.state;
00359     
00360     if (substream.bytes_written != size)
00361         return false;
00362     
00363     return status;
00364 }
00365 
00366 /* Field encoders */
00367 
00368 bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_t *field, const void *src)
00369 {
00370     uint64_t value = 0;
00371     
00372     switch (field->data_size)
00373     {
00374         case 1: value = *(const uint8_t*)src; break;
00375         case 2: value = *(const uint16_t*)src; break;
00376         case 4: value = *(const uint32_t*)src; break;
00377         case 8: value = *(const uint64_t*)src; break;
00378         default: return false;
00379     }
00380     
00381     return pb_encode_varint(stream, value);
00382 }
00383 
00384 bool checkreturn pb_enc_svarint(pb_ostream_t *stream, const pb_field_t *field, const void *src)
00385 {
00386     int64_t value = 0;
00387     
00388     switch (field->data_size)
00389     {
00390         case 4: value = *(const int32_t*)src; break;
00391         case 8: value = *(const int64_t*)src; break;
00392         default: return false;
00393     }
00394     
00395     return pb_encode_svarint(stream, value);
00396 }
00397 
00398 bool checkreturn pb_enc_fixed64(pb_ostream_t *stream, const pb_field_t *field, const void *src)
00399 {
00400     UNUSED(field);
00401     return pb_encode_fixed64(stream, src);
00402 }
00403 
00404 bool checkreturn pb_enc_fixed32(pb_ostream_t *stream, const pb_field_t *field, const void *src)
00405 {
00406     UNUSED(field);
00407     return pb_encode_fixed32(stream, src);
00408 }
00409 
00410 bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src)
00411 {
00412     const pb_bytes_array_t *bytes = (const pb_bytes_array_t*)src;
00413     UNUSED(field);
00414     return pb_encode_string(stream, bytes->bytes, bytes->size);
00415 }
00416 
00417 bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_t *field, const void *src)
00418 {
00419     UNUSED(field);
00420     return pb_encode_string(stream, (const uint8_t*)src, strlen((const char*)src));
00421 }
00422 
00423 bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_t *field, const void *src)
00424 {
00425     if (field->ptr == NULL)
00426         return false;
00427     
00428     return pb_encode_submessage(stream, (const pb_field_t*)field->ptr, src);
00429 }
00430