106 |
107 |
107 return type; |
108 return type; |
108 } |
109 } |
109 |
110 |
110 static CxJsonToken token_create(CxJson *json, bool isstring, size_t start, size_t end) { |
111 static CxJsonToken token_create(CxJson *json, bool isstring, size_t start, size_t end) { |
111 cxmutstr str = cx_mutstrn((char*)json->buffer + start, end - start); |
112 cxmutstr str = cx_mutstrn(json->buffer.space + start, end - start); |
112 bool allocated = false; |
113 bool allocated = false; |
113 if (json->uncompleted.tokentype != CX_JSON_NO_TOKEN) { |
114 if (json->uncompleted.tokentype != CX_JSON_NO_TOKEN) { |
114 allocated = true; |
115 allocated = true; |
115 str = cx_strcat_m(json->uncompleted.content, 1, str); |
116 str = cx_strcat_m(json->uncompleted.content, 1, str); |
116 if (str.ptr == NULL) { |
117 if (str.ptr == NULL) { |
117 return (CxJsonToken){CX_JSON_NO_TOKEN, false, 0, 0}; |
118 return (CxJsonToken){CX_JSON_NO_TOKEN, false, {0, 0}}; |
118 } |
119 } |
119 } |
120 } |
120 json->uncompleted = (CxJsonToken){0}; |
121 json->uncompleted = (CxJsonToken){0}; |
121 CxJsonTokenType ttype; |
122 CxJsonTokenType ttype; |
122 if (isstring) { |
123 if (isstring) { |
130 } |
131 } |
131 if (ttype == CX_JSON_TOKEN_ERROR) { |
132 if (ttype == CX_JSON_TOKEN_ERROR) { |
132 if (allocated) { |
133 if (allocated) { |
133 cx_strfree(&str); |
134 cx_strfree(&str); |
134 } |
135 } |
135 return (CxJsonToken){CX_JSON_TOKEN_ERROR, false, 0, 0}; |
136 return (CxJsonToken){CX_JSON_TOKEN_ERROR, false, {0, 0}}; |
136 } |
137 } |
137 return (CxJsonToken){ttype, allocated, str}; |
138 return (CxJsonToken){ttype, allocated, str}; |
138 } |
139 } |
139 |
140 |
140 static CxJsonTokenType char2ttype(char c) { |
141 static CxJsonTokenType char2ttype(char c) { |
169 return CX_JSON_NO_TOKEN; |
170 return CX_JSON_NO_TOKEN; |
170 } |
171 } |
171 |
172 |
172 static enum cx_json_status token_parse_next(CxJson *json, CxJsonToken *result) { |
173 static enum cx_json_status token_parse_next(CxJson *json, CxJsonToken *result) { |
173 // check if there is data in the buffer |
174 // check if there is data in the buffer |
174 if (json->pos >= json->size) { |
175 if (cxBufferEof(&json->buffer)) { |
175 return json->uncompleted.tokentype == CX_JSON_NO_TOKEN ? |
176 return json->uncompleted.tokentype == CX_JSON_NO_TOKEN ? |
176 CX_JSON_NO_DATA : CX_JSON_INCOMPLETE_DATA; |
177 CX_JSON_NO_DATA : CX_JSON_INCOMPLETE_DATA; |
177 } |
178 } |
178 |
179 |
179 // sanity check |
|
180 if (json->buffer == NULL) { |
|
181 return CX_JSON_NULL_INPUT; |
|
182 } |
|
183 |
|
184 // current token type and start index |
180 // current token type and start index |
185 CxJsonTokenType ttype = json->uncompleted.tokentype; |
181 CxJsonTokenType ttype = json->uncompleted.tokentype; |
186 size_t token_start = json->pos; |
182 size_t token_start = json->buffer.pos; |
187 |
183 |
188 for (size_t i = json->pos; i < json->size; i++) { |
184 for (size_t i = json->buffer.pos; i < json->buffer.size; i++) { |
189 char c = json->buffer[i]; |
185 char c = json->buffer.space[i]; |
190 if (ttype != CX_JSON_TOKEN_STRING) { |
186 if (ttype != CX_JSON_TOKEN_STRING) { |
191 // currently non-string token |
187 // currently non-string token |
192 CxJsonTokenType ctype = char2ttype(c); // start of new token? |
188 CxJsonTokenType ctype = char2ttype(c); // start of new token? |
193 if (ttype == CX_JSON_NO_TOKEN) { |
189 if (ttype == CX_JSON_NO_TOKEN) { |
194 if (ctype == CX_JSON_TOKEN_SPACE) { |
190 if (ctype == CX_JSON_TOKEN_SPACE) { |
197 // begin string |
193 // begin string |
198 ttype = CX_JSON_TOKEN_STRING; |
194 ttype = CX_JSON_TOKEN_STRING; |
199 token_start = i; |
195 token_start = i; |
200 } else if (ctype != CX_JSON_NO_TOKEN) { |
196 } else if (ctype != CX_JSON_NO_TOKEN) { |
201 // single-char token |
197 // single-char token |
202 json->pos = i + 1; |
198 json->buffer.pos = i + 1; |
203 *result = (CxJsonToken){ctype, NULL, 0, 0}; |
199 *result = (CxJsonToken){ctype, NULL, {0, 0}}; |
204 return CX_JSON_NO_ERROR; |
200 return CX_JSON_NO_ERROR; |
205 } else { |
201 } else { |
206 ttype = CX_JSON_TOKEN_LITERAL; // number or literal |
202 ttype = CX_JSON_TOKEN_LITERAL; // number or literal |
207 token_start = i; |
203 token_start = i; |
208 } |
204 } |
228 if (c == '"') { |
224 if (c == '"') { |
229 *result = token_create(json, true, token_start, i + 1); |
225 *result = token_create(json, true, token_start, i + 1); |
230 if (result->tokentype == CX_JSON_NO_TOKEN) { |
226 if (result->tokentype == CX_JSON_NO_TOKEN) { |
231 return CX_JSON_BUFFER_ALLOC_FAILED; |
227 return CX_JSON_BUFFER_ALLOC_FAILED; |
232 } |
228 } |
233 json->pos = i + 1; |
229 json->buffer.pos = i + 1; |
234 return CX_JSON_NO_ERROR; |
230 return CX_JSON_NO_ERROR; |
235 } else if (c == '\\') { |
231 } else if (c == '\\') { |
236 json->tokenizer_escape = true; |
232 json->tokenizer_escape = true; |
237 } |
233 } |
238 } |
234 } |
239 } |
235 } |
240 } |
236 } |
241 |
237 |
242 if (ttype != CX_JSON_NO_TOKEN) { |
238 if (ttype != CX_JSON_NO_TOKEN) { |
243 // uncompleted token |
239 // uncompleted token |
244 size_t uncompeted_len = json->size - token_start; |
240 size_t uncompeted_len = json->buffer.size - token_start; |
245 if (json->uncompleted.tokentype == CX_JSON_NO_TOKEN) { |
241 if (json->uncompleted.tokentype == CX_JSON_NO_TOKEN) { |
246 // current token is uncompleted |
242 // current token is uncompleted |
247 // save current token content |
243 // save current token content |
248 CxJsonToken uncompleted = { |
244 CxJsonToken uncompleted = { |
249 ttype, true, |
245 ttype, true, |
250 cx_strdup(cx_strn(json->buffer + token_start, uncompeted_len)) |
246 cx_strdup(cx_strn(json->buffer.space + token_start, uncompeted_len)) |
251 }; |
247 }; |
252 if (uncompleted.content.ptr == NULL) { |
248 if (uncompleted.content.ptr == NULL) { |
253 return CX_JSON_BUFFER_ALLOC_FAILED; |
249 return CX_JSON_BUFFER_ALLOC_FAILED; |
254 } |
250 } |
255 json->uncompleted = uncompleted; |
251 json->uncompleted = uncompleted; |
256 } else { |
252 } else { |
257 // previously we also had an uncompleted token |
253 // previously we also had an uncompleted token |
258 // combine the uncompleted token with the current token |
254 // combine the uncompleted token with the current token |
259 assert(json->uncompleted.allocated); |
255 assert(json->uncompleted.allocated); |
260 cxmutstr str = cx_strcat_m(json->uncompleted.content, 1, |
256 cxmutstr str = cx_strcat_m(json->uncompleted.content, 1, |
261 cx_strn(json->buffer + token_start, uncompeted_len)); |
257 cx_strn(json->buffer.space + token_start, uncompeted_len)); |
262 if (str.ptr == NULL) { |
258 if (str.ptr == NULL) { |
263 return CX_JSON_BUFFER_ALLOC_FAILED; |
259 return CX_JSON_BUFFER_ALLOC_FAILED; |
264 } |
260 } |
265 json->uncompleted.content = str; |
261 json->uncompleted.content = str; |
266 } |
262 } |
303 return result; |
299 return result; |
304 } |
300 } |
305 |
301 |
306 static int parse_number(cxmutstr str, void *value, bool asint) { |
302 static int parse_number(cxmutstr str, void *value, bool asint) { |
307 char *endptr = NULL; |
303 char *endptr = NULL; |
308 char buf[32]; |
|
309 if (str.length > 30) { |
304 if (str.length > 30) { |
310 return 1; |
305 return 1; |
311 } |
306 } |
312 // TODO: if we can guarantee that we are working on a copied string already, we can avoid this memcpy |
307 // the buffer guarantees that we are working on a copied string |
313 memcpy(buf, str.ptr, str.length); |
308 char c = str.ptr[str.length]; |
314 buf[str.length] = 0; |
309 str.ptr[str.length] = 0; |
315 |
310 |
316 if (asint) { |
311 if (asint) { |
317 long long v = strtoll(buf, &endptr, 10); |
312 long long v = strtoll(str.ptr, &endptr, 10); |
318 *((int64_t*)value) = (int64_t) v; |
313 *((int64_t*)value) = (int64_t) v; |
319 } else { |
314 } else { |
320 // TODO: proper JSON spec number parser |
315 // TODO: proper JSON spec number parser |
321 double v = strtod(buf, &endptr); |
316 double v = strtod(str.ptr, &endptr); |
322 *((double*)value) = v; |
317 *((double*)value) = v; |
323 } |
318 } |
324 |
319 |
325 return (endptr != &buf[str.length]); |
320 // recover from the hack |
|
321 str.ptr[str.length] = c; |
|
322 |
|
323 return endptr != &str.ptr[str.length]; |
326 } |
324 } |
327 |
325 |
328 static CxJsonValue* create_json_value(CxJson *json, CxJsonValueType type) { |
326 static CxJsonValue* create_json_value(CxJson *json, CxJsonValueType type) { |
329 CxJsonValue *v = cxMalloc(json->allocator, sizeof(CxJsonValue)); |
327 CxJsonValue *v = cxMalloc(json->allocator, sizeof(CxJsonValue)); |
330 if (v == NULL) { |
328 if (v == NULL) { |
415 json->states[0] = JP_STATE_VALUE_BEGIN; |
413 json->states[0] = JP_STATE_VALUE_BEGIN; |
416 json->states_size = 1; |
414 json->states_size = 1; |
417 |
415 |
418 json->vbuf = json->vbuf_internal; |
416 json->vbuf = json->vbuf_internal; |
419 json->vbuf_capacity = cx_nmemb(json->vbuf_internal); |
417 json->vbuf_capacity = cx_nmemb(json->vbuf_internal); |
|
418 |
|
419 cxBufferInit(&json->buffer, NULL, 256, NULL, CX_BUFFER_AUTO_EXTEND); |
420 } |
420 } |
421 |
421 |
422 void cxJsonDestroy(CxJson *json) { |
422 void cxJsonDestroy(CxJson *json) { |
|
423 cxBufferDestroy(&json->buffer); |
423 if (json->states != json->states_internal) { |
424 if (json->states != json->states_internal) { |
424 free(json->states); |
425 free(json->states); |
425 } |
426 } |
426 if (json->vbuf != json->vbuf_internal) { |
427 if (json->vbuf != json->vbuf_internal) { |
427 free(json->vbuf); |
428 free(json->vbuf); |
429 cxJsonValueFree(json->parsed); |
430 cxJsonValueFree(json->parsed); |
430 json->parsed = NULL; |
431 json->parsed = NULL; |
431 } |
432 } |
432 |
433 |
433 int cxJsonFilln(CxJson *json, const char *buf, size_t size) { |
434 int cxJsonFilln(CxJson *json, const char *buf, size_t size) { |
434 // TODO: implement rescue buffer like in CxProperties to allow subsequent fills |
435 // we use the UCX buffer to write the data |
435 json->buffer = buf; |
436 // but reset the position immediately to enable parsing |
436 json->size = size; |
437 size_t old_pos = json->buffer.pos; |
437 json->pos = 0; |
438 cxBufferSeek(&json->buffer, 0, SEEK_END); |
438 return 0; |
439 size_t written = cxBufferWrite(buf, 1, size, &json->buffer); |
|
440 if (0 == cxBufferTerminate(&json->buffer)) { |
|
441 written++; |
|
442 } |
|
443 json->buffer.pos = old_pos; |
|
444 return written != size + 1; |
439 } |
445 } |
440 |
446 |
441 static void json_add_state(CxJson *json, int state) { |
447 static void json_add_state(CxJson *json, int state) { |
442 // we have guaranteed the necessary space with cx_array_simple_reserve() |
448 // we have guaranteed the necessary space with cx_array_simple_reserve() |
443 // therefore, we can safely add the state in the simplest way possible |
449 // therefore, we can safely add the state in the simplest way possible |