]> granicus.if.org Git - json-c/commitdiff
Add a json_c_set_serialization_double_format() function to set the *library-wide...
authorEric Haszlakiewicz <erh+git@nimenees.com>
Sun, 18 Jun 2017 18:04:49 +0000 (18:04 +0000)
committerEric Haszlakiewicz <erh+git@nimenees.com>
Sun, 18 Jun 2017 18:12:07 +0000 (18:12 +0000)
configure.ac
json_object.c
json_object.h
tests/test_double_serializer.c
tests/test_double_serializer.expected

index 9f495affcc31521b19558d10851b5966a306328f..364d27d88e4a9e778c5ff9f932516048c7558f2f 100644 (file)
@@ -41,6 +41,18 @@ AC_CHECK_HEADER(inttypes.h,[AC_DEFINE([JSON_C_HAVE_INTTYPES_H],[1],[Public defin
 AC_C_CONST
 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.
 AC_FUNC_VPRINTF
 AC_FUNC_MEMCMP
index 57342b0a97b7866b8afeef570ca880a1df47f6a9..42bb73820f1e78bbd79f12a762ab3bb4988add21 100644 (file)
@@ -692,6 +692,50 @@ int json_object_set_int64(struct json_object *jso,int64_t new_value){
 
 /* 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,
                                                    struct printbuf *pb,
                                                    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");
     else
       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, ',');
   if (p) {
@@ -736,8 +798,13 @@ static int json_object_double_to_json_string_format(struct json_object* jso,
     *(++p) = 0;
     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,
index f86f6783a7c5ad263f70bbad4b863c73440d57ce..e3da64bb9bdf7d34d14ea3f4496020f8d33ee971 100644 (file)
@@ -105,6 +105,22 @@ extern "C" {
 #undef TRUE
 #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_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);
 
+/**
+ * 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.
  *
index 4ef754d792cb3ec53c64963818cfb79084b932a5..c7b229ebdc955c2eb320c398ec2b02e1f4b8397f 100644 (file)
@@ -27,5 +27,31 @@ int main()
        json_object_set_serializer(obj, NULL, NULL, NULL);
        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);
 }
index da645d7ea270b0f5975d1a6efce17f03e56d6b02..6678910ada14fb5453ae5cccda596a0e8b01519a 100644 (file)
@@ -6,3 +6,6 @@ Test explicit serializer with custom userdata:
 obj.to_string(custom)=test
 Test reset serializer:
 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