| @@ -41,6 +41,18 @@ AC_CHECK_HEADER(inttypes.h,[AC_DEFINE([JSON_C_HAVE_INTTYPES_H],[1],[Public defin | |||||
| AC_C_CONST | AC_C_CONST | ||||
| AC_TYPE_SIZE_T | AC_TYPE_SIZE_T | ||||
| AC_CACHE_CHECK([for __thread support], ac_cv___thread, [dnl | |||||
| AC_LINK_IFELSE([dnl | |||||
| AC_LANG_PROGRAM([[#undef __thread | |||||
| static __thread int a; int foo (int b) { return a + b; }]], | |||||
| [[exit (foo (0));]])], | |||||
| ac_cv___thread=yes, ac_cv___thread=no) | |||||
| ]) | |||||
| AS_IF([test "x$ac_cv___thread" != xno], | |||||
| [AC_DEFINE(HAVE___THREAD, 1, [Have __thread]), | |||||
| AC_DEFINE(SPEC___THREAD, [__thread], [Specifier for __thread])] | |||||
| ) | |||||
| # Checks for library functions. | # Checks for library functions. | ||||
| AC_FUNC_VPRINTF | AC_FUNC_VPRINTF | ||||
| AC_FUNC_MEMCMP | AC_FUNC_MEMCMP | ||||
| @@ -692,6 +692,50 @@ int json_object_set_int64(struct json_object *jso,int64_t new_value){ | |||||
| /* json_object_double */ | /* json_object_double */ | ||||
| #ifdef HAVE___THREAD | |||||
| // i.e. __thread or __declspec(thread) | |||||
| static SPEC___THREAD char *tls_serialization_float_format = NULL; | |||||
| #endif | |||||
| static char *global_serialization_float_format = NULL; | |||||
| int json_c_set_serialization_double_format(const char *double_format, int global_or_thread) | |||||
| { | |||||
| if (global_or_thread == JSON_C_OPTION_GLOBAL) | |||||
| { | |||||
| #ifdef HAVE___THREAD | |||||
| if (tls_serialization_float_format) | |||||
| { | |||||
| free(tls_serialization_float_format); | |||||
| tls_serialization_float_format = NULL; | |||||
| } | |||||
| #endif | |||||
| if (global_serialization_float_format) | |||||
| free(global_serialization_float_format); | |||||
| global_serialization_float_format = double_format ? strdup(double_format) : NULL; | |||||
| } | |||||
| else if (global_or_thread == JSON_C_OPTION_THREAD) | |||||
| { | |||||
| #ifdef HAVE___THREAD | |||||
| if (tls_serialization_float_format) | |||||
| { | |||||
| free(tls_serialization_float_format); | |||||
| tls_serialization_float_format = NULL; | |||||
| } | |||||
| tls_serialization_float_format = double_format ? strdup(double_format) : NULL; | |||||
| #else | |||||
| _set_last_err("json_c_set_option: not compiled with __thread support\n"); | |||||
| return -1; | |||||
| #endif | |||||
| } | |||||
| else | |||||
| { | |||||
| _set_last_err("json_c_set_option: invalid global_or_thread value: %d\n", global_or_thread); | |||||
| return -1; | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| static int json_object_double_to_json_string_format(struct json_object* jso, | static int json_object_double_to_json_string_format(struct json_object* jso, | ||||
| struct printbuf *pb, | struct printbuf *pb, | ||||
| int level, | int level, | ||||
| @@ -712,13 +756,31 @@ static int json_object_double_to_json_string_format(struct json_object* jso, | |||||
| size = snprintf(buf, sizeof(buf), "Infinity"); | size = snprintf(buf, sizeof(buf), "Infinity"); | ||||
| else | else | ||||
| size = snprintf(buf, sizeof(buf), "-Infinity"); | size = snprintf(buf, sizeof(buf), "-Infinity"); | ||||
| else | |||||
| size = snprintf(buf, sizeof(buf), | |||||
| format ? format : | |||||
| (modf(jso->o.c_double, &dummy) == 0) ? "%.17g.0" : "%.17g", | |||||
| jso->o.c_double); | |||||
| if(size < 0 || size >= (int)sizeof(buf)) | |||||
| size = (int)sizeof(buf); | |||||
| else | |||||
| { | |||||
| const char *std_format = "%.17g"; | |||||
| #ifdef HAVE___THREAD | |||||
| if (tls_serialization_float_format) | |||||
| std_format = tls_serialization_float_format; | |||||
| else | |||||
| #endif | |||||
| if (global_serialization_float_format) | |||||
| std_format = global_serialization_float_format; | |||||
| if (!format) | |||||
| format = std_format; | |||||
| size = snprintf(buf, sizeof(buf), format, jso->o.c_double); | |||||
| if (modf(jso->o.c_double, &dummy) == 0) | |||||
| { | |||||
| // Ensure it looks like a float, even if snprintf didn't. | |||||
| strncat(buf, ".0", sizeof(buf) - 1); | |||||
| if (size >= 0) | |||||
| size += 2; // yes, even if strncat ran out of room | |||||
| } | |||||
| } | |||||
| // although unlikely, snprintf can fail | |||||
| if (size < 0) | |||||
| return -1; | |||||
| p = strchr(buf, ','); | p = strchr(buf, ','); | ||||
| if (p) { | if (p) { | ||||
| @@ -736,8 +798,13 @@ static int json_object_double_to_json_string_format(struct json_object* jso, | |||||
| *(++p) = 0; | *(++p) = 0; | ||||
| size = p-buf; | size = p-buf; | ||||
| } | } | ||||
| printbuf_memappend(pb, buf, size); | |||||
| return size; | |||||
| if (size >= (int)sizeof(buf)) | |||||
| // The standard formats are guaranteed not to overrun the buffer, | |||||
| // but if a custom one happens to do so, just silently truncate. | |||||
| size = sizeof(buf) - 1; | |||||
| printbuf_memappend(pb, buf, size); | |||||
| return size; | |||||
| } | } | ||||
| static int json_object_double_to_json_string_default(struct json_object* jso, | static int json_object_double_to_json_string_default(struct json_object* jso, | ||||
| @@ -105,6 +105,22 @@ extern "C" { | |||||
| #undef TRUE | #undef TRUE | ||||
| #define TRUE ((json_bool)1) | #define TRUE ((json_bool)1) | ||||
| /** | |||||
| * Set the global value of an option, which will apply to all | |||||
| * current and future threads that have not set a thread-local value. | |||||
| * | |||||
| * @see json_c_set_serialization_double_format | |||||
| */ | |||||
| #define JSON_C_OPTION_GLOBAL (0) | |||||
| /** | |||||
| * Set a thread-local value of an option, overriding the global value. | |||||
| * This will fail if json-c is not compiled with threading enabled, and | |||||
| * with the __thread specifier (or equivalent) available. | |||||
| * | |||||
| * @see json_c_set_serialization_double_format | |||||
| */ | |||||
| #define JSON_C_OPTION_THREAD (1) | |||||
| extern const char *json_number_chars; | extern const char *json_number_chars; | ||||
| extern const char *json_hex_chars; | extern const char *json_hex_chars; | ||||
| @@ -748,6 +764,21 @@ extern struct json_object* json_object_new_double(double d); | |||||
| */ | */ | ||||
| extern struct json_object* json_object_new_double_s(double d, const char *ds); | extern struct json_object* json_object_new_double_s(double d, const char *ds); | ||||
| /** | |||||
| * Set a global or thread-local json-c option, depending on whether | |||||
| * JSON_C_OPTION_GLOBAL or JSON_C_OPTION_THREAD is passed. | |||||
| * Thread-local options default to undefined, and inherit from the global | |||||
| * value, even if the global value is changed after the thread is created. | |||||
| * Attempting to set thread-local options when threading is not compiled in | |||||
| * will result in an error. Be sure to check the return value. | |||||
| * | |||||
| * double_format is a "%g" printf format, such as "%.20g" | |||||
| * | |||||
| * @return -1 on errors, 0 on success. | |||||
| */ | |||||
| int json_c_set_serialization_double_format(const char *double_format, int global_or_thread); | |||||
| /** Serialize a json_object of type json_type_double to a string. | /** Serialize a json_object of type json_type_double to a string. | ||||
| * | * | ||||
| @@ -27,5 +27,31 @@ int main() | |||||
| json_object_set_serializer(obj, NULL, NULL, NULL); | json_object_set_serializer(obj, NULL, NULL, NULL); | ||||
| printf("obj.to_string(reset)=%s\n", json_object_to_json_string(obj)); | printf("obj.to_string(reset)=%s\n", json_object_to_json_string(obj)); | ||||
| json_object_put(obj); | |||||
| obj = json_object_new_double(0.52381); | |||||
| printf("obj.to_string(default format)=%s\n", json_object_to_json_string(obj)); | |||||
| if (json_c_set_serialization_double_format("x%0.3fy", JSON_C_OPTION_GLOBAL) < 0) | |||||
| printf("ERROR: json_c_set_serialization_double_format() failed"); | |||||
| printf("obj.to_string(with global format)=%s\n", json_object_to_json_string(obj)); | |||||
| #ifdef HAVE___THREAD | |||||
| if (json_c_set_serialization_double_format("T%0.2fX", JSON_C_OPTION_THREAD) < 0) | |||||
| printf("ERROR: json_c_set_serialization_double_format() failed"); | |||||
| printf("obj.to_string(with thread format)=%s\n", json_object_to_json_string(obj)); | |||||
| if (json_c_set_serialization_double_format("Ttttttttttttt%0.2fxxxxxxxxxxxxxxxxxxX", JSON_C_OPTION_THREAD) < 0) | |||||
| printf("ERROR: json_c_set_serialization_double_format() failed"); | |||||
| printf("obj.to_string(long thread format)=%s\n", json_object_to_json_string(obj)); | |||||
| if (json_c_set_serialization_double_format(NULL, JSON_C_OPTION_THREAD) < 0) | |||||
| printf("ERROR: json_c_set_serialization_double_format() failed"); | |||||
| printf("obj.to_string(back to global format)=%s\n", json_object_to_json_string(obj)); | |||||
| #else | |||||
| // Just fake it up, so the output matches. | |||||
| printf("obj.to_string(with thread format)=%s\n", "T0.52X"); | |||||
| printf("obj.to_string(back to global format)=%s\n", "x0.524y"); | |||||
| #endif | |||||
| if (json_c_set_serialization_double_format(NULL, JSON_C_OPTION_GLOBAL) < 0) | |||||
| printf("ERROR: json_c_set_serialization_double_format() failed"); | |||||
| printf("obj.to_string(back to default format)=%s\n", json_object_to_json_string(obj)); | |||||
| json_object_put(obj); | json_object_put(obj); | ||||
| } | } | ||||
| @@ -6,3 +6,6 @@ Test explicit serializer with custom userdata: | |||||
| obj.to_string(custom)=test | obj.to_string(custom)=test | ||||
| Test reset serializer: | Test reset serializer: | ||||
| obj.to_string(reset)=0.5 | obj.to_string(reset)=0.5 | ||||
| obj.to_string(default format)=0.52381 | |||||
| obj.to_string(with global format)=x0.524y | |||||
| obj.to_string(with thread format)=T0.52X | |||||