]> git.zerfleddert.de Git - proxmark3-svn/blob - client/jansson/dump.c
Code improved for less memory
[proxmark3-svn] / client / jansson / dump.c
1 /*
2 * Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
3 *
4 * Jansson is free software; you can redistribute it and/or modify
5 * it under the terms of the MIT license. See LICENSE for details.
6 */
7
8 #ifndef _GNU_SOURCE
9 #define _GNU_SOURCE
10 #endif
11
12 #include "jansson_private.h"
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <assert.h>
18 #ifdef HAVE_UNISTD_H
19 #include <unistd.h>
20 #endif
21
22 #include "jansson.h"
23 #include "strbuffer.h"
24 #include "utf.h"
25
26 #define MAX_INTEGER_STR_LENGTH 100
27 #define MAX_REAL_STR_LENGTH 100
28
29 #define FLAGS_TO_INDENT(f) ((f) & 0x1F)
30 #define FLAGS_TO_PRECISION(f) (((f) >> 11) & 0x1F)
31
32 struct buffer {
33 const size_t size;
34 size_t used;
35 char *data;
36 };
37
38 static int dump_to_strbuffer(const char *buffer, size_t size, void *data)
39 {
40 return strbuffer_append_bytes((strbuffer_t *)data, buffer, size);
41 }
42
43 static int dump_to_buffer(const char *buffer, size_t size, void *data)
44 {
45 struct buffer *buf = (struct buffer *)data;
46
47 if(buf->used + size <= buf->size)
48 memcpy(&buf->data[buf->used], buffer, size);
49
50 buf->used += size;
51 return 0;
52 }
53
54 static int dump_to_file(const char *buffer, size_t size, void *data)
55 {
56 FILE *dest = (FILE *)data;
57 if(fwrite(buffer, size, 1, dest) != 1)
58 return -1;
59 return 0;
60 }
61
62 static int dump_to_fd(const char *buffer, size_t size, void *data)
63 {
64 #ifdef HAVE_UNISTD_H
65 int *dest = (int *)data;
66 if(write(*dest, buffer, size) == (ssize_t)size)
67 return 0;
68 #endif
69 return -1;
70 }
71
72 /* 32 spaces (the maximum indentation size) */
73 static const char whitespace[] = " ";
74
75 static int dump_indent(size_t flags, int depth, int space, json_dump_callback_t dump, void *data)
76 {
77 if(FLAGS_TO_INDENT(flags) > 0)
78 {
79 unsigned int ws_count = FLAGS_TO_INDENT(flags), n_spaces = depth * ws_count;
80
81 if(dump("\n", 1, data))
82 return -1;
83
84 while(n_spaces > 0)
85 {
86 int cur_n = n_spaces < sizeof whitespace - 1 ? n_spaces : sizeof whitespace - 1;
87
88 if(dump(whitespace, cur_n, data))
89 return -1;
90
91 n_spaces -= cur_n;
92 }
93 }
94 else if(space && !(flags & JSON_COMPACT))
95 {
96 return dump(" ", 1, data);
97 }
98 return 0;
99 }
100
101 static int dump_string(const char *str, size_t len, json_dump_callback_t dump, void *data, size_t flags)
102 {
103 const char *pos, *end, *lim;
104 int32_t codepoint = 0;
105
106 if(dump("\"", 1, data))
107 return -1;
108
109 end = pos = str;
110 lim = str + len;
111 while(1)
112 {
113 const char *text;
114 char seq[13];
115 int length;
116
117 while(end < lim)
118 {
119 end = utf8_iterate(pos, lim - pos, &codepoint);
120 if(!end)
121 return -1;
122
123 /* mandatory escape or control char */
124 if(codepoint == '\\' || codepoint == '"' || codepoint < 0x20)
125 break;
126
127 /* slash */
128 if((flags & JSON_ESCAPE_SLASH) && codepoint == '/')
129 break;
130
131 /* non-ASCII */
132 if((flags & JSON_ENSURE_ASCII) && codepoint > 0x7F)
133 break;
134
135 pos = end;
136 }
137
138 if(pos != str) {
139 if(dump(str, pos - str, data))
140 return -1;
141 }
142
143 if(end == pos)
144 break;
145
146 /* handle \, /, ", and control codes */
147 length = 2;
148 switch(codepoint)
149 {
150 case '\\': text = "\\\\"; break;
151 case '\"': text = "\\\""; break;
152 case '\b': text = "\\b"; break;
153 case '\f': text = "\\f"; break;
154 case '\n': text = "\\n"; break;
155 case '\r': text = "\\r"; break;
156 case '\t': text = "\\t"; break;
157 case '/': text = "\\/"; break;
158 default:
159 {
160 /* codepoint is in BMP */
161 if(codepoint < 0x10000)
162 {
163 snprintf(seq, sizeof(seq), "\\u%04X", (unsigned int)codepoint);
164 length = 6;
165 }
166
167 /* not in BMP -> construct a UTF-16 surrogate pair */
168 else
169 {
170 int32_t first, last;
171
172 codepoint -= 0x10000;
173 first = 0xD800 | ((codepoint & 0xffc00) >> 10);
174 last = 0xDC00 | (codepoint & 0x003ff);
175
176 snprintf(seq, sizeof(seq), "\\u%04X\\u%04X", (unsigned int)first, (unsigned int)last);
177 length = 12;
178 }
179
180 text = seq;
181 break;
182 }
183 }
184
185 if(dump(text, length, data))
186 return -1;
187
188 str = pos = end;
189 }
190
191 return dump("\"", 1, data);
192 }
193
194 static int compare_keys(const void *key1, const void *key2)
195 {
196 return strcmp(*(const char **)key1, *(const char **)key2);
197 }
198
199 static int loop_check(hashtable_t *parents, const json_t *json, char *key, size_t key_size)
200 {
201 snprintf(key, key_size, "%p", json);
202 if (hashtable_get(parents, key))
203 return -1;
204
205 return hashtable_set(parents, key, json_null());
206 }
207
208 static int do_dump(const json_t *json, size_t flags, int depth,
209 hashtable_t *parents, json_dump_callback_t dump, void *data)
210 {
211 int embed = flags & JSON_EMBED;
212
213 flags &= ~JSON_EMBED;
214
215 if(!json)
216 return -1;
217
218 switch(json_typeof(json)) {
219 case JSON_NULL:
220 return dump("null", 4, data);
221
222 case JSON_TRUE:
223 return dump("true", 4, data);
224
225 case JSON_FALSE:
226 return dump("false", 5, data);
227
228 case JSON_INTEGER:
229 {
230 char buffer[MAX_INTEGER_STR_LENGTH];
231 int size;
232
233 size = snprintf(buffer, MAX_INTEGER_STR_LENGTH,
234 "%" JSON_INTEGER_FORMAT,
235 json_integer_value(json));
236 if(size < 0 || size >= MAX_INTEGER_STR_LENGTH)
237 return -1;
238
239 return dump(buffer, size, data);
240 }
241
242 case JSON_REAL:
243 {
244 char buffer[MAX_REAL_STR_LENGTH];
245 int size;
246 double value = json_real_value(json);
247
248 size = jsonp_dtostr(buffer, MAX_REAL_STR_LENGTH, value,
249 FLAGS_TO_PRECISION(flags));
250 if(size < 0)
251 return -1;
252
253 return dump(buffer, size, data);
254 }
255
256 case JSON_STRING:
257 return dump_string(json_string_value(json), json_string_length(json), dump, data, flags);
258
259 case JSON_ARRAY:
260 {
261 size_t n;
262 size_t i;
263 /* Space for "0x", double the sizeof a pointer for the hex and a terminator. */
264 char key[2 + (sizeof(json) * 2) + 1];
265
266 /* detect circular references */
267 if (loop_check(parents, json, key, sizeof(key)))
268 return -1;
269
270 n = json_array_size(json);
271
272 if(!embed && dump("[", 1, data))
273 return -1;
274 if(n == 0) {
275 hashtable_del(parents, key);
276 return embed ? 0 : dump("]", 1, data);
277 }
278 if(dump_indent(flags, depth + 1, 0, dump, data))
279 return -1;
280
281 for(i = 0; i < n; ++i) {
282 if(do_dump(json_array_get(json, i), flags, depth + 1,
283 parents, dump, data))
284 return -1;
285
286 if(i < n - 1)
287 {
288 if(dump(",", 1, data) ||
289 dump_indent(flags, depth + 1, 1, dump, data))
290 return -1;
291 }
292 else
293 {
294 if(dump_indent(flags, depth, 0, dump, data))
295 return -1;
296 }
297 }
298
299 hashtable_del(parents, key);
300 return embed ? 0 : dump("]", 1, data);
301 }
302
303 case JSON_OBJECT:
304 {
305 void *iter;
306 const char *separator;
307 int separator_length;
308 /* Space for "0x", double the sizeof a pointer for the hex and a terminator. */
309 char loop_key[2 + (sizeof(json) * 2) + 1];
310
311 if(flags & JSON_COMPACT) {
312 separator = ":";
313 separator_length = 1;
314 }
315 else {
316 separator = ": ";
317 separator_length = 2;
318 }
319
320 /* detect circular references */
321 if (loop_check(parents, json, loop_key, sizeof(loop_key)))
322 return -1;
323
324 iter = json_object_iter((json_t *)json);
325
326 if(!embed && dump("{", 1, data))
327 return -1;
328 if(!iter) {
329 hashtable_del(parents, loop_key);
330 return embed ? 0 : dump("}", 1, data);
331 }
332 if(dump_indent(flags, depth + 1, 0, dump, data))
333 return -1;
334
335 if(flags & JSON_SORT_KEYS)
336 {
337 const char **keys;
338 size_t size, i;
339
340 size = json_object_size(json);
341 keys = jsonp_malloc(size * sizeof(const char *));
342 if(!keys)
343 return -1;
344
345 i = 0;
346 while(iter)
347 {
348 keys[i] = json_object_iter_key(iter);
349 iter = json_object_iter_next((json_t *)json, iter);
350 i++;
351 }
352 assert(i == size);
353
354 qsort(keys, size, sizeof(const char *), compare_keys);
355
356 for(i = 0; i < size; i++)
357 {
358 const char *key;
359 json_t *value;
360
361 key = keys[i];
362 value = json_object_get(json, key);
363 assert(value);
364
365 dump_string(key, strlen(key), dump, data, flags);
366 if(dump(separator, separator_length, data) ||
367 do_dump(value, flags, depth + 1, parents, dump, data))
368 {
369 jsonp_free(keys);
370 return -1;
371 }
372
373 if(i < size - 1)
374 {
375 if(dump(",", 1, data) ||
376 dump_indent(flags, depth + 1, 1, dump, data))
377 {
378 jsonp_free(keys);
379 return -1;
380 }
381 }
382 else
383 {
384 if(dump_indent(flags, depth, 0, dump, data))
385 {
386 jsonp_free(keys);
387 return -1;
388 }
389 }
390 }
391
392 jsonp_free(keys);
393 }
394 else
395 {
396 /* Don't sort keys */
397
398 while(iter)
399 {
400 void *next = json_object_iter_next((json_t *)json, iter);
401 const char *key = json_object_iter_key(iter);
402
403 dump_string(key, strlen(key), dump, data, flags);
404 if(dump(separator, separator_length, data) ||
405 do_dump(json_object_iter_value(iter), flags, depth + 1,
406 parents, dump, data))
407 return -1;
408
409 if(next)
410 {
411 if(dump(",", 1, data) ||
412 dump_indent(flags, depth + 1, 1, dump, data))
413 return -1;
414 }
415 else
416 {
417 if(dump_indent(flags, depth, 0, dump, data))
418 return -1;
419 }
420
421 iter = next;
422 }
423 }
424
425 hashtable_del(parents, loop_key);
426 return embed ? 0 : dump("}", 1, data);
427 }
428
429 default:
430 /* not reached */
431 return -1;
432 }
433 }
434
435 char *json_dumps(const json_t *json, size_t flags)
436 {
437 strbuffer_t strbuff;
438 char *result;
439
440 if(strbuffer_init(&strbuff))
441 return NULL;
442
443 if(json_dump_callback(json, dump_to_strbuffer, (void *)&strbuff, flags))
444 result = NULL;
445 else
446 result = jsonp_strdup(strbuffer_value(&strbuff));
447
448 strbuffer_close(&strbuff);
449 return result;
450 }
451
452 size_t json_dumpb(const json_t *json, char *buffer, size_t size, size_t flags)
453 {
454 struct buffer buf = { size, 0, buffer };
455
456 if(json_dump_callback(json, dump_to_buffer, (void *)&buf, flags))
457 return 0;
458
459 return buf.used;
460 }
461
462 int json_dumpf(const json_t *json, FILE *output, size_t flags)
463 {
464 return json_dump_callback(json, dump_to_file, (void *)output, flags);
465 }
466
467 int json_dumpfd(const json_t *json, int output, size_t flags)
468 {
469 return json_dump_callback(json, dump_to_fd, (void *)&output, flags);
470 }
471
472 int json_dump_file(const json_t *json, const char *path, size_t flags)
473 {
474 int result;
475
476 FILE *output = fopen(path, "w");
477 if(!output)
478 return -1;
479
480 result = json_dumpf(json, output, flags);
481
482 if(fclose(output) != 0)
483 return -1;
484
485 return result;
486 }
487
488 int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags)
489 {
490 int res;
491 hashtable_t parents_set;
492
493 if(!(flags & JSON_ENCODE_ANY)) {
494 if(!json_is_array(json) && !json_is_object(json))
495 return -1;
496 }
497
498 if (hashtable_init(&parents_set))
499 return -1;
500 res = do_dump(json, flags, 0, &parents_set, callback, data);
501 hashtable_close(&parents_set);
502
503 return res;
504 }
Impressum, Datenschutz