From f76966b4387607f54f294630ffc6d89a78530787 Mon Sep 17 00:00:00 2001 From: Petri Lehtinen Date: Fri, 26 Mar 2010 21:29:31 +0200 Subject: [PATCH 1/5] Enhance tests for null byte --- .../escaped-null-byte-in-string/error | 2 ++ .../escaped-null-byte-in-string/input | 1 + .../invalid-strip/null-byte-in-string/error | 2 +- .../invalid-strip/null-byte-in-string/input | Bin 34 -> 27 bytes .../invalid-strip/null-byte-outside-string/error | 2 ++ .../invalid-strip/null-byte-outside-string/input | Bin 0 -> 2 bytes .../invalid/escaped-null-byte-in-string/error | 2 ++ .../invalid/escaped-null-byte-in-string/input | 1 + test/suites/invalid/null-byte-in-string/error | 2 +- test/suites/invalid/null-byte-in-string/input | Bin 35 -> 28 bytes .../suites/invalid/null-byte-outside-string/error | 2 ++ .../suites/invalid/null-byte-outside-string/input | Bin 0 -> 3 bytes 12 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 test/suites/invalid-strip/escaped-null-byte-in-string/error create mode 100644 test/suites/invalid-strip/escaped-null-byte-in-string/input create mode 100644 test/suites/invalid-strip/null-byte-outside-string/error create mode 100644 test/suites/invalid-strip/null-byte-outside-string/input create mode 100644 test/suites/invalid/escaped-null-byte-in-string/error create mode 100644 test/suites/invalid/escaped-null-byte-in-string/input create mode 100644 test/suites/invalid/null-byte-outside-string/error create mode 100644 test/suites/invalid/null-byte-outside-string/input diff --git a/test/suites/invalid-strip/escaped-null-byte-in-string/error b/test/suites/invalid-strip/escaped-null-byte-in-string/error new file mode 100644 index 0000000..0fa36e2 --- /dev/null +++ b/test/suites/invalid-strip/escaped-null-byte-in-string/error @@ -0,0 +1,2 @@ +1 +\u0000 is not allowed diff --git a/test/suites/invalid-strip/escaped-null-byte-in-string/input b/test/suites/invalid-strip/escaped-null-byte-in-string/input new file mode 100644 index 0000000..60f7f7b --- /dev/null +++ b/test/suites/invalid-strip/escaped-null-byte-in-string/input @@ -0,0 +1 @@ +["\u0000 (null byte not allowed)"] \ No newline at end of file diff --git a/test/suites/invalid-strip/null-byte-in-string/error b/test/suites/invalid-strip/null-byte-in-string/error index 0fa36e2..273f6d1 100644 --- a/test/suites/invalid-strip/null-byte-in-string/error +++ b/test/suites/invalid-strip/null-byte-in-string/error @@ -1,2 +1,2 @@ 1 -\u0000 is not allowed +control character 0x0 near '"null byte ' diff --git a/test/suites/invalid-strip/null-byte-in-string/input b/test/suites/invalid-strip/null-byte-in-string/input index 60f7f7bbe6b4cdfcb367c68bc81112c40bdedef2..c0d82be95b5b406347e4d8330c2d85d99b5240a7 100644 GIT binary patch literal 27 icma!6$}7#uQAnyRNmXD_$jdKLNX*H}FHcQTiUk0E%Lyd_ literal 34 ncma!6iYYY!0tJn{(wrQHq{@<1g}nR{g~Xhk{PNTkO{G`>x77;= diff --git a/test/suites/invalid-strip/null-byte-outside-string/error b/test/suites/invalid-strip/null-byte-outside-string/error new file mode 100644 index 0000000..f0e68ec --- /dev/null +++ b/test/suites/invalid-strip/null-byte-outside-string/error @@ -0,0 +1,2 @@ +1 +invalid token near end of file diff --git a/test/suites/invalid-strip/null-byte-outside-string/input b/test/suites/invalid-strip/null-byte-outside-string/input new file mode 100644 index 0000000000000000000000000000000000000000..75447d8282481c980d1a33a63b248bb4d2d46c87 GIT binary patch literal 2 Jcma!M0002E09*h7 literal 0 HcmV?d00001 diff --git a/test/suites/invalid/escaped-null-byte-in-string/error b/test/suites/invalid/escaped-null-byte-in-string/error new file mode 100644 index 0000000..0fa36e2 --- /dev/null +++ b/test/suites/invalid/escaped-null-byte-in-string/error @@ -0,0 +1,2 @@ +1 +\u0000 is not allowed diff --git a/test/suites/invalid/escaped-null-byte-in-string/input b/test/suites/invalid/escaped-null-byte-in-string/input new file mode 100644 index 0000000..22ae82b --- /dev/null +++ b/test/suites/invalid/escaped-null-byte-in-string/input @@ -0,0 +1 @@ +["\u0000 (null byte not allowed)"] diff --git a/test/suites/invalid/null-byte-in-string/error b/test/suites/invalid/null-byte-in-string/error index 0fa36e2..273f6d1 100644 --- a/test/suites/invalid/null-byte-in-string/error +++ b/test/suites/invalid/null-byte-in-string/error @@ -1,2 +1,2 @@ 1 -\u0000 is not allowed +control character 0x0 near '"null byte ' diff --git a/test/suites/invalid/null-byte-in-string/input b/test/suites/invalid/null-byte-in-string/input index 22ae82b2a5634ce945c75a89dce7b39b9b0bde2b..268d1f19435baef5ad7a609c65a951f6c8f6b30f 100644 GIT binary patch literal 28 jcma!6$}7#uQAnyRNmXD_$jdKLNX*H}FHcQTisb?Thx!RG literal 35 ocma!6iYYY!0tJn{(wrQHq{@<1g}nR{g~Xhk{PNTkO{G{a0K(=A5dZ)H diff --git a/test/suites/invalid/null-byte-outside-string/error b/test/suites/invalid/null-byte-outside-string/error new file mode 100644 index 0000000..f0e68ec --- /dev/null +++ b/test/suites/invalid/null-byte-outside-string/error @@ -0,0 +1,2 @@ +1 +invalid token near end of file diff --git a/test/suites/invalid/null-byte-outside-string/input b/test/suites/invalid/null-byte-outside-string/input new file mode 100644 index 0000000000000000000000000000000000000000..aa550eb0cd16b047736975b6c92ebd989d1981b3 GIT binary patch literal 3 Kcma!M-~s>v9sp(l literal 0 HcmV?d00001 From bb89a5d4d377f7b6b8c0c9ce3a2698b112447d08 Mon Sep 17 00:00:00 2001 From: Petri Lehtinen Date: Fri, 26 Mar 2010 21:59:53 +0200 Subject: [PATCH 2/5] Estimate real number underflows with 0.0 Earlier it was a decoding error. --- src/load.c | 9 +-------- test/suites/invalid-strip/real-underflow/error | 2 -- test/suites/invalid/real-underflow/error | 2 -- .../{invalid-strip => valid-strip}/real-underflow/input | 0 test/suites/valid-strip/real-underflow/output | 1 + test/suites/{invalid => valid}/real-underflow/input | 0 test/suites/valid/real-underflow/output | 1 + 7 files changed, 3 insertions(+), 12 deletions(-) delete mode 100644 test/suites/invalid-strip/real-underflow/error delete mode 100644 test/suites/invalid/real-underflow/error rename test/suites/{invalid-strip => valid-strip}/real-underflow/input (100%) create mode 100644 test/suites/valid-strip/real-underflow/output rename test/suites/{invalid => valid}/real-underflow/input (100%) create mode 100644 test/suites/valid/real-underflow/output diff --git a/src/load.c b/src/load.c index baf3183..649609a 100644 --- a/src/load.c +++ b/src/load.c @@ -483,14 +483,7 @@ static int lex_scan_number(lex_t *lex, char c, json_error_t *error) value = strtod(saved_text, &end); assert(end == saved_text + lex->saved_text.length); - if(value == 0 && errno == ERANGE) { - error_set(error, lex, "real number underflow"); - goto out; - } - - /* Cannot test for +/-HUGE_VAL because the HUGE_VAL constant is - only defined in C99 mode. So let's trust in sole errno. */ - else if(errno == ERANGE) { + if(errno == ERANGE && value != 0) { error_set(error, lex, "real number overflow"); goto out; } diff --git a/test/suites/invalid-strip/real-underflow/error b/test/suites/invalid-strip/real-underflow/error deleted file mode 100644 index 1b65d40..0000000 --- a/test/suites/invalid-strip/real-underflow/error +++ /dev/null @@ -1,2 +0,0 @@ -1 -real number underflow near '123e-10000000' diff --git a/test/suites/invalid/real-underflow/error b/test/suites/invalid/real-underflow/error deleted file mode 100644 index 1b65d40..0000000 --- a/test/suites/invalid/real-underflow/error +++ /dev/null @@ -1,2 +0,0 @@ -1 -real number underflow near '123e-10000000' diff --git a/test/suites/invalid-strip/real-underflow/input b/test/suites/valid-strip/real-underflow/input similarity index 100% rename from test/suites/invalid-strip/real-underflow/input rename to test/suites/valid-strip/real-underflow/input diff --git a/test/suites/valid-strip/real-underflow/output b/test/suites/valid-strip/real-underflow/output new file mode 100644 index 0000000..92df1df --- /dev/null +++ b/test/suites/valid-strip/real-underflow/output @@ -0,0 +1 @@ +[0.0] \ No newline at end of file diff --git a/test/suites/invalid/real-underflow/input b/test/suites/valid/real-underflow/input similarity index 100% rename from test/suites/invalid/real-underflow/input rename to test/suites/valid/real-underflow/input diff --git a/test/suites/valid/real-underflow/output b/test/suites/valid/real-underflow/output new file mode 100644 index 0000000..92df1df --- /dev/null +++ b/test/suites/valid/real-underflow/output @@ -0,0 +1 @@ +[0.0] \ No newline at end of file From c7c2edae8a6a2ba25c31d50c9f44bbbf080889da Mon Sep 17 00:00:00 2001 From: Petri Lehtinen Date: Fri, 26 Mar 2010 22:00:41 +0200 Subject: [PATCH 3/5] doc: Add chapter on RFC conformance Thanks to Deron Meranda for providing the initial text. --- doc/conformance.rst | 102 ++++++++++++++++++++++++++++++++++++++++++++ doc/index.rst | 1 + 2 files changed, 103 insertions(+) create mode 100644 doc/conformance.rst diff --git a/doc/conformance.rst b/doc/conformance.rst new file mode 100644 index 0000000..785a94d --- /dev/null +++ b/doc/conformance.rst @@ -0,0 +1,102 @@ +*************** +RFC Conformance +*************** + +JSON is specified in :rfc:`4627`, *"The application/json Media Type +for JavaScript Object Notation (JSON)"*. This chapter discusses +Jansson's conformance to this specification. + +Character Encoding +================== + +Jansson only supports UTF-8 encoded JSON texts. It does not support or +auto-detect any of the other encodings mentioned in the RFC, namely +UTF-16LE, UTF-16BE, UTF-32LE or UTF-32BE. Pure ASCII is supported, as +it's a subset of UTF-8. + +Strings +======= + +JSON strings are mapped to C-style null-terminated character arrays, +and UTF-8 encoding is used internally. Strings may not contain +embedded null characters, not even escaped ones. + +For example, trying to decode the following JSON text leads to a parse +error:: + + ["this string contains the null character: \u0000"] + +All other Unicode codepoints U+0001 through U+10FFFF are allowed. + +Numbers +======= + +Real vs. Integer +---------------- + +JSON makes no distinction between real and integer numbers; Jansson +does. Real numbers are mapped to the ``double`` type and integers to +the ``int`` type. + +A JSON number is considered to be a real number if its lexical +representation includes one of ``e``, ``E``, or ``.``; regardless if +its actual numeric value is a true integer (e.g., all of ``1E6``, +``3.0``, ``400E-2``, and ``3.14E3`` are mathematical integers, but +will be treated as real values). + +All other JSON numbers are considered integers. + +When encoding to JSON, real values are always represented +with a fractional part; e.g., the ``double`` value 3.0 will be +represented in JSON as ``3.0``, not ``3``. + +Overflow, Underflow & Precision +------------------------------- + +Real numbers whose absolute values are too small to be represented in +a C double will be silently estimated with 0.0. Thus, depending on +platform, JSON numbers very close to zero such as 1E-999 may result in +0.0. + +Real numbers whose absolute values are too large to be represented in +a C ``double`` type will result in an overflow error (a JSON decoding +error). Thus, depending on platform, JSON numbers like 1E+999 or +-1E+999 may result in a parsing error. + +Likewise, integer numbers whose absolute values are too large to be +represented in the ``int`` type will result in an overflow error (a +JSON decoding error). Thus, depending on platform, JSON numbers like +1000000000000000 may result in parsing error. + +Parsing JSON real numbers may result in a loss of precision. As long +as overflow does not occur (i.e. a total loss of precision), the +rounded approximate value is silently used. Thus the JSON number +1.000000000000000005 may, depending on platform, result in the +``double`` value 1.0. + +Signed zeros +------------ + +JSON makes no statement about what a number means; however Javascript +(ECMAscript) does state that +0.0 and -0.0 must be treated as being +distinct values, i.e. -0.0 |not-equal| 0.0. Jansson relies on the +underlying floating point library in the C environment in which it is +compiled. Therefore it is platform-dependent whether 0.0 and -0.0 will +be distinct values. Most platforms that use the IEEE 754 +floating-point standard will support signed zeros. + +Note that this only applies to floating-point; neither JSON, C, or +IEEE support the concept of signed integer zeros. + +.. |not-equal| unicode:: U+2260 + +Types +----- + +No support is provided in Jansson for any C numeric types other than +``int`` and ``double``. This excludes things such as unsigned types, +``long``, ``long long``, ``long double``, etc. Obviously, shorter +types like ``short`` and ``float`` are implicitly handled via the +ordinary C type coercion rules (subject to overflow semantics). Also, +no support or hooks are provided for any supplemental "bignum" type +add-on packages. diff --git a/doc/index.rst b/doc/index.rst index d6018b9..c7321df 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -34,6 +34,7 @@ Contents gettingstarted tutorial + conformance apiref changes From 29ee3832cf1cee4cfebad8e440b58877437c4615 Mon Sep 17 00:00:00 2001 From: Petri Lehtinen Date: Sun, 28 Mar 2010 21:14:08 +0300 Subject: [PATCH 4/5] Support compilers that don't have the "inline" keyword Use AC_C_INLINE autoconf macro, include config.h where needed, and add a define of JSON_INLINE to jansson.h that has the correct "inline" keyword. --- .gitignore | 1 + configure.ac | 9 +++++++++ src/hashtable.c | 2 ++ src/{jansson.h => jansson.h.in} | 21 +++++++++++++-------- src/value.c | 3 +++ 5 files changed, 28 insertions(+), 8 deletions(-) rename src/{jansson.h => jansson.h.in} (94%) diff --git a/.gitignore b/.gitignore index 4c4115c..fe9488d 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,4 @@ missing stamp-h1 *.pyc *.pc +/src/jansson.h diff --git a/configure.ac b/configure.ac index 6bc9064..746bff2 100644 --- a/configure.ac +++ b/configure.ac @@ -17,6 +17,14 @@ AC_PROG_LIBTOOL # Checks for typedefs, structures, and compiler characteristics. AC_TYPE_INT32_T +AC_C_INLINE +case $ac_cv_c_inline in + yes) json_inline=inline;; + no) json_inline=;; + *) json_inline=$ac_cv_c_inline;; +esac +AC_SUBST([json_inline]) + # Checks for library functions. AC_CONFIG_FILES([ @@ -24,6 +32,7 @@ AC_CONFIG_FILES([ Makefile doc/Makefile src/Makefile + src/jansson.h test/Makefile test/bin/Makefile test/suites/Makefile diff --git a/src/hashtable.c b/src/hashtable.c index 1f8abf4..666ba2a 100644 --- a/src/hashtable.c +++ b/src/hashtable.c @@ -5,6 +5,8 @@ * it under the terms of the MIT license. See LICENSE for details. */ +#include + #include #include "hashtable.h" diff --git a/src/jansson.h b/src/jansson.h.in similarity index 94% rename from src/jansson.h rename to src/jansson.h.in index 78094a7..539400a 100644 --- a/src/jansson.h +++ b/src/jansson.h.in @@ -10,7 +10,10 @@ #include -#ifdef __cplusplus +#ifndef __cplusplus +#define JSON_INLINE @json_inline@ +#else +#define JSON_INLINE inline extern "C" { #endif @@ -56,7 +59,8 @@ json_t *json_true(void); json_t *json_false(void); json_t *json_null(void); -static inline json_t *json_incref(json_t *json) +static JSON_INLINE +json_t *json_incref(json_t *json) { if(json && json->refcount != (unsigned int)-1) ++json->refcount; @@ -66,7 +70,8 @@ static inline json_t *json_incref(json_t *json) /* do not call json_delete directly */ void json_delete(json_t *json); -static inline void json_decref(json_t *json) +static JSON_INLINE +void json_decref(json_t *json) { if(json && json->refcount != (unsigned int)-1 && --json->refcount == 0) json_delete(json); @@ -87,13 +92,13 @@ void *json_object_iter_next(json_t *object, void *iter); const char *json_object_iter_key(void *iter); json_t *json_object_iter_value(void *iter); -static inline +static JSON_INLINE int json_object_set(json_t *object, const char *key, json_t *value) { return json_object_set_new(object, key, json_incref(value)); } -static inline +static JSON_INLINE int json_object_set_nocheck(json_t *object, const char *key, json_t *value) { return json_object_set_new_nocheck(object, key, json_incref(value)); @@ -108,19 +113,19 @@ int json_array_remove(json_t *array, unsigned int index); int json_array_clear(json_t *array); int json_array_extend(json_t *array, json_t *other); -static inline +static JSON_INLINE int json_array_set(json_t *array, unsigned int index, json_t *value) { return json_array_set_new(array, index, json_incref(value)); } -static inline +static JSON_INLINE int json_array_append(json_t *array, json_t *value) { return json_array_append_new(array, json_incref(value)); } -static inline +static JSON_INLINE int json_array_insert(json_t *array, unsigned int index, json_t *value) { return json_array_insert_new(array, index, json_incref(value)); diff --git a/src/value.c b/src/value.c index 3fa7ee3..3788cb1 100644 --- a/src/value.c +++ b/src/value.c @@ -6,6 +6,9 @@ */ #define _GNU_SOURCE + +#include + #include #include From 024106bbfbd7376656876aa8bd00a333d5ada10c Mon Sep 17 00:00:00 2001 From: Petri Lehtinen Date: Sun, 28 Mar 2010 21:16:21 +0300 Subject: [PATCH 5/5] Require autoconf 2.60 The AC_TYPE_INT32_T macro first appeared in autoconf 2.60. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 746bff2..0774c03 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -AC_PREREQ([2.59]) +AC_PREREQ([2.60]) AC_INIT([jansson], [1.2], [petri@digip.org]) AM_INIT_AUTOMAKE([1.10 foreign])