diff --git a/3P/json/.gitignore b/3P/json/.gitignore index f3b18f12..15000ba0 100644 --- a/3P/json/.gitignore +++ b/3P/json/.gitignore @@ -7,6 +7,7 @@ /autom4te.cache /config.guess /json_config.h +/compile /config.h /config.log /config.status @@ -25,6 +26,7 @@ /missing /stamp-h1 /stamp-h2 +/test-driver /tests/Makefile /tests/Makefile.in /tests/test1 @@ -37,10 +39,14 @@ /tests/test_parse_int64 /tests/test_parse /tests/test_cast +/tests/test_charcase +/tests/test_locale /tests/test_null /tests/test_printbuf /tests/test_set_serializer /tests/*.vg.out +/tests/*.log +/tests/*.trs /Debug /Release *.lo diff --git a/3P/json/0.11 b/3P/json/0.12 similarity index 100% rename from 3P/json/0.11 rename to 3P/json/0.12 diff --git a/3P/json/ChangeLog b/3P/json/ChangeLog index 4e74c907..ad1c55ac 100644 --- a/3P/json/ChangeLog +++ b/3P/json/ChangeLog @@ -1,4 +1,43 @@ +0.12 + + * Address security issues: + * CVE-2013-6371: hash collision denial of service + * CVE-2013-6370: buffer overflow if size_t is larger than int + + * Avoid potential overflow in json_object_get_double + + * Eliminate the mc_abort() function and MC_ABORT macro. + + * Make the json_tokener_errors array local. It has been deprecated for + a while, and json_tokener_error_desc() should be used instead. + + * change the floating point output format to %.17g so values with + more than 6 digits show up in the output. + + * Remove the old libjson.so name compatibility support. The library is + only created as libjson-c.so now and headers are only installed + into the ${prefix}/json-c directory. + + * When supported by the linker, add the -Bsymbolic-functions flag. + + * Various changes to fix the build on MSVC. + + * Make strict mode more strict: + * number must not start with 0 + * no single-quote strings + * no comments + * trailing char not allowed + * only allow lowercase literals + + * Added a json_object_new_double_s() convenience function to allow + an exact string representation of a double to be specified when + creating the object and use it in json_tokener_parse_ex() so + a re-serialized object more exactly matches the input. + + * Add support NaN and Infinity + + 0.11 * IMPORTANT: the name of the library has changed to libjson-c.so and @@ -120,7 +159,7 @@ 0.6 * Fix bug in escaping of control characters - Johan Björklund, johbjo09 at kth dot se + Johan Bj�rklund, johbjo09 at kth dot se * Remove include "config.h" from headers (should only be included from .c files) Michael Clark @@ -166,7 +205,7 @@ Added cast and mask to suffice size_t v. unsigned int conversion correctness * json_tokener.c - sign reversal issue on error info for nested object parse - spotted by Johan Björklund (johbjo09 at kth.se) + spotted by Johan Bj�rklund (johbjo09 at kth.se) * json_object.c - escape " in json_escape_str * Change to automake and libtool to build shared and static library Michael Clark diff --git a/3P/json/Doxyfile b/3P/json/Doxyfile index 605e729a..edaffea2 100644 --- a/3P/json/Doxyfile +++ b/3P/json/Doxyfile @@ -23,7 +23,7 @@ PROJECT_NAME = json-c # This could be handy for archiving the generated documentation or # if some version control system is used. -PROJECT_NUMBER = 0.11 +PROJECT_NUMBER = 0.12 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. diff --git a/3P/json/Makefile.am b/3P/json/Makefile.am index df56a692..0001d8a9 100644 --- a/3P/json/Makefile.am +++ b/3P/json/Makefile.am @@ -4,15 +4,9 @@ EXTRA_DIST = README.html README-WIN32.html config.h.win32 doc json-c.vcproj SUBDIRS = . tests lib_LTLIBRARIES = libjson-c.la -if ENABLE_OLDNAME_COMPAT -lib_LTLIBRARIES+=libjson.la -endif pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = json-c.pc -if ENABLE_OLDNAME_COMPAT -pkgconfig_DATA += json.pc -endif libjson_cincludedir = $(includedir)/json-c libjson_cinclude_HEADERS = \ @@ -29,22 +23,15 @@ libjson_cinclude_HEADERS = \ json_tokener.h \ json_util.h \ linkhash.h \ - printbuf.h + printbuf.h \ + random_seed.h #libjsonx_includedir = $(libdir)/json-c-@VERSION@ # #libjsonx_include_HEADERS = \ # json_config.h -libjson_c_la_LDFLAGS = -version-info 2:1:0 -no-undefined - -if ENABLE_OLDNAME_COMPAT -libjson_la_LDFLAGS = -version-info 1:0:1 -no-undefined -ljson-c - -# Temporary libjson library. This will be removed after one release. -libjson_la_LIBADD = -ljson-c -endif - +libjson_c_la_LDFLAGS = -version-info 2:1:0 -no-undefined @JSON_BSYMBOLIC_LDFLAGS@ libjson_c_la_SOURCES = \ arraylist.c \ @@ -55,26 +42,21 @@ libjson_c_la_SOURCES = \ json_tokener.c \ json_util.c \ linkhash.c \ - printbuf.c + printbuf.c \ + random_seed.c distclean-local: -rm -rf $(testsubdir) -rm -rf config.h.in~ Makefile.in aclocal.m4 autom4te.cache/ config.guess config.sub depcomp install-sh ltmain.sh missing + -rm -f INSTALL test-driver tests/Makefile.in compile maintainer-clean-local: -rm -rf configure -if ENABLE_OLDNAME_COMPAT -install-data-hook: - test \! -e "$(DESTDIR)@includedir@/json" || rm "$(DESTDIR)@includedir@/json" - $(LN_S) json-c "$(DESTDIR)@includedir@/json" - uninstall-local: - rm -f "$(DESTDIR)@includedir@/json" rm -rf "$(DESTDIR)@includedir@/json-c" - -endif + rm -f "$(DESTDIR)@includedir@/json" ANDROID_CFLAGS = -I$(top_srcdir) -DHAVE_CONFIG_H diff --git a/3P/json/Makefile.am.inc b/3P/json/Makefile.am.inc index fd68a25f..fec591b6 100644 --- a/3P/json/Makefile.am.inc +++ b/3P/json/Makefile.am.inc @@ -1,2 +1,2 @@ -AM_CFLAGS = -Wall -Werror -Wextra -Wwrite-strings -Wno-unused-parameter -std=gnu99 -D_GNU_SOURCE -D_REENTRANT +AM_CFLAGS = -Wall -Werror -Wno-error=deprecated-declarations -Wextra -Wwrite-strings -Wno-unused-parameter -std=gnu99 -D_GNU_SOURCE -D_REENTRANT diff --git a/3P/json/README b/3P/json/README index ac1e812b..8c7301f0 100644 --- a/3P/json/README +++ b/3P/json/README @@ -12,6 +12,7 @@ Prerequisites: If you're not using a release tarball, you'll also need: autoconf (autoreconf) automake + Make sure you have a complete libtool install, including libtoolize Github repo for json-c: https://github.com/json-c/json-c diff --git a/3P/json/README-WIN32.html b/3P/json/README-WIN32.html index 28fc7d88..abdb39e7 100644 --- a/3P/json/README-WIN32.html +++ b/3P/json/README-WIN32.html @@ -13,7 +13,7 @@ Various functions have been redefined to their Win32 version (i.e. open on win32 is _open)
  • - Implemented missing functions from MS's libc (i.e. vasprintf and strndup)
  • + Implemented missing functions from MS's libc (i.e. vasprintf)
  • Added code to allow Win64 support without integer resizing issues, this probably makes it much nicer on 64bit machines everywhere (i.e. using ptrdiff_t diff --git a/3P/json/RELEASE_CHECKLIST.txt b/3P/json/RELEASE_CHECKLIST.txt index 2e901c35..0bb2b778 100644 --- a/3P/json/RELEASE_CHECKLIST.txt +++ b/3P/json/RELEASE_CHECKLIST.txt @@ -1,7 +1,7 @@ Release checklist: -release=0.11 +release=0.12 git clone https://github.com/json-c/json-c json-c-${release} cd json-c-${release} @@ -9,10 +9,26 @@ Check that the compile works on Linux Check that the compile works on NetBSD Check that the compile works on Windows Check ChangeLog to see if anything should be added. +Make any fixes/changes *before* branching. git branch json-c-${release} git checkout json-c-${release} +------------ + +Update the version in json_c_version.h +Update the version in Doxyfile +Update the version in configure.in + Use ${release}. + +Update the libjson_la_LDFLAGS line in Makefile.am to the new version. + Generally, unless we're doing a major release, change: + -version-info x:y:z + to + -version-info x:y+1:z + +------------ + Generate the configure script and other files: sh autogen.sh git add -f Makefile.in aclocal.m4 config.guess \ @@ -24,11 +40,15 @@ Generate the configure script and other files: git status --ignored git commit +------------ + Generate the doxygen documentation: doxygen git add -f doc git commit doc +------------ + cd .. echo .git > excludes echo autom4te.cache >> excludes @@ -37,25 +57,61 @@ tar -czf json-c-${release}.tar.gz -X excludes json-c-${release} echo doc >> excludes tar -czf json-c-${release}-nodoc.tar.gz -X excludes json-c-${release} +------------ + Tag the branch: cd json-c-${release} -git tag -a json-c-${release}-$(date +%Y%m%d) -git push +git tag -a json-c-${release}-$(date +%Y%m%d) -m "Release json-c-${release}" + +git push origin json-c-${release} git push --tags -Go to https://github.com/json-c/json-c/downloads -Upload the two tarballs. +------------ +Go to Amazon S3 service at: + https://console.aws.amazon.com/s3/ + +Upload the two tarballs in the json-c_releases folder. + When uploading, use "Reduced Redundancy", and make the uploaded files publicly accessible. + +Logout of Amazon S3, and verify that the files are visible. + https://s3.amazonaws.com/json-c_releases/releases/index.html =================================== Post-release checklist: -git branch master -Add new section to CHANGES +git checkout master +Add new section to ChangeLog Update the version in json_c_version.h Update the version in Doxyfile Update the version in configure.in -Update the libjson_la_LDFLAGS line in Makefile.am to the new version. + Use ${release}.99 to indicate a version "newer" than anything on the branch. + +Leave the libjson_la_LDFLAGS line in Makefile.am alone. + For more details see: http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html +------------ + +Update the gh-pages branch with new docs: + +cd json-c-${release} +git checkout json-c-${release} +cd .. + +git clone -b gh-pages https://github.com/json-c/json-c json-c-pages +cd json-c-pages +mkdir json-c-${release} +cp -R ../json-c-${release}/doc json-c-${release}/. +cp ../json-c-${release}/README-WIN32.html json-c-${release}/. +git add json-c-${release} +git commit + +vi index.html + Add/change links to current release. + +------------ + +Send an email to the mailing list. + diff --git a/3P/json/config.h.in b/3P/json/config.h.in index b8b2d17e..0dcab1a3 100644 --- a/3P/json/config.h.in +++ b/3P/json/config.h.in @@ -1,14 +1,43 @@ -/* config.h.in. Generated from configure.in by autoheader. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Enable RDRANR Hardware RNG Hash Seed */ +#undef ENABLE_RDRAND /* Define if .gnu.warning accepts long strings. */ #undef HAS_GNU_WARNING_LONG +/* Define to 1 if you have the declaration of `INFINITY', and to 0 if you + don't. */ +#undef HAVE_DECL_INFINITY + +/* Define to 1 if you have the declaration of `isinf', and to 0 if you don't. + */ +#undef HAVE_DECL_ISINF + +/* Define to 1 if you have the declaration of `isnan', and to 0 if you don't. + */ +#undef HAVE_DECL_ISNAN + +/* Define to 1 if you have the declaration of `nan', and to 0 if you don't. */ +#undef HAVE_DECL_NAN + +/* Define to 1 if you have the declaration of `_finite', and to 0 if you + don't. */ +#undef HAVE_DECL__FINITE + +/* Define to 1 if you have the declaration of `_isnan', and to 0 if you don't. + */ +#undef HAVE_DECL__ISNAN + /* Define to 1 if you have the header file. */ #undef HAVE_DLFCN_H /* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ #undef HAVE_DOPRNT +/* Define to 1 if you have the header file. */ +#undef HAVE_ENDIAN_H + /* Define to 1 if you have the header file. */ #undef HAVE_FCNTL_H @@ -68,9 +97,6 @@ /* Define to 1 if you have the `strncasecmp' function. */ #undef HAVE_STRNCASECMP -/* Define to 1 if you have the `strndup' function. */ -#undef HAVE_STRNDUP - /* Define to 1 if you have the header file. */ #undef HAVE_SYSLOG_H diff --git a/3P/json/configure.ac b/3P/json/configure.ac new file mode 100644 index 00000000..99660a4e --- /dev/null +++ b/3P/json/configure.ac @@ -0,0 +1,107 @@ +AC_PREREQ(2.52) + +# Process this file with autoconf to produce a configure script. +AC_INIT([json-c], 0.12, [json-c@googlegroups.com]) + +AM_INIT_AUTOMAKE + +AC_PROG_MAKE_SET + +AC_ARG_ENABLE(rdrand, + AS_HELP_STRING([--enable-rdrand], + [Enable RDRAND Hardware RNG Hash Seed generation on supported x86/x64 platforms.]), +[if test x$enableval = xyes; then + enable_rdrand=yes + AC_DEFINE(ENABLE_RDRAND, 1, [Enable RDRANR Hardware RNG Hash Seed]) +fi]) + +if test "x$enable_rdrand" = "xyes"; then + AC_MSG_RESULT([RDRAND Hardware RNG Hash Seed enabled on supported x86/x64 platforms]) +else + AC_MSG_RESULT([RDRAND Hardware RNG Hash Seed disabled. Use --enable-rdrand to enable]) +fi + +# Checks for programs. + +# Checks for libraries. + +# Checks for header files. +AM_PROG_CC_C_O +AC_CONFIG_HEADER(config.h) +AC_CONFIG_HEADER(json_config.h) +AC_HEADER_STDC +AC_CHECK_HEADERS(fcntl.h limits.h strings.h syslog.h unistd.h [sys/cdefs.h] [sys/param.h] stdarg.h locale.h endian.h) +AC_CHECK_HEADER(inttypes.h,[AC_DEFINE([JSON_C_HAVE_INTTYPES_H],[1],[Public define for json_inttypes.h])]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_TYPE_SIZE_T + +# Checks for library functions. +AC_FUNC_VPRINTF +AC_FUNC_MEMCMP +AC_FUNC_MALLOC +AC_FUNC_REALLOC +AC_CHECK_FUNCS(strcasecmp strdup strerror snprintf vsnprintf vasprintf open vsyslog strncasecmp setlocale) +AC_CHECK_DECLS([INFINITY], [], [], [[#include ]]) +AC_CHECK_DECLS([nan], [], [], [[#include ]]) +AC_CHECK_DECLS([isnan], [], [], [[#include ]]) +AC_CHECK_DECLS([isinf], [], [], [[#include ]]) +AC_CHECK_DECLS([_isnan], [], [], [[#include ]]) +AC_CHECK_DECLS([_finite], [], [], [[#include ]]) + +#check if .section.gnu.warning accepts long strings (for __warn_references) +AC_LANG_PUSH([C]) + +AC_MSG_CHECKING([if .gnu.warning accepts long strings]) +AC_LINK_IFELSE([AC_LANG_SOURCE([[ +extern void json_object_get(); +__asm__(".section .gnu.json_object_get,\n\t.ascii \"Please link against libjson-c instead of libjson\"\n\t.text"); + +int main(int c,char* v) {return 0;} +]])], [ + AC_DEFINE(HAS_GNU_WARNING_LONG, 1, [Define if .gnu.warning accepts long strings.]) + AC_MSG_RESULT(yes) +], [ + AC_MSG_RESULT(no) +]) + +AC_LANG_POP([C]) + +AM_PROG_LIBTOOL + +# Check for the -Bsymbolic-functions linker flag +AC_ARG_ENABLE([Bsymbolic], + [AS_HELP_STRING([--disable-Bsymbolic], [Avoid linking with -Bsymbolic-function])], + [], + [enable_Bsymbolic=check]) + +AS_IF([test "x$enable_Bsymbolic" = "xcheck"], + [ + saved_LDFLAGS="${LDFLAGS}" + AC_MSG_CHECKING([for -Bsymbolic-functions linker flag]) + LDFLAGS=-Wl,-Bsymbolic-functions + AC_TRY_LINK([], [int main (void) { return 0; }], + [ + AC_MSG_RESULT([yes]) + enable_Bsymbolic=yes + ], + [ + AC_MSG_RESULT([no]) + enable_Bsymbolic=no + ]) + LDFLAGS="${saved_LDFLAGS}" + ]) + +AS_IF([test "x$enable_Bsymbolic" = "xyes"], [JSON_BSYMBOLIC_LDFLAGS=-Wl[,]-Bsymbolic-functions]) +AC_SUBST(JSON_BSYMBOLIC_LDFLAGS) + +AC_CONFIG_FILES([ +Makefile +json-c.pc +tests/Makefile +json-c-uninstalled.pc +]) + +AC_OUTPUT + diff --git a/3P/json/configure.in b/3P/json/configure.in deleted file mode 100644 index d87161c4..00000000 --- a/3P/json/configure.in +++ /dev/null @@ -1,69 +0,0 @@ -AC_PREREQ(2.52) - -# Process this file with autoconf to produce a configure script. -AC_INIT([json-c], 0.11, [json-c@googlegroups.com]) - -AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION) - -AC_PROG_MAKE_SET - -AC_ARG_ENABLE(oldname-compat, - AS_HELP_STRING([--disable-oldname-compat], - [Don't include the old libjson.so library and include/json directory.]), -[], -[enable_oldname_compat=yes] -) -AM_CONDITIONAL(ENABLE_OLDNAME_COMPAT, [test "x${enable_oldname_compat}" != "xno"]) - -# Checks for programs. - -# Checks for libraries. - -# Checks for header files. -AC_CONFIG_HEADER(config.h) -AC_CONFIG_HEADER(json_config.h) -AC_HEADER_STDC -AC_CHECK_HEADERS(fcntl.h limits.h strings.h syslog.h unistd.h [sys/cdefs.h] [sys/param.h] stdarg.h locale.h) -AC_CHECK_HEADER(inttypes.h,[AC_DEFINE([JSON_C_HAVE_INTTYPES_H],[1],[Public define for json_inttypes.h])]) - -# Checks for typedefs, structures, and compiler characteristics. -AC_C_CONST -AC_TYPE_SIZE_T - -# Checks for library functions. -AC_FUNC_VPRINTF -AC_FUNC_MEMCMP -AC_FUNC_MALLOC -AC_FUNC_REALLOC -AC_CHECK_FUNCS(strcasecmp strdup strndup strerror snprintf vsnprintf vasprintf open vsyslog strncasecmp setlocale) - -#check if .section.gnu.warning accepts long strings (for __warn_references) -AC_LANG_PUSH([C]) - -AC_MSG_CHECKING([if .gnu.warning accepts long strings]) -AC_LINK_IFELSE([[ -extern void json_object_get(); -__asm__(".section .gnu.json_object_get,\n\t.ascii \"Please link against libjson-c instead of libjson\"\n\t.text"); - -int main(int c,char* v) {return 0;} -]], [ - AC_DEFINE(HAS_GNU_WARNING_LONG, 1, [Define if .gnu.warning accepts long strings.]) - AC_MSG_RESULT(yes) -], [ - AC_MSG_RESULT(no) -]) - -AC_LANG_POP([C]) - -AM_PROG_LIBTOOL - -AC_CONFIG_FILES([ -Makefile -json.pc -json-c.pc -tests/Makefile -json-c-uninstalled.pc -]) - -AC_OUTPUT - diff --git a/3P/json/debug.c b/3P/json/debug.c index e0294caa..3b64b590 100644 --- a/3P/json/debug.c +++ b/3P/json/debug.c @@ -41,21 +41,6 @@ extern void mc_set_syslog(int syslog) _syslog = syslog; } -void mc_abort(const char *msg, ...) -{ - va_list ap; - va_start(ap, msg); -#if HAVE_VSYSLOG - if(_syslog) { - vsyslog(LOG_ERR, msg, ap); - } else -#endif - vprintf(msg, ap); - va_end(ap); - exit(1); -} - - void mc_debug(const char *msg, ...) { va_list ap; diff --git a/3P/json/debug.h b/3P/json/debug.h index 1e097017..80ca3e43 100644 --- a/3P/json/debug.h +++ b/3P/json/debug.h @@ -23,7 +23,7 @@ extern void mc_set_debug(int debug); extern int mc_get_debug(void); extern void mc_set_syslog(int syslog); -extern void mc_abort(const char *msg, ...); + extern void mc_debug(const char *msg, ...); extern void mc_error(const char *msg, ...); extern void mc_info(const char *msg, ...); @@ -48,7 +48,6 @@ extern void mc_info(const char *msg, ...); #endif -#define MC_ABORT(x, ...) mc_abort(x, ##__VA_ARGS__) #define MC_ERROR(x, ...) mc_error(x, ##__VA_ARGS__) #ifdef MC_MAINTAINER_MODE diff --git a/3P/json/json_c_version.h b/3P/json/json_c_version.h index ff20f950..eed98a49 100644 --- a/3P/json/json_c_version.h +++ b/3P/json/json_c_version.h @@ -9,12 +9,12 @@ #define _json_c_version_h_ #define JSON_C_MAJOR_VERSION 0 -#define JSON_C_MINOR_VERSION 11 +#define JSON_C_MINOR_VERSION 12 #define JSON_C_MICRO_VERSION 0 #define JSON_C_VERSION_NUM ((JSON_C_MAJOR_VERSION << 16) | \ (JSON_C_MINOR_VERSION << 8) | \ JSON_C_MICRO_VERSION) -#define JSON_C_VERSION "0.11" +#define JSON_C_VERSION "0.12" const char *json_c_version(void); /* Returns JSON_C_VERSION */ int json_c_version_num(void); /* Returns JSON_C_VERSION_NUM */ diff --git a/3P/json/json_inttypes.h b/3P/json/json_inttypes.h index 2f84adec..9de8d246 100644 --- a/3P/json/json_inttypes.h +++ b/3P/json/json_inttypes.h @@ -4,7 +4,7 @@ #include "json_config.h" -#if defined(_MSC_VER) && _MSC_VER < 1700 +#if defined(_MSC_VER) && _MSC_VER <= 1700 /* Anything less than Visual Studio C++ 10 is missing stdint.h and inttypes.h */ typedef __int32 int32_t; diff --git a/3P/json/json_object.c b/3P/json/json_object.c index f2b5ce04..6cc73bce 100644 --- a/3P/json/json_object.c +++ b/3P/json/json_object.c @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include "debug.h" #include "printbuf.h" @@ -25,6 +27,7 @@ #include "json_object.h" #include "json_object_private.h" #include "json_util.h" +#include "math_compat.h" #if !defined(HAVE_STRDUP) && defined(_MSC_VER) /* MSC has the version as _strdup */ @@ -33,9 +36,12 @@ # error You do not have strdup on your system. #endif /* HAVE_STRDUP */ -#if !defined(HAVE_STRNDUP) - char* strndup(const char* str, size_t n); -#endif /* !HAVE_STRNDUP */ +#if !defined(HAVE_SNPRINTF) && defined(_MSC_VER) + /* MSC has the version as _snprintf */ +# define snprintf _snprintf +#elif !defined(HAVE_SNPRINTF) +# error You do not have snprintf on your system. +#endif /* HAVE_SNPRINTF */ // Don't define this. It's not thread-safe. /* #define REFCOUNT_DEBUG 1 */ @@ -561,8 +567,20 @@ static int json_object_double_to_json_string(struct json_object* jso, { char buf[128], *p, *q; int size; + /* Although JSON RFC does not support + NaN or Infinity as numeric values + ECMA 262 section 9.8.1 defines + how to handle these cases as strings */ + if(isnan(jso->o.c_double)) + size = snprintf(buf, sizeof(buf), "NaN"); + else if(isinf(jso->o.c_double)) + if(jso->o.c_double > 0) + size = snprintf(buf, sizeof(buf), "Infinity"); + else + size = snprintf(buf, sizeof(buf), "-Infinity"); + else + size = snprintf(buf, sizeof(buf), "%.17g", jso->o.c_double); - size = snprintf(buf, 128, "%f", jso->o.c_double); p = strchr(buf, ','); if (p) { *p = '.'; @@ -585,16 +603,42 @@ static int json_object_double_to_json_string(struct json_object* jso, struct json_object* json_object_new_double(double d) { - struct json_object *jso = json_object_new(json_type_double); - if(!jso) return NULL; - jso->_to_json_string = &json_object_double_to_json_string; - jso->o.c_double = d; - return jso; + struct json_object *jso = json_object_new(json_type_double); + if (!jso) + return NULL; + jso->_to_json_string = &json_object_double_to_json_string; + jso->o.c_double = d; + return jso; +} + +struct json_object* json_object_new_double_s(double d, const char *ds) +{ + struct json_object *jso = json_object_new_double(d); + if (!jso) + return NULL; + + json_object_set_serializer(jso, json_object_userdata_to_json_string, + strdup(ds), json_object_free_userdata); + return jso; +} + +int json_object_userdata_to_json_string(struct json_object *jso, + struct printbuf *pb, int level, int flags) +{ + int userdata_len = strlen(jso->_userdata); + printbuf_memappend(pb, jso->_userdata, userdata_len); + return userdata_len; +} + +void json_object_free_userdata(struct json_object *jso, void *userdata) +{ + free(userdata); } double json_object_get_double(struct json_object *jso) { double cdouble; + char *errPtr = NULL; if(!jso) return 0.0; switch(jso->o_type) { @@ -605,7 +649,36 @@ double json_object_get_double(struct json_object *jso) case json_type_boolean: return jso->o.c_boolean; case json_type_string: - if(sscanf(jso->o.c_string.str, "%lf", &cdouble) == 1) return cdouble; + errno = 0; + cdouble = strtod(jso->o.c_string.str,&errPtr); + + /* if conversion stopped at the first character, return 0.0 */ + if (errPtr == jso->o.c_string.str) + return 0.0; + + /* + * Check that the conversion terminated on something sensible + * + * For example, { "pay" : 123AB } would parse as 123. + */ + if (*errPtr != '\0') + return 0.0; + + /* + * If strtod encounters a string which would exceed the + * capacity of a double, it returns +/- HUGE_VAL and sets + * errno to ERANGE. But +/- HUGE_VAL is also a valid result + * from a conversion, so we need to check errno. + * + * Underflow also sets errno to ERANGE, but it returns 0 in + * that case, which is what we will return anyway. + * + * See CERT guideline ERR30-C + */ + if ((HUGE_VAL == cdouble || -HUGE_VAL == cdouble) && + (ERANGE == errno)) + cdouble = 0.0; + return cdouble; default: return 0.0; } diff --git a/3P/json/json_object.h b/3P/json/json_object.h index f16f4fea..200ac403 100644 --- a/3P/json/json_object.h +++ b/3P/json/json_object.h @@ -13,6 +13,14 @@ #ifndef _json_object_h_ #define _json_object_h_ +#ifdef __GNUC__ +#define THIS_FUNCTION_IS_DEPRECATED(func) func __attribute__ ((deprecated)) +#elif defined(_MSC_VER) +#define THIS_FUNCTION_IS_DEPRECATED(func) __declspec(deprecated) func +#else +#define THIS_FUNCTION_IS_DEPRECATED(func) func +#endif + #include "json_inttypes.h" #ifdef __cplusplus @@ -192,11 +200,30 @@ flags); * @param userdata an optional opaque cookie * @param user_delete an optional function from freeing userdata */ -void json_object_set_serializer(json_object *jso, +extern void json_object_set_serializer(json_object *jso, json_object_to_json_string_fn to_string_func, void *userdata, json_object_delete_fn *user_delete); +/** + * Simply call free on the userdata pointer. + * Can be used with json_object_set_serializer(). + * + * @param jso unused + * @param userdata the pointer that is passed to free(). + */ +json_object_delete_fn json_object_free_userdata; + +/** + * Copy the jso->_userdata string over to pb as-is. + * Can be used with json_object_set_serializer(). + * + * @param jso The object whose _userdata is used. + * @param pb The destination buffer. + * @param level Ignored. + * @param flags Ignored. + */ +json_object_to_json_string_fn json_object_userdata_to_json_string; /* object type methods */ @@ -260,8 +287,8 @@ extern void json_object_object_add(struct json_object* obj, const char *key, * @returns the json_object associated with the given field name * @deprecated Please use json_object_object_get_ex */ -extern struct json_object* json_object_object_get(struct json_object* obj, - const char *key); +THIS_FUNCTION_IS_DEPRECATED(extern struct json_object* json_object_object_get(struct json_object* obj, + const char *key)); /** Get the json_object associated with a given object field. * @@ -309,7 +336,7 @@ extern void json_object_object_del(struct json_object* obj, const char *key); * @param val the local name for the json_object* object variable defined in * the body */ -#if defined(__GNUC__) && !defined(__STRICT_ANSI__) && defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) && __STDC_VERSION__ >= 199901L # define json_object_object_foreach(obj,key,val) \ char *key; \ @@ -493,6 +520,29 @@ extern int64_t json_object_get_int64(struct json_object *obj); */ extern struct json_object* json_object_new_double(double d); +/** + * Create a new json_object of type json_type_double, using + * the exact serialized representation of the value. + * + * This allows for numbers that would otherwise get displayed + * inefficiently (e.g. 12.3 => "12.300000000000001") to be + * serialized with the more convenient form. + * + * Note: this is used by json_tokener_parse_ex() to allow for + * an exact re-serialization of a parsed object. + * + * An equivalent sequence of calls is: + * @code + * jso = json_object_new_double(d); + * json_object_set_serializer(d, json_object_userdata_to_json_string, + * strdup(ds), json_object_free_userdata) + * @endcode + * + * @param d the numeric value of the double. + * @param ds the string representation of the double. This will be copied. + */ +extern struct json_object* json_object_new_double_s(double d, const char *ds); + /** Get the double floating point value of a json_object * * The type is coerced to a double if the passed object is not a double. diff --git a/3P/json/json_object_iterator.h b/3P/json/json_object_iterator.h index f6e7ca62..44c9fb25 100644 --- a/3P/json/json_object_iterator.h +++ b/3P/json/json_object_iterator.h @@ -236,4 +236,4 @@ json_object_iter_equal(const struct json_object_iterator* iter1, #endif -#endif // JSON_OBJECT_ITERATOR_H +#endif /* JSON_OBJECT_ITERATOR_H */ diff --git a/3P/json/json_tokener.c b/3P/json/json_tokener.c index b2b47f9d..19de8efc 100644 --- a/3P/json/json_tokener.c +++ b/3P/json/json_tokener.c @@ -15,6 +15,7 @@ #include "config.h" +#include #include #include #include @@ -49,12 +50,23 @@ # error You do not have strncasecmp on your system. #endif /* HAVE_STRNCASECMP */ -static const char* json_null_str = "null"; -static const char* json_true_str = "true"; -static const char* json_false_str = "false"; +/* Use C99 NAN by default; if not available, nan("") should work too. */ +#ifndef NAN +#define NAN nan("") +#endif /* !NAN */ -// XXX after v0.10 this array will become static: -const char* json_tokener_errors[] = { +static const char json_null_str[] = "null"; +static const int json_null_str_len = sizeof(json_null_str) - 1; +static const char json_inf_str[] = "Infinity"; +static const int json_inf_str_len = sizeof(json_inf_str) - 1; +static const char json_nan_str[] = "NaN"; +static const int json_nan_str_len = sizeof(json_nan_str) - 1; +static const char json_true_str[] = "true"; +static const int json_true_str_len = sizeof(json_true_str) - 1; +static const char json_false_str[] = "false"; +static const int json_false_str_len = sizeof(json_false_str) - 1; + +static const char* json_tokener_errors[] = { "success", "continue", "nesting too deep", @@ -69,12 +81,13 @@ const char* json_tokener_errors[] = { "object value separator ',' expected", "invalid string sequence", "expected comment", + "buffer size overflow" }; const char *json_tokener_error_desc(enum json_tokener_error jerr) { int jerr_int = (int)jerr; - if (jerr_int < 0 || jerr_int > (int)sizeof(json_tokener_errors)) + if (jerr_int < 0 || jerr_int >= (int)(sizeof(json_tokener_errors) / sizeof(json_tokener_errors[0]))) return "Unknown error, invalid json_tokener_error value passed to json_tokener_error_desc()"; return json_tokener_errors[jerr]; } @@ -170,29 +183,6 @@ struct json_object* json_tokener_parse_verbose(const char *str, enum json_tokene return obj; } - -#if !HAVE_STRNDUP -/* CAW: compliant version of strndup() */ -char* strndup(const char* str, size_t n) -{ - if(str) { - size_t len = strlen(str); - size_t nn = json_min(len,n); - char* s = (char*)malloc(sizeof(char) * (nn + 1)); - - if(s) { - memcpy(s, str, nn); - s[nn] = '\0'; - } - - return s; - } - - return NULL; -} -#endif - - #define state tok->stack[tok->depth].state #define saved_state tok->stack[tok->depth].saved_state #define current tok->stack[tok->depth].current @@ -254,6 +244,16 @@ struct json_object* json_tokener_parse_ex(struct json_tokener *tok, tok->char_offset = 0; tok->err = json_tokener_success; + /* this interface is presently not 64-bit clean due to the int len argument + and the internal printbuf interface that takes 32-bit int len arguments + so the function limits the maximum string size to INT32_MAX (2GB). + If the function is called with len == -1 then strlen is called to check + the string length is less than INT32_MAX (2GB) */ + if ((len < -1) || (len == -1 && strlen(str) > INT32_MAX)) { + tok->err = json_tokener_error_size; + return NULL; + } + while (PEEK_CHAR(c, tok)) { redo_char: @@ -265,7 +265,7 @@ struct json_object* json_tokener_parse_ex(struct json_tokener *tok, if ((!ADVANCE_CHAR(str, tok)) || (!PEEK_CHAR(c, tok))) goto out; } - if(c == '/') { + if(c == '/' && !(tok->flags & JSON_TOKENER_STRICT)) { printbuf_reset(tok->pb); printbuf_memappend_fast(tok->pb, &c, 1); state = json_tokener_state_comment_start; @@ -287,14 +287,25 @@ struct json_object* json_tokener_parse_ex(struct json_tokener *tok, saved_state = json_tokener_state_array; current = json_object_new_array(); break; - case 'N': - case 'n': - state = json_tokener_state_null; + case 'I': + case 'i': + state = json_tokener_state_inf; + printbuf_reset(tok->pb); + tok->st_pos = 0; + goto redo_char; + case 'N': + case 'n': + state = json_tokener_state_null; // or NaN printbuf_reset(tok->pb); tok->st_pos = 0; goto redo_char; - case '"': case '\'': + if (tok->flags & JSON_TOKENER_STRICT) { + /* in STRICT mode only double-quote are allowed */ + tok->err = json_tokener_error_parse_unexpected; + goto out; + } + case '"': state = json_tokener_state_string; printbuf_reset(tok->pb); tok->quote_char = c; @@ -339,21 +350,76 @@ struct json_object* json_tokener_parse_ex(struct json_tokener *tok, tok->depth--; goto redo_char; - case json_tokener_state_null: - printbuf_memappend_fast(tok->pb, &c, 1); - if(strncasecmp(json_null_str, tok->pb->buf, - json_min(tok->st_pos+1, (int)strlen(json_null_str))) == 0) { - if(tok->st_pos == (int)strlen(json_null_str)) { - current = NULL; - saved_state = json_tokener_state_finish; - state = json_tokener_state_eatws; - goto redo_char; + case json_tokener_state_inf: /* aka starts with 'i' */ + { + int size; + int size_inf; + int is_negative = 0; + + printbuf_memappend_fast(tok->pb, &c, 1); + size = json_min(tok->st_pos+1, json_null_str_len); + size_inf = json_min(tok->st_pos+1, json_inf_str_len); + char *infbuf = tok->pb->buf; + if (*infbuf == '-') + { + infbuf++; + is_negative = 1; } - } else { - tok->err = json_tokener_error_parse_null; - goto out; + if ((!(tok->flags & JSON_TOKENER_STRICT) && + strncasecmp(json_inf_str, infbuf, size_inf) == 0) || + (strncmp(json_inf_str, infbuf, size_inf) == 0) + ) + { + if (tok->st_pos == json_inf_str_len) + { + current = json_object_new_double(is_negative ? -INFINITY : INFINITY); + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + } else { + tok->err = json_tokener_error_parse_unexpected; + goto out; + } + tok->st_pos++; + } + break; + case json_tokener_state_null: /* aka starts with 'n' */ + { + int size; + int size_nan; + printbuf_memappend_fast(tok->pb, &c, 1); + size = json_min(tok->st_pos+1, json_null_str_len); + size_nan = json_min(tok->st_pos+1, json_nan_str_len); + if((!(tok->flags & JSON_TOKENER_STRICT) && + strncasecmp(json_null_str, tok->pb->buf, size) == 0) + || (strncmp(json_null_str, tok->pb->buf, size) == 0) + ) { + if (tok->st_pos == json_null_str_len) { + current = NULL; + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + } + else if ((!(tok->flags & JSON_TOKENER_STRICT) && + strncasecmp(json_nan_str, tok->pb->buf, size_nan) == 0) || + (strncmp(json_nan_str, tok->pb->buf, size_nan) == 0) + ) + { + if (tok->st_pos == json_nan_str_len) + { + current = json_object_new_double(NAN); + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + } else { + tok->err = json_tokener_error_parse_null; + goto out; + } + tok->st_pos++; } - tok->st_pos++; break; case json_tokener_state_comment_start: @@ -566,28 +632,36 @@ struct json_object* json_tokener_parse_ex(struct json_tokener *tok, break; case json_tokener_state_boolean: - printbuf_memappend_fast(tok->pb, &c, 1); - if(strncasecmp(json_true_str, tok->pb->buf, - json_min(tok->st_pos+1, (int)strlen(json_true_str))) == 0) { - if(tok->st_pos == (int)strlen(json_true_str)) { - current = json_object_new_boolean(1); - saved_state = json_tokener_state_finish; - state = json_tokener_state_eatws; - goto redo_char; + { + int size1, size2; + printbuf_memappend_fast(tok->pb, &c, 1); + size1 = json_min(tok->st_pos+1, json_true_str_len); + size2 = json_min(tok->st_pos+1, json_false_str_len); + if((!(tok->flags & JSON_TOKENER_STRICT) && + strncasecmp(json_true_str, tok->pb->buf, size1) == 0) + || (strncmp(json_true_str, tok->pb->buf, size1) == 0) + ) { + if(tok->st_pos == json_true_str_len) { + current = json_object_new_boolean(1); + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + } else if((!(tok->flags & JSON_TOKENER_STRICT) && + strncasecmp(json_false_str, tok->pb->buf, size2) == 0) + || (strncmp(json_false_str, tok->pb->buf, size2) == 0)) { + if(tok->st_pos == json_false_str_len) { + current = json_object_new_boolean(0); + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + } else { + tok->err = json_tokener_error_parse_boolean; + goto out; } - } else if(strncasecmp(json_false_str, tok->pb->buf, - json_min(tok->st_pos+1, (int)strlen(json_false_str))) == 0) { - if(tok->st_pos == (int)strlen(json_false_str)) { - current = json_object_new_boolean(0); - saved_state = json_tokener_state_finish; - state = json_tokener_state_eatws; - goto redo_char; - } - } else { - tok->err = json_tokener_error_parse_boolean; - goto out; + tok->st_pos++; } - tok->st_pos++; break; case json_tokener_state_number: @@ -606,14 +680,29 @@ struct json_object* json_tokener_parse_ex(struct json_tokener *tok, } if (case_len>0) printbuf_memappend_fast(tok->pb, case_start, case_len); + + // Check for -Infinity + if (tok->pb->buf[0] == '-' && case_len == 1 && + (c == 'i' || c == 'I')) + { + state = json_tokener_state_inf; + goto redo_char; + } } { int64_t num64; double numd; if (!tok->is_double && json_parse_int64(tok->pb->buf, &num64) == 0) { + if (num64 && tok->pb->buf[0]=='0' && (tok->flags & JSON_TOKENER_STRICT)) { + /* in strict mode, number must not start with 0 */ + tok->err = json_tokener_error_parse_number; + goto out; + } current = json_object_new_int64(num64); - } else if(tok->is_double && json_parse_double(tok->pb->buf, &numd) == 0) { - current = json_object_new_double(numd); + } + else if(tok->is_double && json_parse_double(tok->pb->buf, &numd) == 0) + { + current = json_object_new_double_s(numd, tok->pb->buf); } else { tok->err = json_tokener_error_parse_number; goto out; @@ -759,6 +848,13 @@ struct json_object* json_tokener_parse_ex(struct json_tokener *tok, } /* while(POP_CHAR) */ out: + if (c && + (state == json_tokener_state_finish) && + (tok->depth == 0) && + (tok->flags & JSON_TOKENER_STRICT)) { + /* unexpected char after JSON data */ + tok->err = json_tokener_error_parse_unexpected; + } if (!c) { /* We hit an eof char (0) */ if(state != json_tokener_state_finish && saved_state != json_tokener_state_finish) diff --git a/3P/json/json_tokener.h b/3P/json/json_tokener.h index 08e5ff7f..a72d2bde 100644 --- a/3P/json/json_tokener.h +++ b/3P/json/json_tokener.h @@ -33,7 +33,8 @@ enum json_tokener_error { json_tokener_error_parse_object_key_sep, json_tokener_error_parse_object_value_sep, json_tokener_error_parse_string, - json_tokener_error_parse_comment + json_tokener_error_parse_comment, + json_tokener_error_size }; enum json_tokener_state { @@ -60,7 +61,8 @@ enum json_tokener_state { json_tokener_state_object_value_add, json_tokener_state_object_sep, json_tokener_state_array_after_sep, - json_tokener_state_object_field_start_after_sep + json_tokener_state_object_field_start_after_sep, + json_tokener_state_inf }; struct json_tokener_srec @@ -105,14 +107,6 @@ struct json_tokener */ const char *json_tokener_error_desc(enum json_tokener_error jerr); -/** - * @b XXX do not use json_tokener_errors directly. - * After v0.10 this will be removed. - * - * See json_tokener_error_desc() instead. - */ -extern const char* json_tokener_errors[]; - /** * Retrieve the error caused by the last call to json_tokener_parse_ex(), * or json_tokener_success if there is no error. @@ -170,6 +164,11 @@ extern void json_tokener_set_flags(struct json_tokener *tok, int flags); * responsible for calling json_tokener_parse_ex with an appropriate str * parameter starting with the extra characters. * + * This interface is presently not 64-bit clean due to the int len argument + * so the function limits the maximum string size to INT32_MAX (2GB). + * If the function is called with len == -1 then strlen is called to check + * the string length is less than INT32_MAX (2GB) + * * Example: * @code json_object *jobj = NULL; diff --git a/3P/json/json_util.c b/3P/json/json_util.c index 111fa01a..531f9afb 100644 --- a/3P/json/json_util.c +++ b/3P/json/json_util.c @@ -73,7 +73,7 @@ struct json_object* json_object_from_file(const char *filename) int fd, ret; if((fd = open(filename, O_RDONLY)) < 0) { - MC_ERROR("json_object_from_file: error reading file %s: %s\n", + MC_ERROR("json_object_from_file: error opening file %s: %s\n", filename, strerror(errno)); return NULL; } @@ -87,7 +87,7 @@ struct json_object* json_object_from_file(const char *filename) } close(fd); if(ret < 0) { - MC_ABORT("json_object_from_file: error reading file %s: %s\n", + MC_ERROR("json_object_from_file: error reading file %s: %s\n", filename, strerror(errno)); printbuf_free(pb); return NULL; @@ -99,7 +99,7 @@ struct json_object* json_object_from_file(const char *filename) /* extended "format and write to file" function */ -int json_object_to_file_ext(char *filename, struct json_object *obj, int flags) +int json_object_to_file_ext(const char *filename, struct json_object *obj, int flags) { const char *json_str; int fd, ret; @@ -141,7 +141,7 @@ int json_object_to_file_ext(char *filename, struct json_object *obj, int flags) // backwards compatible "format and write to file" function -int json_object_to_file(char *filename, struct json_object *obj) +int json_object_to_file(const char *filename, struct json_object *obj) { return json_object_to_file_ext(filename, obj, JSON_C_TO_STRING_PLAIN); } @@ -159,14 +159,15 @@ int json_parse_double(const char *buf, double *retval) static void sscanf_is_broken_test() { int64_t num64; + int ret_errno, is_int64_min, ret_errno2, is_int64_max; (void)sscanf(" -01234567890123456789012345", "%" SCNd64, &num64); - int ret_errno = errno; - int is_int64_min = (num64 == INT64_MIN); + ret_errno = errno; + is_int64_min = (num64 == INT64_MIN); (void)sscanf(" 01234567890123456789012345", "%" SCNd64, &num64); - int ret_errno2 = errno; - int is_int64_max = (num64 == INT64_MAX); + ret_errno2 = errno; + is_int64_max = (num64 == INT64_MAX); if (ret_errno != ERANGE || !is_int64_min || ret_errno2 != ERANGE || !is_int64_max) diff --git a/3P/json/json_util.h b/3P/json/json_util.h index b9a69c8b..1005e58c 100644 --- a/3P/json/json_util.h +++ b/3P/json/json_util.h @@ -22,8 +22,8 @@ extern "C" { /* utility functions */ extern struct json_object* json_object_from_file(const char *filename); -extern int json_object_to_file(char *filename, struct json_object *obj); -extern int json_object_to_file_ext(char *filename, struct json_object *obj, int flags); +extern int json_object_to_file(const char *filename, struct json_object *obj); +extern int json_object_to_file_ext(const char *filename, struct json_object *obj, int flags); extern int json_parse_int64(const char *buf, int64_t *retval); extern int json_parse_double(const char *buf, double *retval); diff --git a/3P/json/linkhash.c b/3P/json/linkhash.c index 50431485..712c3879 100644 --- a/3P/json/linkhash.c +++ b/3P/json/linkhash.c @@ -17,6 +17,11 @@ #include #include +#ifdef HAVE_ENDIAN_H +# include /* attempt to define endianness */ +#endif + +#include "random_seed.h" #include "linkhash.h" void lh_abort(const char *msg, ...) @@ -39,14 +44,378 @@ int lh_ptr_equal(const void *k1, const void *k2) return (k1 == k2); } +/* + * hashlittle from lookup3.c, by Bob Jenkins, May 2006, Public Domain. + * http://burtleburtle.net/bob/c/lookup3.c + * minor modifications to make functions static so no symbols are exported + * minor mofifications to compile with -Werror + */ + +/* +------------------------------------------------------------------------------- +lookup3.c, by Bob Jenkins, May 2006, Public Domain. + +These are functions for producing 32-bit hashes for hash table lookup. +hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() +are externally useful functions. Routines to test the hash are included +if SELF_TEST is defined. You can use this free for any purpose. It's in +the public domain. It has no warranty. + +You probably want to use hashlittle(). hashlittle() and hashbig() +hash byte arrays. hashlittle() is is faster than hashbig() on +little-endian machines. Intel and AMD are little-endian machines. +On second thought, you probably want hashlittle2(), which is identical to +hashlittle() except it returns two 32-bit hashes for the price of one. +You could implement hashbig2() if you wanted but I haven't bothered here. + +If you want to find a hash of, say, exactly 7 integers, do + a = i1; b = i2; c = i3; + mix(a,b,c); + a += i4; b += i5; c += i6; + mix(a,b,c); + a += i7; + final(a,b,c); +then use c as the hash value. If you have a variable length array of +4-byte integers to hash, use hashword(). If you have a byte array (like +a character string), use hashlittle(). If you have several byte arrays, or +a mix of things, see the comments above hashlittle(). + +Why is this so big? I read 12 bytes at a time into 3 4-byte integers, +then mix those integers. This is fast (you can do a lot more thorough +mixing with 12*3 instructions on 3 integers than you can with 3 instructions +on 1 byte), but shoehorning those bytes into integers efficiently is messy. +------------------------------------------------------------------------------- +*/ + +/* + * My best guess at if you are big-endian or little-endian. This may + * need adjustment. + */ +#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \ + __BYTE_ORDER == __LITTLE_ENDIAN) || \ + (defined(i386) || defined(__i386__) || defined(__i486__) || \ + defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL)) +# define HASH_LITTLE_ENDIAN 1 +# define HASH_BIG_ENDIAN 0 +#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \ + __BYTE_ORDER == __BIG_ENDIAN) || \ + (defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel)) +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 1 +#else +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 0 +#endif + +#define hashsize(n) ((uint32_t)1<<(n)) +#define hashmask(n) (hashsize(n)-1) +#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) + +/* +------------------------------------------------------------------------------- +mix -- mix 3 32-bit values reversibly. + +This is reversible, so any information in (a,b,c) before mix() is +still in (a,b,c) after mix(). + +If four pairs of (a,b,c) inputs are run through mix(), or through +mix() in reverse, there are at least 32 bits of the output that +are sometimes the same for one pair and different for another pair. +This was tested for: +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + is commonly produced by subtraction) look like a single 1-bit + difference. +* the base values were pseudorandom, all zero but one bit set, or + all zero plus a counter that starts at zero. + +Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that +satisfy this are + 4 6 8 16 19 4 + 9 15 3 18 27 15 + 14 9 3 7 17 3 +Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing +for "differ" defined as + with a one-bit base and a two-bit delta. I +used http://burtleburtle.net/bob/hash/avalanche.html to choose +the operations, constants, and arrangements of the variables. + +This does not achieve avalanche. There are input bits of (a,b,c) +that fail to affect some output bits of (a,b,c), especially of a. The +most thoroughly mixed value is c, but it doesn't really even achieve +avalanche in c. + +This allows some parallelism. Read-after-writes are good at doubling +the number of bits affected, so the goal of mixing pulls in the opposite +direction as the goal of parallelism. I did what I could. Rotates +seem to cost as much as shifts on every machine I could lay my hands +on, and rotates are much kinder to the top and bottom bits, so I used +rotates. +------------------------------------------------------------------------------- +*/ +#define mix(a,b,c) \ +{ \ + a -= c; a ^= rot(c, 4); c += b; \ + b -= a; b ^= rot(a, 6); a += c; \ + c -= b; c ^= rot(b, 8); b += a; \ + a -= c; a ^= rot(c,16); c += b; \ + b -= a; b ^= rot(a,19); a += c; \ + c -= b; c ^= rot(b, 4); b += a; \ +} + +/* +------------------------------------------------------------------------------- +final -- final mixing of 3 32-bit values (a,b,c) into c + +Pairs of (a,b,c) values differing in only a few bits will usually +produce values of c that look totally different. This was tested for +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + is commonly produced by subtraction) look like a single 1-bit + difference. +* the base values were pseudorandom, all zero but one bit set, or + all zero plus a counter that starts at zero. + +These constants passed: + 14 11 25 16 4 14 24 + 12 14 25 16 4 14 24 +and these came close: + 4 8 15 26 3 22 24 + 10 8 15 26 3 22 24 + 11 8 15 26 3 22 24 +------------------------------------------------------------------------------- +*/ +#define final(a,b,c) \ +{ \ + c ^= b; c -= rot(b,14); \ + a ^= c; a -= rot(c,11); \ + b ^= a; b -= rot(a,25); \ + c ^= b; c -= rot(b,16); \ + a ^= c; a -= rot(c,4); \ + b ^= a; b -= rot(a,14); \ + c ^= b; c -= rot(b,24); \ +} + + +/* +------------------------------------------------------------------------------- +hashlittle() -- hash a variable-length key into a 32-bit value + k : the key (the unaligned variable-length array of bytes) + length : the length of the key, counting by bytes + initval : can be any 4-byte value +Returns a 32-bit value. Every bit of the key affects every bit of +the return value. Two keys differing by one or two bits will have +totally different hash values. + +The best hash table sizes are powers of 2. There is no need to do +mod a prime (mod is sooo slow!). If you need less than 32 bits, +use a bitmask. For example, if you need only 10 bits, do + h = (h & hashmask(10)); +In which case, the hash table should have hashsize(10) elements. + +If you are hashing n strings (uint8_t **)k, do it like this: + for (i=0, h=0; i 12) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + + /*----------------------------- handle the last (probably partial) block */ + /* + * "k[2]&0xffffff" actually reads beyond the end of the string, but + * then masks off the part it's not allowed to read. Because the + * string is aligned, the masked-off tail is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticably faster for short strings (like English words). + */ +#ifndef VALGRIND + + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff; a+=k[0]; break; + case 6 : b+=k[1]&0xffff; a+=k[0]; break; + case 5 : b+=k[1]&0xff; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff; break; + case 2 : a+=k[0]&0xffff; break; + case 1 : a+=k[0]&0xff; break; + case 0 : return c; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + + const uint8_t *k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ + case 1 : a+=k8[0]; break; + case 0 : return c; + } + +#endif /* !valgrind */ + + } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { + const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ + const uint8_t *k8; + + /*--------------- all but last block: aligned reads and different mixing */ + while (length > 12) + { + a += k[0] + (((uint32_t)k[1])<<16); + b += k[2] + (((uint32_t)k[3])<<16); + c += k[4] + (((uint32_t)k[5])<<16); + mix(a,b,c); + length -= 12; + k += 6; + } + + /*----------------------------- handle the last (probably partial) block */ + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[4]+(((uint32_t)k[5])<<16); + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=k[4]; + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=k[2]; + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=k[0]; + break; + case 1 : a+=k8[0]; + break; + case 0 : return c; /* zero length requires no mixing */ + } + + } else { /* need to read the key one byte at a time */ + const uint8_t *k = (const uint8_t *)key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + a += ((uint32_t)k[1])<<8; + a += ((uint32_t)k[2])<<16; + a += ((uint32_t)k[3])<<24; + b += k[4]; + b += ((uint32_t)k[5])<<8; + b += ((uint32_t)k[6])<<16; + b += ((uint32_t)k[7])<<24; + c += k[8]; + c += ((uint32_t)k[9])<<8; + c += ((uint32_t)k[10])<<16; + c += ((uint32_t)k[11])<<24; + mix(a,b,c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch(length) /* all the case statements fall through */ + { + case 12: c+=((uint32_t)k[11])<<24; + case 11: c+=((uint32_t)k[10])<<16; + case 10: c+=((uint32_t)k[9])<<8; + case 9 : c+=k[8]; + case 8 : b+=((uint32_t)k[7])<<24; + case 7 : b+=((uint32_t)k[6])<<16; + case 6 : b+=((uint32_t)k[5])<<8; + case 5 : b+=k[4]; + case 4 : a+=((uint32_t)k[3])<<24; + case 3 : a+=((uint32_t)k[2])<<16; + case 2 : a+=((uint32_t)k[1])<<8; + case 1 : a+=k[0]; + break; + case 0 : return c; + } + } + + final(a,b,c); + return c; +} + unsigned long lh_char_hash(const void *k) { - unsigned int h = 0; - const char* data = (const char*)k; - - while( *data!=0 ) h = h*129 + (unsigned int)(*data++) + LH_PRIME; + static volatile int random_seed = -1; - return h; + if (random_seed == -1) { + int seed; + /* we can't use -1 as it is the unitialized sentinel */ + while ((seed = json_c_get_random_seed()) == -1); +#if defined __GNUC__ + __sync_val_compare_and_swap(&random_seed, -1, seed); +#elif defined _MSC_VER + InterlockedCompareExchange(&random_seed, seed, -1); +#else +#warning "racy random seed initializtion if used by multiple threads" + random_seed = seed; /* potentially racy */ +#endif + } + + return hashlittle((const char*)k, strlen((const char*)k), random_seed); } int lh_char_equal(const void *k1, const void *k2) diff --git a/3P/json/linkhash.h b/3P/json/linkhash.h index 378de0b7..950d09f3 100644 --- a/3P/json/linkhash.h +++ b/3P/json/linkhash.h @@ -246,7 +246,7 @@ extern struct lh_entry* lh_table_lookup_entry(struct lh_table *t, const void *k) * @return a pointer to the found value or NULL if it does not exist. * @deprecated Use lh_table_lookup_ex instead. */ -extern const void* lh_table_lookup(struct lh_table *t, const void *k); +THIS_FUNCTION_IS_DEPRECATED(extern const void* lh_table_lookup(struct lh_table *t, const void *k)); /** * Lookup a record in the table diff --git a/3P/json/math_compat.h b/3P/json/math_compat.h new file mode 100644 index 00000000..f40b8faf --- /dev/null +++ b/3P/json/math_compat.h @@ -0,0 +1,28 @@ +#ifndef __math_compat_h +#define __math_compat_h + +/* Define isnan and isinf on Windows/MSVC */ + +#ifndef HAVE_DECL_ISNAN +# ifdef HAVE_DECL__ISNAN +#include +#define isnan(x) _isnan(x) +# endif +#endif + +#ifndef HAVE_DECL_ISINF +# ifdef HAVE_DECL__FINITE +#include +#define isinf(x) (!_finite(x)) +# endif +#endif + +#ifndef HAVE_DECL_NAN +#error This platform does not have nan() +#endif + +#ifndef HAVE_DECL_INFINITY +#error This platform does not have INFINITY +#endif + +#endif diff --git a/3P/json/random_seed.c b/3P/json/random_seed.c new file mode 100644 index 00000000..3b520d41 --- /dev/null +++ b/3P/json/random_seed.c @@ -0,0 +1,237 @@ +/* + * random_seed.c + * + * Copyright (c) 2013 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include +#include "config.h" + +#define DEBUG_SEED(s) + + +#if defined ENABLE_RDRAND + +/* cpuid */ + +#if defined __GNUC__ && (defined __i386__ || defined __x86_64__) +#define HAS_X86_CPUID 1 + +static void do_cpuid(int regs[], int h) +{ + __asm__ __volatile__( +#if defined __x86_64__ + "pushq %%rbx;\n" +#else + "pushl %%ebx;\n" +#endif + "cpuid;\n" +#if defined __x86_64__ + "popq %%rbx;\n" +#else + "popl %%ebx;\n" +#endif + : "=a"(regs[0]), [ebx] "=r"(regs[1]), "=c"(regs[2]), "=d"(regs[3]) + : "a"(h)); +} + +#elif defined _MSC_VER + +#define HAS_X86_CPUID 1 +#define do_cpuid __cpuid + +#endif + +/* has_rdrand */ + +#if HAS_X86_CPUID + +static int has_rdrand() +{ + // CPUID.01H:ECX.RDRAND[bit 30] == 1 + int regs[4]; + do_cpuid(regs, 1); + return (regs[2] & (1 << 30)) != 0; +} + +#endif + +/* get_rdrand_seed - GCC x86 and X64 */ + +#if defined __GNUC__ && (defined __i386__ || defined __x86_64__) + +#define HAVE_RDRAND 1 + +static int get_rdrand_seed() +{ + DEBUG_SEED("get_rdrand_seed"); + int _eax; + // rdrand eax + __asm__ __volatile__("1: .byte 0x0F\n" + " .byte 0xC7\n" + " .byte 0xF0\n" + " jnc 1b;\n" + : "=a" (_eax)); + return _eax; +} + +#endif + +#if defined _MSC_VER + +#if _MSC_VER >= 1700 +#define HAVE_RDRAND 1 + +/* get_rdrand_seed - Visual Studio 2012 and above */ + +static int get_rdrand_seed() +{ + DEBUG_SEED("get_rdrand_seed"); + int r; + while (_rdrand32_step(&r) == 0); + return r; +} + +#elif defined _M_IX86 +#define HAVE_RDRAND 1 + +/* get_rdrand_seed - Visual Studio 2010 and below - x86 only */ + +static int get_rdrand_seed() +{ + DEBUG_SEED("get_rdrand_seed"); + int _eax; +retry: + // rdrand eax + __asm _emit 0x0F __asm _emit 0xC7 __asm _emit 0xF0 + __asm jnc retry + __asm mov _eax, eax + return _eax; +} + +#endif +#endif + +#endif /* defined ENABLE_RDRAND */ + + +/* has_dev_urandom */ + +#if defined (__APPLE__) || defined(__unix__) || defined(__linux__) + +#include +#include +#include +#include +#include +#include + +#define HAVE_DEV_RANDOM 1 + +static const char *dev_random_file = "/dev/urandom"; + +static int has_dev_urandom() +{ + struct stat buf; + if (stat(dev_random_file, &buf)) { + return 0; + } + return ((buf.st_mode & S_IFCHR) != 0); +} + + +/* get_dev_random_seed */ + +static int get_dev_random_seed() +{ + DEBUG_SEED("get_dev_random_seed"); + + int fd = open(dev_random_file, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "error opening %s: %s", dev_random_file, strerror(errno)); + exit(1); + } + + int r; + ssize_t nread = read(fd, &r, sizeof(r)); + if (nread != sizeof(r)) { + fprintf(stderr, "error read %s: %s", dev_random_file, strerror(errno)); + exit(1); + } + else if (nread != sizeof(r)) { + fprintf(stderr, "error short read %s", dev_random_file); + exit(1); + } + close(fd); + return r; +} + +#endif + + +/* get_cryptgenrandom_seed */ + +#ifdef WIN32 + +#define HAVE_CRYPTGENRANDOM 1 + +#include +#pragma comment(lib, "advapi32.lib") + +static int get_cryptgenrandom_seed() +{ + DEBUG_SEED("get_cryptgenrandom_seed"); + + HCRYPTPROV hProvider = 0; + int r; + + if (!CryptAcquireContextW(&hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { + fprintf(stderr, "error CryptAcquireContextW"); + exit(1); + } + + if (!CryptGenRandom(hProvider, sizeof(r), (BYTE*)&r)) { + fprintf(stderr, "error CryptGenRandom"); + exit(1); + } + + CryptReleaseContext(hProvider, 0); + + return r; +} + +#endif + + +/* get_time_seed */ + +#include + +static int get_time_seed() +{ + DEBUG_SEED("get_time_seed"); + + return (int)time(NULL) * 433494437; +} + + +/* json_c_get_random_seed */ + +int json_c_get_random_seed() +{ +#if HAVE_RDRAND + if (has_rdrand()) return get_rdrand_seed(); +#endif +#if HAVE_DEV_RANDOM + if (has_dev_urandom()) return get_dev_random_seed(); +#endif +#if HAVE_CRYPTGENRANDOM + return get_cryptgenrandom_seed(); +#endif + return get_time_seed(); +} diff --git a/3P/json/random_seed.h b/3P/json/random_seed.h new file mode 100644 index 00000000..7362d67d --- /dev/null +++ b/3P/json/random_seed.h @@ -0,0 +1,25 @@ +/* + * random_seed.h + * + * Copyright (c) 2013 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef seed_h +#define seed_h + +#ifdef __cplusplus +extern "C" { +#endif + +extern int json_c_get_random_seed(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/3P/json/tests/test_charcase.c b/3P/json/tests/test_charcase.c new file mode 100644 index 00000000..936afeee --- /dev/null +++ b/3P/json/tests/test_charcase.c @@ -0,0 +1,40 @@ +#include +#include +#include +#include +#include + +#include "json.h" +#include "json_tokener.h" + +static void test_case_parse(void); + +int main(int argc, char **argv) +{ + MC_SET_DEBUG(1); + + test_case_parse(); +} + +/* make sure only lowercase forms are parsed in strict mode */ +static void test_case_parse() +{ + struct json_tokener *tok; + json_object *new_obj; + + tok = json_tokener_new(); + json_tokener_set_flags(tok, JSON_TOKENER_STRICT); + + new_obj = json_tokener_parse_ex(tok, "True", 4); + assert (new_obj == NULL); + + new_obj = json_tokener_parse_ex(tok, "False", 5); + assert (new_obj == NULL); + + new_obj = json_tokener_parse_ex(tok, "Null", 4); + assert (new_obj == NULL); + + printf("OK\n"); + + json_tokener_free(tok); +} diff --git a/3P/json/tests/test_charcase.expected b/3P/json/tests/test_charcase.expected new file mode 100644 index 00000000..d86bac9d --- /dev/null +++ b/3P/json/tests/test_charcase.expected @@ -0,0 +1 @@ +OK diff --git a/3P/json/tests/parse_int64.test b/3P/json/tests/test_charcase.test old mode 100755 new mode 100644 similarity index 85% rename from 3P/json/tests/parse_int64.test rename to 3P/json/tests/test_charcase.test index 2b7fbfb6..c967475c --- a/3P/json/tests/parse_int64.test +++ b/3P/json/tests/test_charcase.test @@ -8,5 +8,5 @@ if test -z "$srcdir"; then fi . "$srcdir/test-defs.sh" -run_output_test test_parse_int64 +run_output_test test_charcase exit $? diff --git a/3P/json/tests/test_locale.expected b/3P/json/tests/test_locale.expected index 80317851..0068be4a 100644 --- a/3P/json/tests/test_locale.expected +++ b/3P/json/tests/test_locale.expected @@ -1,2 +1,2 @@ -new_obj.to_string()=[ 1.200000, 3.400000, 123456.780000, 5.000000, 23000000000.000000 ] -new_obj.to_string()=[1.2,3.4,123456.78,5.0,23000000000.0] +new_obj.to_string()=[ 1.2, 3.4, 123456.78, 5.0, 2.3e10 ] +new_obj.to_string()=[1.2,3.4,123456.78,5.0,2.3e10] diff --git a/3P/json/tests/test_parse.c b/3P/json/tests/test_parse.c index 1a59a6bd..8808d0fa 100644 --- a/3P/json/tests/test_parse.c +++ b/3P/json/tests/test_parse.c @@ -47,6 +47,38 @@ static void test_basic_parse() printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj)); json_object_put(new_obj); + new_obj = json_tokener_parse("NaN"); + printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj)); + json_object_put(new_obj); + + new_obj = json_tokener_parse("-NaN"); /* non-sensical, returns null */ + printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj)); + json_object_put(new_obj); + + new_obj = json_tokener_parse("Inf"); /* must use full string, returns null */ + printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj)); + json_object_put(new_obj); + + new_obj = json_tokener_parse("inf"); /* must use full string, returns null */ + printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj)); + json_object_put(new_obj); + + new_obj = json_tokener_parse("Infinity"); + printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj)); + json_object_put(new_obj); + + new_obj = json_tokener_parse("infinity"); + printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj)); + json_object_put(new_obj); + + new_obj = json_tokener_parse("-Infinity"); + printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj)); + json_object_put(new_obj); + + new_obj = json_tokener_parse("-infinity"); + printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj)); + json_object_put(new_obj); + new_obj = json_tokener_parse("True"); printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj)); json_object_put(new_obj); diff --git a/3P/json/tests/test_parse.expected b/3P/json/tests/test_parse.expected index f0af0fa1..d49cbbb1 100644 --- a/3P/json/tests/test_parse.expected +++ b/3P/json/tests/test_parse.expected @@ -3,9 +3,17 @@ new_obj.to_string()="foo" new_obj.to_string()="foo" new_obj.to_string()="ABC" new_obj.to_string()=null +new_obj.to_string()=NaN +new_obj.to_string()=null +new_obj.to_string()=null +new_obj.to_string()=null +new_obj.to_string()=Infinity +new_obj.to_string()=Infinity +new_obj.to_string()=-Infinity +new_obj.to_string()=-Infinity new_obj.to_string()=true new_obj.to_string()=12 -new_obj.to_string()=12.300000 +new_obj.to_string()=12.3 new_obj.to_string()=[ "\n" ] new_obj.to_string()=[ "\nabc\n" ] new_obj.to_string()=[ null ]