LCOV - code coverage report
Current view: top level - flamenco/rpcserver - json_lex.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 358 0.0 %
Date: 2024-11-13 11:58:15 Functions: 0 13 0.0 %

          Line data    Source code
       1             : #include "json_lex.h"
       2             : #include <stdio.h>
       3             : #include <stdlib.h>
       4             : #include <stdarg.h>
       5             : 
       6             : static char* json_lex_append_prepare(json_lex_state_t* lex, ulong sz);
       7             : static void json_lex_append_char(json_lex_state_t* lex, uint ch);
       8             : 
       9             : void json_lex_state_new(struct json_lex_state* state,
      10             :                         const char* json,
      11           0 :                         ulong json_sz) {
      12           0 :   state->json = json;
      13           0 :   state->json_sz = json_sz;
      14           0 :   state->pos = 0;
      15           0 :   state->last_tok = JSON_TOKEN_ERROR;
      16           0 :   state->last_bool = 0;
      17           0 :   state->last_str = state->last_str_firstbuf;
      18           0 :   state->last_str_sz = 0;
      19           0 :   state->last_str_alloc = sizeof(state->last_str_firstbuf);
      20           0 :   state->last_str_firstbuf[0] = '\0';
      21           0 : }
      22             : 
      23           0 : void json_lex_state_delete(struct json_lex_state* state) {
      24           0 :   (void)state;
      25           0 : }
      26             : 
      27             : // Parse a numeric constant
      28           0 : static long json_lex_parse_number(struct json_lex_state* state, const char* start_pos) {
      29             :   // Scan to the end of the number
      30           0 :   const char* pos = start_pos;
      31           0 :   const char* const end_pos = state->json + state->json_sz;
      32           0 :   if (pos < end_pos && *pos == '-')
      33           0 :     pos++;
      34           0 :   while (pos < end_pos && (uchar)(*pos - '0') <= (uchar)9)
      35           0 :     pos++;
      36           0 :   int isfloat = 0;
      37           0 :   if (pos < end_pos && *pos == '.') {
      38           0 :     isfloat = 1;
      39           0 :     pos++;
      40           0 :     while (pos < end_pos && (uchar)(*pos - '0') <= (uchar)9)
      41           0 :       pos++;
      42           0 :   }
      43           0 :   if (pos < end_pos && (*pos == 'e' || *pos == 'E')) {
      44           0 :     isfloat = 1;
      45           0 :     pos++;
      46           0 :     if (pos < end_pos && (*pos == '+' || *pos == '-'))
      47           0 :       pos++;
      48           0 :     while (pos < end_pos && (uchar)(*pos - '0') <= (uchar)9)
      49           0 :       pos++;
      50           0 :   }
      51             : 
      52             :   // Numbers must end on whitespace or punctuation
      53           0 :   if (pos < end_pos) {
      54           0 :     switch (*pos) {
      55           0 :     case ' ': case '\t': case '\r': case '\n':
      56           0 :     case '[': case ']':  case '{':  case '}':
      57           0 :     case ',': case ':':
      58           0 :       break;
      59           0 :     default:
      60           0 :       state->pos = (ulong)(start_pos - state->json);
      61           0 :       json_lex_sprintf(state, "malformed number at position %lu in json", state->pos);
      62           0 :       return JSON_TOKEN_ERROR;
      63           0 :     }
      64           0 :   }
      65             : 
      66             :   // Store the number in string form
      67           0 :   ulong text_sz = (ulong)(pos - start_pos);
      68           0 :   if( text_sz >= state->last_str_alloc ) {
      69           0 :     state->pos = (ulong)(start_pos - state->json);
      70           0 :     json_lex_sprintf(state, "malformed number at position %lu in json", state->pos);
      71           0 :     return JSON_TOKEN_ERROR;
      72           0 :   }
      73           0 :   fd_memcpy(state->last_str, start_pos, text_sz);
      74           0 :   state->last_str[text_sz] = '\0';
      75           0 :   state->last_str_sz = text_sz;
      76           0 :   state->pos = (ulong)(pos - state->json);
      77           0 :   return (isfloat ? JSON_TOKEN_FLOAT : JSON_TOKEN_INTEGER);
      78           0 : }
      79             : 
      80             : // Validate a segment of UTF-8 encoded test. If an error is found, a
      81             : // pointer to it is returned. A NULL is returned if there is no error.
      82           0 : static const char* json_lex_validate_encoding(const char* t, const char* t_end) {
      83             :   /****
      84             : Code Points             First Byte      Second Byte     Third Byte      Fourth Byte
      85             : U+0020..U+007F          20..7F
      86             : U+0080..U+07FF          C2..DF          80..BF
      87             : U+0800..U+0FFF          E0              A0..BF          80..BF
      88             : U+1000..U+CFFF          E1..EC          80..BF          80..BF
      89             : U+D000..U+D7FF          ED              80..9F          80..BF
      90             : U+E000..U+FFFF          EE..EF          80..BF          80..BF
      91             : U+10000..U+3FFFF        F0              90..BF          80..BF          80..BF
      92             : U+40000..U+FFFFF        F1..F3          80..BF          80..BF          80..BF
      93             : U+100000..U+10FFFF      F4              80..8F          80..BF          80..BF
      94             : Also, '"' and '\' are not allowed.
      95             :   ****/
      96             :   // Fast case lookup table based on the leading byte
      97           0 :   static const uchar case_table[0x100] = {
      98           0 :     0 /* 00 */, 0 /* 01 */, 0 /* 02 */, 0 /* 03 */, 0 /* 04 */, 0 /* 05 */, 0 /* 06 */, 0 /* 07 */,
      99           0 :     0 /* 08 */, 1 /* 09 */, 1 /* 0a */, 0 /* 0b */, 1 /* 0c */, 0 /* 0d */, 0 /* 0e */, 0 /* 0f */,
     100           0 :     0 /* 10 */, 0 /* 11 */, 0 /* 12 */, 0 /* 13 */, 0 /* 14 */, 0 /* 15 */, 0 /* 16 */, 0 /* 17 */,
     101           0 :     0 /* 18 */, 0 /* 19 */, 0 /* 1a */, 0 /* 1b */, 0 /* 1c */, 0 /* 1d */, 0 /* 1e */, 0 /* 1f */,
     102           0 :     1 /* 20 */, 1 /* 21 */, 0 /* 22 */, 1 /* 23 */, 1 /* 24 */, 1 /* 25 */, 1 /* 26 */, 1 /* 27 */,
     103           0 :     1 /* 28 */, 1 /* 29 */, 1 /* 2a */, 1 /* 2b */, 1 /* 2c */, 1 /* 2d */, 1 /* 2e */, 1 /* 2f */,
     104           0 :     1 /* 30 */, 1 /* 31 */, 1 /* 32 */, 1 /* 33 */, 1 /* 34 */, 1 /* 35 */, 1 /* 36 */, 1 /* 37 */,
     105           0 :     1 /* 38 */, 1 /* 39 */, 1 /* 3a */, 1 /* 3b */, 1 /* 3c */, 1 /* 3d */, 1 /* 3e */, 1 /* 3f */,
     106           0 :     1 /* 40 */, 1 /* 41 */, 1 /* 42 */, 1 /* 43 */, 1 /* 44 */, 1 /* 45 */, 1 /* 46 */, 1 /* 47 */,
     107           0 :     1 /* 48 */, 1 /* 49 */, 1 /* 4a */, 1 /* 4b */, 1 /* 4c */, 1 /* 4d */, 1 /* 4e */, 1 /* 4f */,
     108           0 :     1 /* 50 */, 1 /* 51 */, 1 /* 52 */, 1 /* 53 */, 1 /* 54 */, 1 /* 55 */, 1 /* 56 */, 1 /* 57 */,
     109           0 :     1 /* 58 */, 1 /* 59 */, 1 /* 5a */, 1 /* 5b */, 0 /* 5c */, 1 /* 5d */, 1 /* 5e */, 1 /* 5f */,
     110           0 :     1 /* 60 */, 1 /* 61 */, 1 /* 62 */, 1 /* 63 */, 1 /* 64 */, 1 /* 65 */, 1 /* 66 */, 1 /* 67 */,
     111           0 :     1 /* 68 */, 1 /* 69 */, 1 /* 6a */, 1 /* 6b */, 1 /* 6c */, 1 /* 6d */, 1 /* 6e */, 1 /* 6f */,
     112           0 :     1 /* 70 */, 1 /* 71 */, 1 /* 72 */, 1 /* 73 */, 1 /* 74 */, 1 /* 75 */, 1 /* 76 */, 1 /* 77 */,
     113           0 :     1 /* 78 */, 1 /* 79 */, 1 /* 7a */, 1 /* 7b */, 1 /* 7c */, 1 /* 7d */, 1 /* 7e */, 1 /* 7f */,
     114           0 :     0 /* 80 */, 0 /* 81 */, 0 /* 82 */, 0 /* 83 */, 0 /* 84 */, 0 /* 85 */, 0 /* 86 */, 0 /* 87 */,
     115           0 :     0 /* 88 */, 0 /* 89 */, 0 /* 8a */, 0 /* 8b */, 0 /* 8c */, 0 /* 8d */, 0 /* 8e */, 0 /* 8f */,
     116           0 :     0 /* 90 */, 0 /* 91 */, 0 /* 92 */, 0 /* 93 */, 0 /* 94 */, 0 /* 95 */, 0 /* 96 */, 0 /* 97 */,
     117           0 :     0 /* 98 */, 0 /* 99 */, 0 /* 9a */, 0 /* 9b */, 0 /* 9c */, 0 /* 9d */, 0 /* 9e */, 0 /* 9f */,
     118           0 :     0 /* a0 */, 0 /* a1 */, 0 /* a2 */, 0 /* a3 */, 0 /* a4 */, 0 /* a5 */, 0 /* a6 */, 0 /* a7 */,
     119           0 :     0 /* a8 */, 0 /* a9 */, 0 /* aa */, 0 /* ab */, 0 /* ac */, 0 /* ad */, 0 /* ae */, 0 /* af */,
     120           0 :     0 /* b0 */, 0 /* b1 */, 0 /* b2 */, 0 /* b3 */, 0 /* b4 */, 0 /* b5 */, 0 /* b6 */, 0 /* b7 */,
     121           0 :     0 /* b8 */, 0 /* b9 */, 0 /* ba */, 0 /* bb */, 0 /* bc */, 0 /* bd */, 0 /* be */, 0 /* bf */,
     122           0 :     0 /* c0 */, 0 /* c1 */, 2 /* c2 */, 2 /* c3 */, 2 /* c4 */, 2 /* c5 */, 2 /* c6 */, 2 /* c7 */,
     123           0 :     2 /* c8 */, 2 /* c9 */, 2 /* ca */, 2 /* cb */, 2 /* cc */, 2 /* cd */, 2 /* ce */, 2 /* cf */,
     124           0 :     2 /* d0 */, 2 /* d1 */, 2 /* d2 */, 2 /* d3 */, 2 /* d4 */, 2 /* d5 */, 2 /* d6 */, 2 /* d7 */,
     125           0 :     2 /* d8 */, 2 /* d9 */, 2 /* da */, 2 /* db */, 2 /* dc */, 2 /* dd */, 2 /* de */, 2 /* df */,
     126           0 :     3 /* e0 */, 4 /* e1 */, 4 /* e2 */, 4 /* e3 */, 4 /* e4 */, 4 /* e5 */, 4 /* e6 */, 4 /* e7 */,
     127           0 :     4 /* e8 */, 4 /* e9 */, 4 /* ea */, 4 /* eb */, 4 /* ec */, 5 /* ed */, 6 /* ee */, 6 /* ef */,
     128           0 :     7 /* f0 */, 8 /* f1 */, 8 /* f2 */, 8 /* f3 */, 9 /* f4 */, 0 /* f5 */, 0 /* f6 */, 0 /* f7 */,
     129           0 :     0 /* f8 */, 0 /* f9 */, 0 /* fa */, 0 /* fb */, 0 /* fc */, 0 /* fd */, 0 /* fe */, 0 /* ff */
     130           0 :   };
     131           0 :   while (t < t_end) {
     132           0 :     switch (case_table[(uchar)t[0]]) {
     133           0 :     case 0: // error
     134           0 :       return t;
     135           0 :     case 1: // 20..7F
     136           0 :       ++t;
     137           0 :       break;
     138           0 :     case 2: // C2..DF
     139             :       // Determine if a character is in a range
     140           0 : #define MATCH(_ch_, _low_, _high_) ((uchar)_ch_ - (uchar)_low_ <= (uchar)(_high_ - _low_))
     141           0 :       if (!(t+2 <= t_end && MATCH(t[1], 0x80, 0xBF)))
     142           0 :         return t;
     143           0 :       t += 2;
     144           0 :       break;
     145           0 :     case 3: // E0
     146           0 :       if (!(t+3 <= t_end && MATCH(t[1], 0xA0, 0xBF) && MATCH(t[2], 0x80, 0xBF)))
     147           0 :         return t;
     148           0 :       t += 3;
     149           0 :       break;
     150           0 :     case 4: // E1..EC
     151           0 :       if (!(t+3 <= t_end && MATCH(t[1], 0x80, 0xBF) && MATCH(t[2], 0x80, 0xBF)))
     152           0 :         return t;
     153           0 :       t += 3;
     154           0 :       break;
     155           0 :     case 5: // ED
     156           0 :       if (!(t+3 <= t_end && MATCH(t[1], 0x80, 0x9F) && MATCH(t[2], 0x80, 0xBF)))
     157           0 :         return t;
     158           0 :       t += 3;
     159           0 :       break;
     160           0 :     case 6: // EE..EF
     161           0 :       if (!(t+3 <= t_end && MATCH(t[1], 0x80, 0xBF) && MATCH(t[2], 0x80, 0xBF)))
     162           0 :         return t;
     163           0 :       t += 3;
     164           0 :       break;
     165           0 :     case 7: // F0
     166           0 :       if (!(t+4 <= t_end && MATCH(t[1], 0x90, 0xBF) && MATCH(t[2], 0x80, 0xBF) && MATCH(t[3], 0x80, 0xBF)))
     167           0 :         return t;
     168           0 :       t += 4;
     169           0 :       break;
     170           0 :     case 8: // F1..F3
     171           0 :       if (!(t+4 <= t_end && MATCH(t[1], 0x80, 0xBF) && MATCH(t[2], 0x80, 0xBF) && MATCH(t[3], 0x80, 0xBF)))
     172           0 :         return t;
     173           0 :       t += 4;
     174           0 :       break;
     175           0 :     case 9: // F4
     176           0 :       if (!(t+4 <= t_end && MATCH(t[1], 0x80, 0x8F) && MATCH(t[2], 0x80, 0xBF) && MATCH(t[3], 0x80, 0xBF)))
     177           0 :         return t;
     178           0 :       t += 4;
     179           0 :       break;
     180           0 :     }
     181           0 : #undef MATCH
     182           0 :   }
     183           0 :   return NULL;
     184           0 : }
     185             : 
     186             : // Parse a json string. All characters are decoded to pure UTF-8
     187           0 : static long json_lex_parse_string(struct json_lex_state* state, const char* start_pos) {
     188           0 :   state->last_str_sz = 0;
     189           0 :   state->last_str[0] = '\0';
     190           0 :   const char* pos = start_pos + 1; // Skip leading quote
     191           0 :   const char* const end_pos = state->json + state->json_sz;
     192             :   // Loop over all characters
     193           0 :   while (pos < end_pos) {
     194           0 :     if (*pos == '"') {
     195           0 :       state->pos = (ulong)(pos + 1 - state->json);
     196           0 :       return JSON_TOKEN_STRING;
     197           0 :     }
     198             : 
     199           0 :     if (*pos != '\\') {
     200             :       // A segment of simple text without escapes
     201           0 :       const char* s = pos;
     202           0 :       do {
     203           0 :         pos++;
     204           0 :       } while (pos < end_pos && *pos != '"' && *pos != '\\');
     205             :       // Make sure the text is correctly encoded
     206           0 :       const char* err_pos = json_lex_validate_encoding(s, pos);
     207           0 :       if (err_pos) {
     208             :         // Report the error
     209           0 :         state->pos = (ulong)(start_pos - state->json);
     210           0 :         json_lex_sprintf(state, "invalid character literal at position %ld in json", err_pos - state->json);
     211           0 :         return JSON_TOKEN_ERROR;
     212           0 :       }
     213             :       // Just copy out the text
     214           0 :       fd_memcpy(json_lex_append_prepare(state, (ulong)(pos - s)), s, (ulong)(pos - s));
     215           0 :       continue; // break out of switch and continue outer loop
     216           0 :     }
     217             : 
     218             :     // Process an escape
     219           0 :     if (pos + 2 > end_pos)
     220           0 :       break;
     221           0 :     uint ch;
     222           0 :     switch (pos[1]) {
     223             :       // Simple escapes
     224           0 :     case '"':  ch = 0x22; pos += 2; break;
     225           0 :     case '\\': ch = 0x5C; pos += 2; break;
     226           0 :     case '/':  ch = 0x2F; pos += 2; break;
     227           0 :     case 'b':  ch = 0x8;  pos += 2; break;
     228           0 :     case 'f':  ch = 0xC;  pos += 2; break;
     229           0 :     case 'n':  ch = 0xA;  pos += 2; break;
     230           0 :     case 'r':  ch = 0xD;  pos += 2; break;
     231           0 :     case 't':  ch = 0x9;  pos += 2; break;
     232             : 
     233           0 :     case 'u': // Hexadecimal escape
     234           0 :       if (pos + 6 <= end_pos) {
     235           0 :         ch = 0;
     236           0 :         unsigned i;
     237           0 :         for (i = 2; i < 6; ++i) {
     238           0 :           char j = pos[i];
     239           0 :           if ((uchar)(j - '0') <= (uchar)9)
     240           0 :             ch = (ch<<4) + (uchar)(j - '0');
     241           0 :           else if ((uchar)(j - 'a') <= (uchar)5)
     242           0 :             ch = (ch<<4) + (uchar)(j - ('a' - 10));
     243           0 :           else if ((uchar)(j - 'A') <= (uchar)5)
     244           0 :             ch = (ch<<4) + (uchar)(j - ('A' - 10));
     245           0 :           else
     246           0 :             break;
     247           0 :         }
     248             :         // See if the loop succeeded
     249           0 :         if (i == 6) {
     250           0 :           pos += 6;
     251           0 :           break; // Fall out of switch to append_char
     252           0 :         }
     253           0 :       }
     254             :       // Fall through to error case
     255           0 :       __attribute__((fallthrough));
     256           0 :     default:
     257           0 :       state->pos = (ulong)(start_pos - state->json);
     258           0 :       json_lex_sprintf(state, "invalid character literal at position %ld in json", pos - state->json);
     259           0 :       return JSON_TOKEN_ERROR;
     260           0 :     }
     261             :     // Append the escaped character
     262           0 :     json_lex_append_char(state, ch);
     263             : 
     264           0 :   }
     265             :   // We were looking for a closing quote
     266           0 :   state->pos = (ulong)(start_pos - state->json);
     267           0 :   json_lex_sprintf(state, "unterminated string starting at position %lu in json", state->pos);
     268           0 :   return JSON_TOKEN_ERROR;
     269           0 : }
     270             : 
     271             : // Report a lexical error
     272           0 : static long json_lex_error(struct json_lex_state* state, const char* pos) {
     273           0 :   state->pos = (ulong)(pos - state->json);
     274           0 :   json_lex_sprintf(state, "lexical error at position %lu in json", state->pos);
     275           0 :   return JSON_TOKEN_ERROR;
     276           0 : }
     277             : 
     278             : // Scan the next lexical token
     279           0 : long json_lex_next_token(struct json_lex_state* state) {
     280           0 :   const char* pos = state->json + state->pos;
     281           0 :   const char* end_pos = state->json + state->json_sz;
     282           0 :   while (pos < end_pos) {
     283           0 :     switch (*pos) {
     284             :       // Whitespace
     285           0 :     case ' ': case '\t': case '\r': case '\n':
     286           0 :       ++pos;
     287           0 :       continue;
     288             : 
     289             :       // Single character cases
     290           0 :     case '[':
     291           0 :       state->pos = (ulong)(pos + 1 - state->json);
     292           0 :       return state->last_tok = JSON_TOKEN_LBRACKET;
     293           0 :     case ']':
     294           0 :       state->pos = (ulong)(pos + 1 - state->json);
     295           0 :       return state->last_tok = JSON_TOKEN_RBRACKET;
     296           0 :     case '{':
     297           0 :       state->pos = (ulong)(pos + 1 - state->json);
     298           0 :       return state->last_tok = JSON_TOKEN_LBRACE;
     299           0 :     case '}':
     300           0 :       state->pos = (ulong)(pos + 1 - state->json);
     301           0 :       return state->last_tok = JSON_TOKEN_RBRACE;
     302           0 :     case ',':
     303           0 :       state->pos = (ulong)(pos + 1 - state->json);
     304           0 :       return state->last_tok = JSON_TOKEN_COMMA;
     305           0 :     case ':':
     306           0 :       state->pos = (ulong)(pos + 1 - state->json);
     307           0 :       return state->last_tok = JSON_TOKEN_COLON;
     308             : 
     309           0 :     case 'n': // null
     310           0 :       if (pos + 4 <= end_pos && pos[1] == 'u' && pos[2] == 'l' && pos[3] == 'l') {
     311           0 :         state->pos = (ulong)(pos + 4 - state->json);
     312           0 :         return state->last_tok = JSON_TOKEN_NULL;
     313           0 :       }
     314           0 :       return state->last_tok = json_lex_error(state, pos);
     315             : 
     316           0 :     case 't': // true
     317           0 :       if (pos + 4 <= end_pos && pos[1] == 'r' && pos[2] == 'u' && pos[3] == 'e') {
     318           0 :         state->pos = (ulong)(pos + 4 - state->json);
     319           0 :         state->last_bool = 1;
     320           0 :         return state->last_tok = JSON_TOKEN_BOOL;
     321           0 :       }
     322           0 :       return state->last_tok = json_lex_error(state, pos);
     323             : 
     324           0 :     case 'f': // false
     325           0 :       if (pos + 5 <= end_pos && pos[1] == 'a' && pos[2] == 'l' && pos[3] == 's' && pos[4] == 'e') {
     326           0 :         state->pos = (ulong)(pos + 5 - state->json);
     327           0 :         state->last_bool = 0;
     328           0 :         return state->last_tok = JSON_TOKEN_BOOL;
     329           0 :       }
     330           0 :       return state->last_tok = json_lex_error(state, pos);
     331             : 
     332             :       // number
     333           0 :     case '-': case '0': case '1': case '2': case '3': case '4': case '5':
     334           0 :     case '6': case '7': case '8': case '9':
     335           0 :       return state->last_tok = json_lex_parse_number(state, pos);
     336             : 
     337           0 :     case '"': // string
     338           0 :       return state->last_tok = json_lex_parse_string(state, pos);
     339             : 
     340           0 :     default: // Any other character
     341           0 :       return state->last_tok = json_lex_error(state, pos);
     342           0 :     }
     343           0 :   }
     344           0 :   state->pos = (ulong)(pos - state->json);
     345           0 :   return state->last_tok = JSON_TOKEN_END;
     346           0 : }
     347             : 
     348           0 : const char* json_lex_get_text(json_lex_state_t* state, ulong* sz) {
     349           0 :   if (sz != NULL)
     350           0 :     *sz = state->last_str_sz;
     351           0 :   return state->last_str;
     352           0 : }
     353             : 
     354             : // Convert the string to an integer (assuming decimal representation)
     355           0 : long json_lex_as_int(json_lex_state_t* lex) {
     356             :   // Gangster conversion of decimal text to int
     357           0 :   const char* i = lex->last_str;
     358           0 :   const char* i_end = i + lex->last_str_sz;
     359           0 :   int isneg = 0;
     360           0 :   if (i < i_end && *i == '-') {
     361           0 :     isneg = 1;
     362           0 :     i++;
     363           0 :   }
     364           0 :   long n = 0;
     365           0 :   while (i < i_end)
     366           0 :     n = n*10 + (*(i++) - '0');
     367           0 :   return (isneg ? -n : n);
     368           0 : }
     369             : 
     370             : // Convert the string to a float
     371           0 : double json_lex_as_float(json_lex_state_t* lex) {
     372           0 :   return strtod(lex->last_str, NULL);
     373           0 : }
     374             : 
     375             : // Reserve space at the end of the string for additional text. The
     376             : // pointer to the new space is returned (e.g. for memcpy).
     377           0 : static char* json_lex_append_prepare(json_lex_state_t* lex, ulong sz) {
     378             :   // Get the new string size
     379           0 :   ulong new_sz = lex->last_str_sz + sz;
     380             :   // Make sure there is enough room, including a null terminator
     381           0 :   if (new_sz + 1 > lex->last_str_alloc) {
     382             :     // Grow the allocation
     383           0 :     do {
     384           0 :       lex->last_str_alloc <<= 1;
     385           0 :     } while (new_sz + 1 > lex->last_str_alloc);
     386           0 :     char* oldstr = lex->last_str;
     387           0 :     lex->last_str = (char*)fd_scratch_alloc(1, lex->last_str_alloc);
     388             :     // Copy the old content to the new space
     389           0 :     fd_memcpy(lex->last_str, oldstr, lex->last_str_sz);
     390           0 :   }
     391             :   // Stick on a null terminator
     392           0 :   char* res = lex->last_str + lex->last_str_sz;
     393           0 :   res[sz] = '\0';
     394           0 :   lex->last_str_sz = new_sz;
     395           0 :   return res;
     396           0 : }
     397             : 
     398             : // Append a unicode character to the string. The character is
     399             : // converted to UTF-8 encoding.
     400           0 : static void json_lex_append_char(json_lex_state_t* lex, uint ch) {
     401             :   // Encode in UTF-8
     402           0 :   if (ch < 0x80) {
     403           0 :     char* dest = json_lex_append_prepare(lex, 1);
     404           0 :     *dest =     (char)ch;
     405           0 :   } else if (ch < 0x800) {
     406           0 :     char* dest = json_lex_append_prepare(lex, 2);
     407           0 :     *(dest++) = (char)((ch>>6) | 0xC0);
     408           0 :     *dest =     (char)((ch & 0x3F) | 0x80);
     409           0 :   } else if (ch < 0x10000) {
     410           0 :     char* dest = json_lex_append_prepare(lex, 3);
     411           0 :     *(dest++) = (char)((ch>>12) | 0xE0);
     412           0 :     *(dest++) = (char)(((ch>>6) & 0x3F) | 0x80);
     413           0 :     *dest =     (char)((ch & 0x3F) | 0x80);
     414           0 :   } else if (ch < 0x110000) {
     415           0 :     char* dest = json_lex_append_prepare(lex, 4);
     416           0 :     *(dest++) = (char)((ch>>18) | 0xF0);
     417           0 :     *(dest++) = (char)(((ch>>12) & 0x3F) | 0x80);
     418           0 :     *(dest++) = (char)(((ch>>6) & 0x3F) | 0x80);
     419           0 :     *dest =     (char)((ch & 0x3F) | 0x80);
     420           0 :   }
     421           0 : }
     422             : 
     423             : // Replaces the string with the result of a formatted printf.
     424           0 : void json_lex_sprintf(json_lex_state_t* lex, const char* format, ...) {
     425           0 :   va_list ap;
     426           0 :   va_start(ap, format);
     427           0 :   int r = vsnprintf(lex->last_str, lex->last_str_alloc, format, ap);
     428           0 :   va_end(ap);
     429           0 :   if (r >= 0) {
     430           0 :     if ((ulong)r >= lex->last_str_alloc) {
     431             :       /* Try again with more space */
     432           0 :       do {
     433           0 :         lex->last_str_alloc <<= 1;
     434           0 :       } while ((ulong)r + 1U > lex->last_str_alloc);
     435           0 :       lex->last_str = (char*)fd_scratch_alloc(1, lex->last_str_alloc);
     436           0 :       va_list ap;
     437           0 :       va_start(ap, format);
     438           0 :       r = vsnprintf(lex->last_str, lex->last_str_alloc, format, ap);
     439           0 :       va_end(ap);
     440           0 :     }
     441           0 :     lex->last_str_sz = (ulong)r;
     442           0 :   } else {
     443           0 :     lex->last_str_sz = 0;
     444           0 :     lex->last_str[0] = '\0';
     445           0 :   }
     446           0 : }

Generated by: LCOV version 1.14