blob: 41faaf7b3e4394db89a22ab4cb0cb7d5b44fa509 [file] [log] [blame]
Leandro Pereirafb192c52017-02-16 15:51:31 -08001/*
2 * Copyright (c) 2017 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7#include <assert.h>
8#include <ctype.h>
9#include <errno.h>
10#include <limits.h>
Leandro Pereirab9b1c182017-03-14 16:50:33 -070011#include <misc/printk.h>
Leandro Pereirafb192c52017-02-16 15:51:31 -080012#include <stdbool.h>
Kumar Gala78908162017-04-19 10:32:08 -050013#include <zephyr/types.h>
Leandro Pereirafb192c52017-02-16 15:51:31 -080014#include <stdlib.h>
15#include <string.h>
16
17#include "json.h"
18
19struct token {
20 enum json_tokens type;
21 char *start;
22 char *end;
23};
24
25struct lexer {
26 void *(*state)(struct lexer *lexer);
Kumar Gala21309ea2017-04-14 10:48:42 -050027 unsigned char *start;
28 unsigned char *pos;
29 unsigned char *end;
Leandro Pereirafb192c52017-02-16 15:51:31 -080030 struct token token;
31};
32
33struct json_obj {
34 struct lexer lexer;
35};
36
37struct json_obj_key_value {
38 const char *key;
39 size_t key_len;
40 struct token value;
41};
42
43static bool lexer_consume(struct lexer *lexer, struct token *token,
44 enum json_tokens empty_token)
45{
46 if (lexer->token.type == empty_token) {
47 return false;
48 }
49
50 *token = lexer->token;
51 lexer->token.type = empty_token;
52
53 return true;
54}
55
56static bool lexer_next(struct lexer *lexer, struct token *token)
57{
58 while (lexer->state) {
59 if (lexer_consume(lexer, token, JSON_TOK_NONE)) {
60 return true;
61 }
62
63 lexer->state = lexer->state(lexer);
64 }
65
66 return lexer_consume(lexer, token, JSON_TOK_EOF);
67}
68
69static void *lexer_json(struct lexer *lexer);
70
71static void emit(struct lexer *lexer, enum json_tokens token)
72{
73 lexer->token.type = token;
74 lexer->token.start = lexer->start;
75 lexer->token.end = lexer->pos;
76 lexer->start = lexer->pos;
77}
78
Kumar Gala21309ea2017-04-14 10:48:42 -050079static unsigned char next(struct lexer *lexer)
Leandro Pereirafb192c52017-02-16 15:51:31 -080080{
81 if (lexer->pos >= lexer->end) {
82 lexer->pos = lexer->end + 1;
83
84 return '\0';
85 }
86
87 return *lexer->pos++;
88}
89
90static void ignore(struct lexer *lexer)
91{
92 lexer->start = lexer->pos;
93}
94
95static void backup(struct lexer *lexer)
96{
97 lexer->pos--;
98}
99
Kumar Gala21309ea2017-04-14 10:48:42 -0500100static unsigned char peek(struct lexer *lexer)
Leandro Pereirafb192c52017-02-16 15:51:31 -0800101{
Kumar Gala21309ea2017-04-14 10:48:42 -0500102 unsigned char chr = next(lexer);
Leandro Pereirafb192c52017-02-16 15:51:31 -0800103
104 backup(lexer);
105
106 return chr;
107}
108
109static void *lexer_string(struct lexer *lexer)
110{
111 ignore(lexer);
112
113 while (true) {
Kumar Gala21309ea2017-04-14 10:48:42 -0500114 unsigned char chr = next(lexer);
Leandro Pereirafb192c52017-02-16 15:51:31 -0800115
116 if (chr == '\0') {
117 emit(lexer, JSON_TOK_ERROR);
118 return NULL;
119 }
120
121 if (chr == '\\') {
122 switch (next(lexer)) {
123 case '"':
124 case '\\':
125 case '/':
126 case 'b':
127 case 'f':
128 case 'n':
129 case 'r':
130 case 't':
131 continue;
132 case 'u':
133 if (!isxdigit(next(lexer))) {
134 goto error;
135 }
136
137 if (!isxdigit(next(lexer))) {
138 goto error;
139 }
140
141 if (!isxdigit(next(lexer))) {
142 goto error;
143 }
144
145 if (!isxdigit(next(lexer))) {
146 goto error;
147 }
148
149 break;
150 default:
151 goto error;
152 }
153 }
154
155 if (chr == '"') {
156 backup(lexer);
157 emit(lexer, JSON_TOK_STRING);
158
159 next(lexer);
160 ignore(lexer);
161
162 return lexer_json;
163 }
164 }
165
166error:
167 emit(lexer, JSON_TOK_ERROR);
168 return NULL;
169}
170
Leandro Pereira95ec49c2017-03-16 10:11:00 -0700171static int accept_run(struct lexer *lexer, const char *run)
172{
173 for (; *run; run++) {
174 if (next(lexer) != *run) {
175 return -EINVAL;
176 }
177 }
178
179 return 0;
180}
181
Leandro Pereirafb192c52017-02-16 15:51:31 -0800182static void *lexer_boolean(struct lexer *lexer)
183{
184 backup(lexer);
185
186 switch (next(lexer)) {
187 case 't':
Leandro Pereira95ec49c2017-03-16 10:11:00 -0700188 if (!accept_run(lexer, "rue")) {
189 emit(lexer, JSON_TOK_TRUE);
190 return lexer_json;
Leandro Pereirafb192c52017-02-16 15:51:31 -0800191 }
Leandro Pereira95ec49c2017-03-16 10:11:00 -0700192 break;
Leandro Pereirafb192c52017-02-16 15:51:31 -0800193 case 'f':
Leandro Pereira95ec49c2017-03-16 10:11:00 -0700194 if (!accept_run(lexer, "alse")) {
195 emit(lexer, JSON_TOK_FALSE);
196 return lexer_json;
Leandro Pereirafb192c52017-02-16 15:51:31 -0800197 }
Leandro Pereira95ec49c2017-03-16 10:11:00 -0700198 break;
Leandro Pereirafb192c52017-02-16 15:51:31 -0800199 }
200
Leandro Pereirafb192c52017-02-16 15:51:31 -0800201 emit(lexer, JSON_TOK_ERROR);
202 return NULL;
203}
204
205static void *lexer_null(struct lexer *lexer)
206{
Leandro Pereira95ec49c2017-03-16 10:11:00 -0700207 if (accept_run(lexer, "ull") < 0) {
208 emit(lexer, JSON_TOK_ERROR);
209 return NULL;
Leandro Pereirafb192c52017-02-16 15:51:31 -0800210 }
211
212 emit(lexer, JSON_TOK_NULL);
213 return lexer_json;
Leandro Pereirafb192c52017-02-16 15:51:31 -0800214}
215
Leandro Pereirafb192c52017-02-16 15:51:31 -0800216static void *lexer_number(struct lexer *lexer)
217{
218 while (true) {
Kumar Gala21309ea2017-04-14 10:48:42 -0500219 unsigned char chr = next(lexer);
Leandro Pereirafb192c52017-02-16 15:51:31 -0800220
221 if (isdigit(chr) || chr == '.') {
222 continue;
223 }
224
225 backup(lexer);
226 emit(lexer, JSON_TOK_NUMBER);
227
228 return lexer_json;
229 }
230}
231
232static void *lexer_json(struct lexer *lexer)
233{
234 while (true) {
Kumar Gala21309ea2017-04-14 10:48:42 -0500235 unsigned char chr = next(lexer);
Leandro Pereirafb192c52017-02-16 15:51:31 -0800236
237 switch (chr) {
238 case '\0':
239 emit(lexer, JSON_TOK_EOF);
240 return NULL;
241 case '}':
242 case '{':
Leandro Pereira67ac6f62017-03-21 13:51:15 -0700243 case '[':
244 case ']':
Leandro Pereirafb192c52017-02-16 15:51:31 -0800245 case ',':
246 case ':':
247 emit(lexer, (enum json_tokens)chr);
248 return lexer_json;
249 case '"':
250 return lexer_string;
251 case 'n':
252 return lexer_null;
253 case 't':
254 case 'f':
255 return lexer_boolean;
256 case '-':
257 if (isdigit(peek(lexer))) {
258 return lexer_number;
259 }
260
261 /* fallthrough */
262 default:
263 if (isspace(chr)) {
Leandro Pereira4c7803b2017-03-23 15:15:11 -0700264 ignore(lexer);
Leandro Pereirafb192c52017-02-16 15:51:31 -0800265 continue;
266 }
267
268 if (isdigit(chr)) {
269 return lexer_number;
270 }
271
272 emit(lexer, JSON_TOK_ERROR);
273 return NULL;
274 }
275 }
276}
277
278static void lexer_init(struct lexer *lexer, char *data, size_t len)
279{
280 lexer->state = lexer_json;
281 lexer->start = data;
282 lexer->pos = data;
283 lexer->end = data + len;
284 lexer->token.type = JSON_TOK_NONE;
285}
286
287static int obj_init(struct json_obj *json, char *data, size_t len)
288{
289 struct token token;
290
291 lexer_init(&json->lexer, data, len);
292
293 if (!lexer_next(&json->lexer, &token)) {
294 return -EINVAL;
295 }
296
297 if (token.type != JSON_TOK_OBJECT_START) {
298 return -EINVAL;
299 }
300
301 return 0;
302}
303
Leandro Pereira67ac6f62017-03-21 13:51:15 -0700304static int element_token(enum json_tokens token)
305{
306 switch (token) {
307 case JSON_TOK_OBJECT_START:
308 case JSON_TOK_LIST_START:
309 case JSON_TOK_STRING:
310 case JSON_TOK_NUMBER:
311 case JSON_TOK_TRUE:
312 case JSON_TOK_FALSE:
313 return 0;
314 default:
315 return -EINVAL;
316 }
317}
318
319static int obj_next(struct json_obj *json,
320 struct json_obj_key_value *kv)
Leandro Pereirafb192c52017-02-16 15:51:31 -0800321{
322 struct token token;
323
324 if (!lexer_next(&json->lexer, &token)) {
325 return -EINVAL;
326 }
327
328 /* Match end of object or next key */
329 switch (token.type) {
330 case JSON_TOK_OBJECT_END:
331 kv->key = NULL;
332 kv->key_len = 0;
333 kv->value = token;
334
335 return 0;
336 case JSON_TOK_COMMA:
337 if (!lexer_next(&json->lexer, &token)) {
338 return -EINVAL;
339 }
340
341 if (token.type != JSON_TOK_STRING) {
342 return -EINVAL;
343 }
344
345 /* fallthrough */
346 case JSON_TOK_STRING:
347 kv->key = token.start;
348 kv->key_len = (size_t)(token.end - token.start);
349 break;
350 default:
351 return -EINVAL;
352 }
353
354 /* Match : after key */
355 if (!lexer_next(&json->lexer, &token)) {
356 return -EINVAL;
357 }
358
359 if (token.type != JSON_TOK_COLON) {
360 return -EINVAL;
361 }
362
363 /* Match value */
364 if (!lexer_next(&json->lexer, &kv->value)) {
365 return -EINVAL;
366 }
367
Leandro Pereira67ac6f62017-03-21 13:51:15 -0700368 return element_token(kv->value.type);
369}
370
371static int arr_next(struct json_obj *json, struct token *value)
372{
373 if (!lexer_next(&json->lexer, value)) {
Leandro Pereirafb192c52017-02-16 15:51:31 -0800374 return -EINVAL;
375 }
Leandro Pereira67ac6f62017-03-21 13:51:15 -0700376
377 if (value->type == JSON_TOK_LIST_END) {
378 return 0;
379 }
380
381 if (value->type == JSON_TOK_COMMA) {
382 if (!lexer_next(&json->lexer, value)) {
383 return -EINVAL;
384 }
385 }
386
387 return element_token(value->type);
Leandro Pereirafb192c52017-02-16 15:51:31 -0800388}
389
Kumar Gala9a74ee12017-04-19 13:09:16 -0500390static int decode_num(const struct token *token, s32_t *num)
Leandro Pereirafb192c52017-02-16 15:51:31 -0800391{
392 /* FIXME: strtod() is not available in newlib/minimal libc,
Leandro Pereira844ef672017-03-21 15:49:25 -0700393 * so using strtol() here.
Leandro Pereirafb192c52017-02-16 15:51:31 -0800394 */
395 char *endptr;
396 char prev_end;
397
398 prev_end = *token->end;
399 *token->end = '\0';
400
401 errno = 0;
402 *num = strtol(token->start, &endptr, 10);
403
404 *token->end = prev_end;
405
406 if (errno != 0) {
407 return -errno;
408 }
409
Leandro Pereira844ef672017-03-21 15:49:25 -0700410 if (endptr != token->end) {
Leandro Pereirafb192c52017-02-16 15:51:31 -0800411 return -EINVAL;
412 }
413
414 return 0;
415}
416
417static bool equivalent_types(enum json_tokens type1, enum json_tokens type2)
418{
419 if (type1 == JSON_TOK_TRUE || type1 == JSON_TOK_FALSE) {
420 return type2 == JSON_TOK_TRUE || type2 == JSON_TOK_FALSE;
421 }
422
423 return type1 == type2;
424}
425
Leandro Pereira67ac6f62017-03-21 13:51:15 -0700426static int obj_parse(struct json_obj *obj,
427 const struct json_obj_descr *descr, size_t descr_len,
428 void *val);
429static int arr_parse(struct json_obj *obj,
430 const struct json_obj_descr *elem_descr,
431 size_t max_elements, void *field, void *val);
432
433static int decode_value(struct json_obj *obj,
434 const struct json_obj_descr *descr,
435 struct token *value, void *field, void *val)
Leandro Pereirafb192c52017-02-16 15:51:31 -0800436{
Leandro Pereira67ac6f62017-03-21 13:51:15 -0700437
438 if (!equivalent_types(value->type, descr->type)) {
439 return -EINVAL;
440 }
441
442 switch (descr->type) {
443 case JSON_TOK_OBJECT_START:
444 return obj_parse(obj, descr->sub_descr,
445 descr->sub_descr_len,
446 field);
447 case JSON_TOK_LIST_START:
448 return arr_parse(obj, descr->element_descr,
449 descr->n_elements, field, val);
450 case JSON_TOK_FALSE:
451 case JSON_TOK_TRUE: {
Leandro Pereira262365c2017-03-23 16:32:03 -0700452 bool *v = field;
Leandro Pereira67ac6f62017-03-21 13:51:15 -0700453
Leandro Pereira262365c2017-03-23 16:32:03 -0700454 *v = value->type == JSON_TOK_TRUE;
Leandro Pereira67ac6f62017-03-21 13:51:15 -0700455
456 return 0;
457 }
458 case JSON_TOK_NUMBER: {
Kumar Gala9a74ee12017-04-19 13:09:16 -0500459 s32_t *num = field;
Leandro Pereira67ac6f62017-03-21 13:51:15 -0700460
461 return decode_num(value, num);
462 }
463 case JSON_TOK_STRING: {
464 char **str = field;
465
466 *value->end = '\0';
467 *str = value->start;
468
469 return 0;
470 }
471 default:
472 return -EINVAL;
473 }
474}
475
476static ptrdiff_t get_elem_size(const struct json_obj_descr *descr)
477{
478 switch (descr->type) {
479 case JSON_TOK_NUMBER:
Kumar Gala9a74ee12017-04-19 13:09:16 -0500480 return sizeof(s32_t);
Leandro Pereira67ac6f62017-03-21 13:51:15 -0700481 case JSON_TOK_STRING:
482 return sizeof(char *);
483 case JSON_TOK_TRUE:
484 case JSON_TOK_FALSE:
485 return sizeof(bool);
486 case JSON_TOK_LIST_START:
487 return descr->n_elements * get_elem_size(descr->element_descr);
488 case JSON_TOK_OBJECT_START: {
489 ptrdiff_t total = 0;
490 size_t i;
491
492 for (i = 0; i < descr->sub_descr_len; i++) {
493 total += get_elem_size(&descr->sub_descr[i]);
494 }
495
496 return total;
497 }
498 default:
499 return -EINVAL;
500 }
501}
502
503static int arr_parse(struct json_obj *obj,
504 const struct json_obj_descr *elem_descr,
505 size_t max_elements, void *field, void *val)
506{
507 ptrdiff_t elem_size = get_elem_size(elem_descr);
508 void *last_elem = (char *)field + elem_size * max_elements;
509 size_t *elements = (size_t *)((char *)val + elem_descr->offset);
510 struct token value;
511
512 assert(elem_size > 0);
513
514 *elements = 0;
515
516 while (!arr_next(obj, &value)) {
517 if (value.type == JSON_TOK_LIST_END) {
518 return 0;
519 }
520
521 if (decode_value(obj, elem_descr, &value, field, val) < 0) {
522 return -EINVAL;
523 }
524
525 (*elements)++;
526
527 field = (char *)field + elem_size;
528 if (field == last_elem) {
529 return -ENOSPC;
530 }
531 }
532
533 return -EINVAL;
534}
535
536static int obj_parse(struct json_obj *obj, const struct json_obj_descr *descr,
537 size_t descr_len, void *val)
538{
Leandro Pereirafb192c52017-02-16 15:51:31 -0800539 struct json_obj_key_value kv;
Kumar Gala9a74ee12017-04-19 13:09:16 -0500540 s32_t decoded_fields = 0;
Leandro Pereirafb192c52017-02-16 15:51:31 -0800541 size_t i;
542 int ret;
543
Leandro Pereira67ac6f62017-03-21 13:51:15 -0700544 while (!obj_next(obj, &kv)) {
Leandro Pereirafb192c52017-02-16 15:51:31 -0800545 if (kv.value.type == JSON_TOK_OBJECT_END) {
Leandro Pereira67ac6f62017-03-21 13:51:15 -0700546 return decoded_fields;
Leandro Pereirafb192c52017-02-16 15:51:31 -0800547 }
548
549 for (i = 0; i < descr_len; i++) {
Leandro Pereira67ac6f62017-03-21 13:51:15 -0700550 void *decode_field = (char *)val + descr[i].offset;
Leandro Pereirafb192c52017-02-16 15:51:31 -0800551
552 /* Field has been decoded already, skip */
553 if (decoded_fields & (1 << i)) {
554 continue;
555 }
556
557 /* Check if it's the i-th field */
558 if (kv.key_len != descr[i].field_name_len) {
559 continue;
560 }
561
562 if (memcmp(kv.key, descr[i].field_name,
Leandro Pereira67ac6f62017-03-21 13:51:15 -0700563 descr[i].field_name_len)) {
Leandro Pereirafb192c52017-02-16 15:51:31 -0800564 continue;
565 }
566
Leandro Pereirafb192c52017-02-16 15:51:31 -0800567 /* Store the decoded value */
Leandro Pereira67ac6f62017-03-21 13:51:15 -0700568 ret = decode_value(obj, &descr[i], &kv.value,
569 decode_field, val);
570 if (ret < 0) {
571 return ret;
Leandro Pereirafb192c52017-02-16 15:51:31 -0800572 }
573
574 decoded_fields |= 1<<i;
Leandro Pereira67ac6f62017-03-21 13:51:15 -0700575 break;
Leandro Pereirafb192c52017-02-16 15:51:31 -0800576 }
577 }
578
579 return -EINVAL;
580}
581
Leandro Pereira67ac6f62017-03-21 13:51:15 -0700582int json_obj_parse(char *payload, size_t len,
583 const struct json_obj_descr *descr, size_t descr_len,
584 void *val)
585{
586 struct json_obj obj;
587 int ret;
588
589 assert(descr_len < (sizeof(ret) * CHAR_BIT - 1));
590
591 ret = obj_init(&obj, payload, len);
592 if (ret < 0) {
593 return ret;
594 }
595
596 return obj_parse(&obj, descr, descr_len, val);
597}
598
Kumar Gala9a74ee12017-04-19 13:09:16 -0500599static u8_t escape_as(u8_t chr)
Leandro Pereira122cdb32017-03-21 15:31:59 -0700600{
601 switch (chr) {
602 case '"':
603 return '"';
604 case '\\':
605 return '\\';
606 case '\b':
607 return 'b';
608 case '\f':
609 return 'f';
610 case '\n':
611 return 'n';
612 case '\r':
613 return 'r';
614 case '\t':
615 return 't';
616 }
617
618 return 0;
619}
Leandro Pereirafb192c52017-02-16 15:51:31 -0800620
Leandro Pereirab9b1c182017-03-14 16:50:33 -0700621static int json_escape_internal(const char *str,
622 json_append_bytes_t append_bytes,
623 void *data)
Leandro Pereirafb192c52017-02-16 15:51:31 -0800624{
Leandro Pereira122cdb32017-03-21 15:31:59 -0700625 const char *cur;
Leandro Pereirab9b1c182017-03-14 16:50:33 -0700626 int ret = 0;
Leandro Pereirafb192c52017-02-16 15:51:31 -0800627
Leandro Pereirab9b1c182017-03-14 16:50:33 -0700628 for (cur = str; ret == 0 && *cur; cur++) {
Kumar Gala9a74ee12017-04-19 13:09:16 -0500629 u8_t escaped = escape_as(*cur);
Leandro Pereirab9b1c182017-03-14 16:50:33 -0700630
Leandro Pereira122cdb32017-03-21 15:31:59 -0700631 if (escaped) {
Kumar Gala9a74ee12017-04-19 13:09:16 -0500632 u8_t bytes[2] = { '\\', escaped };
Leandro Pereirab9b1c182017-03-14 16:50:33 -0700633
634 ret = append_bytes(bytes, 2, data);
Leandro Pereirafb192c52017-02-16 15:51:31 -0800635 } else {
Leandro Pereirab9b1c182017-03-14 16:50:33 -0700636 ret = append_bytes(cur, 1, data);
Leandro Pereirafb192c52017-02-16 15:51:31 -0800637 }
638 }
639
Leandro Pereirab9b1c182017-03-14 16:50:33 -0700640 return ret;
641}
642
Leandro Pereirafb192c52017-02-16 15:51:31 -0800643size_t json_calc_escaped_len(const char *str, size_t len)
644{
645 size_t escaped_len = len;
646 size_t pos;
647
648 for (pos = 0; pos < len; pos++) {
Leandro Pereira122cdb32017-03-21 15:31:59 -0700649 if (escape_as(str[pos])) {
Leandro Pereirafb192c52017-02-16 15:51:31 -0800650 escaped_len++;
651 }
652 }
653
654 return escaped_len;
655}
656
657ssize_t json_escape(char *str, size_t *len, size_t buf_size)
658{
Marti Bolivar3c0eac32017-05-03 15:57:18 -0400659 char *next; /* Points after next character to escape. */
660 char *dest; /* Points after next place to write escaped character. */
661 size_t escaped_len = json_calc_escaped_len(str, *len);
Leandro Pereirafb192c52017-02-16 15:51:31 -0800662
663 if (escaped_len == *len) {
Marti Bolivar3c0eac32017-05-03 15:57:18 -0400664 /*
665 * If no escape is necessary, there is nothing to do.
Leandro Pereirafb192c52017-02-16 15:51:31 -0800666 */
667 return 0;
668 }
669
670 if (escaped_len >= buf_size) {
671 return -ENOMEM;
672 }
673
Marti Bolivar3c0eac32017-05-03 15:57:18 -0400674 /*
675 * By walking backwards in the buffer from the end positions
676 * of both the original and escaped strings, we avoid using
677 * extra space. Characters in the original string are
678 * overwritten only after they have already been escaped.
679 */
680 str[escaped_len] = '\0';
681 for (next = &str[*len], dest = &str[escaped_len]; next != str;) {
682 char next_c = *(--next);
683 char escape = escape_as(next_c);
684
685 if (escape) {
686 *(--dest) = escape;
687 *(--dest) = '\\';
688 } else {
689 *(--dest) = next_c;
690 }
691 }
692 *len = escaped_len;
693
694 return 0;
Leandro Pereirab9b1c182017-03-14 16:50:33 -0700695}
696
697static int encode(const struct json_obj_descr *descr, const void *val,
698 json_append_bytes_t append_bytes, void *data);
699
700static int arr_encode(const struct json_obj_descr *descr, const void *field,
701 const void *val, json_append_bytes_t append_bytes,
702 void *data)
703{
704 struct json_obj_descr elem_descr = { .type = descr->type };
705 ptrdiff_t elem_size = get_elem_size(descr);
706 size_t n_elem = *(size_t *)((char *)val + descr->offset);
707 size_t i;
708 int ret;
709
710 ret = append_bytes("[", 1, data);
711 if (ret < 0) {
712 return ret;
713 }
714
715 for (i = 0; i < n_elem; i++) {
716 ret = encode(&elem_descr, field, append_bytes, data);
717 if (ret < 0) {
718 return ret;
719 }
720
721 if (i < n_elem - 1) {
722 ret = append_bytes(",", 1, data);
723 if (ret < 0) {
724 return ret;
725 }
726 }
727
728 field = (char *)field + elem_size;
729 }
730
731 return append_bytes("]", 1, data);
732}
733
734static int str_encode(const char **str, json_append_bytes_t append_bytes,
735 void *data)
736{
737 int ret;
738
739 ret = append_bytes("\"", 1, data);
740 if (ret < 0) {
741 return ret;
742 }
743
744 ret = json_escape_internal(*str, append_bytes, data);
745 if (!ret) {
746 return append_bytes("\"", 1, data);
747 }
748
749 return ret;
750}
751
Kumar Gala9a74ee12017-04-19 13:09:16 -0500752static int num_encode(const s32_t *num, json_append_bytes_t append_bytes,
Leandro Pereirab9b1c182017-03-14 16:50:33 -0700753 void *data)
754{
Kumar Gala9a74ee12017-04-19 13:09:16 -0500755 char buf[3 * sizeof(s32_t)];
Leandro Pereirab9b1c182017-03-14 16:50:33 -0700756 int ret;
757
758 ret = snprintk(buf, sizeof(buf), "%d", *num);
759 if (ret < 0) {
760 return ret;
761 }
762 if (ret >= (int)sizeof(buf)) {
763 return -ENOMEM;
764 }
765
766 return append_bytes(buf, (size_t)ret, data);
767}
768
769static int bool_encode(const bool *value, json_append_bytes_t append_bytes,
770 void *data)
771{
772 if (*value) {
773 return append_bytes("true", 4, data);
774 }
775
776 return append_bytes("false", 5, data);
777}
778
779static int obj_encode(const struct json_obj_descr *descr, size_t descr_len,
780 const void *val, json_append_bytes_t append_bytes,
781 void *data);
782
783static int encode(const struct json_obj_descr *descr, const void *val,
784 json_append_bytes_t append_bytes, void *data)
785{
786 void *ptr = (char *)val + descr->offset;
787
788 switch (descr->type) {
789 case JSON_TOK_FALSE:
790 case JSON_TOK_TRUE:
791 return bool_encode(ptr, append_bytes, data);
792 case JSON_TOK_STRING:
793 return str_encode(ptr, append_bytes, data);
794 case JSON_TOK_LIST_START:
795 return arr_encode(descr->element_descr, ptr,
796 val, append_bytes, data);
797 case JSON_TOK_OBJECT_START:
798 return obj_encode(descr->sub_descr, descr->sub_descr_len,
799 ptr, append_bytes, data);
800 case JSON_TOK_NUMBER:
801 return num_encode(ptr, append_bytes, data);
802 default:
803 return -EINVAL;
804 }
805}
806
807static int obj_encode(const struct json_obj_descr *descr, size_t descr_len,
808 const void *val, json_append_bytes_t append_bytes,
809 void *data)
810{
811 size_t i;
812 int ret;
813
814 ret = append_bytes("{", 1, data);
815 if (ret < 0) {
816 return ret;
817 }
818
819 for (i = 0; i < descr_len; i++) {
820 ret = str_encode((const char **)&descr[i].field_name,
821 append_bytes, data);
822 if (ret < 0) {
823 return ret;
824 }
825
826 ret = append_bytes(":", 1, data);
827 if (ret < 0) {
828 return ret;
829 }
830
831 ret = encode(&descr[i], val, append_bytes, data);
832 if (ret < 0) {
833 return ret;
834 }
835
836 if (i < descr_len - 1) {
837 ret = append_bytes(",", 1, data);
838 if (ret < 0) {
839 return ret;
840 }
841 }
842 }
843
844 return append_bytes("}", 1, data);
845}
846
847int json_obj_encode(const struct json_obj_descr *descr, size_t descr_len,
848 const void *val, json_append_bytes_t append_bytes,
849 void *data)
850{
851 int ret;
852
853 ret = obj_encode(descr, descr_len, val, append_bytes, data);
854 if (ret < 0) {
855 return ret;
856 }
857
858 return append_bytes("", 1, data);
859}
860
Marti Bolivar3c0eac32017-05-03 15:57:18 -0400861struct appender {
862 char *buffer;
863 size_t used;
864 size_t size;
865};
866
867static int append_bytes_to_buf(const u8_t *bytes, size_t len, void *data)
868{
869 struct appender *appender = data;
870
871 if (len > appender->size - appender->used) {
872 return -ENOMEM;
873 }
874
875 memcpy(appender->buffer + appender->used, bytes, len);
876 appender->used += len;
877 appender->buffer[appender->used] = '\0';
878
879 return 0;
880}
881
Leandro Pereirab9b1c182017-03-14 16:50:33 -0700882int json_obj_encode_buf(const struct json_obj_descr *descr, size_t descr_len,
883 const void *val, char *buffer, size_t buf_size)
884{
885 struct appender appender = { .buffer = buffer, .size = buf_size };
886
887 return json_obj_encode(descr, descr_len, val, append_bytes_to_buf,
888 &appender);
889}
890
Kumar Gala9a74ee12017-04-19 13:09:16 -0500891static int measure_bytes(const u8_t *bytes, size_t len, void *data)
Leandro Pereirab9b1c182017-03-14 16:50:33 -0700892{
893 ssize_t *total = data;
894
895 *total += (ssize_t)len;
896
897 return 0;
898}
899
900ssize_t json_calc_encoded_len(const struct json_obj_descr *descr,
901 size_t descr_len, const void *val)
902{
903 ssize_t total = 0;
904 int ret;
905
906 ret = json_obj_encode(descr, descr_len, val, measure_bytes, &total);
907 if (ret < 0) {
908 return ret;
909 }
910
911 return total;
Leandro Pereirafb192c52017-02-16 15:51:31 -0800912}