Path: blob/master/compat/jansson/dump.c
1299 views
/*1* Copyright (c) 2009-2013 Petri Lehtinen <[email protected]>2*3* Jansson is free software; you can redistribute it and/or modify4* it under the terms of the MIT license. See LICENSE for details.5*/67#ifndef _GNU_SOURCE8#define _GNU_SOURCE9#endif1011#include <stdio.h>12#include <stdlib.h>13#include <string.h>14#include <assert.h>1516#include "jansson.h"17#include "jansson_private.h"18#include "strbuffer.h"19#include "utf.h"2021#define MAX_INTEGER_STR_LENGTH 10022#define MAX_REAL_STR_LENGTH 1002324struct object_key {25size_t serial;26const char *key;27};2829static int dump_to_strbuffer(const char *buffer, size_t size, void *data)30{31return strbuffer_append_bytes((strbuffer_t *)data, buffer, size);32}3334static int dump_to_file(const char *buffer, size_t size, void *data)35{36FILE *dest = (FILE *)data;37if(fwrite(buffer, size, 1, dest) != 1)38return -1;39return 0;40}4142/* 32 spaces (the maximum indentation size) */43static const char whitespace[] = " ";4445static int dump_indent(size_t flags, int depth, int space, json_dump_callback_t dump, void *data)46{47if(JSON_INDENT(flags) > 0)48{49int i, ws_count = JSON_INDENT(flags);5051if(dump("\n", 1, data))52return -1;5354for(i = 0; i < depth; i++)55{56if(dump(whitespace, ws_count, data))57return -1;58}59}60else if(space && !(flags & JSON_COMPACT))61{62return dump(" ", 1, data);63}64return 0;65}6667static int dump_string(const char *str, json_dump_callback_t dump, void *data, size_t flags)68{69const char *pos, *end;70int32_t codepoint;7172if(dump("\"", 1, data))73return -1;7475end = pos = str;76while(1)77{78const char *text;79char seq[13];80int length;8182while(*end)83{84end = utf8_iterate(pos, &codepoint);85if(!end)86return -1;8788/* mandatory escape or control char */89if(codepoint == '\\' || codepoint == '"' || codepoint < 0x20)90break;9192/* slash */93if((flags & JSON_ESCAPE_SLASH) && codepoint == '/')94break;9596/* non-ASCII */97if((flags & JSON_ENSURE_ASCII) && codepoint > 0x7F)98break;99100pos = end;101}102103if(pos != str) {104if(dump(str, pos - str, data))105return -1;106}107108if(end == pos)109break;110111/* handle \, /, ", and control codes */112length = 2;113switch(codepoint)114{115case '\\': text = "\\\\"; break;116case '\"': text = "\\\""; break;117case '\b': text = "\\b"; break;118case '\f': text = "\\f"; break;119case '\n': text = "\\n"; break;120case '\r': text = "\\r"; break;121case '\t': text = "\\t"; break;122case '/': text = "\\/"; break;123default:124{125/* codepoint is in BMP */126if(codepoint < 0x10000)127{128sprintf(seq, "\\u%04x", codepoint);129length = 6;130}131132/* not in BMP -> construct a UTF-16 surrogate pair */133else134{135int32_t first, last;136137codepoint -= 0x10000;138first = 0xD800 | ((codepoint & 0xffc00) >> 10);139last = 0xDC00 | (codepoint & 0x003ff);140141sprintf(seq, "\\u%04x\\u%04x", first, last);142length = 12;143}144145text = seq;146break;147}148}149150if(dump(text, length, data))151return -1;152153str = pos = end;154}155156return dump("\"", 1, data);157}158159static int object_key_compare_keys(const void *key1, const void *key2)160{161return strcmp(((const struct object_key *)key1)->key,162((const struct object_key *)key2)->key);163}164165static int object_key_compare_serials(const void *key1, const void *key2)166{167size_t a = ((const struct object_key *)key1)->serial;168size_t b = ((const struct object_key *)key2)->serial;169170return a < b ? -1 : a == b ? 0 : 1;171}172173static int do_dump(const json_t *json, size_t flags, int depth,174json_dump_callback_t dump, void *data)175{176if(!json)177return -1;178179switch(json_typeof(json)) {180case JSON_NULL:181return dump("null", 4, data);182183case JSON_TRUE:184return dump("true", 4, data);185186case JSON_FALSE:187return dump("false", 5, data);188189case JSON_INTEGER:190{191char buffer[MAX_INTEGER_STR_LENGTH];192int size;193194size = snprintf(buffer, MAX_INTEGER_STR_LENGTH,195"%" JSON_INTEGER_FORMAT,196json_integer_value(json));197if(size < 0 || size >= MAX_INTEGER_STR_LENGTH)198return -1;199200return dump(buffer, size, data);201}202203case JSON_REAL:204{205char buffer[MAX_REAL_STR_LENGTH];206int size;207double value = json_real_value(json);208209size = jsonp_dtostr(buffer, MAX_REAL_STR_LENGTH, value);210if(size < 0)211return -1;212213return dump(buffer, size, data);214}215216case JSON_STRING:217return dump_string(json_string_value(json), dump, data, flags);218219case JSON_ARRAY:220{221int i;222int n;223json_array_t *array;224225/* detect circular references */226array = json_to_array(json);227if(array->visited)228goto array_error;229array->visited = 1;230231n = json_array_size(json);232233if(dump("[", 1, data))234goto array_error;235if(n == 0) {236array->visited = 0;237return dump("]", 1, data);238}239if(dump_indent(flags, depth + 1, 0, dump, data))240goto array_error;241242for(i = 0; i < n; ++i) {243if(do_dump(json_array_get(json, i), flags, depth + 1,244dump, data))245goto array_error;246247if(i < n - 1)248{249if(dump(",", 1, data) ||250dump_indent(flags, depth + 1, 1, dump, data))251goto array_error;252}253else254{255if(dump_indent(flags, depth, 0, dump, data))256goto array_error;257}258}259260array->visited = 0;261return dump("]", 1, data);262263array_error:264array->visited = 0;265return -1;266}267268case JSON_OBJECT:269{270json_object_t *object;271void *iter;272const char *separator;273int separator_length;274275if(flags & JSON_COMPACT) {276separator = ":";277separator_length = 1;278}279else {280separator = ": ";281separator_length = 2;282}283284/* detect circular references */285object = json_to_object(json);286if(object->visited)287goto object_error;288object->visited = 1;289290iter = json_object_iter((json_t *)json);291292if(dump("{", 1, data))293goto object_error;294if(!iter) {295object->visited = 0;296return dump("}", 1, data);297}298if(dump_indent(flags, depth + 1, 0, dump, data))299goto object_error;300301if(flags & JSON_SORT_KEYS || flags & JSON_PRESERVE_ORDER)302{303struct object_key *keys;304size_t size, i;305int (*cmp_func)(const void *, const void *);306307size = json_object_size(json);308keys = jsonp_malloc(size * sizeof(struct object_key));309if(!keys)310goto object_error;311312i = 0;313while(iter)314{315keys[i].serial = hashtable_iter_serial(iter);316keys[i].key = json_object_iter_key(iter);317iter = json_object_iter_next((json_t *)json, iter);318i++;319}320assert(i == size);321322if(flags & JSON_SORT_KEYS)323cmp_func = object_key_compare_keys;324else325cmp_func = object_key_compare_serials;326327qsort(keys, size, sizeof(struct object_key), cmp_func);328329for(i = 0; i < size; i++)330{331const char *key;332json_t *value;333334key = keys[i].key;335value = json_object_get(json, key);336assert(value);337338dump_string(key, dump, data, flags);339if(dump(separator, separator_length, data) ||340do_dump(value, flags, depth + 1, dump, data))341{342jsonp_free(keys);343goto object_error;344}345346if(i < size - 1)347{348if(dump(",", 1, data) ||349dump_indent(flags, depth + 1, 1, dump, data))350{351jsonp_free(keys);352goto object_error;353}354}355else356{357if(dump_indent(flags, depth, 0, dump, data))358{359jsonp_free(keys);360goto object_error;361}362}363}364365jsonp_free(keys);366}367else368{369/* Don't sort keys */370371while(iter)372{373void *next = json_object_iter_next((json_t *)json, iter);374375dump_string(json_object_iter_key(iter), dump, data, flags);376if(dump(separator, separator_length, data) ||377do_dump(json_object_iter_value(iter), flags, depth + 1,378dump, data))379goto object_error;380381if(next)382{383if(dump(",", 1, data) ||384dump_indent(flags, depth + 1, 1, dump, data))385goto object_error;386}387else388{389if(dump_indent(flags, depth, 0, dump, data))390goto object_error;391}392393iter = next;394}395}396397object->visited = 0;398return dump("}", 1, data);399400object_error:401object->visited = 0;402return -1;403}404405default:406/* not reached */407return -1;408}409}410411char *json_dumps(const json_t *json, size_t flags)412{413strbuffer_t strbuff;414char *result;415416if(strbuffer_init(&strbuff))417return NULL;418419if(json_dump_callback(json, dump_to_strbuffer, (void *)&strbuff, flags))420result = NULL;421else422result = jsonp_strdup(strbuffer_value(&strbuff));423424strbuffer_close(&strbuff);425return result;426}427428int json_dumpf(const json_t *json, FILE *output, size_t flags)429{430return json_dump_callback(json, dump_to_file, (void *)output, flags);431}432433int json_dump_file(const json_t *json, const char *path, size_t flags)434{435int result;436437FILE *output = fopen(path, "w");438if(!output)439return -1;440441result = json_dumpf(json, output, flags);442443fclose(output);444return result;445}446447int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags)448{449if(!(flags & JSON_ENCODE_ANY)) {450if(!json_is_array(json) && !json_is_object(json))451return -1;452}453454return do_dump(json, flags, 0, callback, data);455}456457458