LCOV - code coverage report
Current view: top level - src/runtime - json_parser.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 248 314 79.0 %
Date: 2016-11-30 13:12:14 Functions: 15 17 88.2 %
Branches: 164 406 40.4 %

           Branch data     Line data    Source code
       1                 :            : #include "flatcc/flatcc_rtconfig.h"
       2                 :            : #include "flatcc/flatcc_json_parser.h"
       3                 :            : 
       4                 :            : #if FLATCC_USE_GRISU3 && !defined(PORTABLE_USE_GRISU3)
       5                 :            : #define PORTABLE_USE_GRISU3 1
       6                 :            : #endif
       7                 :            : #include "flatcc/portable/pparsefp.h"
       8                 :            : 
       9                 :            : #if FLATCC_USE_SSE4_2
      10                 :            : #ifdef __SSE4_2__
      11                 :            : #define USE_SSE4_2
      12                 :            : #endif
      13                 :            : #endif
      14                 :            : 
      15                 :            : #ifdef USE_SSE4_2
      16                 :            : #include <nmmintrin.h>
      17                 :            : #define cmpistri(end, haystack, needle, flags)                              \
      18                 :            :         if (end - haystack >= 16) do {                                      \
      19                 :            :         int i;                                                              \
      20                 :            :         __m128i a = _mm_loadu_si128((const __m128i *)(needle));             \
      21                 :            :         do {                                                                \
      22                 :            :             __m128i b = _mm_loadu_si128((const __m128i *)(haystack));       \
      23                 :            :             i = _mm_cmpistri(a, b, flags);                                  \
      24                 :            :             haystack += i;                                                  \
      25                 :            :         } while (i == 16 && end - haystack >= 16);                          \
      26                 :            :         } while(0)
      27                 :            : #endif
      28                 :            : 
      29         [ #  # ]:          0 : const char *flatcc_json_parser_error_string(int err)
      30                 :            : {
      31                 :            :     switch (err) {
      32                 :            : #define XX(no, str)                                                         \
      33                 :            :     case flatcc_json_parser_error_##no:                                     \
      34                 :            :         return str;
      35                 :            :         FLATCC_JSON_PARSE_ERROR_MAP(XX)
      36                 :            : #undef XX
      37                 :            :     default:
      38                 :            :         return "unknown";
      39                 :            :     }
      40                 :            : }
      41                 :            : 
      42                 :          0 : const char *flatcc_json_parser_set_error(flatcc_json_parser_t *ctx, const char *loc, const char *end, int err)
      43                 :            : {
      44 [ #  # ][ #  # ]:          0 :     if (!ctx->error) {
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
      45                 :          0 :         ctx->error = err;
      46                 :          0 :         ctx->pos = (int)(loc - ctx->line_start + 1);
      47                 :         52 :         ctx->error_loc = loc;
      48                 :            :     }
      49                 :          0 :     return end;
      50                 :            : }
      51                 :            : 
      52                 :         71 : const char *flatcc_json_parser_string_part(flatcc_json_parser_t *ctx, const char *buf, const char *end)
      53                 :            : {
      54                 :            : /*
      55                 :            :  * Disabled because it doesn't catch all control characters, but is
      56                 :            :  * useful for performance testing.
      57                 :            :  */
      58                 :            : #if 0
      59                 :            : //#ifdef USE_SSE4_2
      60                 :            :     cmpistri(end, buf, "\"\\\0\r\n\t\v\f", _SIDD_POSITIVE_POLARITY);
      61                 :            : #else
      62                 :            :     /*
      63                 :            :      * Testing for signed char >= 0x20 would also capture UTF-8
      64                 :            :      * encodings that we could verify, and also invalid encodings like
      65                 :            :      * 0xff, but we do not wan't to enforce strict UTF-8.
      66                 :            :      */
      67 [ +  - ][ +  + ]:        512 :     while (buf != end && *buf != '\"' && ((unsigned char)*buf) >= 0x20 && *buf != '\\') {
         [ +  - ][ +  + ]
      68                 :        441 :         ++buf;
      69                 :            :     }
      70                 :            : #endif
      71         [ -  + ]:         71 :     if (buf == end) {
      72                 :            :         return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_unterminated_string);
      73                 :            :     }
      74         [ +  + ]:         71 :     if (*buf == '"') {
      75                 :            :         return buf;
      76                 :            :     }
      77         [ -  + ]:         14 :     if (*buf < 0x20) {
      78                 :            :         return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_invalid_character);
      79                 :            :     }
      80                 :            :     return buf;
      81                 :            : }
      82                 :            : 
      83                 :        308 : const char *flatcc_json_parser_space_ext(flatcc_json_parser_t *ctx, const char *buf, const char *end)
      84                 :            : {
      85                 :            : again:
      86                 :            : #ifdef USE_SSE4_2
      87                 :            :     /*
      88                 :            :      * We can include line break, but then error reporting suffers and
      89                 :            :      * it really makes no big difference.
      90                 :            :      */
      91                 :            :     //cmpistri(end, buf, "\x20\t\v\f\r\n", _SIDD_NEGATIVE_POLARITY);
      92                 :            :     cmpistri(end, buf, "\x20\t\v\f", _SIDD_NEGATIVE_POLARITY);
      93                 :            : #else
      94                 :            : #if FLATCC_ALLOW_UNALIGNED_ACCESS
      95         [ +  + ]:        287 :     while (end - buf >= 16) {
      96         [ +  - ]:        107 :         if (*buf > 0x20) {
      97                 :            :             return buf;
      98                 :            :         }
      99                 :            : #if FLATCC_JSON_PARSE_WIDE_SPACE
     100                 :            :         if (((uint64_t *)buf)[0] != 0x2020202020202020) {
     101                 :            : descend:
     102                 :            :             if (((uint32_t *)buf)[0] == 0x20202020) {
     103                 :            :                 buf += 4;
     104                 :            :             }
     105                 :            : #endif
     106         [ +  + ]:        107 :             if (((uint16_t *)buf)[0] == 0x2020) {
     107                 :         59 :                 buf += 2;
     108                 :            :             }
     109         [ +  + ]:        107 :             if (*buf == 0x20) {
     110                 :         38 :                 ++buf;
     111                 :            :             }
     112         [ +  + ]:        107 :             if (*buf > 0x20) {
     113                 :            :                 return buf;
     114                 :            :             }
     115                 :            :             break;
     116                 :            : #if FLATCC_JSON_PARSE_WIDE_SPACE
     117                 :            :         }
     118                 :            :         if (((uint64_t *)buf)[1] != 0x2020202020202020) {
     119                 :            :             buf += 8;
     120                 :            :             goto descend;
     121                 :            :         }
     122                 :            :         buf += 16;
     123                 :            : #endif
     124                 :            :     }
     125                 :            : #endif
     126                 :            : #endif
     127 [ +  + ][ +  + ]:        755 :     while (buf != end && *buf == 0x20) {
     128                 :        575 :         ++buf;
     129                 :            :     }
     130 [ +  + ][ +  + ]:        232 :     while (buf != end && *buf <= 0x20) {
     131   [ +  +  +  -  :        102 :         switch (*buf) {
                      - ]
     132 [ +  - ][ -  + ]:          1 :         case 0x0d: buf += (end - buf > 1 && buf[1] == 0x0a);
     133                 :            :             /* Fall through consuming following LF or treating CR as LF. */
     134                 :         52 :         case 0x0a: ++ctx->line; ctx->line_start = ++buf; continue;
     135                 :          0 :         case 0x09: ++buf; continue;
     136                 :            :         case 0x20: goto again; /* Don't consume here, sync with power of 2 spaces. */
     137                 :            :         default: return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_unexpected_character);
     138                 :            :         }
     139                 :            :     }
     140                 :            :     return buf;
     141                 :            : }
     142                 :            : 
     143                 :         14 : const char *flatcc_json_parser_string_escape(flatcc_json_parser_t *ctx, const char *buf, const char *end, flatcc_json_parser_escape_buffer_t code)
     144                 :            : {
     145                 :            :     char c, v;
     146                 :            :     unsigned short u, x;
     147                 :            : 
     148 [ +  - ][ -  + ]:         14 :     if (end - buf < 2 || buf[0] != '\\') {
     149                 :          0 :         code[0] = 0;
     150                 :            :         return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_invalid_escape);
     151                 :            :     }
     152   [ +  +  +  +  :         14 :     switch (buf[1]) {
          +  +  +  +  +  
                   +  - ]
     153                 :            :     case 'x':
     154                 :            :         v = 0;
     155                 :          3 :         code[0] = 1;
     156         [ -  + ]:          3 :         if (end - buf < 4) {
     157                 :          0 :             code[0] = 0;
     158                 :            :             return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_invalid_escape);
     159                 :            :         }
     160                 :          3 :         c = buf[2];
     161         [ +  + ]:          3 :         if (c >= '0' && c <= '9') {
     162                 :          1 :             v |= (c - '0') << 4;
     163                 :            :         } else {
     164                 :            :             /* Lower case. */
     165                 :          2 :             c |= 0x20;
     166         [ +  - ]:          2 :             if (c >= 'a' && c <= 'f') {
     167                 :          2 :                 v |= (c - 'a' + 10) << 4;
     168                 :            :             } else {
     169                 :          0 :                 code[0] = 0;
     170                 :            :                 return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_invalid_escape);
     171                 :            :             }
     172                 :            :         }
     173                 :          3 :         c = buf[3];
     174         [ +  - ]:          3 :         if (c >= '0' && c <= '9') {
     175                 :          3 :             v |= c - '0';
     176                 :            :         } else {
     177                 :            :             /* Lower case. */
     178                 :          0 :             c |= 0x20;
     179         [ #  # ]:          0 :             if (c >= 'a' && c <= 'f') {
     180                 :          0 :                 v |= c - 'a' + 10;
     181                 :            :             } else {
     182                 :          0 :                 code[0] = 0;
     183                 :            :                 return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_invalid_escape);
     184                 :            :             }
     185                 :            :         }
     186                 :          3 :         code[1] = v;
     187                 :          3 :         return buf + 4;
     188                 :            :     case 'u':
     189         [ -  + ]:          3 :         if (end - buf < 6) {
     190                 :          0 :             code[0] = 0;
     191                 :            :             return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_invalid_escape);
     192                 :            :         }
     193                 :            :         u = 0;
     194                 :          3 :         c = buf[2];
     195         [ +  - ]:          3 :         if (c >= '0' && c <= '9') {
     196                 :          3 :             x = c - '0';
     197                 :          3 :             u = x << 12;
     198                 :            :         } else {
     199                 :            :             /* Lower case. */
     200                 :          0 :             c |= 0x20;
     201         [ #  # ]:          0 :             if (c >= 'a' && c <= 'f') {
     202                 :          0 :                 x = c - 'a' + 10;
     203                 :          0 :                 u |= x << 12;
     204                 :            :             } else {
     205                 :          0 :                 code[0] = 0;
     206                 :            :                 return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_invalid_escape);
     207                 :            :             }
     208                 :            :         }
     209                 :          3 :         c = buf[3];
     210         [ +  - ]:          3 :         if (c >= '0' && c <= '9') {
     211                 :          3 :             x = c - '0';
     212                 :          3 :             u |= x << 8;
     213                 :            :         } else {
     214                 :            :             /* Lower case. */
     215                 :          0 :             c |= 0x20;
     216         [ #  # ]:          0 :             if (c >= 'a' && c <= 'f') {
     217                 :          0 :                 x = c - 'a' + 10;
     218                 :          0 :                 u |= x << 8;
     219                 :            :             } else {
     220                 :          0 :                 code[0] = 0;
     221                 :            :                 return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_invalid_escape);
     222                 :            :             }
     223                 :            :         }
     224                 :          3 :         c = buf[4];
     225         [ +  + ]:          3 :         if (c >= '0' && c <= '9') {
     226                 :          2 :             x = c - '0';
     227                 :          2 :             u |= x << 4;
     228                 :            :         } else {
     229                 :            :             /* Lower case. */
     230                 :          1 :             c |= 0x20;
     231         [ +  - ]:          1 :             if (c >= 'a' && c <= 'f') {
     232                 :          1 :                 x = c - 'a' + 10;
     233                 :          1 :                 u |= x << 4;
     234                 :            :             } else {
     235                 :          0 :                 code[0] = 0;
     236                 :            :                 return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_invalid_escape);
     237                 :            :             }
     238                 :            :         }
     239                 :          3 :         c = buf[5];
     240         [ +  + ]:          3 :         if (c >= '0' && c <= '9') {
     241                 :          2 :             x = c - '0';
     242                 :          2 :             u |= x;
     243                 :            :         } else {
     244                 :            :             /* Lower case. */
     245                 :          1 :             c |= 0x20;
     246         [ +  - ]:          1 :             if (c >= 'a' && c <= 'f') {
     247                 :          1 :                 x = c - 'a' + 10;
     248                 :          1 :                 u |= x;
     249                 :            :             } else {
     250                 :          0 :                 code[0] = 0;
     251                 :            :                 return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_invalid_escape);
     252                 :            :             }
     253                 :            :         }
     254         [ -  + ]:          3 :         if (u <= 0x7f) {
     255                 :          0 :             code[0] = 1;
     256                 :          0 :             code[1] = (char)u;
     257         [ +  + ]:          3 :         } else if (u <= 0x7ff) {
     258                 :          1 :             code[0] = 2;
     259                 :          1 :             code[1] = (char)(0xc0 | (u >> 6));
     260                 :          1 :             code[2] = (char)(0x80 | (u & 0x3f));
     261                 :            :         } else {
     262                 :          2 :             code[0] = 3;
     263                 :          2 :             code[1] = (char)(0xe0 | (u >> 12));
     264                 :          2 :             code[2] = (char)(0x80 | ((u >> 6) & 0x3f));
     265                 :          2 :             code[3] = (char)(0x80 | (u & 0x3f));
     266                 :            :             /* We do not report failure on invalid unicode range. */
     267                 :            :         }
     268                 :          3 :         return buf + 6;
     269                 :            :     case 't':
     270                 :          1 :         code[0] = 1;
     271                 :          1 :         code[1] = '\t';
     272                 :          1 :         return buf + 2;
     273                 :            :     case 'n':
     274                 :          1 :         code[0] = 1;
     275                 :          1 :         code[1] = '\n';
     276                 :          1 :         return buf + 2;
     277                 :            :     case 'r':
     278                 :          1 :         code[0] = 1;
     279                 :          1 :         code[1] = '\r';
     280                 :          1 :         return buf + 2;
     281                 :            :     case 'b':
     282                 :          1 :         code[0] = 1;
     283                 :          1 :         code[1] = '\b';
     284                 :          1 :         return buf + 2;
     285                 :            :     case 'f':
     286                 :          1 :         code[0] = 1;
     287                 :          1 :         code[1] = '\f';
     288                 :          1 :         return buf + 2;
     289                 :            :     case '\"':
     290                 :          1 :         code[0] = 1;
     291                 :          1 :         code[1] = '\"';
     292                 :          1 :         return buf + 2;
     293                 :            :     case '\\':
     294                 :          1 :         code[0] = 1;
     295                 :          1 :         code[1] = '\\';
     296                 :          1 :         return buf + 2;
     297                 :            :     case '/':
     298                 :          1 :         code[0] = 1;
     299                 :          1 :         code[1] = '/';
     300                 :          1 :         return buf + 2;
     301                 :            :     default:
     302                 :          0 :         code[0] = 0;
     303                 :            :         return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_invalid_escape);
     304                 :            :     }
     305                 :            : }
     306                 :            : 
     307                 :            : /* Only applies to unquoted constants during generic parsring, otherwise it is skipped as a string. */
     308                 :          4 : const char *flatcc_json_parser_skip_constant(flatcc_json_parser_t *ctx, const char *buf, const char *end)
     309                 :            : {
     310                 :            :     char c;
     311                 :            :     const char *k;
     312                 :            : 
     313         [ +  - ]:         23 :     while (buf != end) {
     314                 :         23 :         c = *buf;
     315 [ +  - ][ +  - ]:         23 :         if ((c & 0x80) || (c == '_') || (c >= '0' && c <= '9') || c == '.') {
                 [ -  + ]
     316                 :          0 :             ++buf;
     317                 :          0 :             continue;
     318                 :            :         }
     319                 :            :         /* Upper case. */
     320                 :         23 :         c |= 0x20;
     321         [ +  + ]:         23 :         if (c >= 'a' && c <= 'z') {
     322                 :         19 :             ++buf;
     323                 :         19 :             continue;
     324                 :            :         }
     325                 :          4 :         buf = flatcc_json_parser_space(ctx, (k = buf), end);
     326         [ -  + ]:         23 :         if (buf == k) {
     327                 :            :             return buf;
     328                 :            :         }
     329                 :            :     }
     330                 :            :     return buf;
     331                 :            : }
     332                 :            : 
     333                 :         31 : const char *flatcc_json_parser_match_constant(flatcc_json_parser_t *ctx, const char *buf, const char *end, int pos, int *more)
     334                 :            : {
     335                 :         31 :     const char *mark = buf, *k = buf + pos;
     336                 :            : 
     337         [ -  + ]:         31 :     if (end - buf <= pos) {
     338                 :          0 :         *more = 0;
     339                 :          0 :         return buf;
     340                 :            :     }
     341                 :            : #if FLATCC_JSON_PARSE_ALLOW_UNQUOTED
     342         [ +  + ]:         31 :     if (ctx->unquoted) {
     343                 :         17 :         buf = flatcc_json_parser_space(ctx, k, end);
     344         [ -  + ]:         17 :         if (buf == end) {
     345                 :            :             /*
     346                 :            :              * We cannot make a decision on more.
     347                 :            :              * Just return end and let parser handle sync point in
     348                 :            :              * case it is able to resume parse later on.
     349                 :            :              * For the same reason we do not lower ctx->unquoted.
     350                 :            :              */
     351                 :          0 :             *more = 0;
     352                 :          0 :             return buf;
     353                 :            :         }
     354         [ +  + ]:         17 :         if (buf != k) {
     355                 :          7 :             char c = *buf;
     356                 :            :             /*
     357                 :            :              * Space was seen - and thus we have a valid match.
     358                 :            :              * If the next char is an identifier start symbol
     359                 :            :              * we raise the more flag to support syntax like:
     360                 :            :              *
     361                 :            :              *     `flags: Hungry Sleepy Awake, ...`
     362                 :            :              */
     363         [ -  + ]:          7 :             if (c == '_' || (c & 0x80)) {
     364                 :          0 :                 *more = 1;
     365                 :          0 :                 return buf;
     366                 :            :             }
     367                 :          7 :             c |= 0x20;
     368         [ -  + ]:          7 :             if (c >= 'a' && c <= 'z') {
     369                 :          0 :                 *more = 1;
     370                 :          0 :                 return buf;
     371                 :            :             }
     372                 :            :         }
     373                 :            :         /*
     374                 :            :          * Space was not seen, so the match is only valid if followed
     375                 :            :          * by a JSON separator symbol, and there cannot be more values
     376                 :            :          * following so `more` is lowered.
     377                 :            :          */
     378                 :         17 :         *more = 0;
     379 [ -  + ][ #  # ]:         17 :         if (*buf == ',' || *buf == '}' || *buf == ']') {
     380                 :            :             return buf;
     381                 :            :         }
     382                 :          0 :         return mark;
     383                 :            :     }
     384                 :            : #endif
     385                 :            :     buf = k;
     386         [ +  + ]:         14 :     if (*buf == 0x20) {
     387                 :         10 :         ++buf;
     388 [ +  - ][ +  + ]:         15 :         while (buf != end && *buf == 0x20) {
     389                 :          5 :             ++buf;
     390                 :            :         }
     391         [ -  + ]:         10 :         if (buf == end) {
     392                 :          0 :             *more = 0;
     393                 :          0 :             return buf;
     394                 :            :         }
     395                 :            :         /* We accept untrimmed space like "  Green  Blue  ". */
     396         [ +  + ]:         10 :         if (*buf != '\"') {
     397                 :          8 :             *more = 1;
     398                 :          8 :             return buf;
     399                 :            :         }
     400                 :            :     }
     401      [ -  +  - ]:          6 :     switch (*buf) {
     402                 :            :     case '\\':
     403                 :          0 :         *more = 0;
     404                 :            :         return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_invalid_escape);
     405                 :            :     case '\"':
     406                 :          6 :         buf = flatcc_json_parser_space(ctx, buf + 1, 0);
     407                 :          6 :         *more = 0;
     408                 :          6 :         return buf;
     409                 :            :     }
     410                 :          0 :     *more = 0;
     411                 :          0 :     return mark;
     412                 :            : }
     413                 :            : 
     414                 :          4 : const char *flatcc_json_parser_unmatched_symbol(flatcc_json_parser_t *ctx, const char *buf, const char *end)
     415                 :            : {
     416         [ +  - ]:          4 :     if (ctx->flags & flatcc_json_parser_f_skip_unknown) {
     417                 :          4 :         buf = flatcc_json_parser_symbol_end(ctx, buf, end);
     418                 :          4 :         buf = flatcc_json_parser_space(ctx, buf, end);
     419 [ +  - ][ +  - ]:          4 :         if (buf != end && *buf == ':') {
     420                 :          4 :             ++buf;
     421                 :          4 :             buf = flatcc_json_parser_space(ctx, buf, end);
     422                 :            :         } else {
     423                 :            :             return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_expected_colon);
     424                 :            :         }
     425                 :          4 :         return flatcc_json_parser_generic_json(ctx, buf, end);
     426                 :            :     } else {
     427                 :            :         return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_unknown_symbol);
     428                 :            :     }
     429                 :            : }
     430                 :            : 
     431                 :          2 : static const char *__flatcc_json_parser_number(flatcc_json_parser_t *ctx, const char *buf, const char *end)
     432                 :            : {
     433         [ +  - ]:          2 :     if (buf == end) {
     434                 :            :         return buf;
     435                 :            :     }
     436         [ -  + ]:          2 :     if (*buf == '-') {
     437                 :          0 :         ++buf;
     438         [ #  # ]:          0 :         if (buf == end) {
     439                 :            :             return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_invalid_numeric);
     440                 :            :         }
     441                 :            :     }
     442         [ -  + ]:          2 :     if (*buf == '0') {
     443                 :          0 :         ++buf;
     444                 :            :     } else {
     445         [ -  + ]:          2 :         if (*buf < '1' || *buf > '9') {
     446                 :            :             return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_invalid_numeric);
     447                 :            :         }
     448                 :          2 :         ++buf;
     449 [ +  - ][ -  + ]:          2 :         while (buf != end && *buf >= '0' && *buf <= '9') {
                 [ #  # ]
     450                 :          0 :             ++buf;
     451                 :            :         }
     452                 :            :     }
     453         [ +  - ]:          2 :     if (buf != end) {
     454         [ +  + ]:          2 :         if (*buf == '.') {
     455                 :          1 :             ++buf;
     456         [ -  + ]:          1 :             if (*buf < '0' || *buf > '9') {
     457                 :            :                 return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_invalid_numeric);
     458                 :            :             }
     459                 :          1 :             ++buf;
     460 [ +  - ][ -  + ]:          1 :             while (buf != end && *buf >= '0' && *buf <= '9') {
                 [ #  # ]
     461                 :          0 :                 ++buf;
     462                 :            :             }
     463                 :            :         }
     464                 :            :     }
     465 [ +  - ][ -  + ]:          2 :     if (buf != end && (*buf == 'e' || *buf == 'E')) {
     466                 :          0 :         ++buf;
     467         [ #  # ]:          0 :         if (buf == end) {
     468                 :            :             return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_invalid_numeric);
     469                 :            :         }
     470         [ #  # ]:          0 :         if (*buf == '+' || *buf == '-') {
     471                 :          0 :             ++buf;
     472                 :            :         }
     473 [ #  # ][ #  # ]:          0 :         if (buf == end || *buf < '0' || *buf > '9') {
                 [ #  # ]
     474                 :            :             return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_invalid_numeric);
     475                 :            :         }
     476                 :          0 :         ++buf;
     477 [ #  # ][ #  # ]:          0 :         while (buf != end && *buf >= '0' && *buf <= '9') {
                 [ #  # ]
     478                 :          0 :             ++buf;
     479                 :            :         }
     480                 :            :     }
     481                 :            : 
     482                 :            :     /*
     483                 :            :      * For strtod termination we must ensure the tail is not valid
     484                 :            :      * including non-json exponent types. The simplest approach is
     485                 :            :      * to accept anything that could be valid json successor
     486                 :            :      * characters and reject end of buffer since we expect a closing
     487                 :            :      * '}'.
     488                 :            :      *
     489                 :            :      * The ',' is actually not safe if strtod uses a non-POSIX locale.
     490                 :            :      */
     491         [ +  - ]:          2 :     if (buf != end) {
     492         [ -  + ]:          2 :         switch (*buf) {
     493                 :            :         case ',':
     494                 :            :         case ':':
     495                 :            :         case ']':
     496                 :            :         case '}':
     497                 :            :         case ' ':
     498                 :            :         case '\r':
     499                 :            :         case '\t':
     500                 :            :         case '\n':
     501                 :            :         case '\v':
     502                 :            :             return buf;
     503                 :            :         }
     504                 :            :     }
     505                 :            :     return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_invalid_numeric);
     506                 :            : }
     507                 :            : 
     508                 :          2 : const char *flatcc_json_parser_double(flatcc_json_parser_t *ctx, const char *buf, const char *end, double *v)
     509                 :            : {
     510                 :            :     int of;
     511                 :            :     
     512                 :          2 :     *v = 0.0;
     513                 :          2 :     buf = parse_double(buf, (int)(end - buf), v);
     514         [ -  + ]:          2 :     if (buf == 0) {
     515         [ #  # ]:          0 :         if ((of = parse_double_is_range_error(*v))) {
     516         [ #  # ]:          0 :             if (of > 0) {
     517                 :            :                 flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_overflow);
     518                 :            :             } else {
     519                 :            :                 flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_underflow);
     520                 :            :             }
     521                 :            :         } else {
     522                 :            :             flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_invalid_numeric);
     523                 :            :         }
     524                 :            :         return end;
     525                 :            :     }
     526                 :            :     return buf;
     527                 :            : }
     528                 :            : 
     529                 :          3 : const char *flatcc_json_parser_float(flatcc_json_parser_t *ctx, const char *buf, const char *end, float *v)
     530                 :            : {
     531                 :            :     int of;
     532                 :            : 
     533                 :          3 :     *v = 0.0;
     534                 :          3 :     buf = parse_float(buf, (int)(end - buf), v);
     535         [ -  + ]:          3 :     if (buf == 0) {
     536         [ #  # ]:          0 :         if ((of = parse_float_is_range_error(*v))) {
     537         [ #  # ]:          0 :             if (of > 0) {
     538                 :            :                 flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_overflow);
     539                 :            :             } else {
     540                 :            :                 flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_underflow);
     541                 :            :             }
     542                 :            :         } else {
     543                 :            :             flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_invalid_numeric);
     544                 :            :         }
     545                 :            :         return end;
     546                 :            :     }
     547                 :            :     return buf;
     548                 :            : }
     549                 :            : 
     550                 :          7 : const char *flatcc_json_parser_generic_json(flatcc_json_parser_t *ctx, const char *buf, const char *end)
     551                 :            : {
     552                 :            :     char stack[FLATCC_JSON_PARSE_GENERIC_MAX_NEST];
     553                 :            :     char *sp, *spend;
     554                 :            :     const char *k;
     555                 :            :     char code[4];
     556                 :         17 :     int more = 0;
     557                 :            : 
     558                 :            :     sp = stack;
     559                 :            :     spend = sp + FLATCC_JSON_PARSE_GENERIC_MAX_NEST;
     560                 :            : 
     561                 :            : again:
     562         [ +  - ]:         17 :     if (buf == end) {
     563                 :            :         return buf;
     564                 :            :     }
     565 [ +  + ][ +  + ]:         17 :     if (sp != stack && sp[-1] == '}') {
     566                 :            :         /* Inside an object, about to read field name. */
     567                 :            :         buf = flatcc_json_parser_symbol_start(ctx, buf, end);
     568                 :          8 :         buf = flatcc_json_parser_symbol_end(ctx, buf, end);
     569                 :          8 :         buf = flatcc_json_parser_space(ctx, buf, end);
     570         [ -  + ]:          8 :         if (buf == end) {
     571                 :            :             return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_unbalanced_object);
     572                 :            :         }
     573         [ -  + ]:          8 :         if (*buf != ':') {
     574                 :            :             return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_expected_colon);
     575                 :            :         }
     576                 :          8 :         buf = flatcc_json_parser_space(ctx, buf + 1, end);
     577                 :            :     }
     578   [ +  +  +  +  :         17 :     switch (*buf) {
                      + ]
     579                 :            :     case '\"':
     580                 :            :         buf = flatcc_json_parser_string_start(ctx, buf, end);
     581 [ +  - ][ +  - ]:          4 :         while (buf != end && *buf != '\"') {
     582                 :          4 :             buf = flatcc_json_parser_string_part(ctx, buf, end);
     583 [ +  - ][ -  + ]:          4 :             if (buf != end && *buf == '\"') {
     584                 :            :                 break;
     585                 :            :             }
     586                 :          0 :             buf = flatcc_json_parser_string_escape(ctx, buf, end, code);
     587                 :            :         }
     588                 :            :         buf = flatcc_json_parser_string_end(ctx, buf, end);
     589                 :            :         break;
     590                 :            :     case '-':
     591                 :            :     case '0': case '1': case '2': case '3': case '4':
     592                 :            :     case '5': case '6': case '7': case '8': case '9':
     593                 :          2 :         buf = __flatcc_json_parser_number(ctx, buf, end);
     594                 :         12 :         break;
     595                 :            : #if !FLATCC_JSON_PARSE_ALLOW_UNQUOTED
     596                 :            :     case 't': case 'f':
     597                 :            :         {
     598                 :            :             uint8_t v;
     599                 :            :             buf = flatcc_json_parser_bool(ctx, (k = buf), end, &v);
     600                 :            :             if (k == buf) {
     601                 :            :                 return flatcc_json_parser_set_error(ctx, buf, end, end, flatcc_json_parser_error_unexpected_character);
     602                 :            :             }
     603                 :            :         }
     604                 :            :         break;
     605                 :            :     case 'n':
     606                 :            :         buf = flatcc_json_parser_null(ctx, (k = buf), end);
     607                 :            :         if (k == buf) {
     608                 :            :             return flatcc_json_parser_set_error(ctx, buf, end, end, flatcc_json_parser_error_unexpected_character);
     609                 :            :         }
     610                 :            :         break;
     611                 :            : #endif
     612                 :            :     case '[':
     613         [ -  + ]:          2 :         if (sp == spend) {
     614                 :            :             return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_deep_nesting);
     615                 :            :         }
     616                 :          2 :         *sp++ = ']';
     617                 :          2 :         buf = flatcc_json_parser_space(ctx, buf + 1, end);
     618 [ -  + ][ +  + ]:          2 :         if (buf != end && *buf == ']') {
     619                 :            :             break;
     620                 :            :         }
     621                 :            :         goto again;
     622                 :            :     case '{':
     623         [ -  + ]:          5 :         if (sp == spend) {
     624                 :            :             return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_deep_nesting);
     625                 :            :         }
     626                 :          5 :         *sp++ = '}';
     627                 :          5 :         buf = flatcc_json_parser_space(ctx, buf + 1, end);
     628 [ -  + ][ +  + ]:          5 :         if (buf != end && *buf == '}') {
     629                 :            :             break;
     630                 :            :         }
     631                 :            :         goto again;
     632                 :            : 
     633                 :            :     default:
     634                 :            : #if FLATCC_JSON_PARSE_ALLOW_UNQUOTED
     635                 :          4 :         buf = flatcc_json_parser_skip_constant(ctx, (k = buf), end);
     636         [ -  + ]:          4 :         if (k == buf) {
     637                 :            :             return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_unexpected_character);
     638                 :            :         }
     639                 :            :         break;
     640                 :            : #else
     641                 :            :         return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_unexpected_character);
     642                 :            : #endif
     643                 :            :     }
     644 [ +  - ][ +  + ]:         19 :     while (buf != end && sp != stack) {
     645                 :         12 :         --sp;
     646         [ +  + ]:         12 :         if (*sp == ']') {
     647                 :          3 :             buf = flatcc_json_parser_array_end(ctx, buf, end, &more);
     648                 :            :         } else {
     649                 :          9 :             buf = flatcc_json_parser_object_end(ctx, buf, end, &more);
     650                 :            :         }
     651         [ +  + ]:         12 :         if (more) {
     652                 :            :             ++sp;
     653                 :            :             goto again;
     654                 :            :         }
     655                 :            :     }
     656 [ -  + ][ #  # ]:          7 :     if (buf == end && sp != stack) {
     657         [ #  # ]:          0 :         return flatcc_json_parser_set_error(ctx, buf, end, sp[-1] == ']' ?
     658                 :            :                 flatcc_json_parser_error_unbalanced_array :
     659                 :            :                 flatcc_json_parser_error_unbalanced_object);
     660                 :            :     }
     661                 :            :     /* Any ',', ']', or '}' belongs to parent context. */
     662                 :            :     return buf;
     663                 :            : }
     664                 :            : 
     665                 :        102 : const char *flatcc_json_parser_integer(flatcc_json_parser_t *ctx, const char *buf, const char *end,
     666                 :            :         int *value_sign, uint64_t *value)
     667                 :            : {
     668                 :            :     uint64_t x0, x = 0;
     669                 :            :     const char *k;
     670                 :            : 
     671         [ +  - ]:        102 :     if (buf == end) {
     672                 :            :         return buf;
     673                 :            :     }
     674                 :            :     k = buf;
     675                 :        102 :     *value_sign = *buf == '-';
     676                 :        102 :     buf += *value_sign;
     677 [ +  - ][ +  + ]:        332 :     while (buf != end && *buf >= '0' && *buf <= '9') {
                 [ +  + ]
     678                 :            :         x0 = x;
     679                 :        230 :         x = x * 10 + *buf - '0';
     680         [ -  + ]:        230 :         if (x0 > x) {
     681         [ #  # ]:          0 :             return flatcc_json_parser_set_error(ctx, buf, end, value_sign ?
     682                 :            :                     flatcc_json_parser_error_underflow : flatcc_json_parser_error_overflow);
     683                 :            :         }
     684                 :        230 :         ++buf;
     685                 :            :     }
     686         [ +  + ]:        102 :     if (buf == k) {
     687                 :            :         /* Give up, but don't fail the parse just yet, it might be a valid symbol. */
     688                 :            :         return buf;
     689                 :            :     }
     690 [ +  - ][ +  - ]:         79 :     if (buf != end && (*buf == 'e' || *buf == 'E' || *buf == '.')) {
                 [ -  + ]
     691                 :            :         return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_float_unexpected);
     692                 :            :     }
     693                 :         79 :     *value = x;
     694                 :         79 :     return buf;
     695                 :            : }
     696                 :            : 
     697                 :            : 
     698                 :            : /* UNIONS */
     699                 :            : 
     700                 :            : /*
     701                 :            :  * Unions are difficult to parse because the type field may appear after
     702                 :            :  * the union table and because having two fields opens up for many more
     703                 :            :  * possible error scenarios. We must store each union of a table
     704                 :            :  * temporarily - this cannot be in the generated table parser function
     705                 :            :  * because there could be many unions (about 2^15 with default voffsets)
     706                 :            :  * although usually there will be only a few. We can also not store the
     707                 :            :  * data encoded in the existing table buffer in builder because we may
     708                 :            :  * have to remove it due to schema forwarding and removing it messes up
     709                 :            :  * the table layout. We also cannot naively allocate it dynamically for
     710                 :            :  * performance reasons. Instead we place the temporary union data in a
     711                 :            :  * separate frame from the table buffer, but on a similar stack. This is
     712                 :            :  * called the user stack and we manage one frame per table that is known
     713                 :            :  * to contain unions.
     714                 :            :  *
     715                 :            :  * Even the temporary structures in place we still cannot parse a union
     716                 :            :  * before we know its type. Due to JSON typically sorting fields
     717                 :            :  * alphabetically in various pretty printers, we are likely to receive
     718                 :            :  * the type late with (`<union_name>_type` following `<union_name>`.
     719                 :            :  * To deal with this we store a backtracking pointer and parses the
     720                 :            :  * table generically in a first pass and reparse the table once the type
     721                 :            :  * is known. This can happen recursively with nested tables containing
     722                 :            :  * unions which is why we need to have a stack frame.
     723                 :            :  *
     724                 :            :  * If the type field is stored first we just store the type in the
     725                 :            :  * custom frame and immediately parses the table with the right type
     726                 :            :  * once we see it. The parse will be much faster and we can strongly
     727                 :            :  * recommend that flatbuffer serializers do this, but we cannot require
     728                 :            :  * it.
     729                 :            :  *
     730                 :            :  * The actual overhead of dealing with the custom stack frame is fairly
     731                 :            :  * cheap once we get past the first custom stack allocation.
     732                 :            :  *
     733                 :            :  * We cannot update the builder before both the table and table type
     734                 :            :  * has been parsed because the the type might have to be ingored due
     735                 :            :  * to schema forwarding. Therefore the union type must be cached or
     736                 :            :  * reread. This happens trivially be calling the union parser with the
     737                 :            :  * type as argument, but it is important to be aware of before
     738                 :            :  * refactoring the code.
     739                 :            :  *
     740                 :            :  * The user frame is created at table start and remains valid until
     741                 :            :  * table exit, but we cannot assume the pointers to the frame remain
     742                 :            :  * valid. Specifically we cannot use frame pointers after calling
     743                 :            :  * the union parser. This means the union type must be cached or reread
     744                 :            :  * so it can be added to the table. Because the type is passed to
     745                 :            :  * the union parser this caching happens automatically but it is still
     746                 :            :  * important to be aware that it is required.
     747                 :            :  *
     748                 :            :  * The frame reserves temporary information for all unions the table
     749                 :            :  * holds, enumerated 0 <= `union_index` < `union_total`
     750                 :            :  * where the `union_total` is fixed type specific number.
     751                 :            :  *
     752                 :            :  * The `type_present` is needed because union types range from 0..255
     753                 :            :  * and we need an extra bit do distinguish not present from union type
     754                 :            :  * `NONE = 0`.
     755                 :            :  */
     756                 :            : 
     757                 :            : typedef struct {
     758                 :            :     const char *backtrace;
     759                 :            :     const char *line_start;
     760                 :            :     uint8_t type_present;
     761                 :            :     uint8_t type;
     762                 :            :     int line;
     763                 :            : } __flatcc_json_parser_union_entry_t;
     764                 :            : 
     765                 :            : typedef struct {
     766                 :            :     size_t union_total;
     767                 :            :     size_t union_count;
     768                 :            :     __flatcc_json_parser_union_entry_t unions[1];
     769                 :            : } __flatcc_json_parser_union_frame_t;
     770                 :            : 
     771                 :         49 : const char *flatcc_json_parser_prepare_unions(flatcc_json_parser_t *ctx,
     772                 :            :         const char *buf, const char *end, size_t union_total)
     773                 :            : {
     774                 :            :     __flatcc_json_parser_union_frame_t *f;
     775                 :            : 
     776         [ -  + ]:         49 :     if (!(f = flatcc_builder_enter_user_frame(ctx->ctx,
     777                 :         49 :                 sizeof(__flatcc_json_parser_union_frame_t) + (union_total - 1) *
     778                 :            :                 sizeof(__flatcc_json_parser_union_entry_t)))) {
     779                 :            :         return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_runtime);
     780                 :            :     }
     781                 :            :     /* Frames have zeroed memory. */
     782                 :         49 :     f->union_total = union_total;
     783                 :         49 :     return buf;
     784                 :            : }
     785                 :            : 
     786                 :         49 : const char *flatcc_json_parser_finalize_unions(flatcc_json_parser_t *ctx,
     787                 :            :         const char *buf, const char *end)
     788                 :            : {
     789                 :         49 :     __flatcc_json_parser_union_frame_t *f = flatcc_builder_get_user_frame(ctx->ctx);
     790                 :            : 
     791         [ -  + ]:         49 :     if (f->union_count) {
     792                 :            :         buf = flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_union_incomplete);
     793                 :            :     }
     794                 :         49 :     flatcc_builder_exit_user_frame(ctx->ctx);
     795                 :         49 :     return buf;
     796                 :            : }
     797                 :            : 
     798                 :          5 : const char *flatcc_json_parser_union(flatcc_json_parser_t *ctx,
     799                 :            :         const char *buf, const char *end, size_t union_index,
     800                 :            :         flatbuffers_voffset_t id, flatcc_json_parser_union_f *parse)
     801                 :            : {
     802                 :          5 :     __flatcc_json_parser_union_frame_t *f = flatcc_builder_get_user_frame(ctx->ctx);
     803                 :            :     __flatcc_json_parser_union_entry_t *e = &f->unions[union_index];
     804                 :            : 
     805         [ -  + ]:          5 :     if (e->backtrace) {
     806                 :            :         return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_duplicate);
     807                 :            :     }
     808         [ +  + ]:          5 :     if (!e->type_present) {
     809                 :            :         /* If we supported table: null, we should not count it, but we don't. */
     810                 :          3 :         ++f->union_count;
     811                 :          3 :         e->line = ctx->line;
     812                 :          3 :         e->line_start = ctx->line_start;
     813                 :          3 :         buf = flatcc_json_parser_generic_json(ctx, (e->backtrace = buf), end);
     814                 :            :     } else {
     815         [ -  + ]:          2 :         if (e->type == 0) {
     816                 :            :             return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_union_none);
     817                 :            :         }
     818                 :          2 :         --f->union_count;
     819                 :          2 :         buf = parse(ctx, buf, end, e->type, id);
     820                 :            :     }
     821                 :            :     return buf;
     822                 :            : }
     823                 :            : 
     824                 :          7 : const char *flatcc_json_parser_union_type(flatcc_json_parser_t *ctx,
     825                 :            :         const char *buf, const char *end, size_t union_index, flatbuffers_voffset_t id,
     826                 :            :         flatcc_json_parser_integral_symbol_f *type_parsers[],
     827                 :            :         flatcc_json_parser_union_f *union_parser)
     828                 :            : {
     829                 :          7 :     __flatcc_json_parser_union_frame_t *f = flatcc_builder_get_user_frame(ctx->ctx);
     830                 :          7 :     __flatcc_json_parser_union_entry_t *e = f->unions + union_index;
     831                 :            : 
     832                 :            :     const char *mark;
     833                 :            :     int line;
     834                 :            :     const char *line_start;
     835                 :            : 
     836         [ -  + ]:          7 :     if (e->type_present) {
     837                 :            :         return flatcc_json_parser_set_error(ctx, buf, end, flatcc_json_parser_error_duplicate);
     838                 :            :     }
     839                 :          7 :     e->type_present = 1;
     840                 :          7 :     buf = flatcc_json_parser_uint8(ctx, (mark = buf), end, &e->type);
     841         [ +  + ]:          7 :     if (mark == buf) {
     842                 :          6 :         buf = flatcc_json_parser_symbolic_uint8(ctx, buf, end, type_parsers, &e->type);
     843                 :            :     }
     844                 :            :     /* Only count the union if the type is not NONE. */
     845         [ +  + ]:          7 :     if (e->backtrace == 0) {
     846                 :          4 :         f->union_count += e->type != 0;
     847                 :          4 :         return buf;
     848                 :            :     }
     849                 :            :     assert(f->union_count);
     850                 :          3 :     --f->union_count;
     851                 :            :     /*
     852                 :            :      * IMPORTANT: we cannot access any value in the frame or entry
     853                 :            :      * pointer after calling union parse because it might cause the
     854                 :            :      * stack to reallocate. We should read the frame pointer again if
     855                 :            :      * needed - we don't but remember it if refactoring code.
     856                 :            :      *
     857                 :            :      * IMPORTANT 2: Do not assign buf here. We are backtracking.
     858                 :            :      */
     859                 :          3 :     line = ctx->line;
     860                 :          3 :     line_start = ctx->line_start;
     861                 :          3 :     ctx->line = e->line;
     862                 :          3 :     ctx->line_start = e->line_start;
     863         [ +  - ]:          3 :     if (end == union_parser(ctx, e->backtrace, end, e->type, id)) {
     864                 :            :         return end;
     865                 :            :     }
     866                 :          3 :     ctx->line = line;
     867                 :          3 :     ctx->line_start = line_start;
     868                 :          3 :     return buf;
     869                 :            : }

Generated by: LCOV version 1.12