From ec03909831364712c125e6bde51364ece685fedf Mon Sep 17 00:00:00 2001 From: Kevin Harwell <kharwell@digium.com> Date: Fri, 28 Aug 2020 16:31:40 -0500 Subject: [PATCH] conversions: Add string to signed integer conversion functions Change-Id: Id603b0b03b78eb84c7fca030a08b343c0d5973f9 --- include/asterisk/conversions.h | 54 ++++++++++++ main/conversions.c | 51 +++++++++++ tests/test_conversions.c | 153 ++++++++++++++++++++++++++++++++- 3 files changed, 257 insertions(+), 1 deletion(-) diff --git a/include/asterisk/conversions.h b/include/asterisk/conversions.h index 55906e6649..852d80fc48 100644 --- a/include/asterisk/conversions.h +++ b/include/asterisk/conversions.h @@ -25,6 +25,24 @@ #include <stdint.h> +/*! + * \brief Convert the given string to a signed integer + * + * This function will return failure for the following reasons: + * + * The given string to convert is NULL + * The given string to convert is empty. + * The given string to convert contains non numeric values + * Once converted the number is out of range (less than INT_MIN + * or greater than INT_MAX) + * + * \param str The string to convert + * \param res [out] The converted value + * + * \returns -1 if it fails to convert, 0 on success + */ +int ast_str_to_int(const char *str, int *res); + /*! * \brief Convert the given string to an unsigned integer * @@ -43,6 +61,24 @@ */ int ast_str_to_uint(const char *str, unsigned int *res); +/*! + * \brief Convert the given string to a signed long + * + * This function will return failure for the following reasons: + * + * The given string to convert is NULL + * The given string to convert is empty. + * The given string to convert contains non numeric values + * Once converted the number is out of range (less than LONG_MIN + * or greater than LONG_MAX) + * + * \param str The string to convert + * \param res [out] The converted value + * + * \returns -1 if it fails to convert, 0 on success + */ +int ast_str_to_long(const char *str, long *res); + /*! * \brief Convert the given string to an unsigned long * @@ -61,6 +97,24 @@ int ast_str_to_uint(const char *str, unsigned int *res); */ int ast_str_to_ulong(const char *str, unsigned long *res); +/*! + * \brief Convert the given string to a signed max size integer + * + * This function will return failure for the following reasons: + * + * The given string to convert is NULL + * The given string to convert is empty. + * The given string to convert contains non numeric values + * Once converted the number is out of range (less than INTMAX_MIN + * or greater than INTMAX_MAX) + * + * \param str The string to convert + * \param res [out] The converted value + * + * \returns -1 if it fails to convert, 0 on success + */ +int ast_str_to_imax(const char *str, intmax_t *res); + /*! * \brief Convert the given string to an unsigned max size integer * diff --git a/main/conversions.c b/main/conversions.c index 2fd3146a8c..007e24d758 100644 --- a/main/conversions.c +++ b/main/conversions.c @@ -41,6 +41,18 @@ static int str_is_negative(const char **str) return **str == '-'; } +int ast_str_to_int(const char *str, int *res) +{ + intmax_t val; + + if (ast_str_to_imax(str, &val) || val < INT_MIN || val > INT_MAX) { + return -1; + } + + *res = val; + return 0; +} + int ast_str_to_uint(const char *str, unsigned int *res) { uintmax_t val; @@ -53,6 +65,18 @@ int ast_str_to_uint(const char *str, unsigned int *res) return 0; } +int ast_str_to_long(const char *str, long *res) +{ + intmax_t val; + + if (ast_str_to_imax(str, &val) || val < LONG_MIN || val > LONG_MAX) { + return -1; + } + + *res = val; + return 0; +} + int ast_str_to_ulong(const char *str, unsigned long *res) { uintmax_t val; @@ -65,6 +89,33 @@ int ast_str_to_ulong(const char *str, unsigned long *res) return 0; } +int ast_str_to_imax(const char *str, intmax_t *res) +{ + char *end; + intmax_t val; + + if (!str) { + return -1; + } + + errno = 0; + val = strtoimax(str, &end, 0); + + /* + * If str equals end then no digits were found. If end is not pointing to + * a null character then the string contained some numbers that could be + * converted, but some characters that could not, which we'll consider + * invalid. + */ + if (str == end || *end != '\0' || (errno == ERANGE && + (val == INTMAX_MIN || val == INTMAX_MAX))) { + return -1; + } + + *res = val; + return 0; +} + int ast_str_to_umax(const char *str, uintmax_t *res) { char *end; diff --git a/tests/test_conversions.c b/tests/test_conversions.c index 1a765b4a9d..f49c8e6800 100644 --- a/tests/test_conversions.c +++ b/tests/test_conversions.c @@ -37,6 +37,54 @@ #define CATEGORY "/main/conversions/" +AST_TEST_DEFINE(str_to_int) +{ + const char *invalid = "abc"; + const char *invalid_partial = "7abc"; + const char *negative = "-7"; + const char *negative_spaces = " -7"; + const char *negative_out_of_range = "-9999999999"; + const char *out_of_range = "9999999999"; + const char *spaces = " "; + const char *valid = "7"; + const char *valid_spaces = " 7"; + int val; + char str[64]; + + switch (cmd) { + case TEST_INIT: + info->name = __func__; + info->category = CATEGORY; + info->summary = "convert a string to a signed integer"; + info->description = info->summary; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + ast_test_validate(test, ast_str_to_int(NULL, &val)); + ast_test_validate(test, ast_str_to_int("\0", &val)); + ast_test_validate(test, ast_str_to_int(invalid, &val)); + ast_test_validate(test, ast_str_to_int(invalid_partial, &val)); + ast_test_validate(test, !ast_str_to_int(negative, &val)); + ast_test_validate(test, !ast_str_to_int(negative_spaces, &val)); + ast_test_validate(test, ast_str_to_int(negative_out_of_range, &val)); + ast_test_validate(test, ast_str_to_int(out_of_range, &val)); + ast_test_validate(test, ast_str_to_int(spaces, &val)); + ast_test_validate(test, !ast_str_to_int(valid, &val)); + ast_test_validate(test, !ast_str_to_int(valid_spaces, &val)); + + ast_test_validate(test, snprintf(str, sizeof(str), "%d", INT_MAX) > 0); + ast_test_validate(test, !ast_str_to_int(str, &val)); + ast_test_validate(test, val == INT_MAX); + + ast_test_validate(test, snprintf(str, sizeof(str), "%d", INT_MIN) > 0); + ast_test_validate(test, !ast_str_to_int(str, &val)); + ast_test_validate(test, val == INT_MIN); + + return AST_TEST_PASS; +} + AST_TEST_DEFINE(str_to_uint) { const char *invalid = "abc"; @@ -79,6 +127,54 @@ AST_TEST_DEFINE(str_to_uint) return AST_TEST_PASS; } +AST_TEST_DEFINE(str_to_long) +{ + const char *invalid = "abc"; + const char *invalid_partial = "7abc"; + const char *negative = "-7"; + const char *negative_spaces = " -7"; + const char *negative_out_of_range = "-99999999999999999999"; + const char *out_of_range = "99999999999999999999"; + const char *spaces = " "; + const char *valid = "7"; + const char *valid_spaces = " 7"; + long val; + char str[64]; + + switch (cmd) { + case TEST_INIT: + info->name = __func__; + info->category = CATEGORY; + info->summary = "convert a string to a signed long"; + info->description = info->summary; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + ast_test_validate(test, ast_str_to_long(NULL, &val)); + ast_test_validate(test, ast_str_to_long("\0", &val)); + ast_test_validate(test, ast_str_to_long(invalid, &val)); + ast_test_validate(test, ast_str_to_long(invalid_partial, &val)); + ast_test_validate(test, !ast_str_to_long(negative, &val)); + ast_test_validate(test, !ast_str_to_long(negative_spaces, &val)); + ast_test_validate(test, ast_str_to_long(negative_out_of_range, &val)); + ast_test_validate(test, ast_str_to_long(out_of_range, &val)); + ast_test_validate(test, ast_str_to_long(spaces, &val)); + ast_test_validate(test, !ast_str_to_long(valid, &val)); + ast_test_validate(test, !ast_str_to_long(valid_spaces, &val)); + + ast_test_validate(test, snprintf(str, sizeof(str), "%ld", LONG_MAX) > 0); + ast_test_validate(test, !ast_str_to_long(str, &val)); + ast_test_validate(test, val == LONG_MAX); + + ast_test_validate(test, snprintf(str, sizeof(str), "%ld", LONG_MIN) > 0); + ast_test_validate(test, !ast_str_to_long(str, &val)); + ast_test_validate(test, val == LONG_MIN); + + return AST_TEST_PASS; +} + AST_TEST_DEFINE(str_to_ulong) { const char *invalid = "abc"; @@ -121,6 +217,55 @@ AST_TEST_DEFINE(str_to_ulong) return AST_TEST_PASS; } +AST_TEST_DEFINE(str_to_imax) +{ + const char *invalid = "abc"; + const char *invalid_partial = "7abc"; + const char *negative = "-7"; + const char *negative_spaces = " -7"; + const char *negative_out_of_range = "-99999999999999999999999999999999999999999999999999"; + const char *out_of_range = "99999999999999999999999999999999999999999999999999"; + const char *spaces = " "; + const char *valid = "7"; + const char *valid_spaces = " 7"; + intmax_t val; + char str[64]; + + switch (cmd) { + case TEST_INIT: + info->name = __func__; + info->category = CATEGORY; + info->summary = "convert a string to a signed max size integer"; + info->description = info->summary; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + ast_test_validate(test, ast_str_to_imax(NULL, &val)); + ast_test_validate(test, ast_str_to_imax("\0", &val)); + ast_test_validate(test, ast_str_to_imax(invalid, &val)); + ast_test_validate(test, ast_str_to_imax(invalid_partial, &val)); + ast_test_validate(test, !ast_str_to_imax(negative, &val)); + ast_test_validate(test, !ast_str_to_imax(negative_spaces, &val)); + ast_test_validate(test, ast_str_to_imax(negative_out_of_range, &val)); + ast_test_validate(test, ast_str_to_imax(out_of_range, &val)); + ast_test_validate(test, ast_str_to_imax(spaces, &val)); + ast_test_validate(test, !ast_str_to_imax(valid, &val)); + ast_test_validate(test, !ast_str_to_imax(valid_spaces, &val)); + + ast_test_validate(test, snprintf(str, sizeof(str), "%jd", INTMAX_MAX) > 0); + ast_test_validate(test, !ast_str_to_imax(str, &val)); + ast_test_validate(test, val == INTMAX_MAX); + + ast_test_validate(test, snprintf(str, sizeof(str), "%jd", INTMAX_MIN) > 0); + ast_test_validate(test, !ast_str_to_imax(str, &val)); + ast_test_validate(test, val == INTMAX_MIN); + + return AST_TEST_PASS; +} + + AST_TEST_DEFINE(str_to_umax) { const char *invalid = "abc"; @@ -156,7 +301,7 @@ AST_TEST_DEFINE(str_to_umax) ast_test_validate(test, !ast_str_to_umax(valid, &val)); ast_test_validate(test, !ast_str_to_umax(valid_spaces, &val)); - ast_test_validate(test, snprintf(str, sizeof(str), "%lu", UINTMAX_MAX) > 0); + ast_test_validate(test, snprintf(str, sizeof(str), "%ju", UINTMAX_MAX) > 0); ast_test_validate(test, !ast_str_to_umax(str, &val)); ast_test_validate(test, val == UINTMAX_MAX); @@ -165,16 +310,22 @@ AST_TEST_DEFINE(str_to_umax) static int load_module(void) { + AST_TEST_REGISTER(str_to_int); AST_TEST_REGISTER(str_to_uint); + AST_TEST_REGISTER(str_to_long); AST_TEST_REGISTER(str_to_ulong); + AST_TEST_REGISTER(str_to_imax); AST_TEST_REGISTER(str_to_umax); return AST_MODULE_LOAD_SUCCESS; } static int unload_module(void) { + AST_TEST_UNREGISTER(str_to_int); AST_TEST_UNREGISTER(str_to_uint); + AST_TEST_UNREGISTER(str_to_long); AST_TEST_UNREGISTER(str_to_ulong); + AST_TEST_UNREGISTER(str_to_imax); AST_TEST_UNREGISTER(str_to_umax); return 0; } -- GitLab