LCOV - code coverage report
Current view: top level - ballet/nanopb - pb_common.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 137 226 60.6 %
Date: 2025-08-05 05:04:49 Functions: 8 11 72.7 %

          Line data    Source code
       1             : /* pb_common.c: Common support functions for pb_encode.c and pb_decode.c.
       2             :  *
       3             :  * 2014 Petteri Aimonen <jpa@kapsi.fi>
       4             :  */
       5             : 
       6             : #include "pb_common.h"
       7             : 
       8             : static bool load_descriptor_values(pb_field_iter_t *iter)
       9         480 : {
      10         480 :     uint32_t word0;
      11         480 :     uint32_t data_offset;
      12         480 :     int_least8_t size_offset;
      13             : 
      14         480 :     if (iter->index >= iter->descriptor->field_count)
      15           9 :         return false;
      16             : 
      17         471 :     word0 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]);
      18         471 :     iter->type = (pb_type_t)((word0 >> 8) & 0xFF);
      19             : 
      20         471 :     switch(word0 & 3)
      21         471 :     {
      22          39 :         case 0: {
      23             :             /* 1-word format */
      24          39 :             iter->array_size = 1;
      25          39 :             iter->tag = (pb_size_t)((word0 >> 2) & 0x3F);
      26          39 :             size_offset = (int_least8_t)((word0 >> 24) & 0x0F);
      27          39 :             data_offset = (word0 >> 16) & 0xFF;
      28          39 :             iter->data_size = (pb_size_t)((word0 >> 28) & 0x0F);
      29          39 :             break;
      30           0 :         }
      31             : 
      32         432 :         case 1: {
      33             :             /* 2-word format */
      34         432 :             uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]);
      35             : 
      36         432 :             iter->array_size = (pb_size_t)((word0 >> 16) & 0x0FFF);
      37         432 :             iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 28) << 6));
      38         432 :             size_offset = (int_least8_t)((word0 >> 28) & 0x0F);
      39         432 :             data_offset = word1 & 0xFFFF;
      40         432 :             iter->data_size = (pb_size_t)((word1 >> 16) & 0x0FFF);
      41         432 :             break;
      42           0 :         }
      43             : 
      44           0 :         case 2: {
      45             :             /* 4-word format */
      46           0 :             uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]);
      47           0 :             uint32_t word2 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 2]);
      48           0 :             uint32_t word3 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 3]);
      49             : 
      50           0 :             iter->array_size = (pb_size_t)(word0 >> 16);
      51           0 :             iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 8) << 6));
      52           0 :             size_offset = (int_least8_t)(word1 & 0xFF);
      53           0 :             data_offset = word2;
      54           0 :             iter->data_size = (pb_size_t)word3;
      55           0 :             break;
      56           0 :         }
      57             : 
      58           0 :         default: {
      59             :             /* 8-word format */
      60           0 :             uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]);
      61           0 :             uint32_t word2 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 2]);
      62           0 :             uint32_t word3 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 3]);
      63           0 :             uint32_t word4 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 4]);
      64             : 
      65           0 :             iter->array_size = (pb_size_t)word4;
      66           0 :             iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 8) << 6));
      67           0 :             size_offset = (int_least8_t)(word1 & 0xFF);
      68           0 :             data_offset = word2;
      69           0 :             iter->data_size = (pb_size_t)word3;
      70           0 :             break;
      71           0 :         }
      72         471 :     }
      73             : 
      74         471 :     if (!iter->message)
      75           0 :     {
      76             :         /* Avoid doing arithmetic on null pointers, it is undefined */
      77           0 :         iter->pField = NULL;
      78           0 :         iter->pSize = NULL;
      79           0 :     }
      80         471 :     else
      81         471 :     {
      82         471 :         iter->pField = (char*)iter->message + data_offset;
      83             : 
      84         471 :         if (size_offset)
      85         156 :         {
      86         156 :             iter->pSize = (char*)iter->pField - size_offset;
      87         156 :         }
      88         315 :         else if (PB_HTYPE(iter->type) == PB_HTYPE_REPEATED &&
      89         315 :                  (PB_ATYPE(iter->type) == PB_ATYPE_STATIC ||
      90          36 :                   PB_ATYPE(iter->type) == PB_ATYPE_POINTER))
      91           0 :         {
      92             :             /* Fixed count array */
      93           0 :             iter->pSize = &iter->array_size;
      94           0 :         }
      95         315 :         else
      96         315 :         {
      97         315 :             iter->pSize = NULL;
      98         315 :         }
      99             : 
     100         471 :         if (PB_ATYPE(iter->type) == PB_ATYPE_POINTER && iter->pField != NULL)
     101           0 :         {
     102           0 :             iter->pData = *(void**)iter->pField;
     103           0 :         }
     104         471 :         else
     105         471 :         {
     106         471 :             iter->pData = iter->pField;
     107         471 :         }
     108         471 :     }
     109             : 
     110         471 :     if (PB_LTYPE_IS_SUBMSG(iter->type))
     111         192 :     {
     112         192 :         iter->submsg_desc = iter->descriptor->submsg_info[iter->submessage_index];
     113         192 :     }
     114         279 :     else
     115         279 :     {
     116         279 :         iter->submsg_desc = NULL;
     117         279 :     }
     118             : 
     119         471 :     return true;
     120         471 : }
     121             : 
     122             : static void advance_iterator(pb_field_iter_t *iter)
     123         318 : {
     124         318 :     iter->index++;
     125             : 
     126         318 :     if (iter->index >= iter->descriptor->field_count)
     127         105 :     {
     128             :         /* Restart */
     129         105 :         iter->index = 0;
     130         105 :         iter->field_info_index = 0;
     131         105 :         iter->submessage_index = 0;
     132         105 :         iter->required_field_index = 0;
     133         105 :     }
     134         213 :     else
     135         213 :     {
     136             :         /* Increment indexes based on previous field type.
     137             :          * All field info formats have the following fields:
     138             :          * - lowest 2 bits tell the amount of words in the descriptor (2^n words)
     139             :          * - bits 2..7 give the lowest bits of tag number.
     140             :          * - bits 8..15 give the field type.
     141             :          */
     142         213 :         uint32_t prev_descriptor = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]);
     143         213 :         pb_type_t prev_type = (prev_descriptor >> 8) & 0xFF;
     144         213 :         pb_size_t descriptor_len = (pb_size_t)(1 << (prev_descriptor & 3));
     145             : 
     146             :         /* Add to fields.
     147             :          * The cast to pb_size_t is needed to avoid -Wconversion warning.
     148             :          * Because the data is is constants from generator, there is no danger of overflow.
     149             :          */
     150         213 :         iter->field_info_index = (pb_size_t)(iter->field_info_index + descriptor_len);
     151         213 :         iter->required_field_index = (pb_size_t)(iter->required_field_index + (PB_HTYPE(prev_type) == PB_HTYPE_REQUIRED));
     152         213 :         iter->submessage_index = (pb_size_t)(iter->submessage_index + PB_LTYPE_IS_SUBMSG(prev_type));
     153         213 :     }
     154         318 : }
     155             : 
     156             : bool pb_field_iter_begin(pb_field_iter_t *iter, const pb_msgdesc_t *desc, void *message)
     157         162 : {
     158         162 :     memset(iter, 0, sizeof(*iter));
     159             : 
     160         162 :     iter->descriptor = desc;
     161         162 :     iter->message = message;
     162             : 
     163         162 :     return load_descriptor_values(iter);
     164         162 : }
     165             : 
     166             : bool pb_field_iter_begin_extension(pb_field_iter_t *iter, pb_extension_t *extension)
     167           0 : {
     168           0 :     const pb_msgdesc_t *msg = (const pb_msgdesc_t*)extension->type->arg;
     169           0 :     bool status;
     170             : 
     171           0 :     uint32_t word0 = PB_PROGMEM_READU32(msg->field_info[0]);
     172           0 :     if (PB_ATYPE(word0 >> 8) == PB_ATYPE_POINTER)
     173           0 :     {
     174             :         /* For pointer extensions, the pointer is stored directly
     175             :          * in the extension structure. This avoids having an extra
     176             :          * indirection. */
     177           0 :         status = pb_field_iter_begin(iter, msg, &extension->dest);
     178           0 :     }
     179           0 :     else
     180           0 :     {
     181           0 :         status = pb_field_iter_begin(iter, msg, extension->dest);
     182           0 :     }
     183             : 
     184           0 :     iter->pSize = &extension->found;
     185           0 :     return status;
     186           0 : }
     187             : 
     188             : bool pb_field_iter_next(pb_field_iter_t *iter)
     189         261 : {
     190         261 :     advance_iterator(iter);
     191         261 :     (void)load_descriptor_values(iter);
     192         261 :     return iter->index != 0;
     193         261 : }
     194             : 
     195             : bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag)
     196         174 : {
     197         174 :     if (iter->tag == tag)
     198         117 :     {
     199         117 :         return true; /* Nothing to do, correct field already. */
     200         117 :     }
     201          57 :     else if (tag > iter->descriptor->largest_tag)
     202           0 :     {
     203           0 :         return false;
     204           0 :     }
     205          57 :     else
     206          57 :     {
     207          57 :         pb_size_t start = iter->index;
     208          57 :         uint32_t fieldinfo;
     209             : 
     210          57 :         if (tag < iter->tag)
     211           0 :         {
     212             :             /* Fields are in tag number order, so we know that tag is between
     213             :              * 0 and our start position. Setting index to end forces
     214             :              * advance_iterator() call below to restart from beginning. */
     215           0 :             iter->index = iter->descriptor->field_count;
     216           0 :         }
     217             : 
     218          57 :         do
     219          57 :         {
     220             :             /* Advance iterator but don't load values yet */
     221          57 :             advance_iterator(iter);
     222             : 
     223             :             /* Do fast check for tag number match */
     224          57 :             fieldinfo = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]);
     225             : 
     226          57 :             if (((fieldinfo >> 2) & 0x3F) == (tag & 0x3F))
     227          57 :             {
     228             :                 /* Good candidate, check further */
     229          57 :                 (void)load_descriptor_values(iter);
     230             : 
     231          57 :                 if (iter->tag == tag &&
     232          57 :                     PB_LTYPE(iter->type) != PB_LTYPE_EXTENSION)
     233          57 :                 {
     234             :                     /* Found it */
     235          57 :                     return true;
     236          57 :                 }
     237          57 :             }
     238          57 :         } while (iter->index != start);
     239             : 
     240             :         /* Searched all the way back to start, and found nothing. */
     241           0 :         (void)load_descriptor_values(iter);
     242           0 :         return false;
     243          57 :     }
     244         174 : }
     245             : 
     246             : bool pb_field_iter_find_extension(pb_field_iter_t *iter)
     247           0 : {
     248           0 :     if (PB_LTYPE(iter->type) == PB_LTYPE_EXTENSION)
     249           0 :     {
     250           0 :         return true;
     251           0 :     }
     252           0 :     else
     253           0 :     {
     254           0 :         pb_size_t start = iter->index;
     255           0 :         uint32_t fieldinfo;
     256             : 
     257           0 :         do
     258           0 :         {
     259             :             /* Advance iterator but don't load values yet */
     260           0 :             advance_iterator(iter);
     261             : 
     262             :             /* Do fast check for field type */
     263           0 :             fieldinfo = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]);
     264             : 
     265           0 :             if (PB_LTYPE((fieldinfo >> 8) & 0xFF) == PB_LTYPE_EXTENSION)
     266           0 :             {
     267           0 :                 return load_descriptor_values(iter);
     268           0 :             }
     269           0 :         } while (iter->index != start);
     270             : 
     271             :         /* Searched all the way back to start, and found nothing. */
     272           0 :         (void)load_descriptor_values(iter);
     273           0 :         return false;
     274           0 :     }
     275           0 : }
     276             : 
     277             : static void *pb_const_cast(const void *p)
     278          18 : {
     279             :     /* Note: this casts away const, in order to use the common field iterator
     280             :      * logic for both encoding and decoding. The cast is done using union
     281             :      * to avoid spurious compiler warnings. */
     282          18 :     union {
     283          18 :         void *p1;
     284          18 :         const void *p2;
     285          18 :     } t;
     286          18 :     t.p2 = p;
     287          18 :     return t.p1;
     288          18 : }
     289             : 
     290             : bool pb_field_iter_begin_const(pb_field_iter_t *iter, const pb_msgdesc_t *desc, const void *message)
     291          18 : {
     292          18 :     return pb_field_iter_begin(iter, desc, pb_const_cast(message));
     293          18 : }
     294             : 
     295             : bool pb_field_iter_begin_extension_const(pb_field_iter_t *iter, const pb_extension_t *extension)
     296           0 : {
     297           0 :     return pb_field_iter_begin_extension(iter, (pb_extension_t*)pb_const_cast(extension));
     298           0 : }
     299             : 
     300             : bool pb_default_field_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field)
     301          42 : {
     302          42 :     if (field->data_size == sizeof(pb_callback_t))
     303          42 :     {
     304          42 :         pb_callback_t *pCallback = (pb_callback_t*)field->pData;
     305             : 
     306          42 :         if (pCallback != NULL)
     307          42 :         {
     308          42 :             if (istream != NULL && pCallback->funcs.decode != NULL)
     309          42 :             {
     310          42 :                 return pCallback->funcs.decode(istream, field, &pCallback->arg);
     311          42 :             }
     312             : 
     313           0 :             if (ostream != NULL && pCallback->funcs.encode != NULL)
     314           0 :             {
     315           0 :                 return pCallback->funcs.encode(ostream, field, &pCallback->arg);
     316           0 :             }
     317           0 :         }
     318          42 :     }
     319             : 
     320           0 :     return true; /* Success, but didn't do anything */
     321             : 
     322          42 : }
     323             : 
     324             : #ifdef PB_VALIDATE_UTF8
     325             : 
     326             : /* This function checks whether a string is valid UTF-8 text.
     327             :  *
     328             :  * Algorithm is adapted from https://www.cl.cam.ac.uk/~mgk25/ucs/utf8_check.c
     329             :  * Original copyright: Markus Kuhn <http://www.cl.cam.ac.uk/~mgk25/> 2005-03-30
     330             :  * Licensed under "Short code license", which allows use under MIT license or
     331             :  * any compatible with it.
     332             :  */
     333             : 
     334             : bool pb_validate_utf8(const char *str)
     335             : {
     336             :     const pb_byte_t *s = (const pb_byte_t*)str;
     337             :     while (*s)
     338             :     {
     339             :         if (*s < 0x80)
     340             :         {
     341             :             /* 0xxxxxxx */
     342             :             s++;
     343             :         }
     344             :         else if ((s[0] & 0xe0) == 0xc0)
     345             :         {
     346             :             /* 110XXXXx 10xxxxxx */
     347             :             if ((s[1] & 0xc0) != 0x80 ||
     348             :                 (s[0] & 0xfe) == 0xc0)                        /* overlong? */
     349             :                 return false;
     350             :             else
     351             :                 s += 2;
     352             :         }
     353             :         else if ((s[0] & 0xf0) == 0xe0)
     354             :         {
     355             :             /* 1110XXXX 10Xxxxxx 10xxxxxx */
     356             :             if ((s[1] & 0xc0) != 0x80 ||
     357             :                 (s[2] & 0xc0) != 0x80 ||
     358             :                 (s[0] == 0xe0 && (s[1] & 0xe0) == 0x80) ||    /* overlong? */
     359             :                 (s[0] == 0xed && (s[1] & 0xe0) == 0xa0) ||    /* surrogate? */
     360             :                 (s[0] == 0xef && s[1] == 0xbf &&
     361             :                 (s[2] & 0xfe) == 0xbe))                 /* U+FFFE or U+FFFF? */
     362             :                 return false;
     363             :             else
     364             :                 s += 3;
     365             :         }
     366             :         else if ((s[0] & 0xf8) == 0xf0)
     367             :         {
     368             :             /* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */
     369             :             if ((s[1] & 0xc0) != 0x80 ||
     370             :                 (s[2] & 0xc0) != 0x80 ||
     371             :                 (s[3] & 0xc0) != 0x80 ||
     372             :                 (s[0] == 0xf0 && (s[1] & 0xf0) == 0x80) ||    /* overlong? */
     373             :                 (s[0] == 0xf4 && s[1] > 0x8f) || s[0] > 0xf4) /* > U+10FFFF? */
     374             :                 return false;
     375             :             else
     376             :                 s += 4;
     377             :         }
     378             :         else
     379             :         {
     380             :             return false;
     381             :         }
     382             :     }
     383             : 
     384             :     return true;
     385             : }
     386             : 
     387             : #endif
     388             : 

Generated by: LCOV version 1.14