Line data Source code
1 : #include <stdio.h>
2 : #include <stdlib.h>
3 : #include <assert.h>
4 : #include "fd_methods.h"
5 : #include "fd_webserver.h"
6 : #include "../../util/fd_util.h"
7 :
8 : // Read the next json lexical token and report any error to the client
9 : #define NEXT_TOKEN \
10 0 : do { \
11 0 : prevpos = lex->pos; \
12 0 : prevtoken = lex->last_tok; \
13 0 : token = json_lex_next_token(lex); \
14 0 : if (token == JSON_TOKEN_ERROR) return 0; \
15 0 : } while (0)
16 :
17 : #define UNNEXT_TOKEN \
18 0 : lex->pos = prevpos; \
19 0 : lex->last_tok = prevtoken;
20 :
21 : // Report a json parsing syntax error
22 : #define SYNTAX_ERROR(format, ...) \
23 0 : do { \
24 0 : json_lex_sprintf(lex, format, __VA_ARGS__); \
25 0 : return 0; \
26 0 : } while (0)
27 :
28 : // Parse a generic json value. The values argument is used for storing
29 : // leaf values for later access. path describes the path through the
30 : // json syntax tree to this value.
31 : int
32 0 : json_values_parse(json_lex_state_t* lex, struct json_values* values, struct json_path* path) {
33 0 : ulong prevpos;
34 0 : long token;
35 0 : long prevtoken;
36 :
37 : // Prepare to update the path to include a new element
38 0 : if (path->len == JSON_MAX_PATH)
39 0 : SYNTAX_ERROR("json value is too nested at position %lu", lex->pos);
40 0 : uint* path_last = &path->elems[path->len ++];
41 :
42 0 : NEXT_TOKEN;
43 0 : switch (token) {
44 0 : case JSON_TOKEN_LBRACE: // Start a new json object
45 0 : do {
46 0 : NEXT_TOKEN;
47 0 : if (token == JSON_TOKEN_RBRACE)
48 0 : break;
49 0 : if (token != JSON_TOKEN_STRING)
50 0 : SYNTAX_ERROR("expected string key at position %lu", prevpos);
51 : // Translate the key string to a known keyword ID. We only allow
52 : // a predetermined set of keys.
53 0 : ulong key_sz;
54 0 : const char* key = json_lex_get_text(lex, &key_sz);
55 0 : long keyid = fd_webserver_json_keyword(key, key_sz);
56 0 : if (keyid == KEYW_UNKNOWN)
57 0 : SYNTAX_ERROR("unrecognized string key at position %lu", prevpos);
58 : // Append to the path
59 0 : *path_last = ((JSON_TOKEN_LBRACE<<16) | (uint)keyid);
60 :
61 0 : NEXT_TOKEN;
62 0 : if (token != JSON_TOKEN_COLON)
63 0 : SYNTAX_ERROR("expected colon at position %lu", prevpos);
64 :
65 : // Recursively parse the inner value
66 0 : if (!json_values_parse(lex, values, path))
67 0 : return 0;
68 :
69 0 : NEXT_TOKEN;
70 0 : if (token == JSON_TOKEN_RBRACE)
71 0 : break;
72 0 : if (token != JSON_TOKEN_COMMA)
73 0 : SYNTAX_ERROR("expected comma at position %lu", prevpos);
74 0 : } while(1);
75 0 : break;
76 :
77 0 : case JSON_TOKEN_LBRACKET: { // Start an array
78 0 : uint i = 0;
79 0 : do {
80 : // Append to the path
81 0 : *path_last = ((JSON_TOKEN_LBRACKET<<16) | i);
82 : // Recursively parse the array element
83 0 : if (!json_values_parse(lex, values, path))
84 0 : return 0;
85 :
86 0 : NEXT_TOKEN;
87 0 : if (token == JSON_TOKEN_RBRACKET)
88 0 : break;
89 0 : if (token != JSON_TOKEN_COMMA)
90 0 : SYNTAX_ERROR("expected comma at position %lu", prevpos);
91 :
92 0 : ++i;
93 0 : } while(1);
94 0 : break;
95 0 : }
96 :
97 0 : case JSON_TOKEN_STRING: {
98 : // Append to the path
99 0 : *path_last = (JSON_TOKEN_STRING<<16);
100 : // Store the leaf value in values, indexed by the current path
101 0 : ulong str_sz;
102 0 : const char* str = json_lex_get_text(lex, &str_sz);
103 0 : json_add_value(values, path, str, str_sz);
104 0 : break;
105 0 : }
106 :
107 0 : case JSON_TOKEN_INTEGER: {
108 : // Append to the path
109 0 : *path_last = (JSON_TOKEN_INTEGER<<16);
110 : // Store the leaf value in values, indexed by the current path
111 0 : long val = json_lex_as_int(lex);
112 0 : json_add_value(values, path, &val, sizeof(val));
113 0 : break;
114 0 : }
115 :
116 0 : case JSON_TOKEN_FLOAT: {
117 : // Append to the path
118 0 : *path_last = (JSON_TOKEN_FLOAT<<16);
119 : // Store the leaf value in values, indexed by the current path
120 0 : double val = json_lex_as_float(lex);
121 0 : json_add_value(values, path, &val, sizeof(val));
122 0 : break;
123 0 : }
124 :
125 0 : case JSON_TOKEN_BOOL:
126 : // Append to the path
127 0 : *path_last = (JSON_TOKEN_BOOL<<16);
128 : // Store the leaf value in values, indexed by the current path
129 0 : json_add_value(values, path, &lex->last_bool, sizeof(lex->last_bool));
130 0 : break;
131 :
132 0 : case JSON_TOKEN_NULL:
133 : // Append to the path
134 0 : *path_last = (JSON_TOKEN_NULL<<16);
135 : // Store the leaf value in values, indexed by the current path
136 0 : json_add_value(values, path, NULL, 0);
137 0 : break;
138 :
139 0 : case JSON_TOKEN_RBRACKET:
140 0 : if (prevtoken == JSON_TOKEN_LBRACKET) {
141 : /* Empty array */
142 0 : UNNEXT_TOKEN;
143 0 : break;
144 0 : }
145 0 : SYNTAX_ERROR("unexpected ']' at position %lu", prevpos);
146 0 : break;
147 :
148 0 : case JSON_TOKEN_RBRACE:
149 0 : if (prevtoken == JSON_TOKEN_LBRACE) {
150 : /* Empty object */
151 0 : UNNEXT_TOKEN;
152 0 : break;
153 0 : }
154 0 : SYNTAX_ERROR("unexpected '}' at position %lu", prevpos);
155 0 : break;
156 :
157 0 : default:
158 0 : SYNTAX_ERROR("expected json value at position %lu", prevpos);
159 0 : }
160 :
161 0 : path->len --;
162 0 : return 1;
163 0 : }
164 :
165 : // Initialize a json_values
166 0 : void json_values_new(struct json_values* values) {
167 0 : values->num_values = 0;
168 0 : values->buf = values->buf_init;
169 0 : values->buf_sz = 0;
170 0 : values->buf_alloc = sizeof(values->buf_init);
171 0 : }
172 :
173 : // Destroy a json_values
174 0 : void json_values_delete(struct json_values* values) {
175 0 : (void)values;
176 0 : }
177 :
178 : // Add a parsed value to a json_values
179 0 : void json_add_value(struct json_values* values, struct json_path* path, const void* data, ulong data_sz) {
180 0 : if (values->num_values == JSON_MAX_PATHS) {
181 : // Ignore when we have too many values. In the actual requests
182 : // that we expect to handle, the number of values is modest.
183 0 : return;
184 0 : }
185 :
186 : // Get the new buffer size after we add the new data (plus null terminator)
187 0 : ulong new_buf_sz = values->buf_sz + data_sz + 1;
188 0 : new_buf_sz = ((new_buf_sz + 7UL) & ~7UL); // 8-byte align
189 0 : if (new_buf_sz > values->buf_alloc) {
190 : // Grow the allocation
191 0 : do {
192 0 : values->buf_alloc <<= 1;
193 0 : } while (new_buf_sz > values->buf_alloc);
194 0 : char* newbuf = (char*)fd_scratch_alloc(1, values->buf_alloc);
195 0 : fd_memcpy(newbuf, values->buf, values->buf_sz);
196 0 : values->buf = newbuf;
197 0 : }
198 :
199 : // Add a new value to the table
200 0 : uint i = values->num_values++;
201 0 : struct json_path* path2 = &values->values[i].path;
202 0 : uint len = path2->len = path->len;
203 0 : for (uint j = 0; j < len; ++j)
204 0 : path2->elems[j] = path->elems[j];
205 : // Copy out the data
206 0 : ulong off = values->values[i].data_offset = values->buf_sz;
207 0 : values->values[i].data_sz = data_sz;
208 0 : fd_memcpy(values->buf + off, data, data_sz);
209 0 : values->buf[off + data_sz] = '\0';
210 0 : values->buf_sz = new_buf_sz;
211 0 : }
212 :
213 : // Retrieve a value at a given path. A NULL is returned if the path
214 : // isn't found
215 0 : const void* json_get_value(struct json_values* values, const uint* path_elems, uint path_sz, ulong* data_sz) {
216 : // Loop through the values
217 0 : for (uint i = 0; i < values->num_values; ++i) {
218 : // Compare paths
219 0 : struct json_path* path = &values->values[i].path;
220 0 : if (path->len == path_sz) {
221 0 : for (uint j = 0; ; ++j) {
222 0 : if (j == path_sz) {
223 0 : *data_sz = values->values[i].data_sz;
224 0 : return values->buf + values->values[i].data_offset;
225 0 : }
226 0 : if (path->elems[j] != path_elems[j])
227 0 : break;
228 0 : }
229 0 : }
230 0 : }
231 : // Not found
232 0 : *data_sz = 0;
233 0 : return NULL;
234 0 : }
235 :
236 0 : const void* json_get_value_multi(struct json_values* values, const uint* path_elems, uint path_sz, ulong* data_sz, uint * pos) {
237 : // Loop through the values
238 0 : for (uint i = *pos; i < values->num_values; ++i) {
239 : // Compare paths
240 0 : struct json_path* path = &values->values[i].path;
241 0 : if (path->len == path_sz) {
242 0 : for (uint j = 0; ; ++j) {
243 0 : if (j == path_sz) {
244 0 : *data_sz = values->values[i].data_sz;
245 0 : *pos = j+1;
246 0 : return values->buf + values->values[i].data_offset;
247 0 : }
248 0 : if (path->elems[j] != path_elems[j])
249 0 : break;
250 0 : }
251 0 : }
252 0 : }
253 : // Not found
254 0 : *data_sz = 0;
255 0 : *pos = values->num_values;
256 0 : return NULL;
257 0 : }
258 :
259 : // Dump the values and paths to stdout
260 0 : void json_values_printout(struct json_values* values) {
261 0 : for (uint i = 0; i < values->num_values; ++i) {
262 0 : struct json_path* path = &values->values[i].path;
263 0 : const char* data = values->buf + values->values[i].data_offset;
264 0 : ulong data_sz = values->values[i].data_sz;
265 0 : for (uint j = 0; j < path->len; ++j) {
266 0 : uint e = path->elems[j];
267 0 : switch (e >> 16U) {
268 0 : case JSON_TOKEN_LBRACE:
269 0 : printf(" (object|%s)", un_fd_webserver_json_keyword(e & 0xffffUL));
270 0 : break;
271 0 : case JSON_TOKEN_LBRACKET:
272 0 : printf(" (array|%lu)", e & 0xffffUL);
273 0 : break;
274 0 : case JSON_TOKEN_STRING:
275 0 : printf(" STRING \"");
276 0 : fwrite(data, 1, data_sz, stdout);
277 0 : printf("\"");
278 0 : break;
279 0 : case JSON_TOKEN_INTEGER:
280 0 : assert(data_sz == sizeof(long));
281 0 : printf(" INT %ld", *(long*)data);
282 0 : break;
283 0 : case JSON_TOKEN_FLOAT:
284 0 : assert(data_sz == sizeof(double));
285 0 : printf(" FLOAT %g", *(double*)data);
286 0 : break;
287 0 : case JSON_TOKEN_BOOL:
288 0 : assert(data_sz == sizeof(int));
289 0 : printf(" BOOL %d", *(int*)data);
290 0 : break;
291 0 : case JSON_TOKEN_NULL:
292 0 : printf(" NULL");
293 0 : break;
294 0 : }
295 0 : }
296 0 : printf("\n");
297 0 : }
298 0 : }
|