From 45a4ae4e1fdca20480daf10b3873c2768625185d Mon Sep 17 00:00:00 2001 From: Ahson Khan Date: Fri, 31 Jul 2020 23:57:17 -0700 Subject: [PATCH] Limit use of sscanf only to double parsing, using a custom implementation for {u}int{32|64} parsing. (#974) * Limit use of sscanf only to double parsing, using a custom implementation for {u}int{32|64} parsing. * Add a few more atod test cases. * Add explicit casts to avoid implicit conversion warnings. * Fix bug where input is too small and add test cases. * Address PR feedback - add and update comments. --- sdk/src/azure/core/az_span.c | 341 ++++++++++++++++++++++++---------- sdk/tests/core/test_az_span.c | 170 +++++++++-------- 2 files changed, 342 insertions(+), 169 deletions(-) diff --git a/sdk/src/azure/core/az_span.c b/sdk/src/azure/core/az_span.c index 93532247f7e..8f5687538eb 100644 --- a/sdk/src/azure/core/az_span.c +++ b/sdk/src/azure/core/az_span.c @@ -100,108 +100,109 @@ AZ_NODISCARD bool az_span_is_content_equal_ignoring_case(az_span span1, az_span return true; } -static bool _is_valid_start_of_number(uint8_t first_byte, bool is_negative_allowed) +AZ_NODISCARD az_result az_span_atou64(az_span source, uint64_t* out_number) { - // ".123" or " 123" are considered invalid - // 'n'/'N' is for "nan" and 'i'/'I' is for "inf" - bool result = isdigit(first_byte) || first_byte == '+' || first_byte == 'n' || first_byte == 'N' - || first_byte == 'i' || first_byte == 'I'; + _az_PRECONDITION_VALID_SPAN(source, 1, false); + _az_PRECONDITION_NOT_NULL(out_number); + + int32_t const span_size = az_span_size(source); - // The first character can only be negative for int32, int64, and double. - if (is_negative_allowed) + if (span_size < 1) { - result = result || first_byte == '-'; + return AZ_ERROR_UNEXPECTED_CHAR; } - return result; -} - -// Disable the following warning just for this particular use case. -// C4996: 'sscanf': This function or variable may be unsafe. Consider using sscanf_s instead. -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4996) -#endif - -static void _az_span_ato_number_helper( - az_span source, - bool is_negative_allowed, - char* format, - void* result, - bool* success) -{ - int32_t size = az_span_size(source); - - _az_PRECONDITION_RANGE(1, size, 99); - - // This check is necessary to prevent sscanf from reading bytes past the end of the span, when the - // span might contain whitespace or other invalid bytes at the start. + // If the first character is not a digit or an optional + sign, return error. + int32_t starting_index = 0; uint8_t* source_ptr = az_span_ptr(source); - if (size < 1 || !_is_valid_start_of_number(source_ptr[0], is_negative_allowed)) + uint8_t next_byte = source_ptr[0]; + + if (!isdigit(next_byte)) { - *success = false; - return; + // There must be another byte after a sign. + // The loop below checks that it must be a digit. + if (next_byte != '+' || span_size < 2) + { + return AZ_ERROR_UNEXPECTED_CHAR; + } + starting_index++; } - // Starting at 1 to skip the '%' character - format[1] = (char)((size / 10) + '0'); - format[2] = (char)((size % 10) + '0'); + uint64_t value = 0; - // sscanf might set errno, so save it to restore later. - int32_t previous_err_no = errno; - errno = 0; + for (int32_t i = starting_index; i < span_size; ++i) + { + next_byte = source_ptr[i]; + if (!isdigit(next_byte)) + { + return AZ_ERROR_UNEXPECTED_CHAR; + } + uint64_t const d = (uint64_t)next_byte - '0'; - int32_t chars_consumed = 0; - int32_t n = sscanf((char*)source_ptr, format, result, &chars_consumed); + // Check whether the next digit will cause an integer overflow. + // Before actually doing the math below, this is checking whether value * 10 + d > UINT64_MAX. + if ((UINT64_MAX - d) / 10 < value) + { + return AZ_ERROR_UNEXPECTED_CHAR; + } - if (success != NULL) - { - // True if the entire source was consumed by sscanf, it set the result argument, and it didn't - // set errno to some non-zero value. - *success = size == chars_consumed && n == 1 && errno == 0; + value = value * 10 + d; } - // Restore errno back to its original value before the call to sscanf potentially changed it. - errno = previous_err_no; + *out_number = value; + return AZ_OK; } -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -AZ_NODISCARD az_result az_span_atou64(az_span source, uint64_t* out_number) +AZ_NODISCARD az_result az_span_atou32(az_span source, uint32_t* out_number) { _az_PRECONDITION_VALID_SPAN(source, 1, false); _az_PRECONDITION_NOT_NULL(out_number); - // Stack based string to allow thread-safe mutation by _az_span_ato_number_helper - char format_template[9] = "%00llu%n"; - bool success = false; - _az_span_ato_number_helper(source, false, format_template, out_number, &success); + int32_t const span_size = az_span_size(source); - return success ? AZ_OK : AZ_ERROR_UNEXPECTED_CHAR; -} + if (span_size < 1) + { + return AZ_ERROR_UNEXPECTED_CHAR; + } -AZ_NODISCARD az_result az_span_atou32(az_span source, uint32_t* out_number) -{ - _az_PRECONDITION_VALID_SPAN(source, 1, false); - _az_PRECONDITION_NOT_NULL(out_number); + // If the first character is not a digit or an optional + sign, return error. + int32_t starting_index = 0; + uint8_t* source_ptr = az_span_ptr(source); + uint8_t next_byte = source_ptr[0]; - uint64_t placeholder = 0; + if (!isdigit(next_byte)) + { + // There must be another byte after a sign. + // The loop below checks that it must be a digit. + if (next_byte != '+' || span_size < 2) + { + return AZ_ERROR_UNEXPECTED_CHAR; + } + starting_index++; + } - // Stack based string to allow thread-safe mutation by _az_span_ato_number_helper - // Using a format string for uint64_t to properly validate values that are out-of-range for - // uint32_t. - char format_template[9] = "%00llu%n"; - bool success = false; - _az_span_ato_number_helper(source, false, format_template, &placeholder, &success); + uint32_t value = 0; - if (placeholder > UINT32_MAX || !success) + for (int32_t i = starting_index; i < span_size; ++i) { - return AZ_ERROR_UNEXPECTED_CHAR; + next_byte = source_ptr[i]; + if (!isdigit(next_byte)) + { + return AZ_ERROR_UNEXPECTED_CHAR; + } + uint32_t const d = (uint32_t)next_byte - '0'; + + // Check whether the next digit will cause an integer overflow. + // Before actually doing the math below, this is checking whether value * 10 + d > UINT32_MAX. + if ((UINT32_MAX - d) / 10 < value) + { + return AZ_ERROR_UNEXPECTED_CHAR; + } + + value = value * 10 + d; } - *out_number = (uint32_t)placeholder; + *out_number = value; return AZ_OK; } @@ -210,12 +211,69 @@ AZ_NODISCARD az_result az_span_atoi64(az_span source, int64_t* out_number) _az_PRECONDITION_VALID_SPAN(source, 1, false); _az_PRECONDITION_NOT_NULL(out_number); - // Stack based string to allow thread-safe mutation by _az_span_ato_number_helper - char format_template[9] = "%00lld%n"; - bool success = false; - _az_span_ato_number_helper(source, true, format_template, out_number, &success); + int32_t const span_size = az_span_size(source); - return success ? AZ_OK : AZ_ERROR_UNEXPECTED_CHAR; + if (span_size < 1) + { + return AZ_ERROR_UNEXPECTED_CHAR; + } + + // If the first character is not a digit, - sign, or an optional + sign, return error. + int32_t starting_index = 0; + uint8_t* source_ptr = az_span_ptr(source); + uint8_t next_byte = source_ptr[0]; + int64_t sign = 1; + + if (!isdigit(next_byte)) + { + // There must be another byte after a sign. + // The loop below checks that it must be a digit. + if (next_byte != '+') + { + if (next_byte != '-') + { + return AZ_ERROR_UNEXPECTED_CHAR; + } + sign = -1; + } + if (span_size < 2) + { + return AZ_ERROR_UNEXPECTED_CHAR; + } + starting_index++; + } + + // if sign < 0, (-1 * sign + 1) / 2 = 1 + // else, (-1 * sign + 1) / 2 = 0 + // This is necessary to correctly account for the fact that the absolute value of INT64_MIN is 1 + // more than than the absolute value of INT64_MAX. + uint64_t sign_factor = (uint64_t)(-1 * sign + 1) / 2; + + // Using unsigned int while parsing to account for potential overflow. + uint64_t value = 0; + + for (int32_t i = starting_index; i < span_size; ++i) + { + next_byte = source_ptr[i]; + if (!isdigit(next_byte)) + { + return AZ_ERROR_UNEXPECTED_CHAR; + } + uint64_t const d = (uint64_t)next_byte - '0'; + + // Check whether the next digit will cause an integer overflow. + // Before actually doing the math below, this is checking whether value * 10 + d > INT64_MAX, or + // in the case of negative numbers, checking whether value * 10 + d > INT64_MAX + 1. + if ((uint64_t)(INT64_MAX - d + sign_factor) / 10 < value) + { + return AZ_ERROR_UNEXPECTED_CHAR; + } + + value = value * 10 + d; + } + + *out_number = (int64_t)value * sign; + return AZ_OK; } AZ_NODISCARD az_result az_span_atoi32(az_span source, int32_t* out_number) @@ -223,37 +281,134 @@ AZ_NODISCARD az_result az_span_atoi32(az_span source, int32_t* out_number) _az_PRECONDITION_VALID_SPAN(source, 1, false); _az_PRECONDITION_NOT_NULL(out_number); - int64_t placeholder = 0; + int32_t const span_size = az_span_size(source); - // Stack based string to allow thread-safe mutation by _az_span_ato_number_helper - // Using a format string for int64_t to properly validate values that are out-of-range for - // int32_t. - char format_template[9] = "%00lld%n"; - bool success = false; - _az_span_ato_number_helper(source, true, format_template, &placeholder, &success); - - if (placeholder > INT32_MAX || placeholder < INT32_MIN || !success) + if (span_size < 1) { return AZ_ERROR_UNEXPECTED_CHAR; } - *out_number = (int32_t)placeholder; + // If the first character is not a digit, - sign, or an optional + sign, return error. + int32_t starting_index = 0; + uint8_t* source_ptr = az_span_ptr(source); + uint8_t next_byte = source_ptr[0]; + int32_t sign = 1; + + if (!isdigit(next_byte)) + { + // There must be another byte after a sign. + // The loop below checks that it must be a digit. + if (next_byte != '+') + { + if (next_byte != '-') + { + return AZ_ERROR_UNEXPECTED_CHAR; + } + sign = -1; + } + if (span_size < 2) + { + return AZ_ERROR_UNEXPECTED_CHAR; + } + starting_index++; + } + + // if sign < 0, (-1 * sign + 1) / 2 = 1 + // else, (-1 * sign + 1) / 2 = 0 + // This is necessary to correctly account for the fact that the absolute value of INT32_MIN is 1 + // more than than the absolute value of INT32_MAX. + uint32_t sign_factor = (uint32_t)(-1 * sign + 1) / 2; + + // Using unsigned int while parsing to account for potential overflow. + uint32_t value = 0; + + for (int32_t i = starting_index; i < span_size; ++i) + { + next_byte = source_ptr[i]; + if (!isdigit(next_byte)) + { + return AZ_ERROR_UNEXPECTED_CHAR; + } + uint32_t const d = (uint32_t)next_byte - '0'; + + // Check whether the next digit will cause an integer overflow. + // Before actually doing the math below, this is checking whether value * 10 + d > INT32_MAX, or + // in the case of negative numbers, checking whether value * 10 + d > INT32_MAX + 1. + if ((uint32_t)(INT32_MAX - d + sign_factor) / 10 < value) + { + return AZ_ERROR_UNEXPECTED_CHAR; + } + + value = value * 10 + d; + } + + *out_number = (int32_t)value * sign; return AZ_OK; } +static bool _is_valid_start_of_double(uint8_t first_byte) +{ + // ".123" or " 123" are considered invalid + // 'n'/'N' is for "nan" and 'i'/'I' is for "inf" + bool result = isdigit(first_byte) || first_byte == '+' || first_byte == '-' || first_byte == 'n' + || first_byte == 'N' || first_byte == 'i' || first_byte == 'I'; + + return result; +} + +// Disable the following warning just for this particular use case. +// C4996: 'sscanf': This function or variable may be unsafe. Consider using sscanf_s instead. +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4996) +#endif + AZ_NODISCARD az_result az_span_atod(az_span source, double* out_number) { _az_PRECONDITION_VALID_SPAN(source, 1, false); _az_PRECONDITION_NOT_NULL(out_number); - // Stack based string to allow thread-safe mutation by _az_span_ato_number_helper - char format_template[8] = "%00lf%n"; - bool success = false; - _az_span_ato_number_helper(source, true, format_template, out_number, &success); + int32_t size = az_span_size(source); + + _az_PRECONDITION_RANGE(1, size, 99); + + // This check is necessary to prevent sscanf from reading bytes past the end of the span, when the + // span might contain whitespace or other invalid bytes at the start. + uint8_t* source_ptr = az_span_ptr(source); + if (size < 1 || !_is_valid_start_of_double(source_ptr[0])) + { + return AZ_ERROR_UNEXPECTED_CHAR; + } + + // Stack based string to allow thread-safe mutation. + // The length is 8 to allow space for the null-terminating character. + char format[8] = "%00lf%n"; + + // Starting at 1 to skip the '%' character + format[1] = (char)((size / 10) + '0'); + format[2] = (char)((size % 10) + '0'); + + // sscanf might set errno, so save it to restore later. + int32_t previous_err_no = errno; + errno = 0; + + int32_t chars_consumed = 0; + int32_t n = sscanf((char*)source_ptr, format, out_number, &chars_consumed); + + // True if the entire source was consumed by sscanf, it set the out_number argument, and it didn't + // set errno to some non-zero value. + bool success = size == chars_consumed && n == 1 && errno == 0; + + // Restore errno back to its original value before the call to sscanf potentially changed it. + errno = previous_err_no; return success ? AZ_OK : AZ_ERROR_UNEXPECTED_CHAR; } +#ifdef _MSC_VER +#pragma warning(pop) +#endif + AZ_NODISCARD int32_t az_span_find(az_span source, az_span target) { /* This function implements the Naive string-search algorithm. diff --git a/sdk/tests/core/test_az_span.c b/sdk/tests/core/test_az_span.c index 0e5f5892892..dc194463d01 100644 --- a/sdk/tests/core/test_az_span.c +++ b/sdk/tests/core/test_az_span.c @@ -164,12 +164,23 @@ static void az_span_atox_return_errors(void** state) az_span_atox_return_errors_helper(AZ_SPAN_FROM_STR("test")); az_span_atox_return_errors_helper(AZ_SPAN_FROM_STR(" ")); + az_span_atox_return_errors_helper(AZ_SPAN_FROM_STR(" 1")); + az_span_atox_return_errors_helper(AZ_SPAN_FROM_STR("-")); + az_span_atox_return_errors_helper(AZ_SPAN_FROM_STR("+")); + az_span_atox_return_errors_helper(AZ_SPAN_FROM_STR("--1")); + az_span_atox_return_errors_helper(AZ_SPAN_FROM_STR("++1")); + az_span_atox_return_errors_helper(AZ_SPAN_FROM_STR("-+")); + az_span_atox_return_errors_helper(AZ_SPAN_FROM_STR("+-")); + az_span_atox_return_errors_helper(AZ_SPAN_FROM_STR("-0+")); + az_span_atox_return_errors_helper(AZ_SPAN_FROM_STR("0-")); + az_span_atox_return_errors_helper(AZ_SPAN_FROM_STR("+0-")); az_span_atox_return_errors_helper(AZ_SPAN_FROM_STR("1-")); az_span_atox_return_errors_helper(AZ_SPAN_FROM_STR("123a")); az_span_atox_return_errors_helper(AZ_SPAN_FROM_STR("123,")); az_span_atox_return_errors_helper(AZ_SPAN_FROM_STR("123 ")); az_span_atox_return_errors_helper(AZ_SPAN_FROM_STR("--123")); az_span_atox_return_errors_helper(AZ_SPAN_FROM_STR("-+123")); + az_span_atox_return_errors_helper(AZ_SPAN_FROM_STR("+-123")); az_span_atox_return_errors_helper(AZ_SPAN_FROM_STR(" -1-")); az_span_atox_return_errors_helper(AZ_SPAN_FROM_STR("- INFINITY")); az_span_atox_return_errors_helper(AZ_SPAN_FROM_STR("- 0")); @@ -177,6 +188,9 @@ static void az_span_atox_return_errors(void** state) az_span_atox_return_errors_helper(AZ_SPAN_FROM_STR("1.-e3")); az_span_atox_return_errors_helper(AZ_SPAN_FROM_STR("1.-e/3")); az_span_atox_return_errors_helper_exclude_double(AZ_SPAN_FROM_STR("1.23")); + az_span_atox_return_errors_helper_exclude_double(AZ_SPAN_FROM_STR("-1.23")); + az_span_atox_return_errors_helper_exclude_double(AZ_SPAN_FROM_STR("11e2")); + az_span_atox_return_errors_helper_exclude_double(AZ_SPAN_FROM_STR("-1.1e+2")); az_span_atox_return_errors_helper_exclude_double(AZ_SPAN_FROM_STR("1.23e3")); az_span_atox_return_errors_helper_exclude_double(AZ_SPAN_FROM_STR("99999999999999999999")); az_span_atox_return_errors_helper_exclude_double(AZ_SPAN_FROM_STR("999999999999999999999")); @@ -193,13 +207,20 @@ static void az_span_atou32_test(void** state) assert_int_equal(value, 0); assert_int_equal(az_span_atou32(AZ_SPAN_FROM_STR("1024"), &value), AZ_OK); assert_int_equal(value, 1024); + assert_int_equal(az_span_atou32(AZ_SPAN_FROM_STR("+1024"), &value), AZ_OK); + assert_int_equal(value, 1024); + assert_int_equal(az_span_atou32(AZ_SPAN_FROM_STR("001024"), &value), AZ_OK); + assert_int_equal(value, 1024); assert_int_equal(az_span_atou32(AZ_SPAN_FROM_STR("2147483647"), &value), AZ_OK); assert_int_equal(value, 2147483647); assert_int_equal(az_span_atou32(AZ_SPAN_FROM_STR("4294967295"), &value), AZ_OK); assert_int_equal(value, 4294967295); + assert_int_equal(az_span_atou32(AZ_SPAN_FROM_STR("00004294967295"), &value), AZ_OK); + assert_int_equal(value, 4294967295); + assert_int_equal(az_span_atou32(AZ_SPAN_FROM_STR("-123"), &value), AZ_ERROR_UNEXPECTED_CHAR); assert_int_equal( - az_span_atou32(AZ_SPAN_FROM_STR("-123"), &value), AZ_ERROR_UNEXPECTED_CHAR); + az_span_atou32(AZ_SPAN_FROM_STR("42949672950"), &value), AZ_ERROR_UNEXPECTED_CHAR); assert_int_equal( az_span_atou32(AZ_SPAN_FROM_STR("-2147483648"), &value), AZ_ERROR_UNEXPECTED_CHAR); assert_int_equal( @@ -211,14 +232,11 @@ static void az_span_atou32_test(void** state) assert_int_equal( az_span_atou32(AZ_SPAN_FROM_STR("42949672950"), &value), AZ_ERROR_UNEXPECTED_CHAR); assert_int_equal( - az_span_atou32(AZ_SPAN_FROM_STR("9223372036854775807"), &value), - AZ_ERROR_UNEXPECTED_CHAR); + az_span_atou32(AZ_SPAN_FROM_STR("9223372036854775807"), &value), AZ_ERROR_UNEXPECTED_CHAR); assert_int_equal( - az_span_atou32(AZ_SPAN_FROM_STR("9223372036854775808"), &value), - AZ_ERROR_UNEXPECTED_CHAR); + az_span_atou32(AZ_SPAN_FROM_STR("9223372036854775808"), &value), AZ_ERROR_UNEXPECTED_CHAR); assert_int_equal( - az_span_atou32(AZ_SPAN_FROM_STR("18446744073709551615"), &value), - AZ_ERROR_UNEXPECTED_CHAR); + az_span_atou32(AZ_SPAN_FROM_STR("18446744073709551615"), &value), AZ_ERROR_UNEXPECTED_CHAR); } static void az_span_atoi32_test(void** state) @@ -230,13 +248,21 @@ static void az_span_atoi32_test(void** state) assert_int_equal(value, 0); assert_int_equal(az_span_atoi32(AZ_SPAN_FROM_STR("1024"), &value), AZ_OK); assert_int_equal(value, 1024); + assert_int_equal(az_span_atoi32(AZ_SPAN_FROM_STR("+1024"), &value), AZ_OK); + assert_int_equal(value, 1024); assert_int_equal(az_span_atoi32(AZ_SPAN_FROM_STR("-1024"), &value), AZ_OK); assert_int_equal(value, -1024); + assert_int_equal(az_span_atoi32(AZ_SPAN_FROM_STR("001024"), &value), AZ_OK); + assert_int_equal(value, 1024); assert_int_equal(az_span_atoi32(AZ_SPAN_FROM_STR("2147483647"), &value), AZ_OK); assert_int_equal(value, 2147483647); assert_int_equal(az_span_atoi32(AZ_SPAN_FROM_STR("-2147483648"), &value), AZ_OK); assert_int_equal(value, -2147483647 - 1); + assert_int_equal(az_span_atoi32(AZ_SPAN_FROM_STR("-00002147483648"), &value), AZ_OK); + assert_int_equal(value, -2147483647 - 1); + assert_int_equal( + az_span_atoi32(AZ_SPAN_FROM_STR("21474836470"), &value), AZ_ERROR_UNEXPECTED_CHAR); assert_int_equal( az_span_atoi32(AZ_SPAN_FROM_STR("2147483648"), &value), AZ_ERROR_UNEXPECTED_CHAR); assert_int_equal( @@ -246,8 +272,7 @@ static void az_span_atoi32_test(void** state) assert_int_equal( az_span_atoi32(AZ_SPAN_FROM_STR("-4294967296"), &value), AZ_ERROR_UNEXPECTED_CHAR); assert_int_equal( - az_span_atoi32(AZ_SPAN_FROM_STR("9223372036854775807"), &value), - AZ_ERROR_UNEXPECTED_CHAR); + az_span_atoi32(AZ_SPAN_FROM_STR("9223372036854775807"), &value), AZ_ERROR_UNEXPECTED_CHAR); } static void az_span_atou64_test(void** state) @@ -259,6 +284,10 @@ static void az_span_atou64_test(void** state) assert_int_equal(value, 0); assert_int_equal(az_span_atou64(AZ_SPAN_FROM_STR("1024"), &value), AZ_OK); assert_int_equal(value, 1024); + assert_int_equal(az_span_atou64(AZ_SPAN_FROM_STR("+1024"), &value), AZ_OK); + assert_int_equal(value, 1024); + assert_int_equal(az_span_atou64(AZ_SPAN_FROM_STR("001024"), &value), AZ_OK); + assert_int_equal(value, 1024); assert_int_equal(az_span_atou64(AZ_SPAN_FROM_STR("2147483647"), &value), AZ_OK); assert_int_equal(value, 2147483647); assert_int_equal(az_span_atou64(AZ_SPAN_FROM_STR("4294967295"), &value), AZ_OK); @@ -267,19 +296,16 @@ static void az_span_atou64_test(void** state) assert_int_equal(value, 9223372036854775807UL); assert_int_equal(az_span_atou64(AZ_SPAN_FROM_STR("18446744073709551615"), &value), AZ_OK); assert_int_equal(value, 18446744073709551615UL); + assert_int_equal(az_span_atou64(AZ_SPAN_FROM_STR("000018446744073709551615"), &value), AZ_OK); + assert_int_equal(value, 18446744073709551615UL); + assert_int_equal(az_span_atou64(AZ_SPAN_FROM_STR("-123"), &value), AZ_ERROR_UNEXPECTED_CHAR); assert_int_equal( - az_span_atou64(AZ_SPAN_FROM_STR("18446744073709551616"), &value), - AZ_ERROR_UNEXPECTED_CHAR); - assert_int_equal( - az_span_atou64(AZ_SPAN_FROM_STR("-9223372036854775809"), &value), - AZ_ERROR_UNEXPECTED_CHAR); + az_span_atou64(AZ_SPAN_FROM_STR("184467440737095516150"), &value), AZ_ERROR_UNEXPECTED_CHAR); assert_int_equal( - az_span_atou64(AZ_SPAN_FROM_STR("-42"), &value), AZ_ERROR_UNEXPECTED_CHAR); + az_span_atou64(AZ_SPAN_FROM_STR("18446744073709551616"), &value), AZ_ERROR_UNEXPECTED_CHAR); assert_int_equal( - az_span_atou64(AZ_SPAN_FROM_STR("1.2"), &value), AZ_ERROR_UNEXPECTED_CHAR); - assert_int_equal( - az_span_atou64(AZ_SPAN_FROM_STR("-1.2"), &value), AZ_ERROR_UNEXPECTED_CHAR); + az_span_atou64(AZ_SPAN_FROM_STR("-9223372036854775809"), &value), AZ_ERROR_UNEXPECTED_CHAR); } static void az_span_atoi64_test(void** state) @@ -291,8 +317,12 @@ static void az_span_atoi64_test(void** state) assert_int_equal(value, 0); assert_int_equal(az_span_atoi64(AZ_SPAN_FROM_STR("1024"), &value), AZ_OK); assert_int_equal(value, 1024); + assert_int_equal(az_span_atoi64(AZ_SPAN_FROM_STR("+1024"), &value), AZ_OK); + assert_int_equal(value, 1024); assert_int_equal(az_span_atoi64(AZ_SPAN_FROM_STR("-1024"), &value), AZ_OK); assert_int_equal(value, -1024); + assert_int_equal(az_span_atoi64(AZ_SPAN_FROM_STR("001024"), &value), AZ_OK); + assert_int_equal(value, 1024); assert_int_equal(az_span_atoi64(AZ_SPAN_FROM_STR("2147483647"), &value), AZ_OK); assert_int_equal(value, 2147483647); assert_int_equal(az_span_atoi64(AZ_SPAN_FROM_STR("-2147483648"), &value), AZ_OK); @@ -305,23 +335,21 @@ static void az_span_atoi64_test(void** state) assert_int_equal(value, 9223372036854775807L); assert_int_equal(az_span_atoi64(AZ_SPAN_FROM_STR("-9223372036854775808"), &value), AZ_OK); assert_int_equal(value, -9223372036854775807L - 1); + assert_int_equal(az_span_atoi64(AZ_SPAN_FROM_STR("-00009223372036854775808"), &value), AZ_OK); + assert_int_equal(value, -9223372036854775807L - 1); assert_int_equal( - az_span_atoi64(AZ_SPAN_FROM_STR("9223372036854775808"), &value), - AZ_ERROR_UNEXPECTED_CHAR); + az_span_atoi64(AZ_SPAN_FROM_STR("92233720368547758070"), &value), AZ_ERROR_UNEXPECTED_CHAR); assert_int_equal( - az_span_atoi64(AZ_SPAN_FROM_STR("18446744073709551615"), &value), - AZ_ERROR_UNEXPECTED_CHAR); + az_span_atoi64(AZ_SPAN_FROM_STR("12233720368547758070"), &value), AZ_ERROR_UNEXPECTED_CHAR); assert_int_equal( - az_span_atoi64(AZ_SPAN_FROM_STR("18446744073709551616"), &value), - AZ_ERROR_UNEXPECTED_CHAR); + az_span_atoi64(AZ_SPAN_FROM_STR("9223372036854775808"), &value), AZ_ERROR_UNEXPECTED_CHAR); assert_int_equal( - az_span_atoi64(AZ_SPAN_FROM_STR("-9223372036854775809"), &value), - AZ_ERROR_UNEXPECTED_CHAR); + az_span_atoi64(AZ_SPAN_FROM_STR("18446744073709551615"), &value), AZ_ERROR_UNEXPECTED_CHAR); assert_int_equal( - az_span_atoi64(AZ_SPAN_FROM_STR("1.2"), &value), AZ_ERROR_UNEXPECTED_CHAR); + az_span_atoi64(AZ_SPAN_FROM_STR("18446744073709551616"), &value), AZ_ERROR_UNEXPECTED_CHAR); assert_int_equal( - az_span_atoi64(AZ_SPAN_FROM_STR("-1.2"), &value), AZ_ERROR_UNEXPECTED_CHAR); + az_span_atoi64(AZ_SPAN_FROM_STR("-9223372036854775809"), &value), AZ_ERROR_UNEXPECTED_CHAR); } // Disable warning for float comparisons, for this particular test @@ -345,6 +373,10 @@ static void az_span_atod_test(void** state) assert_true(value == 0); assert_int_equal(az_span_atod(AZ_SPAN_FROM_STR("1024"), &value), AZ_OK); assert_true(value == 1024); + assert_int_equal(az_span_atod(AZ_SPAN_FROM_STR("+1024"), &value), AZ_OK); + assert_true(value == 1024); + assert_int_equal(az_span_atod(AZ_SPAN_FROM_STR("001024"), &value), AZ_OK); + assert_true(value == 1024); assert_int_equal(az_span_atod(AZ_SPAN_FROM_STR("-1024"), &value), AZ_OK); assert_true(value == -1024); assert_int_equal(az_span_atod(AZ_SPAN_FROM_STR("2147483647"), &value), AZ_OK); @@ -362,6 +394,8 @@ static void az_span_atod_test(void** state) assert_int_equal(az_span_atod(AZ_SPAN_FROM_STR("1.23e3"), &value), AZ_OK); assert_true(value == 1.23e3); + assert_int_equal(az_span_atod(AZ_SPAN_FROM_STR("+001.23e3"), &value), AZ_OK); + assert_true(value == 1.23e3); assert_int_equal(az_span_atod(AZ_SPAN_FROM_STR("1.23"), &value), AZ_OK); assert_true(value == 1.23); assert_int_equal(az_span_atod(AZ_SPAN_FROM_STR("-123.456e-78"), &value), AZ_OK); @@ -535,6 +569,14 @@ static void az_span_atod_test(void** state) assert_int_equal( az_span_atod(AZ_SPAN_FROM_STR("18446744073709551615.18446744073709551615"), &value), AZ_OK); assert_true(value == 18446744073709551615.18446744073709551615); + assert_int_equal( + az_span_atod(AZ_SPAN_FROM_STR("+000018446744073709551615.18446744073709551615"), &value), + AZ_OK); + assert_true(value == 18446744073709551615.18446744073709551615); + assert_int_equal( + az_span_atod(AZ_SPAN_FROM_STR("-000018446744073709551615.18446744073709551615"), &value), + AZ_OK); + assert_true(value == -18446744073709551615.18446744073709551615); assert_int_equal(az_span_atod(AZ_SPAN_FROM_STR("1e16"), &value), AZ_OK); assert_true(value == 1e16); assert_int_equal(az_span_atod(AZ_SPAN_FROM_STR("12345.123e15"), &value), AZ_OK); @@ -554,8 +596,7 @@ static void az_span_atod_test(void** state) assert_int_equal(az_span_atod(AZ_SPAN_FROM_STR("1.8e309"), &value), AZ_OK); assert_true(value == INFINITY); #else - assert_int_equal( - az_span_atod(AZ_SPAN_FROM_STR("1.8e309"), &value), AZ_ERROR_UNEXPECTED_CHAR); + assert_int_equal(az_span_atod(AZ_SPAN_FROM_STR("1.8e309"), &value), AZ_ERROR_UNEXPECTED_CHAR); #endif // _MSC_VER } @@ -584,41 +625,25 @@ static void az_span_ato_number_whitespace_or_invalid_not_allowed(void** state) az_span_atoi64(AZ_SPAN_FROM_STR(" 123"), &value_i64), AZ_ERROR_UNEXPECTED_CHAR); assert_int_equal( az_span_atou64(AZ_SPAN_FROM_STR(" 123"), &value_u64), AZ_ERROR_UNEXPECTED_CHAR); - assert_int_equal( - az_span_atod(AZ_SPAN_FROM_STR(" 123"), &value_d), AZ_ERROR_UNEXPECTED_CHAR); + assert_int_equal(az_span_atod(AZ_SPAN_FROM_STR(" 123"), &value_d), AZ_ERROR_UNEXPECTED_CHAR); - assert_int_equal( - az_span_atoi32(AZ_SPAN_FROM_STR("\n123"), &value_i32), AZ_ERROR_UNEXPECTED_CHAR); - assert_int_equal( - az_span_atou32(AZ_SPAN_FROM_STR("\n123"), &value_u32), AZ_ERROR_UNEXPECTED_CHAR); - assert_int_equal( - az_span_atoi64(AZ_SPAN_FROM_STR("\n123"), &value_i64), AZ_ERROR_UNEXPECTED_CHAR); - assert_int_equal( - az_span_atou64(AZ_SPAN_FROM_STR("\n123"), &value_u64), AZ_ERROR_UNEXPECTED_CHAR); - assert_int_equal( - az_span_atod(AZ_SPAN_FROM_STR("\n123"), &value_d), AZ_ERROR_UNEXPECTED_CHAR); + assert_int_equal(az_span_atoi32(AZ_SPAN_FROM_STR("\n123"), &value_i32), AZ_ERROR_UNEXPECTED_CHAR); + assert_int_equal(az_span_atou32(AZ_SPAN_FROM_STR("\n123"), &value_u32), AZ_ERROR_UNEXPECTED_CHAR); + assert_int_equal(az_span_atoi64(AZ_SPAN_FROM_STR("\n123"), &value_i64), AZ_ERROR_UNEXPECTED_CHAR); + assert_int_equal(az_span_atou64(AZ_SPAN_FROM_STR("\n123"), &value_u64), AZ_ERROR_UNEXPECTED_CHAR); + assert_int_equal(az_span_atod(AZ_SPAN_FROM_STR("\n123"), &value_d), AZ_ERROR_UNEXPECTED_CHAR); - assert_int_equal( - az_span_atoi32(AZ_SPAN_FROM_STR("a123"), &value_i32), AZ_ERROR_UNEXPECTED_CHAR); - assert_int_equal( - az_span_atou32(AZ_SPAN_FROM_STR("a123"), &value_u32), AZ_ERROR_UNEXPECTED_CHAR); - assert_int_equal( - az_span_atoi64(AZ_SPAN_FROM_STR("a123"), &value_i64), AZ_ERROR_UNEXPECTED_CHAR); - assert_int_equal( - az_span_atou64(AZ_SPAN_FROM_STR("a123"), &value_u64), AZ_ERROR_UNEXPECTED_CHAR); - assert_int_equal( - az_span_atod(AZ_SPAN_FROM_STR("a123"), &value_d), AZ_ERROR_UNEXPECTED_CHAR); + assert_int_equal(az_span_atoi32(AZ_SPAN_FROM_STR("a123"), &value_i32), AZ_ERROR_UNEXPECTED_CHAR); + assert_int_equal(az_span_atou32(AZ_SPAN_FROM_STR("a123"), &value_u32), AZ_ERROR_UNEXPECTED_CHAR); + assert_int_equal(az_span_atoi64(AZ_SPAN_FROM_STR("a123"), &value_i64), AZ_ERROR_UNEXPECTED_CHAR); + assert_int_equal(az_span_atou64(AZ_SPAN_FROM_STR("a123"), &value_u64), AZ_ERROR_UNEXPECTED_CHAR); + assert_int_equal(az_span_atod(AZ_SPAN_FROM_STR("a123"), &value_d), AZ_ERROR_UNEXPECTED_CHAR); - assert_int_equal( - az_span_atoi32(AZ_SPAN_FROM_STR("- 123"), &value_i32), AZ_ERROR_UNEXPECTED_CHAR); - assert_int_equal( - az_span_atou32(AZ_SPAN_FROM_STR("- 123"), &value_u32), AZ_ERROR_UNEXPECTED_CHAR); - assert_int_equal( - az_span_atoi64(AZ_SPAN_FROM_STR("- 123"), &value_i64), AZ_ERROR_UNEXPECTED_CHAR); - assert_int_equal( - az_span_atou64(AZ_SPAN_FROM_STR("- 123"), &value_u64), AZ_ERROR_UNEXPECTED_CHAR); - assert_int_equal( - az_span_atod(AZ_SPAN_FROM_STR("- 123"), &value_d), AZ_ERROR_UNEXPECTED_CHAR); + assert_int_equal(az_span_atoi32(AZ_SPAN_FROM_STR("- 123"), &value_i32), AZ_ERROR_UNEXPECTED_CHAR); + assert_int_equal(az_span_atou32(AZ_SPAN_FROM_STR("- 123"), &value_u32), AZ_ERROR_UNEXPECTED_CHAR); + assert_int_equal(az_span_atoi64(AZ_SPAN_FROM_STR("- 123"), &value_i64), AZ_ERROR_UNEXPECTED_CHAR); + assert_int_equal(az_span_atou64(AZ_SPAN_FROM_STR("- 123"), &value_u64), AZ_ERROR_UNEXPECTED_CHAR); + assert_int_equal(az_span_atod(AZ_SPAN_FROM_STR("- 123"), &value_d), AZ_ERROR_UNEXPECTED_CHAR); assert_int_equal( az_span_atoi32(AZ_SPAN_FROM_STR("-\n123"), &value_i32), AZ_ERROR_UNEXPECTED_CHAR); @@ -628,8 +653,7 @@ static void az_span_ato_number_whitespace_or_invalid_not_allowed(void** state) az_span_atoi64(AZ_SPAN_FROM_STR("-\n123"), &value_i64), AZ_ERROR_UNEXPECTED_CHAR); assert_int_equal( az_span_atou64(AZ_SPAN_FROM_STR("-\n123"), &value_u64), AZ_ERROR_UNEXPECTED_CHAR); - assert_int_equal( - az_span_atod(AZ_SPAN_FROM_STR("-\n123"), &value_d), AZ_ERROR_UNEXPECTED_CHAR); + assert_int_equal(az_span_atod(AZ_SPAN_FROM_STR("-\n123"), &value_d), AZ_ERROR_UNEXPECTED_CHAR); } static void az_span_ato_number_no_out_of_bounds_reads(void** state) @@ -643,8 +667,7 @@ static void az_span_ato_number_no_out_of_bounds_reads(void** state) // within the span slice assert_int_equal( az_span_atoi32(az_span_slice(source, 0, 6), &value_i32), AZ_ERROR_UNEXPECTED_CHAR); - assert_int_equal( - az_span_atod(az_span_slice(source, 0, 6), &value_d), AZ_ERROR_UNEXPECTED_CHAR); + assert_int_equal(az_span_atod(az_span_slice(source, 0, 6), &value_d), AZ_ERROR_UNEXPECTED_CHAR); assert_int_equal(az_span_atoi32(az_span_slice(source, 3, 6), &value_i32), AZ_OK); assert_int_equal(value_i32, 123); @@ -656,8 +679,7 @@ static void az_span_ato_number_no_out_of_bounds_reads(void** state) // within the span slice assert_int_equal( az_span_atoi32(az_span_slice(source, 0, 6), &value_i32), AZ_ERROR_UNEXPECTED_CHAR); - assert_int_equal( - az_span_atod(az_span_slice(source, 0, 6), &value_d), AZ_ERROR_UNEXPECTED_CHAR); + assert_int_equal(az_span_atod(az_span_slice(source, 0, 6), &value_d), AZ_ERROR_UNEXPECTED_CHAR); assert_int_equal(az_span_atoi32(az_span_slice(source, 3, 6), &value_i32), AZ_OK); assert_int_equal(value_i32, 123); @@ -669,8 +691,7 @@ static void az_span_ato_number_no_out_of_bounds_reads(void** state) // within the span slice assert_int_equal( az_span_atoi32(az_span_slice(source, 0, 6), &value_i32), AZ_ERROR_UNEXPECTED_CHAR); - assert_int_equal( - az_span_atod(az_span_slice(source, 0, 6), &value_d), AZ_ERROR_UNEXPECTED_CHAR); + assert_int_equal(az_span_atod(az_span_slice(source, 0, 6), &value_d), AZ_ERROR_UNEXPECTED_CHAR); assert_int_equal(az_span_atoi32(az_span_slice(source, 3, 6), &value_i32), AZ_OK); assert_int_equal(value_i32, 123); @@ -680,8 +701,7 @@ static void az_span_ato_number_no_out_of_bounds_reads(void** state) source = AZ_SPAN_FROM_STR(" 123-"); assert_int_equal( az_span_atoi32(az_span_slice(source, 0, 6), &value_i32), AZ_ERROR_UNEXPECTED_CHAR); - assert_int_equal( - az_span_atod(az_span_slice(source, 0, 6), &value_d), AZ_ERROR_UNEXPECTED_CHAR); + assert_int_equal(az_span_atod(az_span_slice(source, 0, 6), &value_d), AZ_ERROR_UNEXPECTED_CHAR); assert_int_equal(az_span_atoi32(az_span_slice(source, 3, 6), &value_i32), AZ_OK); assert_int_equal(value_i32, 123); @@ -691,13 +711,11 @@ static void az_span_ato_number_no_out_of_bounds_reads(void** state) source = AZ_SPAN_FROM_STR(" 12-4"); assert_int_equal( az_span_atoi32(az_span_slice(source, 0, 6), &value_i32), AZ_ERROR_UNEXPECTED_CHAR); - assert_int_equal( - az_span_atod(az_span_slice(source, 0, 6), &value_d), AZ_ERROR_UNEXPECTED_CHAR); + assert_int_equal(az_span_atod(az_span_slice(source, 0, 6), &value_d), AZ_ERROR_UNEXPECTED_CHAR); assert_int_equal( az_span_atoi32(az_span_slice(source, 3, 6), &value_i32), AZ_ERROR_UNEXPECTED_CHAR); - assert_int_equal( - az_span_atod(az_span_slice(source, 3, 6), &value_d), AZ_ERROR_UNEXPECTED_CHAR); + assert_int_equal(az_span_atod(az_span_slice(source, 3, 6), &value_d), AZ_ERROR_UNEXPECTED_CHAR); source = AZ_SPAN_FROM_STR("n1"); assert_int_equal(az_span_atoi32(source, &value_i32), AZ_ERROR_UNEXPECTED_CHAR);