diff --git a/json_object.c b/json_object.c index cc4c1e2..d67a61a 100644 --- a/json_object.c +++ b/json_object.c @@ -721,6 +721,7 @@ int32_t json_object_get_int(const struct json_object *jso) int64_t cint64 = 0; double cdouble; enum json_type o_type; + errno = 0; if (!jso) return 0; @@ -756,17 +757,34 @@ int32_t json_object_get_int(const struct json_object *jso) { case json_type_int: /* Make sure we return the correct values for out of range numbers. */ - if (cint64 <= INT32_MIN) + if (cint64 < INT32_MIN) + { + errno = ERANGE; return INT32_MIN; - if (cint64 >= INT32_MAX) + } + if (cint64 > INT32_MAX) + { + errno = ERANGE; return INT32_MAX; + } return (int32_t)cint64; case json_type_double: cdouble = JC_DOUBLE_C(jso)->c_double; - if (cdouble <= INT32_MIN) + if (cdouble < INT32_MIN) + { + errno = ERANGE; return INT32_MIN; - if (cdouble >= INT32_MAX) + } + if (cdouble > INT32_MAX) + { + errno = ERANGE; return INT32_MAX; + } + if (isnan(cdouble)) + { + errno = EINVAL; + return INT32_MIN; + } return (int32_t)cdouble; case json_type_boolean: return JC_BOOL_C(jso)->c_boolean; default: return 0; @@ -801,6 +819,7 @@ struct json_object *json_object_new_uint64(uint64_t i) int64_t json_object_get_int64(const struct json_object *jso) { int64_t cint; + errno = 0; if (!jso) return 0; @@ -813,8 +832,11 @@ int64_t json_object_get_int64(const struct json_object *jso) { case json_object_int_type_int64: return jsoint->cint.c_int64; case json_object_int_type_uint64: - if (jsoint->cint.c_uint64 >= INT64_MAX) + if (jsoint->cint.c_uint64 > INT64_MAX) + { + errno = ERANGE; return INT64_MAX; + } return (int64_t)jsoint->cint.c_uint64; default: json_abort("invalid cint_type"); } @@ -822,10 +844,21 @@ int64_t json_object_get_int64(const struct json_object *jso) case json_type_double: // INT64_MAX can't be exactly represented as a double // so cast to tell the compiler it's ok to round up. - if (JC_DOUBLE_C(jso)->c_double >= (double)INT64_MAX) + if (JC_DOUBLE_C(jso)->c_double > (double)INT64_MAX) + { + errno = ERANGE; return INT64_MAX; - if (JC_DOUBLE_C(jso)->c_double <= INT64_MIN) + } + if (JC_DOUBLE_C(jso)->c_double < (double)INT64_MIN) + { + errno = ERANGE; + return INT64_MIN; + } + if (isnan(JC_DOUBLE_C(jso)->c_double)) + { + errno = EINVAL; return INT64_MIN; + } return (int64_t)JC_DOUBLE_C(jso)->c_double; case json_type_boolean: return JC_BOOL_C(jso)->c_boolean; case json_type_string: @@ -839,6 +872,7 @@ int64_t json_object_get_int64(const struct json_object *jso) uint64_t json_object_get_uint64(const struct json_object *jso) { uint64_t cuint; + errno = 0; if (!jso) return 0; @@ -851,7 +885,10 @@ uint64_t json_object_get_uint64(const struct json_object *jso) { case json_object_int_type_int64: if (jsoint->cint.c_int64 < 0) + { + errno = ERANGE; return 0; + } return (uint64_t)jsoint->cint.c_int64; case json_object_int_type_uint64: return jsoint->cint.c_uint64; default: json_abort("invalid cint_type"); @@ -860,10 +897,21 @@ uint64_t json_object_get_uint64(const struct json_object *jso) case json_type_double: // UINT64_MAX can't be exactly represented as a double // so cast to tell the compiler it's ok to round up. - if (JC_DOUBLE_C(jso)->c_double >= (double)UINT64_MAX) + if (JC_DOUBLE_C(jso)->c_double > (double)UINT64_MAX) + { + errno = ERANGE; return UINT64_MAX; + } if (JC_DOUBLE_C(jso)->c_double < 0) + { + errno = ERANGE; + return 0; + } + if (isnan(JC_DOUBLE_C(jso)->c_double)) + { + errno = EINVAL; return 0; + } return (uint64_t)JC_DOUBLE_C(jso)->c_double; case json_type_boolean: return JC_BOOL_C(jso)->c_boolean; case json_type_string: diff --git a/json_object.h b/json_object.h index b767b02..e21138e 100644 --- a/json_object.h +++ b/json_object.h @@ -693,7 +693,7 @@ JSON_EXPORT struct json_object *json_object_new_boolean(json_bool b); * The type is coerced to a json_bool if the passed object is not a json_bool. * integer and double objects will return 0 if there value is zero * or 1 otherwise. If the passed object is a string it will return - * 1 if it has a non zero length. + * 1 if it has a non zero length. * If any other object type is passed 0 will be returned, even non-empty * json_type_array and json_type_object objects. * @@ -739,9 +739,12 @@ JSON_EXPORT struct json_object *json_object_new_uint64(uint64_t i); /** Get the int value of a json_object * * The type is coerced to a int if the passed object is not a int. - * double objects will return their integer conversion. Strings will be - * parsed as an integer. If no conversion exists then 0 is returned - * and errno is set to EINVAL. null is equivalent to 0 (no error values set) + * double objects will return their integer conversion except for NaN values + * which return INT32_MIN and the errno is set to EINVAL. + * Strings will be parsed as an integer. If no conversion exists then 0 is + * returned and errno is set to EINVAL. null is equivalent to 0 (no error values + * set). + * Sets errno to ERANGE if the value exceeds the range of int. * * Note that integers are stored internally as 64-bit values. * If the value of too big or too small to fit into 32-bit, INT32_MAX or @@ -783,8 +786,12 @@ JSON_EXPORT int json_object_int_inc(struct json_object *obj, int64_t val); /** Get the int value of a json_object * * The type is coerced to a int64 if the passed object is not a int64. - * double objects will return their int64 conversion. Strings will be - * parsed as an int64. If no conversion exists then 0 is returned. + * double objects will return their int64 conversion except for NaN values + * which return INT64_MIN and the errno is set to EINVAL. + * Strings will be parsed as an int64. If no conversion exists then 0 is + * returned and errno is set to EINVAL. null is equivalent to 0 (no error values + * set). + * Sets errno to ERANGE if the value exceeds the range of int64. * * NOTE: Set errno to 0 directly before a call to this function to determine * whether or not conversion was successful (it does not clear the value for @@ -798,8 +805,12 @@ JSON_EXPORT int64_t json_object_get_int64(const struct json_object *obj); /** Get the uint value of a json_object * * The type is coerced to a uint64 if the passed object is not a uint64. - * double objects will return their uint64 conversion. Strings will be - * parsed as an uint64. If no conversion exists then 0 is returned. + * double objects will return their uint64 conversion except for NaN values + * which return 0 and the errno is set to EINVAL. + * Strings will be parsed as an uint64. If no conversion exists then 0 is + * returned and errno is set to EINVAL. null is equivalent to 0 (no error values + * set). + * Sets errno to ERANGE if the value exceeds the range of uint64. * * NOTE: Set errno to 0 directly before a call to this function to determine * whether or not conversion was successful (it does not clear the value for diff --git a/tests/test_cast.c b/tests/test_cast.c index 02e19ea..32aa92e 100644 --- a/tests/test_cast.c +++ b/tests/test_cast.c @@ -36,6 +36,7 @@ int main(int argc, char **argv) \"array_with_zero\": [ 0 ],\n\ \"empty_object\": {},\n\ \"nonempty_object\": { \"a\": 123 },\n\ + \"nan\": NaN,\n\ }"; /* Note: 2147483649 = INT_MAX + 2 */ /* Note: 9223372036854775809 = INT64_MAX + 2 */ @@ -62,6 +63,7 @@ int main(int argc, char **argv) getit(new_obj, "array_with_zero"); getit(new_obj, "empty_object"); getit(new_obj, "nonempty_object"); + getit(new_obj, "nan"); // Now check the behaviour of the json_object_is_type() function. printf("\n================================\n"); @@ -75,6 +77,7 @@ int main(int argc, char **argv) checktype(new_obj, "int64_number"); checktype(new_obj, "negative_number"); checktype(new_obj, "a_null"); + checktype(new_obj, "nan"); json_object_put(new_obj); diff --git a/tests/test_cast.expected b/tests/test_cast.expected index 6a19de9..3ec6107 100644 --- a/tests/test_cast.expected +++ b/tests/test_cast.expected @@ -12,6 +12,7 @@ Parsed input: { "array_with_zero": [ 0 ], "empty_object": {}, "nonempty_object": { "a": 123 }, + "nan": NaN, } Result is not NULL new_obj.string_of_digits json_object_get_type()=string @@ -92,6 +93,12 @@ new_obj.nonempty_object json_object_get_int64()=0 new_obj.nonempty_object json_object_get_uint64()=0 new_obj.nonempty_object json_object_get_boolean()=0 new_obj.nonempty_object json_object_get_double()=0.000000 +new_obj.nan json_object_get_type()=double +new_obj.nan json_object_get_int()=-2147483648 +new_obj.nan json_object_get_int64()=-9223372036854775808 +new_obj.nan json_object_get_uint64()=0 +new_obj.nan json_object_get_boolean()=1 +new_obj.nan json_object_get_double()=nan ================================ json_object_is_type: null,boolean,double,int,object,array,string @@ -104,3 +111,4 @@ new_obj.boolean_false : 0,1,0,0,0,0,0 new_obj.int64_number : 0,0,0,1,0,0,0 new_obj.negative_number : 0,0,0,1,0,0,0 new_obj.a_null : 1,0,0,0,0,0,0 +new_obj.nan : 0,0,1,0,0,0,0 diff --git a/tests/test_int_get.c b/tests/test_int_get.c index be30364..e694fb3 100644 --- a/tests/test_int_get.c +++ b/tests/test_int_get.c @@ -4,9 +4,12 @@ #include #include #include +#include #include "json.h" +#define I32_MAX_S "2147483647" +#define I32_MIN_S "-2147483648" #define I64_MAX_S "9223372036854775807" #define I64_OVER "9223372036854775808" #define I64_MIN_S "-9223372036854775808" @@ -25,6 +28,7 @@ #define N_I64 json_object_new_int64 #define N_U64 json_object_new_uint64 #define N_STR json_object_new_string +#define N_DBL json_object_new_double int main(int argc, char **argv) { @@ -41,10 +45,15 @@ int main(int argc, char **argv) CHECK_GET_INT(N_I64(INT32_MAX), INT32_MAX && errno == 0); CHECK_GET_INT(N_I64(INT32_MIN), INT32_MIN && errno == 0); - CHECK_GET_INT(N_I64(INT64_MAX), INT32_MAX && errno == 0); - CHECK_GET_INT(N_I64(INT64_MIN), INT32_MIN && errno == 0); - CHECK_GET_INT(N_STR(I64_MAX_S), INT32_MAX && errno == 0); - CHECK_GET_INT(N_STR(I64_MIN_S), INT32_MIN && errno == 0); + CHECK_GET_INT(N_I64(INT64_MAX), INT32_MAX && errno == ERANGE); + CHECK_GET_INT(N_I64(INT64_MIN), INT32_MIN && errno == ERANGE); + CHECK_GET_INT(N_STR(I32_MAX_S), INT32_MAX && errno == 0); + CHECK_GET_INT(N_STR(I32_MIN_S), INT32_MIN && errno == 0); + CHECK_GET_INT(N_STR(I64_MAX_S), INT32_MAX && errno == ERANGE); + CHECK_GET_INT(N_STR(I64_MIN_S), INT32_MIN && errno == ERANGE); + CHECK_GET_INT(N_DBL(INFINITY), INT32_MAX && errno == ERANGE); + CHECK_GET_INT(N_DBL(-INFINITY), INT32_MIN && errno == ERANGE); + CHECK_GET_INT(N_DBL(NAN), INT32_MIN && errno == EINVAL); printf("INT GET PASSED\n"); CHECK_GET_INT64(N_I64(INT64_MAX), INT64_MAX && errno == 0); @@ -53,11 +62,17 @@ int main(int argc, char **argv) CHECK_GET_INT64(N_STR(I64_MIN_S), INT64_MIN && errno == 0); CHECK_GET_INT64(N_STR(I64_OVER), INT64_MAX && errno == ERANGE); CHECK_GET_INT64(N_STR(I64_UNDER), INT64_MIN && errno == ERANGE); + CHECK_GET_INT64(N_DBL(INFINITY), INT64_MAX && errno == ERANGE); + CHECK_GET_INT64(N_DBL(-INFINITY), INT64_MIN && errno == ERANGE); + CHECK_GET_INT64(N_DBL(NAN), INT64_MIN && errno == EINVAL); printf("INT64 GET PASSED\n"); CHECK_GET_UINT64(N_U64(UINT64_MAX), UINT64_MAX && errno == 0); CHECK_GET_UINT64(N_U64(-1), UINT64_MAX && errno == 0); CHECK_GET_UINT64(N_STR(U64_OUT_S), UINT64_MAX && errno == ERANGE); + CHECK_GET_UINT64(N_DBL(INFINITY), UINT64_MAX && errno == ERANGE); + CHECK_GET_UINT64(N_DBL(-INFINITY), 0 && errno == ERANGE); + CHECK_GET_UINT64(N_DBL(NAN), 0 && errno == EINVAL); printf("UINT64 GET PASSED\n"); printf("PASSED\n");