Explicitly handle NaN values when converting to integerpull/884/head
@@ -721,6 +721,7 @@ int32_t json_object_get_int(const struct json_object *jso) | |||||
int64_t cint64 = 0; | int64_t cint64 = 0; | ||||
double cdouble; | double cdouble; | ||||
enum json_type o_type; | enum json_type o_type; | ||||
errno = 0; | |||||
if (!jso) | if (!jso) | ||||
return 0; | return 0; | ||||
@@ -756,17 +757,34 @@ int32_t json_object_get_int(const struct json_object *jso) | |||||
{ | { | ||||
case json_type_int: | case json_type_int: | ||||
/* Make sure we return the correct values for out of range numbers. */ | /* 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; | return INT32_MIN; | ||||
if (cint64 >= INT32_MAX) | |||||
} | |||||
if (cint64 > INT32_MAX) | |||||
{ | |||||
errno = ERANGE; | |||||
return INT32_MAX; | return INT32_MAX; | ||||
} | |||||
return (int32_t)cint64; | return (int32_t)cint64; | ||||
case json_type_double: | case json_type_double: | ||||
cdouble = JC_DOUBLE_C(jso)->c_double; | cdouble = JC_DOUBLE_C(jso)->c_double; | ||||
if (cdouble <= INT32_MIN) | |||||
if (cdouble < INT32_MIN) | |||||
{ | |||||
errno = ERANGE; | |||||
return INT32_MIN; | return INT32_MIN; | ||||
if (cdouble >= INT32_MAX) | |||||
} | |||||
if (cdouble > INT32_MAX) | |||||
{ | |||||
errno = ERANGE; | |||||
return INT32_MAX; | return INT32_MAX; | ||||
} | |||||
if (isnan(cdouble)) | |||||
{ | |||||
errno = EINVAL; | |||||
return INT32_MIN; | |||||
} | |||||
return (int32_t)cdouble; | return (int32_t)cdouble; | ||||
case json_type_boolean: return JC_BOOL_C(jso)->c_boolean; | case json_type_boolean: return JC_BOOL_C(jso)->c_boolean; | ||||
default: return 0; | 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 json_object_get_int64(const struct json_object *jso) | ||||
{ | { | ||||
int64_t cint; | int64_t cint; | ||||
errno = 0; | |||||
if (!jso) | if (!jso) | ||||
return 0; | 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_int64: return jsoint->cint.c_int64; | ||||
case json_object_int_type_uint64: | 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_MAX; | ||||
} | |||||
return (int64_t)jsoint->cint.c_uint64; | return (int64_t)jsoint->cint.c_uint64; | ||||
default: json_abort("invalid cint_type"); | 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: | case json_type_double: | ||||
// INT64_MAX can't be exactly represented as a double | // INT64_MAX can't be exactly represented as a double | ||||
// so cast to tell the compiler it's ok to round up. | // 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; | 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_MIN; | ||||
} | |||||
return (int64_t)JC_DOUBLE_C(jso)->c_double; | return (int64_t)JC_DOUBLE_C(jso)->c_double; | ||||
case json_type_boolean: return JC_BOOL_C(jso)->c_boolean; | case json_type_boolean: return JC_BOOL_C(jso)->c_boolean; | ||||
case json_type_string: | 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 json_object_get_uint64(const struct json_object *jso) | ||||
{ | { | ||||
uint64_t cuint; | uint64_t cuint; | ||||
errno = 0; | |||||
if (!jso) | if (!jso) | ||||
return 0; | return 0; | ||||
@@ -851,7 +885,10 @@ uint64_t json_object_get_uint64(const struct json_object *jso) | |||||
{ | { | ||||
case json_object_int_type_int64: | case json_object_int_type_int64: | ||||
if (jsoint->cint.c_int64 < 0) | if (jsoint->cint.c_int64 < 0) | ||||
{ | |||||
errno = ERANGE; | |||||
return 0; | return 0; | ||||
} | |||||
return (uint64_t)jsoint->cint.c_int64; | return (uint64_t)jsoint->cint.c_int64; | ||||
case json_object_int_type_uint64: return jsoint->cint.c_uint64; | case json_object_int_type_uint64: return jsoint->cint.c_uint64; | ||||
default: json_abort("invalid cint_type"); | 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: | case json_type_double: | ||||
// UINT64_MAX can't be exactly represented as a double | // UINT64_MAX can't be exactly represented as a double | ||||
// so cast to tell the compiler it's ok to round up. | // 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; | return UINT64_MAX; | ||||
} | |||||
if (JC_DOUBLE_C(jso)->c_double < 0) | 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 0; | ||||
} | |||||
return (uint64_t)JC_DOUBLE_C(jso)->c_double; | return (uint64_t)JC_DOUBLE_C(jso)->c_double; | ||||
case json_type_boolean: return JC_BOOL_C(jso)->c_boolean; | case json_type_boolean: return JC_BOOL_C(jso)->c_boolean; | ||||
case json_type_string: | case json_type_string: | ||||
@@ -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. | * 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 | * 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 | * 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 | * If any other object type is passed 0 will be returned, even non-empty | ||||
* json_type_array and json_type_object objects. | * 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 | /** Get the int value of a json_object | ||||
* | * | ||||
* The type is coerced to a int if the passed object is not a int. | * 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. | * 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 | * 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 | /** Get the int value of a json_object | ||||
* | * | ||||
* The type is coerced to a int64 if the passed object is not a int64. | * 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 | * 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 | * 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 | /** Get the uint value of a json_object | ||||
* | * | ||||
* The type is coerced to a uint64 if the passed object is not a uint64. | * 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 | * 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 | * whether or not conversion was successful (it does not clear the value for | ||||
@@ -36,6 +36,7 @@ int main(int argc, char **argv) | |||||
\"array_with_zero\": [ 0 ],\n\ | \"array_with_zero\": [ 0 ],\n\ | ||||
\"empty_object\": {},\n\ | \"empty_object\": {},\n\ | ||||
\"nonempty_object\": { \"a\": 123 },\n\ | \"nonempty_object\": { \"a\": 123 },\n\ | ||||
\"nan\": NaN,\n\ | |||||
}"; | }"; | ||||
/* Note: 2147483649 = INT_MAX + 2 */ | /* Note: 2147483649 = INT_MAX + 2 */ | ||||
/* Note: 9223372036854775809 = INT64_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, "array_with_zero"); | ||||
getit(new_obj, "empty_object"); | getit(new_obj, "empty_object"); | ||||
getit(new_obj, "nonempty_object"); | getit(new_obj, "nonempty_object"); | ||||
getit(new_obj, "nan"); | |||||
// Now check the behaviour of the json_object_is_type() function. | // Now check the behaviour of the json_object_is_type() function. | ||||
printf("\n================================\n"); | printf("\n================================\n"); | ||||
@@ -75,6 +77,7 @@ int main(int argc, char **argv) | |||||
checktype(new_obj, "int64_number"); | checktype(new_obj, "int64_number"); | ||||
checktype(new_obj, "negative_number"); | checktype(new_obj, "negative_number"); | ||||
checktype(new_obj, "a_null"); | checktype(new_obj, "a_null"); | ||||
checktype(new_obj, "nan"); | |||||
json_object_put(new_obj); | json_object_put(new_obj); | ||||
@@ -12,6 +12,7 @@ Parsed input: { | |||||
"array_with_zero": [ 0 ], | "array_with_zero": [ 0 ], | ||||
"empty_object": {}, | "empty_object": {}, | ||||
"nonempty_object": { "a": 123 }, | "nonempty_object": { "a": 123 }, | ||||
"nan": NaN, | |||||
} | } | ||||
Result is not NULL | Result is not NULL | ||||
new_obj.string_of_digits json_object_get_type()=string | 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_uint64()=0 | ||||
new_obj.nonempty_object json_object_get_boolean()=0 | new_obj.nonempty_object json_object_get_boolean()=0 | ||||
new_obj.nonempty_object json_object_get_double()=0.000000 | 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 | 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.int64_number : 0,0,0,1,0,0,0 | ||||
new_obj.negative_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.a_null : 1,0,0,0,0,0,0 | ||||
new_obj.nan : 0,0,1,0,0,0,0 |
@@ -4,9 +4,12 @@ | |||||
#include <assert.h> | #include <assert.h> | ||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <errno.h> | #include <errno.h> | ||||
#include <math.h> | |||||
#include "json.h" | #include "json.h" | ||||
#define I32_MAX_S "2147483647" | |||||
#define I32_MIN_S "-2147483648" | |||||
#define I64_MAX_S "9223372036854775807" | #define I64_MAX_S "9223372036854775807" | ||||
#define I64_OVER "9223372036854775808" | #define I64_OVER "9223372036854775808" | ||||
#define I64_MIN_S "-9223372036854775808" | #define I64_MIN_S "-9223372036854775808" | ||||
@@ -25,6 +28,7 @@ | |||||
#define N_I64 json_object_new_int64 | #define N_I64 json_object_new_int64 | ||||
#define N_U64 json_object_new_uint64 | #define N_U64 json_object_new_uint64 | ||||
#define N_STR json_object_new_string | #define N_STR json_object_new_string | ||||
#define N_DBL json_object_new_double | |||||
int main(int argc, char **argv) | 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_MAX), INT32_MAX && errno == 0); | ||||
CHECK_GET_INT(N_I64(INT32_MIN), INT32_MIN && 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"); | printf("INT GET PASSED\n"); | ||||
CHECK_GET_INT64(N_I64(INT64_MAX), INT64_MAX && errno == 0); | 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_MIN_S), INT64_MIN && errno == 0); | ||||
CHECK_GET_INT64(N_STR(I64_OVER), INT64_MAX && errno == ERANGE); | 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_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"); | printf("INT64 GET PASSED\n"); | ||||
CHECK_GET_UINT64(N_U64(UINT64_MAX), UINT64_MAX && errno == 0); | 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_U64(-1), UINT64_MAX && errno == 0); | ||||
CHECK_GET_UINT64(N_STR(U64_OUT_S), UINT64_MAX && errno == ERANGE); | 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("UINT64 GET PASSED\n"); | ||||
printf("PASSED\n"); | printf("PASSED\n"); | ||||