add temporary implementations for string to number and first test cases default tip

Mon, 23 Dec 2024 00:34:05 +0100

author
Mike Becker <universe@uap-core.de>
date
Mon, 23 Dec 2024 00:34:05 +0100
changeset 1052
e997862a57d8
parent 1051
7d17bd1103d7

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",

mercurial