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