commit 4f793ab6b7c88fdcf426f3a2af76c633ed555a3e
parent 90bdbf39c299cdfbae83e8007821ba0734f0da1e
Author: vaplv <vaplv@free.fr>
Date: Wed, 28 Oct 2015 11:53:51 +0100
Add and test some C string conversion functions
Add and test the following functions:
- cstr_to_double
- cstr_to_float
- cstr_to_long
- cstr_to_int
- cstr_to_uint
- cstr_to_list_double
- cstr_to_list_float
Diffstat:
7 files changed, 407 insertions(+), 2 deletions(-)
diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt
@@ -41,6 +41,7 @@ set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH})
set(RSYS_FILES_SRC
clock_time.c
+ cstr.c
float44.c
image.c
library.c
@@ -65,6 +66,7 @@ set(RSYS_FILES_INC_API
binary_heap.h
clock_time.h
condition.h
+ cstr.h
dynamic_array.h
dynamic_array_char.h
dynamic_array_double.h
@@ -148,6 +150,7 @@ if(NOT NO_TEST)
new_test(test_atomic)
new_test(test_binary_heap rsys)
+ new_test(test_cstr rsys)
new_test(test_dynamic_array rsys)
new_test(test_float2 ${MATH_LIB})
new_test(test_float3 ${MATH_LIB})
diff --git a/src/cstr.c b/src/cstr.c
@@ -0,0 +1,22 @@
+/* Copyright (C) 2013-2015 Vincent Forest (vaplv@free.fr)
+ *
+ * The RSys library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The RSys library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the RSys library. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "cstr_to_list.h"
+
+#define CSTR_LIST_TYPE double
+#include "cstr_to_list.h"
+
+#define CSTR_LIST_TYPE float
+#include "cstr_to_list.h"
diff --git a/src/cstr.h b/src/cstr.h
@@ -0,0 +1,118 @@
+/* Copyright (C) 2013-2015 Vincent Forest (vaplv@free.fr)
+ *
+ * The RSys library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The RSys library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the RSys library. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef CSTR_H
+#define CSTR_H
+
+#include "rsys.h"
+
+#include <float.h>
+#include <limits.h>
+#include <stdlib.h>
+
+static INLINE res_T
+cstr_to_double(const char* str, double* dst)
+{
+ char* end;
+ ASSERT(dst);
+ if(!str) return RES_BAD_ARG;
+ *dst = strtod(str, &end);
+ if(end == str)
+ return RES_BAD_ARG;
+ for(;*end != '\0'; ++end) {
+ if(*end != ' ' && *end != '\t')
+ return RES_BAD_ARG;
+ }
+ return RES_OK;
+}
+
+static INLINE res_T
+cstr_to_float(const char* str, float* dst)
+{
+ double dbl;
+ res_T res;
+ ASSERT(dst);
+ res = cstr_to_double(str, &dbl);
+ if(res != RES_OK) return res;
+ if((dbl > 0.0 && dbl < FLT_MIN && dbl > FLT_MAX)
+ || (dbl < 0.0 && dbl >-FLT_MIN && dbl <-FLT_MAX))
+ return RES_BAD_ARG;
+ *dst = (float)dbl;
+ return RES_OK;
+}
+
+static INLINE res_T
+cstr_to_long(const char* str, long* dst)
+{
+ char* end;
+ ASSERT(dst);
+ if(!str) return RES_BAD_ARG;
+ *dst = strtol(str, &end, 10/* base */);
+ if(end == str)
+ return RES_BAD_ARG;
+ for(;*end != '\0'; ++end) {
+ if(*end != ' ' && *end != '\t')
+ return RES_BAD_ARG;
+ }
+ return RES_OK;
+}
+
+static INLINE res_T
+cstr_to_int(const char* str, int* dst)
+{
+ long l;
+ res_T res;
+ ASSERT(dst);
+ res = cstr_to_long(str, &l);
+ if(res != RES_OK)
+ return res;
+ if(l > INT_MAX || l < INT_MIN)
+ return RES_BAD_ARG;
+ *dst = (int)l;
+ return RES_OK;
+}
+
+static INLINE res_T
+cstr_to_uint(const char* str, unsigned* dst)
+{
+ long l;
+ res_T res;
+ ASSERT(dst);
+ res = cstr_to_long(str, &l);
+ if(res != RES_OK)
+ return res;
+ if(l < 0 || (sizeof(long) > sizeof(unsigned) && l > (long)UINT_MAX))
+ return RES_BAD_ARG;
+ *dst = (unsigned)l;
+ return RES_OK;
+}
+
+/* Convert a string "A:B:C:D:E:F" in a list of { A, B, C, D, E, F } */
+RSYS_API res_T
+cstr_to_list_double
+ (const char* str,
+ double dst[], /* May be NULL */
+ size_t* length, /* May be NULL. Length of the filled list */
+ const size_t max_length); /* Maximum size of dst */
+
+RSYS_API res_T
+cstr_to_list_float
+ (const char* str,
+ float dst[], /* May be NULL */
+ size_t* length, /* May be NULL. Length of the filled list */
+ const size_t max_length); /* Maximum size of dst */
+
+#endif /* CSTR_H */
+
diff --git a/src/cstr_to_list.h b/src/cstr_to_list.h
@@ -0,0 +1,94 @@
+/* Copyright (C) 2013-2015 Vincent Forest (vaplv@free.fr)
+ *
+ * The RSys library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The RSys library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the RSys library. If not, see <http://www.gnu.org/licenses/>. */
+
+/*
+ * Generate the cstr_to_list conversion function with respect to the
+ * CSTR_LIST_TYPE macro.
+ */
+
+#ifndef CSTR_LIST_TYPE
+#ifndef CSTR_TO_LIST_H
+#define CSTR_TO_LIST_H
+
+#include "cstr.h"
+#include "dynamic_array_char.h"
+
+#endif /* CSTR_TO_LIST_H */
+#else /* defined(CSTR_LIST_TYPE) */
+
+#ifndef CSTR_LIST_TYPE
+ #error "Missing the CSTR_LIST_TYPE macro defining the type of the list"
+#endif
+
+res_T
+CONCAT(cstr_to_list_, CSTR_LIST_TYPE)
+ (const char* str,
+ CSTR_LIST_TYPE dst[],
+ size_t* length,
+ const size_t max_length) /* Maximum size of dst */
+
+{
+ struct darray_char buf;
+ size_t str_len;
+ size_t i;
+ char* tk;
+ res_T res = RES_OK;
+
+ if(!str) {
+ /* Avoid exit block since buf is not initialised */
+ return RES_BAD_ARG;
+ }
+ if(!dst && !length) { /* Useless invocation */
+ /* Avoid exit block since buf is not initialised */
+ return RES_OK;
+ }
+
+ /* Copy str in a temporary buffer to parse */
+ darray_char_init(NULL, &buf);
+ str_len = strlen(str);
+ res = darray_char_resize(&buf, str_len + 1/*null char*/);
+ if(res != RES_OK) goto error;
+ strncpy(darray_char_data_get(&buf), str, str_len + 1/*null char*/);
+
+ /* Parse the string */
+ tk = strtok(darray_char_data_get(&buf), ":");
+ for(i=0; tk; tk = strtok(NULL, ":"), ++i) {
+ if(dst) {
+ if(i >= max_length) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ res = CONCAT(cstr_to_, CSTR_LIST_TYPE)(tk, dst + i);
+ } else {
+ CSTR_LIST_TYPE d;
+ res = CONCAT(cstr_to_, CSTR_LIST_TYPE)(tk, &d);
+ }
+ if(res != RES_OK) goto error;
+ }
+
+ if(length)
+ *length = i;
+
+exit:
+ darray_char_release(&buf);
+ return res;
+error:
+ goto exit;
+}
+
+#undef CSTR_LIST_TYPE
+
+#endif /* defined(CSTR_LIST_TYPE) */
+
diff --git a/src/str.h b/src/str.h
@@ -119,7 +119,7 @@ str_copy(struct str* dst, const struct str* src)
}
static INLINE res_T
-str_copy_and_clear( struct str* dst, struct str* src )
+str_copy_and_clear(struct str* dst, struct str* src)
{
res_T res = RES_OK;
ASSERT(dst && src);
diff --git a/src/test_cstr.c b/src/test_cstr.c
@@ -0,0 +1,169 @@
+/* Copyright (C) 2013-2015 Vincent Forest (vaplv@free.fr)
+ *
+ * The RSys library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The RSys library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the RSys library. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "cstr.h"
+#include "math.h"
+
+int
+main(int argc, char** argv)
+{
+ char buf[128];
+ double dlist[4];
+ float flist[4];
+ size_t len;
+ double d;
+ float f;
+ long l;
+ int i;
+ unsigned u;
+ (void)argc, (void)argv;
+
+ CHECK(cstr_to_double(NULL, &d), RES_BAD_ARG);
+ CHECK(cstr_to_double("a", &d), RES_BAD_ARG);
+ CHECK(cstr_to_double(STR(PI), &d), RES_OK);
+ CHECK(d, PI);
+ CHECK(cstr_to_double(" 1.e-3", &d), RES_OK);
+ CHECK(d, 1.e-3);
+ CHECK(cstr_to_double("+1.E+3 ", &d), RES_OK);
+ CHECK(d, 1.e3);
+ CHECK(cstr_to_double("INF", &d), RES_OK);
+ CHECK(d, INF);
+ CHECK(cstr_to_double("INFINITY", &d), RES_OK);
+ CHECK(d, INF);
+ CHECK(cstr_to_double("", &d), RES_BAD_ARG);
+
+ CHECK(cstr_to_float(NULL, &f), RES_BAD_ARG);
+ CHECK(cstr_to_float("a", &f), RES_BAD_ARG);
+ CHECK(cstr_to_float(STR(PI), &f), RES_OK);
+ CHECK(f, (float)PI);
+ CHECK(cstr_to_float("1.e-1", &f), RES_OK);
+ CHECK(f, 1.e-1f);
+ CHECK(cstr_to_float("+1.E+3", &f), RES_OK);
+ CHECK(f, 1.e3f);
+ CHECK(cstr_to_float("INF", &f), RES_OK);
+ CHECK(f, (float)INF);
+ CHECK(cstr_to_float("INFINITY", &f), RES_OK);
+ CHECK(f, (float)INF);
+ CHECK(cstr_to_float(STR(DBL_MAX), &f), RES_BAD_ARG);
+ CHECK(cstr_to_float(STR(DBL_MIN), &f), RES_BAD_ARG);
+
+ CHECK(cstr_to_long(NULL, &l), RES_BAD_ARG);
+ CHECK(cstr_to_long("a", &l), RES_BAD_ARG);
+ CHECK(cstr_to_long("1.e-3", &l), RES_BAD_ARG);
+ CHECK(cstr_to_long("1", &l), RES_OK);
+ CHECK(l, 1);
+ CHECK(cstr_to_long("-1", &l), RES_OK);
+ CHECK(l, -1);
+ CHECK(cstr_to_long("+1234567890", &l), RES_OK);
+ CHECK(l, 1234567890);
+ CHECK(cstr_to_long(" \t+1234567890 \t ", &l), RES_OK);
+ CHECK(l, 1234567890);
+ CHECK(cstr_to_long(" \t+1234567890 \t a", &l), RES_BAD_ARG);
+
+ CHECK(cstr_to_int(NULL, &i), RES_BAD_ARG);
+ CHECK(cstr_to_int("a", &i), RES_BAD_ARG);
+ CHECK(cstr_to_int("1.e-1", &i), RES_BAD_ARG);
+ CHECK(cstr_to_int("1", &i), RES_OK);
+ CHECK(i, 1);
+ CHECK(cstr_to_int("-2", &i), RES_OK);
+ CHECK(i, -2);
+ CHECK(cstr_to_int("\t-2 ", &i), RES_OK);
+ CHECK(i, -2);
+ sprintf(buf, "%d", INT_MAX);
+ CHECK(cstr_to_int(buf, &i), RES_OK);
+ CHECK(i, INT_MAX);
+ sprintf(buf, "%d", INT_MIN);
+ CHECK(cstr_to_int(buf, &i), RES_OK);
+ CHECK(i, INT_MIN);
+ sprintf(buf, "%ld", (long)INT_MAX+1);
+ CHECK(cstr_to_int(buf, &i), RES_BAD_ARG);
+ sprintf(buf, "%ld", (long)INT_MIN-1);
+ CHECK(cstr_to_int(buf, &i), RES_BAD_ARG);
+
+ CHECK(cstr_to_uint(NULL, &u), RES_BAD_ARG);
+ CHECK(cstr_to_uint("a", &u), RES_BAD_ARG);
+ CHECK(cstr_to_uint("-1", &u), RES_BAD_ARG);
+ CHECK(cstr_to_uint("0.", &u), RES_BAD_ARG);
+ CHECK(cstr_to_uint("0", &u), RES_OK);
+ CHECK(u, 0);
+ CHECK(cstr_to_uint("+2", &u), RES_OK);
+ CHECK(u, 2);
+ CHECK(cstr_to_uint(" \t+123 \t ", &u), RES_OK);
+ CHECK(u, 123);
+ sprintf(buf, "%u", UINT_MAX);
+ CHECK(cstr_to_uint(buf, &u), RES_OK);
+ CHECK(u, UINT_MAX);
+ sprintf(buf, "%lu", (unsigned long)UINT_MAX+1);
+ CHECK(cstr_to_uint(buf, &u), RES_BAD_ARG);
+
+ CHECK(cstr_to_list_double(NULL, dlist, NULL, 3), RES_BAD_ARG);
+ CHECK(cstr_to_list_double("a", dlist, NULL, 3), RES_BAD_ARG);
+ CHECK(cstr_to_list_double("1.e-3:2.0:"STR(PI), dlist, NULL, 3), RES_OK);
+ CHECK(dlist[0], 1.e-3);
+ CHECK(dlist[1], 2.0);
+ CHECK(dlist[2], PI);
+ CHECK(cstr_to_list_double("1.e-3:2.0:", dlist, &len, 3), RES_OK);
+ CHECK(len, 2);
+ CHECK(dlist[0], 1.e-3);
+ CHECK(dlist[1], 2.0);
+ CHECK(cstr_to_list_double("-1.0:0.5:1.2:4.3", dlist, &len, 2), RES_BAD_ARG);
+ CHECK(cstr_to_list_double("-1.0:0.5:1.2:4.3a", NULL, &len, 0), RES_BAD_ARG);
+ CHECK(cstr_to_list_double("-1.0:0.5:1.2:4.3", NULL, &len, 0), RES_OK);
+ CHECK(len, 4);
+ CHECK(cstr_to_list_double("-1.0:0.5:1.2:4.3", dlist, NULL, len), RES_OK);
+ CHECK(dlist[0], -1.0);
+ CHECK(dlist[1], 0.5);
+ CHECK(dlist[2], 1.2);
+ CHECK(dlist[3], 4.3);
+ CHECK(cstr_to_list_double(" \t -1.0:0.5:1.2:INF \t", dlist, NULL, 4), RES_OK);
+ CHECK(dlist[0], -1.0);
+ CHECK(dlist[1], 0.5);
+ CHECK(dlist[2], 1.2);
+ CHECK(dlist[3], INF);
+ CHECK(cstr_to_list_double(" \t -1.0:0.5:1.2:4.3 \ta", dlist, NULL, 4), RES_BAD_ARG);
+ dlist[1] = dlist[2] = dlist[3] = -1.0;
+ CHECK(cstr_to_list_double("1.0", dlist, NULL, 1), RES_OK);
+ CHECK(dlist[0], 1.0);
+ CHECK(dlist[1], -1.0);
+ CHECK(dlist[2], -1.0);
+ CHECK(dlist[3], -1.0);
+
+ CHECK(cstr_to_list_float(NULL, flist, NULL, 3), RES_BAD_ARG);
+ CHECK(cstr_to_list_float("a", flist, NULL, 3), RES_BAD_ARG);
+ CHECK(cstr_to_list_float("1.e-3:2.0:"STR(PI), flist, NULL, 3), RES_OK);
+ CHECK(flist[0], 1.e-3f);
+ CHECK(flist[1], 2.0f);
+ CHECK(flist[2], (float)PI);
+ CHECK(cstr_to_list_float("1.e-3:2.0:", flist, &len, 3), RES_OK);
+ CHECK(len, 2);
+ CHECK(flist[0], 1.e-3f);
+ CHECK(flist[1], 2.0f);
+ CHECK(cstr_to_list_float("-1.0:0.5:1.2:4.3", flist, &len, 2), RES_BAD_ARG);
+ CHECK(cstr_to_list_float("-1.0:0.5:1.2:4.3a", NULL, &len, 0), RES_BAD_ARG);
+ CHECK(cstr_to_list_float("-1.0:0.5:1.2:4.3", NULL, &len, 0), RES_OK);
+ CHECK(len, 4);
+ CHECK(cstr_to_list_float("-1.0:0.5:1.2:4.3", flist, NULL, len), RES_OK);
+ CHECK(flist[0], -1.0f);
+ CHECK(flist[1], 0.5f);
+ CHECK(flist[2], 1.2f);
+ CHECK(flist[3], 4.3f);
+ CHECK(cstr_to_list_float(" \t -1.0:0.5:1.2:INF \t", flist, NULL, 4), RES_OK);
+ CHECK(flist[0], -1.0f);
+ CHECK(flist[1], 0.5f);
+ CHECK(flist[2], 1.2f);
+ CHECK(flist[3], (float)INF);
+
+ return 0;
+}
diff --git a/src/test_math.c b/src/test_math.c
@@ -50,7 +50,6 @@ main(int argc, char** argv)
CHECK(eq_eps(PI, 3.14159265358979323846, 1.e-8), 1);
CHECK(eq_eps(RCP_PI, 1.0/PI, 1.e-8), 1);
-
CHECK(1.f/0.f, (float)INF);
CHECK(-1.f/0.f, (float)-INF);
NCHECK(1.0/0.0, -INF);