wrap-json: add packing tool for json
[src/app-framework-binder.git] / src / wrap-json.c
1 /*
2  Copyright (C) 2016, 2017 "IoT.bzh"
3
4  author: José Bollo <jose.bollo@iot.bzh>
5
6  Licensed under the Apache License, Version 2.0 (the "License");
7  you may not use this file except in compliance with the License.
8  You may obtain a copy of the License at
9
10      http://www.apache.org/licenses/LICENSE-2.0
11
12  Unless required by applicable law or agreed to in writing, software
13  distributed under the License is distributed on an "AS IS" BASIS,
14  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  See the License for the specific language governing permissions and
16  limitations under the License.
17 */
18
19 #include <string.h>
20
21 #include "wrap-json.h"
22
23 #define STACKCOUNT 32
24 #define STRCOUNT   8
25
26 enum {
27         wrap_json_pack_error_none,
28         wrap_json_pack_error_null_object,
29         wrap_json_pack_error_truncated,
30         wrap_json_pack_error_internal_error,
31         wrap_json_pack_error_out_of_memory,
32         wrap_json_pack_error_invalid_character,
33         wrap_json_pack_error_too_long,
34         wrap_json_pack_error_too_deep,
35         wrap_json_pack_error_null_spec,
36         wrap_json_pack_error_null_key,
37         wrap_json_pack_error_null_string,
38         _wrap_json_pack_error_count_
39 };
40
41 static const char ignore_all[] = " \t\n\r,:";
42 static const char accept_arr[] = "][{snbiIfoO";
43 static const char accept_key[] = "s}";
44 #define accept_any (&accept_arr[1])
45
46 static const char *pack_errors[_wrap_json_pack_error_count_] =
47 {
48         [wrap_json_pack_error_none] = "unknown error",
49         [wrap_json_pack_error_null_object] = "null object",
50         [wrap_json_pack_error_truncated] = "truncated",
51         [wrap_json_pack_error_internal_error] = "internal error",
52         [wrap_json_pack_error_out_of_memory] = "out of memory",
53         [wrap_json_pack_error_invalid_character] = "invalid character",
54         [wrap_json_pack_error_too_long] = "too long",
55         [wrap_json_pack_error_too_deep] = "too deep",
56         [wrap_json_pack_error_null_spec] = "spec is NULL",
57         [wrap_json_pack_error_null_key] = "key is NULL",
58         [wrap_json_pack_error_null_string] = "string is NULL"
59 };
60
61 int wrap_json_pack_error_position(int rc)
62 {
63         if (rc < 0)
64                 rc = -rc;
65         return (rc >> 4) + 1;
66 }
67
68 int wrap_json_pack_error_code(int rc)
69 {
70         if (rc < 0)
71                 rc = -rc;
72         return rc & 15;
73 }
74
75 const char *wrap_json_pack_error_string(int rc)
76 {
77         rc = wrap_json_pack_error_code(rc);
78         if (rc >= sizeof pack_errors / sizeof *pack_errors)
79                 rc = 0;
80         return pack_errors[rc];
81 }
82
83
84
85 static inline const char *skip(const char *d)
86 {
87         while (*d && strchr(ignore_all, *d))
88                 d++;
89         return d;
90 }
91
92 int wrap_json_vpack(struct json_object **result, const char *desc, va_list args)
93 {
94         int nstr, notnull, nullable, rc;
95         size_t sz, dsz, ssz;
96         char *s;
97         char c;
98         const char *d;
99         char buffer[256];
100         struct { const char *str; size_t sz; } strs[STRCOUNT];
101         struct { struct json_object *cont, *key; const char *acc; char type; } stack[STACKCOUNT], *top;
102         struct json_object *obj;
103
104         ssz = sizeof buffer;
105         s = buffer;
106         top = stack;
107         top->key = NULL;
108         top->cont = NULL;
109         top->acc = accept_any;
110         top->type = 0;
111         if (!desc)
112                 goto null_spec;
113         d = skip(desc);
114         for(;;) {
115                 c = *d;
116                 if (!c)
117                         goto truncated;
118                 if (!strchr(top->acc, c))
119                         goto invalid_character;
120                 d = skip(++d);
121                 switch(c) {
122                 case 's':
123                         nullable = 0;
124                         notnull = 0;
125                         nstr = 0;
126                         sz = 0;
127                         for (;;) {
128                                 strs[nstr].str = va_arg(args, const char*);
129                                 if (strs[nstr].str)
130                                         notnull = 1;
131                                 if (*d == '?') {
132                                         d = skip(++d);
133                                         nullable = 1;
134                                 }
135                                 switch(*d) {
136                                 case '%': strs[nstr].sz = va_arg(args, size_t); d = skip(++d); break;
137                                 case '#': strs[nstr].sz = (size_t)va_arg(args, int); d = skip(++d); break;
138                                 default: strs[nstr].sz = strs[nstr].str ? strlen(strs[nstr].str) : 0; break;
139                                 }
140                                 sz += strs[nstr++].sz;
141                                 if (*d == '?') {
142                                         d = skip(++d);
143                                         nullable = 1;
144                                 }
145                                 if (*d != '+')
146                                         break;
147                                 if (nstr >= STRCOUNT)
148                                         goto too_long;
149                                 d = skip(++d);
150                         }
151                         if (*d == '*')
152                                 nullable = 1;
153                         if (notnull) {
154                                 if (sz > ssz) {
155                                         ssz += ssz;
156                                         if (ssz < sz)
157                                                 ssz = sz;
158                                         s = alloca(sz);
159                                 }
160                                 dsz = sz;
161                                 while (nstr) {
162                                         nstr--;
163                                         dsz -= strs[nstr].sz;
164                                         memcpy(&s[dsz], strs[nstr].str, strs[nstr].sz);
165                                 }
166                                 obj = json_object_new_string_len(s, sz);
167                                 if (!obj)
168                                         goto out_of_memory;
169                         } else if (nullable)
170                                 obj = NULL;
171                         else
172                                 goto null_string;
173                         break;
174                 case 'n':
175                         obj = NULL;
176                         break;
177                 case 'b':
178                         obj = json_object_new_boolean(va_arg(args, int));
179                         if (!obj)
180                                 goto out_of_memory;
181                         break;
182                 case 'i':
183                         obj = json_object_new_int(va_arg(args, int));
184                         if (!obj)
185                                 goto out_of_memory;
186                         break;
187                 case 'I':
188                         obj = json_object_new_int64(va_arg(args, int64_t));
189                         if (!obj)
190                                 goto out_of_memory;
191                         break;
192                 case 'f':
193                         obj = json_object_new_double(va_arg(args, double));
194                         if (!obj)
195                                 goto out_of_memory;
196                         break;
197                 case 'o':
198                 case 'O':
199                         obj = va_arg(args, struct json_object*);
200                         if (*d == '?')
201                                 d = skip(++d);
202                         else if (*d != '*' && !obj)
203                                 goto null_object;
204                         if (c == 'O')
205                                 json_object_get(obj);
206                         break;
207                 case '[':
208                 case '{':
209                         if (++top >= &stack[STACKCOUNT])
210                                 goto too_deep;
211                         top->key = NULL;
212                         if (c == '[') {
213                                 top->type = ']';
214                                 top->acc = accept_arr;
215                                 top->cont = json_object_new_array();
216                         } else {
217                                 top->type = '}';
218                                 top->acc = accept_key;
219                                 top->cont = json_object_new_object();
220                         }
221                         if (!top->cont)
222                                 goto out_of_memory;
223                         continue;
224                 case '}':
225                 case ']':
226                         if (c != top->type || top <= stack)
227                                 goto invalid_character;
228                         obj = (top--)->cont;
229                         if (*d == '*' && !(c == '}' ? json_object_object_length(obj) : json_object_array_length(obj))) {
230                                 json_object_put(obj);
231                                 obj = NULL;
232                         }
233                         break;
234                 default:
235                         goto internal_error;
236                 }
237                 switch (top->type) {
238                 case 0:
239                         if (top != stack)
240                                 goto internal_error;
241                         if (*d)
242                                 goto invalid_character;
243                         *result = obj;
244                         return 0;
245                 case ']':
246                         if (obj || *d != '*')
247                                 json_object_array_add(top->cont, obj);
248                         if (*d == '*')
249                                 d = skip(++d);
250                         break;
251                 case '}':
252                         if (!obj)
253                                 goto null_key;
254                         top->key = obj;
255                         top->acc = accept_any;
256                         top->type = ':';
257                         break;
258                 case ':':
259                         if (obj || *d != '*')
260                                 json_object_object_add(top->cont, json_object_get_string(top->key), obj);
261                         if (*d == '*')
262                                 d = skip(++d);
263                         json_object_put(top->key);
264                         top->key = NULL;
265                         top->acc = accept_key;
266                         top->type = '}';
267                         break;
268                 }
269         }
270
271 null_object:
272         rc = wrap_json_pack_error_null_object;
273         goto error;
274 truncated:
275         rc = wrap_json_pack_error_truncated;
276         goto error;
277 internal_error:
278         rc = wrap_json_pack_error_internal_error;
279         goto error;
280 out_of_memory:
281         rc = wrap_json_pack_error_out_of_memory;
282         goto error;
283 invalid_character:
284         rc = wrap_json_pack_error_invalid_character;
285         goto error;
286 too_long:
287         rc = wrap_json_pack_error_too_long;
288         goto error;
289 too_deep:
290         rc = wrap_json_pack_error_too_deep;
291         goto error;
292 null_spec:
293         rc = wrap_json_pack_error_null_spec;
294         goto error;
295 null_key:
296         rc = wrap_json_pack_error_null_key;
297         goto error;
298 null_string:
299         rc = wrap_json_pack_error_null_string;
300         goto error;
301 error:
302         do {
303                 json_object_put(top->key);
304                 json_object_put(top->cont);
305         } while (--top >= stack);
306         *result = NULL;
307         rc = rc | (int)((d - desc) << 4);
308         return -rc;
309 }
310
311 int wrap_json_pack(struct json_object **result, const char *desc, ...)
312 {
313         int rc;
314         va_list args;
315
316         va_start(args, desc);
317         rc = wrap_json_vpack(result, desc, args);
318         va_end(args);
319         return rc;
320 }
321
322 #if 1
323 #include <stdio.h>
324
325 void T(const char *desc, ...)
326 {
327         int rc;
328         va_list args;
329         struct json_object *result;
330
331         va_start(args, desc);
332         rc = wrap_json_vpack(&result, desc, args);
333         va_end(args);
334         if (!rc) 
335                 printf("  SUCCESS %s\n\n", json_object_to_json_string(result));
336         else
337                 printf("  ERROR[char %d err %d] %s\n\n", wrap_json_pack_error_position(rc), wrap_json_pack_error_code(rc), wrap_json_pack_error_string(rc));
338         json_object_put(result);
339 }
340
341 #define t(...) printf("testing(%s)\n",#__VA_ARGS__); T(__VA_ARGS__);
342
343 int main()
344 {
345         char buffer[4] = {'t', 'e', 's', 't'};
346
347         t("n");
348         t("b", 1);
349         t("b", 0);
350         t("i", 1);
351         t("I", (uint64_t)0x123456789abcdef);
352         t("f", 3.14);
353         t("s", "test");
354         t("s?", "test");
355         t("s?", NULL);
356         t("s#", "test asdf", 4);
357         t("s%", "test asdf", (size_t)4);
358         t("s#", buffer, 4);
359         t("s%", buffer, (size_t)4);
360         t("s++", "te", "st", "ing");
361         t("s#+#+", "test", 1, "test", 2, "test");
362         t("s%+%+", "test", (size_t)1, "test", (size_t)2, "test");
363         t("{}", 1.0);
364         t("[]", 1.0);
365         t("o", json_object_new_int(1));
366         t("o?", json_object_new_int(1));
367         t("o?", NULL);
368         t("O", json_object_new_int(1));
369         t("O?", json_object_new_int(1));
370         t("O?", NULL);
371         t("{s:[]}", "foo");
372         t("{s+#+: []}", "foo", "barbar", 3, "baz");
373         t("{s:s,s:o,s:O}", "a", NULL, "b", NULL, "c", NULL);
374         t("{s:**}", "a", NULL);
375         t("{s:s*,s:o*,s:O*}", "a", NULL, "b", NULL, "c", NULL);
376         t("[i,i,i]", 0, 1, 2);
377         t("[s,o,O]", NULL, NULL, NULL);
378         t("[**]", NULL);
379         t("[s*,o*,O*]", NULL, NULL, NULL);
380         t(" s ", "test");
381         t("[ ]");
382         t("[ i , i,  i ] ", 1, 2, 3);
383         t("{\n\n1");
384         t("[}");
385         t("{]");
386         t("[");
387         t("{");
388         t("[i]a", 42);
389         t("ia", 42);
390         t("s", NULL);
391         t("+", NULL);
392         t(NULL);
393         t("{s:i}", NULL, 1);
394         t("{ {}: s }", "foo");
395         t("{ s: {},  s:[ii{} }", "foo", "bar", 12, 13);
396         t("[[[[[   [[[[[  [[[[ }]]]] ]]]] ]]]]]");
397         return 0;
398 }
399
400 #endif
401
402