dump: Optimize indenting
[jansson.git] / src / dump.c
1 /*
2  * Copyright (c) 2009 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 <unistd.h>
13
14 #include <jansson.h>
15 #include "strbuffer.h"
16
17 typedef int (*dump_func)(const char *buffer, int size, void *data);
18
19 struct string
20 {
21     char *buffer;
22     int length;
23     int size;
24 };
25
26 static int dump_to_strbuffer(const char *buffer, int size, void *data)
27 {
28     return strbuffer_append_bytes((strbuffer_t *)data, buffer, size);
29 }
30
31 static int dump_to_file(const char *buffer, int size, void *data)
32 {
33     FILE *dest = (FILE *)data;
34     if(fwrite(buffer, size, 1, dest) != 1)
35         return -1;
36     return 0;
37 }
38
39 /* 256 spaces (the maximum indentation size) */
40 static char whitespace[] = "                                                                                                                                                                                                                                                                ";
41
42 static int dump_indent(uint32_t flags, int depth, dump_func dump, void *data)
43 {
44     if(JSON_INDENT(flags) > 0)
45     {
46         int i, ws_count = JSON_INDENT(flags);
47
48         if(dump("\n", 1, data))
49             return -1;
50
51         for(i = 0; i < depth; i++)
52         {
53             if(dump(whitespace, ws_count, data))
54                 return -1;
55         }
56     }
57     return 0;
58 }
59
60 static int dump_string(const char *str, dump_func dump, void *data)
61 {
62     const char *end;
63
64     if(dump("\"", 1, data))
65         return -1;
66
67     end = str;
68     while(1)
69     {
70         const char *text;
71         char seq[7];
72         int length;
73
74         while(*end && *end != '\\' && *end != '"' && (*end < 0 || *end > 0x1F))
75             end++;
76
77         if(end != str) {
78             if(dump(str, end - str, data))
79                 return -1;
80         }
81
82         if(!*end)
83             break;
84
85         /* handle \, ", and control codes */
86         length = 2;
87         switch(*end)
88         {
89             case '\\': text = "\\\\"; break;
90             case '\"': text = "\\\""; break;
91             case '\b': text = "\\b"; break;
92             case '\f': text = "\\f"; break;
93             case '\n': text = "\\n"; break;
94             case '\r': text = "\\r"; break;
95             case '\t': text = "\\t"; break;
96             default:
97             {
98                 sprintf(seq, "\\u00%02x", *end);
99                 text = seq;
100                 length = 6;
101                 break;
102             }
103         }
104
105         if(dump(text, length, data))
106             return -1;
107
108         end++;
109         str = end;
110     }
111
112     return dump("\"", 1, data);
113 }
114
115 static int do_dump(const json_t *json, uint32_t flags, int depth,
116                    dump_func dump, void *data)
117 {
118     switch(json_typeof(json)) {
119         case JSON_NULL:
120             return dump("null", 4, data);
121
122         case JSON_TRUE:
123             return dump("true", 4, data);
124
125         case JSON_FALSE:
126             return dump("false", 5, data);
127
128         case JSON_INTEGER:
129         {
130             char *buffer;
131             int size, ret;
132
133             size = asprintf(&buffer, "%d", json_integer_value(json));
134             if(size == -1)
135                 return -1;
136
137             ret = dump(buffer, size, data);
138             free(buffer);
139             return ret;
140         }
141
142         case JSON_REAL:
143         {
144             char *buffer;
145             int size, ret;
146
147             size = asprintf(&buffer, "%.17f", json_real_value(json));
148             if(size == -1)
149                 return -1;
150
151             ret = dump(buffer, size, data);
152             free(buffer);
153             return ret;
154         }
155
156         case JSON_STRING:
157             return dump_string(json_string_value(json), dump, data);
158
159         case JSON_ARRAY:
160         {
161             int i;
162             int n = json_array_size(json);
163
164             if(dump("[", 1, data))
165                 return -1;
166             if(n == 0)
167                 return dump("]", 1, data);
168             if(dump_indent(flags, depth + 1, dump, data))
169                 return -1;
170
171             for(i = 0; i < n; ++i) {
172                 if(do_dump(json_array_get(json, i), flags, depth + 1,
173                            dump, data))
174                     return -1;
175
176                 if(i < n - 1)
177                 {
178                     if(dump(",", 1, data) ||
179                        dump_indent(flags, depth + 1, dump, data))
180                         return -1;
181                 }
182                 else
183                 {
184                     if(dump_indent(flags, depth, dump, data))
185                         return -1;
186                 }
187             }
188             return dump("]", 1, data);
189         }
190
191         case JSON_OBJECT:
192         {
193             void *iter = json_object_iter((json_t *)json);
194
195             if(dump("{", 1, data))
196                 return -1;
197             if(!iter)
198                 return dump("}", 1, data);
199             if(dump_indent(flags, depth + 1, dump, data))
200                 return -1;
201
202             while(iter)
203             {
204                 void *next = json_object_iter_next((json_t *)json, iter);
205
206                 dump_string(json_object_iter_key(iter), dump, data);
207                 if(dump(": ", 2, data) ||
208                    do_dump(json_object_iter_value(iter), flags, depth + 1,
209                            dump, data))
210                     return -1;
211
212                 if(next)
213                 {
214                     if(dump(",", 1, data) ||
215                        dump_indent(flags, depth + 1, dump, data))
216                         return -1;
217                 }
218                 else
219                 {
220                     if(dump_indent(flags, depth, dump, data))
221                         return -1;
222                 }
223
224                 iter = next;
225             }
226             return dump("}", 1, data);
227         }
228
229         default:
230             /* not reached */
231             return -1;
232     }
233 }
234
235
236 char *json_dumps(const json_t *json, uint32_t flags)
237 {
238     strbuffer_t strbuff;
239     char *result;
240
241     if(!json_is_array(json) && !json_is_object(json))
242         return NULL;
243
244     if(strbuffer_init(&strbuff))
245       return NULL;
246
247     if(do_dump(json, flags, 0, dump_to_strbuffer, (void *)&strbuff))
248         return NULL;
249
250     if(dump_to_strbuffer("\n", 1, (void *)&strbuff))
251         return NULL;
252
253     result = strdup(strbuffer_value(&strbuff));
254     strbuffer_close(&strbuff);
255
256     return result;
257 }
258
259 int json_dumpf(const json_t *json, FILE *output, uint32_t flags)
260 {
261     if(!json_is_array(json) && !json_is_object(json))
262         return -1;
263
264     if(do_dump(json, flags, 0, dump_to_file, (void *)output))
265         return -1;
266     return dump_to_file("\n", 1, (void *)output);
267 }
268
269 int json_dump_file(const json_t *json, const char *path, uint32_t flags)
270 {
271     int result;
272
273     FILE *output = fopen(path, "w");
274     if(!output)
275         return -1;
276
277     result = json_dumpf(json, output, flags);
278
279     fclose(output);
280     return result;
281 }