Mon, 23 Dec 2024 00:34:05 +0100
add temporary implementations for string to number and first test cases
issue #532
src/string.c | file | annotate | diff | comparison | revisions | |
tests/test_string.c | file | annotate | diff | comparison | revisions | |
tests/ucxtest.c | file | annotate | diff | comparison | revisions |
--- a/src/string.c Mon Dec 23 00:33:27 2024 +0100 +++ b/src/string.c Mon Dec 23 00:34:05 2024 +0100 @@ -815,8 +815,8 @@ } #define cx_strtoX_signed_impl(rtype, rmin, rmax) \ - int64_t result; \ - if (cx_strtoi64_lc(str, &result, base, groupsep)) { \ + long long result; \ + if (cx_strtoll_lc(str, &result, base, groupsep)) { \ return -1; \ } \ if (result < rmin || result > rmax) { \ @@ -839,8 +839,17 @@ } int cx_strtoll_lc(cxstring str, long long *output, int base, const char *groupsep) { - assert(sizeof(long long) == sizeof(int64_t)); // should be true on all platforms - return cx_strtoi64_lc(str, (int64_t*) output, base, groupsep); + // TODO: replace temporary implementation + (void) groupsep; // unused in temp impl + char *s = malloc(str.length + 1); + memcpy(s, str.ptr, str.length); + s[str.length] = '\0'; + char *e; + errno = 0; + *output = strtoll(s, &e, base); + int r = errno || !(e && *e == '\0'); + free(s); + return r; } int cx_strtoi8_lc(cxstring str, int8_t *output, int base, const char *groupsep) { @@ -856,15 +865,17 @@ } int cx_strtoi64_lc(cxstring str, int64_t *output, int base, const char *groupsep) { - // TODO: implement - return -1; + assert(sizeof(long long) == sizeof(int64_t)); // should be true on all platforms + return cx_strtoll_lc(str, (long long*) output, base, groupsep); } int cx_strtoz_lc(cxstring str, ssize_t *output, int base, const char *groupsep) { -#if CX_WORDSIZE == 32 - return cx_strtoi32_lc(str, output, base, groupsep); +#if SSIZE_MAX == INT32_MAX + return cx_strtoi32_lc(str, (int32_t*) output, base, groupsep); +#elif SSIZE_MAX == INT64_MAX + return cx_strtoll_lc(str, (long long*) output, base, groupsep); #else - return cx_strtoi64_lc(str, output, base, groupsep); +#error "unsupported ssize_t size" #endif } @@ -893,8 +904,16 @@ } int cx_strtoull_lc(cxstring str, unsigned long long *output, int base, const char *groupsep) { - assert(sizeof(unsigned long long) == sizeof(uint64_t)); // should be true on all platforms - return cx_strtou64_lc(str, (uint64_t*) output, base, groupsep); + // TODO: replace temporary implementation + (void) groupsep; // unused in temp impl + char *s = malloc(str.length + 1); + memcpy(s, str.ptr, str.length); + s[str.length] = '\0'; + char *e; + *output = strtoull(s, &e, base); + int r = !(e && *e == '\0'); + free(s); + return r; } int cx_strtou8_lc(cxstring str, uint8_t *output, int base, const char *groupsep) { @@ -910,24 +929,44 @@ } int cx_strtou64_lc(cxstring str, uint64_t *output, int base, const char *groupsep) { - // TODO: implement - return -1; + assert(sizeof(unsigned long long) == sizeof(uint64_t)); // should be true on all platforms + return cx_strtoull_lc(str, (unsigned long long*) output, base, groupsep); } int cx_strtouz_lc(cxstring str, size_t *output, int base, const char *groupsep) { -#if CX_WORDSIZE == 32 - return cx_strtou32_lc(str, output, base, groupsep); +#if SIZE_MAX == UINT32_MAX + return cx_strtou32_lc(str, (uint32_t*) output, base, groupsep); +#elif SIZE_MAX == UINT64_MAX + return cx_strtoull_lc(str, (unsigned long long *) output, base, groupsep); #else - return cx_strtou64_lc(str, output, base, groupsep); +#error "unsupported size_t size" #endif } int cx_strtof_lc(cxstring str, float *output, char decsep, const char *groupsep) { - // TODO: impl - return -1; + // TODO: replace temporary implementation + (void) groupsep; // unused in temp impl + (void) decsep; // unused in temp impl + char *s = malloc(str.length + 1); + memcpy(s, str.ptr, str.length); + s[str.length] = '\0'; + char *e; + *output = strtof(s, &e); + int r = !(e && *e == '\0'); + free(s); + return r; } int cx_strtod_lc(cxstring str, double *output, char decsep, const char *groupsep) { - // TODO: impl - return -1; + // TODO: replace temporary implementation + (void) groupsep; // unused in temp impl + (void) decsep; // unused in temp impl + char *s = malloc(str.length + 1); + memcpy(s, str.ptr, str.length); + s[str.length] = '\0'; + char *e; + *output = strtod(s, &e); + int r = !(e && *e == '\0'); + free(s); + return r; } \ No newline at end of file
--- a/tests/test_string.c Mon Dec 23 00:33:27 2024 +0100 +++ b/tests/test_string.c Mon Dec 23 00:34:05 2024 +0100 @@ -31,6 +31,9 @@ #include "cx/string.h" +#include <limits.h> +#include <errno.h> + #define ASSERT_ZERO_TERMINATED(str) CX_TEST_ASSERTM((str).ptr[(str).length] == '\0', \ #str " is not zero terminated") @@ -970,6 +973,159 @@ cx_strfree(&str); } +#define test_strtoint_impl(suffix, num, base, var, min, max) \ + do { \ + errno = 0; \ + int r = cx_strto##var(cx_str( #num), &var, base); \ + if ((min) <= (num##suffix) && (num##suffix) <= (max)) { \ + CX_TEST_ASSERTM(0 == r, "failed for "#num); \ + CX_TEST_ASSERT(0 == errno); \ + CX_TEST_ASSERT((num##suffix) == (0##suffix)+(var)); \ + } else { \ + CX_TEST_ASSERTM(0 != r, "out-of-range not detected for "#num " in variant "#var); \ + CX_TEST_ASSERT(ERANGE == errno); \ + } \ + } while (0) + +#define test_strtoint_rollout_signed_impl(num, base) \ + test_strtoint_impl(LL, num, base, s, SHRT_MIN, SHRT_MAX); \ + test_strtoint_impl(LL, num, base, i, INT_MIN, INT_MAX); \ + test_strtoint_impl(LL, num, base, l, LONG_MIN, LONG_MAX); \ + test_strtoint_impl(LL, num, base, ll, LLONG_MIN, LLONG_MAX); \ + test_strtoint_impl(LL, num, base, i8, INT8_MIN, INT8_MAX); \ + test_strtoint_impl(LL, num, base, i16, INT16_MIN, INT16_MAX); \ + test_strtoint_impl(LL, num, base, i32, INT32_MIN, INT32_MAX); \ + test_strtoint_impl(LL, num, base, i64, INT64_MIN, INT64_MAX); \ + test_strtoint_impl(LL, num, base, z, -SSIZE_MAX-1, SSIZE_MAX) + +#define test_strtoint_rollout_signed(num, base) \ + test_strtoint_rollout_signed_impl(num, base); \ + test_strtoint_rollout_signed_impl(-num, base) + +#define test_strtoint_rollout(num, base) \ + test_strtoint_impl(ULL, num, base, us, 0, USHRT_MAX); \ + test_strtoint_impl(ULL, num, base, u, 0, UINT_MAX); \ + test_strtoint_impl(ULL, num, base, ul, 0, ULONG_MAX); \ + test_strtoint_impl(ULL, num, base, ull, 0, ULLONG_MAX); \ + test_strtoint_impl(ULL, num, base, u8, 0, UINT8_MAX); \ + test_strtoint_impl(ULL, num, base, u16, 0, UINT16_MAX); \ + test_strtoint_impl(ULL, num, base, u32, 0, UINT32_MAX); \ + test_strtoint_impl(ULL, num, base, u64, 0, UINT64_MAX); \ + test_strtoint_impl(ULL, num, base, uz, 0, SIZE_MAX) + +CX_TEST(test_string_to_signed_integer) { + short s; + int i; + long l; + long long ll; + int8_t i8; + int16_t i16; + int32_t i32; + int64_t i64; + ssize_t z; + CX_TEST_DO { + // do some brute force tests with all ranges + test_strtoint_rollout_signed(47, 10); + test_strtoint_rollout_signed(210, 10); + test_strtoint_rollout_signed(5678, 10); + test_strtoint_rollout_signed(40678, 10); + test_strtoint_rollout_signed(1350266537, 10); + test_strtoint_rollout_signed(3350266537, 10); + test_strtoint_rollout_signed(473350266537, 10); + test_strtoint_rollout_signed(057, 8); + test_strtoint_rollout_signed(0322, 8); + test_strtoint_rollout_signed(013056, 8); + test_strtoint_rollout_signed(0117346, 8); + test_strtoint_rollout_signed(012036667251, 8); + test_strtoint_rollout_signed(030754201251, 8); + test_strtoint_rollout_signed(06706567757251, 8); + test_strtoint_rollout_signed(0767716340165362204025, 8); + test_strtoint_rollout_signed(0x65, 16); + test_strtoint_rollout_signed(0xf5, 16); + test_strtoint_rollout_signed(0xABC5, 16); + test_strtoint_rollout_signed(0xFBC5, 16); + test_strtoint_rollout_signed(0x6df9CE03, 16); + test_strtoint_rollout_signed(0xFdf9CE03, 16); + test_strtoint_rollout_signed(0x6df9CE03AbC90815, 16); + // TODO: roll out base 2 tests, but that needs C23 + + // do some special case tests + + // can fit only in unsigned long long + errno = 0; + CX_TEST_ASSERT(0 != cx_strtoll(cx_str("0x8df9CE03AbC90815"), &ll, 16)); + CX_TEST_ASSERT(errno == ERANGE); + + // TODO: implement more special cases + } +} + +CX_TEST(test_string_to_unsigned_integer) { + unsigned short us; + unsigned int u; + unsigned long ul; + unsigned long long ull; + uint8_t u8; + uint16_t u16; + uint32_t u32; + uint64_t u64; + size_t uz; + CX_TEST_DO { + // do some brute force tests with all ranges + test_strtoint_rollout(47, 10); + test_strtoint_rollout(210, 10); + test_strtoint_rollout(5678, 10); + test_strtoint_rollout(40678, 10); + test_strtoint_rollout(1350266537, 10); + test_strtoint_rollout(3350266537, 10); + test_strtoint_rollout(473350266537, 10); + test_strtoint_rollout(057, 8); + test_strtoint_rollout(0322, 8); + test_strtoint_rollout(013056, 8); + test_strtoint_rollout(0117346, 8); + test_strtoint_rollout(012036667251, 8); + test_strtoint_rollout(030754201251, 8); + test_strtoint_rollout(06706567757251, 8); + test_strtoint_rollout(01767716340165362204025, 8); + test_strtoint_rollout(0x65, 16); + test_strtoint_rollout(0xf5, 16); + test_strtoint_rollout(0xABC5, 16); + test_strtoint_rollout(0xFBC5, 16); + test_strtoint_rollout(0x6df9CE03, 16); + test_strtoint_rollout(0xFdf9CE03, 16); + test_strtoint_rollout(0x6df9CE03AbC90815, 16); + test_strtoint_rollout(0xfdf9CE03AbC90815, 16); + // TODO: roll out base 2 tests, but that needs C23 + + // do some special case tests + + // TODO: implement tests + } +} + +CX_TEST(test_string_to_float) { + float f; + CX_TEST_DO { + CX_TEST_ASSERT(0 == cx_strtof(cx_str("11.3"), &f)); + CX_TEST_ASSERT(11.3f == f); + } +} + +CX_TEST(test_string_to_double) { + double d; + CX_TEST_DO { + CX_TEST_ASSERT(0 == cx_strtod(cx_str("11.3"), &d)); + CX_TEST_ASSERT(11.3 == d); + } +} + +CX_TEST(test_string_to_float_german) { + float f; + CX_TEST_DO { + // TODO: implement + (void)f; + } +} CxTestSuite *cx_test_suite_string(void) { CxTestSuite *suite = cx_test_suite_new("string"); @@ -1006,3 +1162,15 @@ return suite; } + +CxTestSuite *cx_test_suite_string_to_number(void) { + CxTestSuite *suite = cx_test_suite_new("string to number"); + + cx_test_register(suite, test_string_to_signed_integer); + cx_test_register(suite, test_string_to_unsigned_integer); + cx_test_register(suite, test_string_to_float); + cx_test_register(suite, test_string_to_double); + cx_test_register(suite, test_string_to_float_german); + + return suite; +}
--- a/tests/ucxtest.c Mon Dec 23 00:33:27 2024 +0100 +++ b/tests/ucxtest.c Mon Dec 23 00:34:05 2024 +0100 @@ -35,6 +35,7 @@ CxTestSuite *cx_test_suite_streams(void); CxTestSuite *cx_test_suite_compare(void); CxTestSuite *cx_test_suite_string(void); +CxTestSuite *cx_test_suite_string_to_number(void); CxTestSuite *cx_test_suite_buffer(void); CxTestSuite *cx_test_suite_hash_key(void); CxTestSuite *cx_test_suite_hash_map(void); @@ -66,6 +67,7 @@ cx_test_suite_streams(), cx_test_suite_compare(), cx_test_suite_string(), + //cx_test_suite_string_to_number(), cx_test_suite_buffer(), cx_test_suite_hash_key(), cx_test_suite_hash_map(), @@ -80,7 +82,8 @@ cx_test_suite_properties(), cx_test_suite_mempool(), cx_test_suite_json(), - cx_test_suite_printf() + cx_test_suite_printf(), + cx_test_suite_string_to_number() ); printf("=== OVERALL RESULT ===\n"); printf(" Total: %u\n Success: %u\n Failure: %u\n",