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 : }
|