2 * Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
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.
12 #include "jansson_private.h"
23 #include "strbuffer.h"
26 #define MAX_INTEGER_STR_LENGTH 100
27 #define MAX_REAL_STR_LENGTH 100
29 #define FLAGS_TO_INDENT(f) ((f) & 0x1F)
30 #define FLAGS_TO_PRECISION(f) (((f) >> 11) & 0x1F)
38 static int dump_to_strbuffer(const char *buffer
, size_t size
, void *data
)
40 return strbuffer_append_bytes((strbuffer_t
*)data
, buffer
, size
);
43 static int dump_to_buffer(const char *buffer
, size_t size
, void *data
)
45 struct buffer
*buf
= (struct buffer
*)data
;
47 if(buf
->used
+ size
<= buf
->size
)
48 memcpy(&buf
->data
[buf
->used
], buffer
, size
);
54 static int dump_to_file(const char *buffer
, size_t size
, void *data
)
56 FILE *dest
= (FILE *)data
;
57 if(fwrite(buffer
, size
, 1, dest
) != 1)
62 static int dump_to_fd(const char *buffer
, size_t size
, void *data
)
64 int *dest
= (int *)data
;
66 if(write(*dest
, buffer
, size
) == (ssize_t
)size
)
72 /* 32 spaces (the maximum indentation size) */
73 static const char whitespace
[] = " ";
75 static int dump_indent(size_t flags
, int depth
, int space
, json_dump_callback_t dump
, void *data
)
77 if(FLAGS_TO_INDENT(flags
) > 0)
79 unsigned int ws_count
= FLAGS_TO_INDENT(flags
), n_spaces
= depth
* ws_count
;
81 if(dump("\n", 1, data
))
86 int cur_n
= n_spaces
< sizeof whitespace
- 1 ? n_spaces
: sizeof whitespace
- 1;
88 if(dump(whitespace
, cur_n
, data
))
94 else if(space
&& !(flags
& JSON_COMPACT
))
96 return dump(" ", 1, data
);
101 static int dump_string(const char *str
, size_t len
, json_dump_callback_t dump
, void *data
, size_t flags
)
103 const char *pos
, *end
, *lim
;
106 if(dump("\"", 1, data
))
119 end
= utf8_iterate(pos
, lim
- pos
, &codepoint
);
123 /* mandatory escape or control char */
124 if(codepoint
== '\\' || codepoint
== '"' || codepoint
< 0x20)
128 if((flags
& JSON_ESCAPE_SLASH
) && codepoint
== '/')
132 if((flags
& JSON_ENSURE_ASCII
) && codepoint
> 0x7F)
139 if(dump(str
, pos
- str
, data
))
146 /* handle \, /, ", and control codes */
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;
160 /* codepoint is in BMP */
161 if(codepoint
< 0x10000)
163 snprintf(seq
, sizeof(seq
), "\\u%04X", (unsigned int)codepoint
);
167 /* not in BMP -> construct a UTF-16 surrogate pair */
172 codepoint
-= 0x10000;
173 first
= 0xD800 | ((codepoint
& 0xffc00) >> 10);
174 last
= 0xDC00 | (codepoint
& 0x003ff);
176 snprintf(seq
, sizeof(seq
), "\\u%04X\\u%04X", (unsigned int)first
, (unsigned int)last
);
185 if(dump(text
, length
, data
))
191 return dump("\"", 1, data
);
194 static int compare_keys(const void *key1
, const void *key2
)
196 return strcmp(*(const char **)key1
, *(const char **)key2
);
199 static int loop_check(hashtable_t
*parents
, const json_t
*json
, char *key
, size_t key_size
)
201 snprintf(key
, key_size
, "%p", json
);
202 if (hashtable_get(parents
, key
))
205 return hashtable_set(parents
, key
, json_null());
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
)
211 int embed
= flags
& JSON_EMBED
;
213 flags
&= ~JSON_EMBED
;
218 switch(json_typeof(json
)) {
220 return dump("null", 4, data
);
223 return dump("true", 4, data
);
226 return dump("false", 5, data
);
230 char buffer
[MAX_INTEGER_STR_LENGTH
];
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
)
239 return dump(buffer
, size
, data
);
244 char buffer
[MAX_REAL_STR_LENGTH
];
246 double value
= json_real_value(json
);
248 size
= jsonp_dtostr(buffer
, MAX_REAL_STR_LENGTH
, value
,
249 FLAGS_TO_PRECISION(flags
));
253 return dump(buffer
, size
, data
);
257 return dump_string(json_string_value(json
), json_string_length(json
), dump
, data
, flags
);
263 /* Space for "0x", double the sizeof a pointer for the hex and a terminator. */
264 char key
[2 + (sizeof(json
) * 2) + 1];
266 /* detect circular references */
267 if (loop_check(parents
, json
, key
, sizeof(key
)))
270 n
= json_array_size(json
);
272 if(!embed
&& dump("[", 1, data
))
275 hashtable_del(parents
, key
);
276 return embed
? 0 : dump("]", 1, data
);
278 if(dump_indent(flags
, depth
+ 1, 0, dump
, data
))
281 for(i
= 0; i
< n
; ++i
) {
282 if(do_dump(json_array_get(json
, i
), flags
, depth
+ 1,
283 parents
, dump
, data
))
288 if(dump(",", 1, data
) ||
289 dump_indent(flags
, depth
+ 1, 1, dump
, data
))
294 if(dump_indent(flags
, depth
, 0, dump
, data
))
299 hashtable_del(parents
, key
);
300 return embed
? 0 : dump("]", 1, data
);
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 key
[2 + (sizeof(json
) * 2) + 1];
311 if(flags
& JSON_COMPACT
) {
313 separator_length
= 1;
317 separator_length
= 2;
320 /* detect circular references */
321 if (loop_check(parents
, json
, key
, sizeof(key
)))
324 iter
= json_object_iter((json_t
*)json
);
326 if(!embed
&& dump("{", 1, data
))
329 hashtable_del(parents
, key
);
330 return embed
? 0 : dump("}", 1, data
);
332 if(dump_indent(flags
, depth
+ 1, 0, dump
, data
))
335 if(flags
& JSON_SORT_KEYS
)
340 size
= json_object_size(json
);
341 keys
= jsonp_malloc(size
* sizeof(const char *));
348 keys
[i
] = json_object_iter_key(iter
);
349 iter
= json_object_iter_next((json_t
*)json
, iter
);
354 qsort(keys
, size
, sizeof(const char *), compare_keys
);
356 for(i
= 0; i
< size
; i
++)
362 value
= json_object_get(json
, key
);
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
))
375 if(dump(",", 1, data
) ||
376 dump_indent(flags
, depth
+ 1, 1, dump
, data
))
384 if(dump_indent(flags
, depth
, 0, dump
, data
))
396 /* Don't sort keys */
400 void *next
= json_object_iter_next((json_t
*)json
, iter
);
401 const char *key
= json_object_iter_key(iter
);
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
))
411 if(dump(",", 1, data
) ||
412 dump_indent(flags
, depth
+ 1, 1, dump
, data
))
417 if(dump_indent(flags
, depth
, 0, dump
, data
))
425 hashtable_del(parents
, key
);
426 return embed
? 0 : dump("}", 1, data
);
435 char *json_dumps(const json_t
*json
, size_t flags
)
440 if(strbuffer_init(&strbuff
))
443 if(json_dump_callback(json
, dump_to_strbuffer
, (void *)&strbuff
, flags
))
446 result
= jsonp_strdup(strbuffer_value(&strbuff
));
448 strbuffer_close(&strbuff
);
452 size_t json_dumpb(const json_t
*json
, char *buffer
, size_t size
, size_t flags
)
454 struct buffer buf
= { size
, 0, buffer
};
456 if(json_dump_callback(json
, dump_to_buffer
, (void *)&buf
, flags
))
462 int json_dumpf(const json_t
*json
, FILE *output
, size_t flags
)
464 return json_dump_callback(json
, dump_to_file
, (void *)output
, flags
);
467 int json_dumpfd(const json_t
*json
, int output
, size_t flags
)
469 return json_dump_callback(json
, dump_to_fd
, (void *)&output
, flags
);
472 int json_dump_file(const json_t
*json
, const char *path
, size_t flags
)
476 FILE *output
= fopen(path
, "w");
480 result
= json_dumpf(json
, output
, flags
);
482 if(fclose(output
) != 0)
488 int json_dump_callback(const json_t
*json
, json_dump_callback_t callback
, void *data
, size_t flags
)
491 hashtable_t parents_set
;
493 if(!(flags
& JSON_ENCODE_ANY
)) {
494 if(!json_is_array(json
) && !json_is_object(json
))
498 if (hashtable_init(&parents_set
))
500 res
= do_dump(json
, flags
, 0, &parents_set
, callback
, data
);
501 hashtable_close(&parents_set
);