PageRenderTime 93ms CodeModel.GetById 17ms app.highlight 34ms RepoModel.GetById 32ms app.codeStats 5ms

/jansson/src/dump.c

http://github.com/nicolasff/webdis
C | 461 lines | 437 code | 16 blank | 8 comment | 15 complexity | 689f39b79ab59041a4ca87b82d3a4e64 MD5 | raw file
  1/*
  2 * Copyright (c) 2009, 2010 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
 22typedef int (*dump_func)(const char *buffer, int size, void *data);
 23
 24struct string
 25{
 26    char *buffer;
 27    int length;
 28    int size;
 29};
 30
 31static int dump_to_strbuffer(const char *buffer, int size, void *data)
 32{
 33    return strbuffer_append_bytes((strbuffer_t *)data, buffer, size);
 34}
 35
 36static int dump_to_file(const char *buffer, int size, void *data)
 37{
 38    FILE *dest = (FILE *)data;
 39    if(fwrite(buffer, size, 1, dest) != 1)
 40        return -1;
 41    return 0;
 42}
 43
 44/* 32 spaces (the maximum indentation size) */
 45static char whitespace[] = "                                ";
 46
 47static int dump_indent(size_t flags, int depth, int space, dump_func dump, void *data)
 48{
 49    if(JSON_INDENT(flags) > 0)
 50    {
 51        int i, ws_count = JSON_INDENT(flags);
 52
 53        if(dump("\n", 1, data))
 54            return -1;
 55
 56        for(i = 0; i < depth; i++)
 57        {
 58            if(dump(whitespace, ws_count, data))
 59                return -1;
 60        }
 61    }
 62    else if(space && !(flags & JSON_COMPACT))
 63    {
 64        return dump(" ", 1, data);
 65    }
 66    return 0;
 67}
 68
 69static int dump_string(const char *str, int ascii, dump_func dump, void *data)
 70{
 71    const char *pos, *end;
 72    int32_t codepoint;
 73
 74    if(dump("\"", 1, data))
 75        return -1;
 76
 77    end = pos = str;
 78    while(1)
 79    {
 80        const char *text;
 81        char seq[13];
 82        int length;
 83
 84        while(*end)
 85        {
 86            end = utf8_iterate(pos, &codepoint);
 87            if(!end)
 88                return -1;
 89
 90            /* mandatory escape or control char */
 91            if(codepoint == '\\' || codepoint == '"' || codepoint < 0x20)
 92                break;
 93
 94            /* non-ASCII */
 95            if(ascii && codepoint > 0x7F)
 96                break;
 97
 98            pos = end;
 99        }
100
101        if(pos != str) {
102            if(dump(str, pos - str, data))
103                return -1;
104        }
105
106        if(end == pos)
107            break;
108
109        /* handle \, ", and control codes */
110        length = 2;
111        switch(codepoint)
112        {
113            case '\\': text = "\\\\"; break;
114            case '\"': text = "\\\""; break;
115            case '\b': text = "\\b"; break;
116            case '\f': text = "\\f"; break;
117            case '\n': text = "\\n"; break;
118            case '\r': text = "\\r"; break;
119            case '\t': text = "\\t"; break;
120            default:
121            {
122                /* codepoint is in BMP */
123                if(codepoint < 0x10000)
124                {
125                    sprintf(seq, "\\u%04x", codepoint);
126                    length = 6;
127                }
128
129                /* not in BMP -> construct a UTF-16 surrogate pair */
130                else
131                {
132                    int32_t first, last;
133
134                    codepoint -= 0x10000;
135                    first = 0xD800 | ((codepoint & 0xffc00) >> 10);
136                    last = 0xDC00 | (codepoint & 0x003ff);
137
138                    sprintf(seq, "\\u%04x\\u%04x", first, last);
139                    length = 12;
140                }
141
142                text = seq;
143                break;
144            }
145        }
146
147        if(dump(text, length, data))
148            return -1;
149
150        str = pos = end;
151    }
152
153    return dump("\"", 1, data);
154}
155
156static int object_key_compare_keys(const void *key1, const void *key2)
157{
158    return strcmp((*(const object_key_t **)key1)->key,
159                  (*(const object_key_t **)key2)->key);
160}
161
162static int object_key_compare_serials(const void *key1, const void *key2)
163{
164    return (*(const object_key_t **)key1)->serial -
165           (*(const object_key_t **)key2)->serial;
166}
167
168static int do_dump(const json_t *json, size_t flags, int depth,
169                   dump_func 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
202            size = snprintf(buffer, MAX_REAL_STR_LENGTH, "%.17g",
203                            json_real_value(json));
204            if(size >= MAX_REAL_STR_LENGTH)
205                return -1;
206
207            /* Make sure there's a dot or 'e' in the output. Otherwise
208               a real is converted to an integer when decoding */
209            if(strchr(buffer, '.') == NULL &&
210               strchr(buffer, 'e') == NULL)
211            {
212                if(size + 2 >= MAX_REAL_STR_LENGTH) {
213                    /* No space to append ".0" */
214                    return -1;
215                }
216                buffer[size] = '.';
217                buffer[size + 1] = '0';
218                size += 2;
219            }
220
221            return dump(buffer, size, data);
222        }
223
224        case JSON_STRING:
225            return dump_string(json_string_value(json), ascii, dump, data);
226
227        case JSON_ARRAY:
228        {
229            int i;
230            int n;
231            json_array_t *array;
232
233            /* detect circular references */
234            array = json_to_array(json);
235            if(array->visited)
236                goto array_error;
237            array->visited = 1;
238
239            n = json_array_size(json);
240
241            if(dump("[", 1, data))
242                goto array_error;
243            if(n == 0) {
244                array->visited = 0;
245                return dump("]", 1, data);
246            }
247            if(dump_indent(flags, depth + 1, 0, dump, data))
248                goto array_error;
249
250            for(i = 0; i < n; ++i) {
251                if(do_dump(json_array_get(json, i), flags, depth + 1,
252                           dump, data))
253                    goto array_error;
254
255                if(i < n - 1)
256                {
257                    if(dump(",", 1, data) ||
258                       dump_indent(flags, depth + 1, 1, dump, data))
259                        goto array_error;
260                }
261                else
262                {
263                    if(dump_indent(flags, depth, 0, dump, data))
264                        goto array_error;
265                }
266            }
267
268            array->visited = 0;
269            return dump("]", 1, data);
270
271        array_error:
272            array->visited = 0;
273            return -1;
274        }
275
276        case JSON_OBJECT:
277        {
278            json_object_t *object;
279            void *iter;
280            const char *separator;
281            int separator_length;
282
283            if(flags & JSON_COMPACT) {
284                separator = ":";
285                separator_length = 1;
286            }
287            else {
288                separator = ": ";
289                separator_length = 2;
290            }
291
292            /* detect circular references */
293            object = json_to_object(json);
294            if(object->visited)
295                goto object_error;
296            object->visited = 1;
297
298            iter = json_object_iter((json_t *)json);
299
300            if(dump("{", 1, data))
301                goto object_error;
302            if(!iter) {
303                object->visited = 0;
304                return dump("}", 1, data);
305            }
306            if(dump_indent(flags, depth + 1, 0, dump, data))
307                goto object_error;
308
309            if(flags & JSON_SORT_KEYS || flags & JSON_PRESERVE_ORDER)
310            {
311                const object_key_t **keys;
312                size_t size, i;
313                int (*cmp_func)(const void *, const void *);
314
315                size = json_object_size(json);
316                keys = malloc(size * sizeof(object_key_t *));
317                if(!keys)
318                    goto object_error;
319
320                i = 0;
321                while(iter)
322                {
323                    keys[i] = jsonp_object_iter_fullkey(iter);
324                    iter = json_object_iter_next((json_t *)json, iter);
325                    i++;
326                }
327                assert(i == size);
328
329                if(flags & JSON_SORT_KEYS)
330                    cmp_func = object_key_compare_keys;
331                else
332                    cmp_func = object_key_compare_serials;
333
334                qsort(keys, size, sizeof(object_key_t *), cmp_func);
335
336                for(i = 0; i < size; i++)
337                {
338                    const char *key;
339                    json_t *value;
340
341                    key = keys[i]->key;
342                    value = json_object_get(json, key);
343                    assert(value);
344
345                    dump_string(key, ascii, dump, data);
346                    if(dump(separator, separator_length, data) ||
347                       do_dump(value, flags, depth + 1, dump, data))
348                    {
349                        free(keys);
350                        goto object_error;
351                    }
352
353                    if(i < size - 1)
354                    {
355                        if(dump(",", 1, data) ||
356                           dump_indent(flags, depth + 1, 1, dump, data))
357                        {
358                            free(keys);
359                            goto object_error;
360                        }
361                    }
362                    else
363                    {
364                        if(dump_indent(flags, depth, 0, dump, data))
365                        {
366                            free(keys);
367                            goto object_error;
368                        }
369                    }
370                }
371
372                free(keys);
373            }
374            else
375            {
376                /* Don't sort keys */
377
378                while(iter)
379                {
380                    void *next = json_object_iter_next((json_t *)json, iter);
381
382                    dump_string(json_object_iter_key(iter), ascii, dump, data);
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
419char *json_dumps(const json_t *json, size_t flags)
420{
421    strbuffer_t strbuff;
422    char *result;
423
424    if(!json_is_array(json) && !json_is_object(json))
425        return NULL;
426
427    if(strbuffer_init(&strbuff))
428        return NULL;
429
430    if(do_dump(json, flags, 0, dump_to_strbuffer, (void *)&strbuff)) {
431        strbuffer_close(&strbuff);
432        return NULL;
433    }
434
435    result = strdup(strbuffer_value(&strbuff));
436    strbuffer_close(&strbuff);
437
438    return result;
439}
440
441int json_dumpf(const json_t *json, FILE *output, size_t flags)
442{
443    if(!json_is_array(json) && !json_is_object(json))
444        return -1;
445
446    return do_dump(json, flags, 0, dump_to_file, (void *)output);
447}
448
449int json_dump_file(const json_t *json, const char *path, size_t flags)
450{
451    int result;
452
453    FILE *output = fopen(path, "w");
454    if(!output)
455        return -1;
456
457    result = json_dumpf(json, output, flags);
458
459    fclose(output);
460    return result;
461}