37f5a40d2fb69ca1cfc513274919fe32788df6c7
[jansson.git] / src / dump.c
1 /*
2  * Copyright (c) 2009-2011 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 #define _GNU_SOURCE
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <assert.h>
13
14 #include <jansson.h>
15 #include "jansson_private.h"
16 #include "strbuffer.h"
17 #include "utf.h"
18
19 #define MAX_INTEGER_STR_LENGTH  100
20 #define MAX_REAL_STR_LENGTH     100
21
22 struct string
23 {
24     char *buffer;
25     int length;
26     int size;
27 };
28
29 static int dump_to_strbuffer(const char *buffer, size_t size, void *data)
30 {
31     return strbuffer_append_bytes((strbuffer_t *)data, buffer, size);
32 }
33
34 static int dump_to_file(const char *buffer, size_t size, void *data)
35 {
36     FILE *dest = (FILE *)data;
37     if(fwrite(buffer, size, 1, dest) != 1)
38         return -1;
39     return 0;
40 }
41
42 /* 32 spaces (the maximum indentation size) */
43 static char whitespace[] = "                                ";
44
45 static int dump_indent(size_t flags, int depth, int space, json_dump_callback_t dump, void *data)
46 {
47     if(JSON_INDENT(flags) > 0)
48     {
49         int i, ws_count = JSON_INDENT(flags);
50
51         if(dump("\n", 1, data))
52             return -1;
53
54         for(i = 0; i < depth; i++)
55         {
56             if(dump(whitespace, ws_count, data))
57                 return -1;
58         }
59     }
60     else if(space && !(flags & JSON_COMPACT))
61     {
62         return dump(" ", 1, data);
63     }
64     return 0;
65 }
66
67 static int dump_string(const char *str, int ascii, json_dump_callback_t dump, void *data)
68 {
69     const char *pos, *end;
70     int32_t codepoint;
71
72     if(dump("\"", 1, data))
73         return -1;
74
75     end = pos = str;
76     while(1)
77     {
78         const char *text;
79         char seq[13];
80         int length;
81
82         while(*end)
83         {
84             end = utf8_iterate(pos, &codepoint);
85             if(!end)
86                 return -1;
87
88             /* mandatory escape or control char */
89             if(codepoint == '\\' || codepoint == '"' || codepoint < 0x20)
90                 break;
91
92             /* non-ASCII */
93             if(ascii && codepoint > 0x7F)
94                 break;
95
96             pos = end;
97         }
98
99         if(pos != str) {
100             if(dump(str, pos - str, data))
101                 return -1;
102         }
103
104         if(end == pos)
105             break;
106
107         /* handle \, ", and control codes */
108         length = 2;
109         switch(codepoint)
110         {
111             case '\\': text = "\\\\"; break;
112             case '\"': text = "\\\""; break;
113             case '\b': text = "\\b"; break;
114             case '\f': text = "\\f"; break;
115             case '\n': text = "\\n"; break;
116             case '\r': text = "\\r"; break;
117             case '\t': text = "\\t"; break;
118             default:
119             {
120                 /* codepoint is in BMP */
121                 if(codepoint < 0x10000)
122                 {
123                     sprintf(seq, "\\u%04x", codepoint);
124                     length = 6;
125                 }
126
127                 /* not in BMP -> construct a UTF-16 surrogate pair */
128                 else
129                 {
130                     int32_t first, last;
131
132                     codepoint -= 0x10000;
133                     first = 0xD800 | ((codepoint & 0xffc00) >> 10);
134                     last = 0xDC00 | (codepoint & 0x003ff);
135
136                     sprintf(seq, "\\u%04x\\u%04x", first, last);
137                     length = 12;
138                 }
139
140                 text = seq;
141                 break;
142             }
143         }
144
145         if(dump(text, length, data))
146             return -1;
147
148         str = pos = end;
149     }
150
151     return dump("\"", 1, data);
152 }
153
154 static int object_key_compare_keys(const void *key1, const void *key2)
155 {
156     return strcmp((*(const object_key_t **)key1)->key,
157                   (*(const object_key_t **)key2)->key);
158 }
159
160 static int object_key_compare_serials(const void *key1, const void *key2)
161 {
162     size_t a = (*(const object_key_t **)key1)->serial;
163     size_t b = (*(const object_key_t **)key2)->serial;
164
165     return a < b ? -1 : a == b ? 0 : 1;
166 }
167
168 static int do_dump(const json_t *json, size_t flags, int depth,
169                    json_dump_callback_t dump, void *data)
170 {
171     int ascii = flags & JSON_ENSURE_ASCII ? 1 : 0;
172
173     switch(json_typeof(json)) {
174         case JSON_NULL:
175             return dump("null", 4, data);
176
177         case JSON_TRUE:
178             return dump("true", 4, data);
179
180         case JSON_FALSE:
181             return dump("false", 5, data);
182
183         case JSON_INTEGER:
184         {
185             char buffer[MAX_INTEGER_STR_LENGTH];
186             int size;
187
188             size = snprintf(buffer, MAX_INTEGER_STR_LENGTH,
189                             "%" JSON_INTEGER_FORMAT,
190                             json_integer_value(json));
191             if(size >= MAX_INTEGER_STR_LENGTH)
192                 return -1;
193
194             return dump(buffer, size, data);
195         }
196
197         case JSON_REAL:
198         {
199             char buffer[MAX_REAL_STR_LENGTH];
200             int size;
201             double value = json_real_value(json);
202
203             size = jsonp_dtostr(buffer, MAX_REAL_STR_LENGTH, value);
204             if(size < 0)
205                 return -1;
206
207             return dump(buffer, size, data);
208         }
209
210         case JSON_STRING:
211             return dump_string(json_string_value(json), ascii, dump, data);
212
213         case JSON_ARRAY:
214         {
215             int i;
216             int n;
217             json_array_t *array;
218
219             /* detect circular references */
220             array = json_to_array(json);
221             if(array->visited)
222                 goto array_error;
223             array->visited = 1;
224
225             n = json_array_size(json);
226
227             if(dump("[", 1, data))
228                 goto array_error;
229             if(n == 0) {
230                 array->visited = 0;
231                 return dump("]", 1, data);
232             }
233             if(dump_indent(flags, depth + 1, 0, dump, data))
234                 goto array_error;
235
236             for(i = 0; i < n; ++i) {
237                 if(do_dump(json_array_get(json, i), flags, depth + 1,
238                            dump, data))
239                     goto array_error;
240
241                 if(i < n - 1)
242                 {
243                     if(dump(",", 1, data) ||
244                        dump_indent(flags, depth + 1, 1, dump, data))
245                         goto array_error;
246                 }
247                 else
248                 {
249                     if(dump_indent(flags, depth, 0, dump, data))
250                         goto array_error;
251                 }
252             }
253
254             array->visited = 0;
255             return dump("]", 1, data);
256
257         array_error:
258             array->visited = 0;
259             return -1;
260         }
261
262         case JSON_OBJECT:
263         {
264             json_object_t *object;
265             void *iter;
266             const char *separator;
267             int separator_length;
268
269             if(flags & JSON_COMPACT) {
270                 separator = ":";
271                 separator_length = 1;
272             }
273             else {
274                 separator = ": ";
275                 separator_length = 2;
276             }
277
278             /* detect circular references */
279             object = json_to_object(json);
280             if(object->visited)
281                 goto object_error;
282             object->visited = 1;
283
284             iter = json_object_iter((json_t *)json);
285
286             if(dump("{", 1, data))
287                 goto object_error;
288             if(!iter) {
289                 object->visited = 0;
290                 return dump("}", 1, data);
291             }
292             if(dump_indent(flags, depth + 1, 0, dump, data))
293                 goto object_error;
294
295             if(flags & JSON_SORT_KEYS || flags & JSON_PRESERVE_ORDER)
296             {
297                 const object_key_t **keys;
298                 size_t size, i;
299                 int (*cmp_func)(const void *, const void *);
300
301                 size = json_object_size(json);
302                 keys = jsonp_malloc(size * sizeof(object_key_t *));
303                 if(!keys)
304                     goto object_error;
305
306                 i = 0;
307                 while(iter)
308                 {
309                     keys[i] = jsonp_object_iter_fullkey(iter);
310                     iter = json_object_iter_next((json_t *)json, iter);
311                     i++;
312                 }
313                 assert(i == size);
314
315                 if(flags & JSON_SORT_KEYS)
316                     cmp_func = object_key_compare_keys;
317                 else
318                     cmp_func = object_key_compare_serials;
319
320                 qsort(keys, size, sizeof(object_key_t *), cmp_func);
321
322                 for(i = 0; i < size; i++)
323                 {
324                     const char *key;
325                     json_t *value;
326
327                     key = keys[i]->key;
328                     value = json_object_get(json, key);
329                     assert(value);
330
331                     dump_string(key, ascii, dump, data);
332                     if(dump(separator, separator_length, data) ||
333                        do_dump(value, flags, depth + 1, dump, data))
334                     {
335                         jsonp_free(keys);
336                         goto object_error;
337                     }
338
339                     if(i < size - 1)
340                     {
341                         if(dump(",", 1, data) ||
342                            dump_indent(flags, depth + 1, 1, dump, data))
343                         {
344                             jsonp_free(keys);
345                             goto object_error;
346                         }
347                     }
348                     else
349                     {
350                         if(dump_indent(flags, depth, 0, dump, data))
351                         {
352                             jsonp_free(keys);
353                             goto object_error;
354                         }
355                     }
356                 }
357
358                 jsonp_free(keys);
359             }
360             else
361             {
362                 /* Don't sort keys */
363
364                 while(iter)
365                 {
366                     void *next = json_object_iter_next((json_t *)json, iter);
367
368                     dump_string(json_object_iter_key(iter), ascii, dump, data);
369                     if(dump(separator, separator_length, data) ||
370                        do_dump(json_object_iter_value(iter), flags, depth + 1,
371                                dump, data))
372                         goto object_error;
373
374                     if(next)
375                     {
376                         if(dump(",", 1, data) ||
377                            dump_indent(flags, depth + 1, 1, dump, data))
378                             goto object_error;
379                     }
380                     else
381                     {
382                         if(dump_indent(flags, depth, 0, dump, data))
383                             goto object_error;
384                     }
385
386                     iter = next;
387                 }
388             }
389
390             object->visited = 0;
391             return dump("}", 1, data);
392
393         object_error:
394             object->visited = 0;
395             return -1;
396         }
397
398         default:
399             /* not reached */
400             return -1;
401     }
402 }
403
404 char *json_dumps(const json_t *json, size_t flags)
405 {
406     strbuffer_t strbuff;
407     char *result;
408
409     if(strbuffer_init(&strbuff))
410         return NULL;
411
412     if(json_dump_callback(json, dump_to_strbuffer, (void *)&strbuff, flags))
413         result = NULL;
414     else
415         result = jsonp_strdup(strbuffer_value(&strbuff));
416
417     strbuffer_close(&strbuff);
418     return result;
419 }
420
421 int json_dumpf(const json_t *json, FILE *output, size_t flags)
422 {
423     return json_dump_callback(json, dump_to_file, (void *)output, flags);
424 }
425
426 int json_dump_file(const json_t *json, const char *path, size_t flags)
427 {
428     int result;
429
430     FILE *output = fopen(path, "w");
431     if(!output)
432         return -1;
433
434     result = json_dumpf(json, output, flags);
435
436     fclose(output);
437     return result;
438 }
439
440 int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags)
441 {
442     if(!(flags & JSON_ENCODE_ANY)) {
443         if(!json_is_array(json) && !json_is_object(json))
444            return -1;
445     }
446
447     return do_dump(json, flags, 0, callback, data);
448 }