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

Generated by: LCOV version 1.14