diff --git a/doc/apiref.rst b/doc/apiref.rst index d3f6227..9c29c79 100644 --- a/doc/apiref.rst +++ b/doc/apiref.rst @@ -905,6 +905,16 @@ can be ORed together to obtain *flags*. .. versionadded:: 2.4 +``JSON_REAL_PRECISION(n)`` + Output all real numbers with at most *n* digits of precision. The + valid range for *n* is between 0 and 31 (inclusive), and other + values result in an undefined behavior. + + By default, the precision is 17, to correctly and losslessly encode + all IEEE 754 double precision floating point numbers. + + .. versionadded:: 2.7 + The following functions perform the actual JSON encoding. The result is in UTF-8. diff --git a/src/dump.c b/src/dump.c index 7eddd5a..79cb2f2 100644 --- a/src/dump.c +++ b/src/dump.c @@ -22,6 +22,9 @@ #define MAX_INTEGER_STR_LENGTH 100 #define MAX_REAL_STR_LENGTH 100 +#define FLAGS_TO_INDENT(f) ((f) & 0x1F) +#define FLAGS_TO_PRECISION(f) (((f) >> 11) & 0x1F) + struct object_key { size_t serial; const char *key; @@ -45,9 +48,9 @@ static const char whitespace[] = " "; static int dump_indent(size_t flags, int depth, int space, json_dump_callback_t dump, void *data) { - if(JSON_INDENT(flags) > 0) + if(FLAGS_TO_INDENT(flags) > 0) { - int i, ws_count = JSON_INDENT(flags); + int i, ws_count = FLAGS_TO_INDENT(flags); if(dump("\n", 1, data)) return -1; @@ -208,7 +211,8 @@ static int do_dump(const json_t *json, size_t flags, int depth, int size; double value = json_real_value(json); - size = jsonp_dtostr(buffer, MAX_REAL_STR_LENGTH, value); + size = jsonp_dtostr(buffer, MAX_REAL_STR_LENGTH, value, + FLAGS_TO_PRECISION(flags)); if(size < 0) return -1; diff --git a/src/jansson.h b/src/jansson.h index 85215f4..bcb2087 100644 --- a/src/jansson.h +++ b/src/jansson.h @@ -259,13 +259,14 @@ json_t *json_load_callback(json_load_callback_t callback, void *data, size_t fla /* encoding */ -#define JSON_INDENT(n) (n & 0x1F) -#define JSON_COMPACT 0x20 -#define JSON_ENSURE_ASCII 0x40 -#define JSON_SORT_KEYS 0x80 -#define JSON_PRESERVE_ORDER 0x100 -#define JSON_ENCODE_ANY 0x200 -#define JSON_ESCAPE_SLASH 0x400 +#define JSON_INDENT(n) ((n) & 0x1F) +#define JSON_COMPACT 0x20 +#define JSON_ENSURE_ASCII 0x40 +#define JSON_SORT_KEYS 0x80 +#define JSON_PRESERVE_ORDER 0x100 +#define JSON_ENCODE_ANY 0x200 +#define JSON_ESCAPE_SLASH 0x400 +#define JSON_REAL_PRECISION(n) (((n) & 0x1F) << 11) typedef int (*json_dump_callback_t)(const char *buffer, size_t size, void *data); diff --git a/src/jansson_private.h b/src/jansson_private.h index c6f437c..e100726 100644 --- a/src/jansson_private.h +++ b/src/jansson_private.h @@ -81,7 +81,7 @@ void jsonp_error_vset(json_error_t *error, int line, int column, /* Locale independent string<->double conversions */ int jsonp_strtod(strbuffer_t *strbuffer, double *out); -int jsonp_dtostr(char *buffer, size_t size, double value); +int jsonp_dtostr(char *buffer, size_t size, double value, int prec); /* Wrappers for custom memory functions */ void* jsonp_malloc(size_t size); diff --git a/src/strconv.c b/src/strconv.c index 3a70c6f..f6616dc 100644 --- a/src/strconv.c +++ b/src/strconv.c @@ -78,13 +78,16 @@ int jsonp_strtod(strbuffer_t *strbuffer, double *out) return 0; } -int jsonp_dtostr(char *buffer, size_t size, double value) +int jsonp_dtostr(char *buffer, size_t size, double value, int precision) { int ret; char *start, *end; size_t length; - ret = snprintf(buffer, size, "%.17g", value); + if (precision == 0) + precision = 17; + + ret = snprintf(buffer, size, "%.*g", precision, value); if(ret < 0) return -1; diff --git a/test/bin/json_process.c b/test/bin/json_process.c index 724648c..7ae1d06 100644 --- a/test/bin/json_process.c +++ b/test/bin/json_process.c @@ -39,6 +39,7 @@ struct config { int use_env; int have_hashseed; int hashseed; + int precision; } conf; #define l_isspace(c) ((c) == ' ' || (c) == '\n' || (c) == '\r' || (c) == '\t') @@ -108,6 +109,8 @@ static void read_conf(FILE *conffile) conf.preserve_order = atoi(val); if (!strcmp(line, "JSON_SORT_KEYS")) conf.sort_keys = atoi(val); + if (!strcmp(line, "JSON_REAL_PRECISION")) + conf.precision = atoi(val); if (!strcmp(line, "STRIP")) conf.strip = atoi(val); if (!strcmp(line, "HASHSEED")) { @@ -176,11 +179,10 @@ int use_conf(char *test_path) fclose(conffile); } - if (conf.indent < 0 || conf.indent > 255) { + if (conf.indent < 0 || conf.indent > 31) { fprintf(stderr, "invalid value for JSON_INDENT: %d\n", conf.indent); return 2; } - if (conf.indent) flags |= JSON_INDENT(conf.indent); @@ -196,6 +198,14 @@ int use_conf(char *test_path) if (conf.sort_keys) flags |= JSON_SORT_KEYS; + if (conf.precision < 0 || conf.precision > 31) { + fprintf(stderr, "invalid value for JSON_REAL_PRECISION: %d\n", + conf.precision); + return 2; + } + if (conf.precision) + flags |= JSON_REAL_PRECISION(conf.precision); + if (conf.have_hashseed) json_object_seed(conf.hashseed); @@ -245,7 +255,7 @@ static int getenv_int(const char *name) int use_env() { - int indent; + int indent, precision; size_t flags = 0; json_t *json; json_error_t error; @@ -258,11 +268,10 @@ int use_env() #endif indent = getenv_int("JSON_INDENT"); - if(indent < 0 || indent > 255) { + if(indent < 0 || indent > 31) { fprintf(stderr, "invalid value for JSON_INDENT: %d\n", indent); return 2; } - if(indent > 0) flags |= JSON_INDENT(indent); @@ -278,9 +287,19 @@ int use_env() if(getenv_int("JSON_SORT_KEYS")) flags |= JSON_SORT_KEYS; + precision = getenv_int("JSON_REAL_PRECISION"); + if(precision < 0 || precision > 31) { + fprintf(stderr, "invalid value for JSON_REAL_PRECISION: %d\n", + precision); + return 2; + } + if(getenv("HASHSEED")) json_object_seed(getenv_int("HASHSEED")); + if(precision > 0) + flags |= JSON_REAL_PRECISION(precision); + if(getenv_int("STRIP")) { /* Load to memory, strip leading and trailing whitespace */ size_t size = 0, used = 0;