json_object_get_int/int64/uint64() now sets errno to ERANGE when the source value can't be represented in the target type.pull/882/head
| @@ -757,17 +757,29 @@ 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)) | if (isnan(cdouble)) | ||||
| { | { | ||||
| errno = EINVAL; | errno = EINVAL; | ||||
| @@ -820,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"); | ||||
| } | } | ||||
| @@ -829,10 +844,16 @@ 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; | return INT64_MIN; | ||||
| } | |||||
| if (isnan(JC_DOUBLE_C(jso)->c_double)) | if (isnan(JC_DOUBLE_C(jso)->c_double)) | ||||
| { | { | ||||
| errno = EINVAL; | errno = EINVAL; | ||||
| @@ -864,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"); | ||||
| @@ -873,10 +897,16 @@ 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; | return 0; | ||||
| } | |||||
| if (isnan(JC_DOUBLE_C(jso)->c_double)) | if (isnan(JC_DOUBLE_C(jso)->c_double)) | ||||
| { | { | ||||
| errno = EINVAL; | errno = EINVAL; | ||||
| @@ -743,7 +743,8 @@ JSON_EXPORT struct json_object *json_object_new_uint64(uint64_t i); | |||||
| * which return INT32_MIN and the errno is set to EINVAL. | * 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 | * 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 | * returned and errno is set to EINVAL. null is equivalent to 0 (no error values | ||||
| * set) | |||||
| * 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 | ||||
| @@ -789,7 +790,8 @@ JSON_EXPORT int json_object_int_inc(struct json_object *obj, int64_t val); | |||||
| * which return INT64_MIN and the errno is set to EINVAL. | * 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 | * 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 | * returned and errno is set to EINVAL. null is equivalent to 0 (no error values | ||||
| * set) | |||||
| * 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 | ||||
| @@ -807,7 +809,8 @@ JSON_EXPORT int64_t json_object_get_int64(const struct json_object *obj); | |||||
| * which return 0 and the errno is set to EINVAL. | * which return 0 and the errno is set to EINVAL. | ||||
| * Strings will be parsed as an uint64. If no conversion exists then 0 is | * 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 | * returned and errno is set to EINVAL. null is equivalent to 0 (no error values | ||||
| * set) | |||||
| * 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 | ||||
| @@ -8,6 +8,8 @@ | |||||
| #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" | ||||
| @@ -43,12 +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); | 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); | ||||
| @@ -57,12 +62,16 @@ 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); | 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); | CHECK_GET_UINT64(N_DBL(NAN), 0 && errno == EINVAL); | ||||
| printf("UINT64 GET PASSED\n"); | printf("UINT64 GET PASSED\n"); | ||||