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