From 6826df07dacd91f1ebc895548d8eb41783ef1dd7 Mon Sep 17 00:00:00 2001
From: Peter Hutterer <peter.hutterer@who-t.net>
Date: Mon, 31 May 2021 15:06:35 +1000
Subject: [PATCH] spa/utils: add spa_atou32, spa_atoi64,spa_atou64, spa_atod,
 spa_atof

Same as the existing spa_atoi32 but for uint32_t, {u}int64_t, float and
double.
---
 spa/include/spa/utils/string.h | 126 +++++++++++++++++++++++++++++
 spa/tests/test-utils.c         | 142 +++++++++++++++++++++++++++++++++
 2 files changed, 268 insertions(+)

diff --git a/spa/include/spa/utils/string.h b/spa/include/spa/utils/string.h
index 363ad823ba..8eea8795ae 100644
--- a/spa/include/spa/utils/string.h
+++ b/spa/include/spa/utils/string.h
@@ -89,6 +89,84 @@ static inline bool spa_atoi32(const char *str, int32_t *val, int base)
 	return true;
 }
 
+/**
+ * Convert \a str to an uint32_t with the given \a base and store the
+ * result in \a val.
+ *
+ * On failure, the value of \a val is unmodified.
+ *
+ * \return true on success, false otherwise
+ */
+static inline bool spa_atou32(const char *str, uint32_t *val, int base)
+{
+	char *endptr;
+	long v;
+
+	if (!str || *str =='\0')
+		return false;
+
+	errno = 0;
+	v = strtoul(str, &endptr, base);
+	if (errno != 0 || *endptr != '\0')
+		return false;
+
+	if (v != (uint32_t)v)
+		return false;
+
+	*val = v;
+	return true;
+}
+
+/**
+ * Convert \a str to an int64_t with the given \a base and store the
+ * result in \a val.
+ *
+ * On failure, the value of \a val is unmodified.
+ *
+ * \return true on success, false otherwise
+ */
+static inline bool spa_atoi64(const char *str, int64_t *val, int base)
+{
+	char *endptr;
+	long long v;
+
+	if (!str || *str =='\0')
+		return false;
+
+	errno = 0;
+	v = strtoll(str, &endptr, base);
+	if (errno != 0 || *endptr != '\0')
+		return false;
+
+	*val = v;
+	return true;
+}
+
+/**
+ * Convert \a str to an uint64_t with the given \a base and store the
+ * result in \a val.
+ *
+ * On failure, the value of \a val is unmodified.
+ *
+ * \return true on success, false otherwise
+ */
+static inline bool spa_atou64(const char *str, uint64_t *val, int base)
+{
+	char *endptr;
+	unsigned long long v;
+
+	if (!str || *str =='\0')
+		return false;
+
+	errno = 0;
+	v = strtoull(str, &endptr, base);
+	if (errno != 0 || *endptr != '\0')
+		return false;
+
+	*val = v;
+	return true;
+}
+
 /**
  * Convert \a str to a boolean. Allowed boolean values are "true" and a
  * literal "1", anything else is false.
@@ -100,6 +178,54 @@ static inline bool spa_atob(const char *str)
 	return spa_streq(str, "true") || spa_streq(str, "1");
 }
 
+/**
+ * Convert \a str to a float and store the result in \a val.
+ *
+ * On failure, the value of \a val is unmodified.
+ *
+ * \return true on success, false otherwise
+ */
+static inline bool spa_atof(const char *str, float *val)
+{
+	char *endptr;
+	float v;
+
+	if (!str || *str =='\0')
+		return false;
+
+	errno = 0;
+	v = strtof(str, &endptr);
+	if (errno != 0 || *endptr != '\0')
+		return false;
+
+	*val = v;
+	return true;
+}
+
+/**
+ * Convert \a str to a double and store the result in \a val.
+ *
+ * On failure, the value of \a val is unmodified.
+ *
+ * \return true on success, false otherwise
+ */
+static inline bool spa_atod(const char *str, double *val)
+{
+	char *endptr;
+	double v;
+
+	if (!str || *str =='\0')
+		return false;
+
+	errno = 0;
+	v = strtod(str, &endptr);
+	if (errno != 0 || *endptr != '\0')
+		return false;
+
+	*val = v;
+	return true;
+}
+
 /**
  * \}
  */
diff --git a/spa/tests/test-utils.c b/spa/tests/test-utils.c
index fec76e8225..baa79fcbf7 100644
--- a/spa/tests/test-utils.c
+++ b/spa/tests/test-utils.c
@@ -22,6 +22,8 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
+#include <locale.h>
+
 #include <spa/utils/defs.h>
 #include <spa/utils/result.h>
 #include <spa/utils/dict.h>
@@ -479,6 +481,141 @@ static void test_strtol(void)
 	spa_assert(!spa_atoi32(NULL, &v, 16) && v == 0xabcd);
 }
 
+static void test_strtoul(void)
+{
+	uint32_t v;
+
+	spa_assert(spa_atou32("0", &v, 0) && v == 0);
+	spa_assert(spa_atou32("0", &v, 16) && v == 0);
+	spa_assert(spa_atou32("0", &v, 32) && v == 0);
+	spa_assert(spa_atou32("+1", &v, 0) && v == 1);
+	spa_assert(spa_atou32("+1234", &v, 0) && v == 1234);
+	spa_assert(spa_atou32("+4294967295", &v, 0) && v == 4294967295);
+	spa_assert(spa_atou32("4294967295", &v, 0) && v == 4294967295);
+	spa_assert(spa_atou32("65535", &v, 0) && v == 0xffff);
+	spa_assert(spa_atou32("65535", &v, 10) && v == 0xffff);
+	spa_assert(spa_atou32("65535", &v, 16) && v == 0x65535);
+	spa_assert(spa_atou32("0xff", &v, 0) && v == 0xff);
+	spa_assert(spa_atou32("0xff", &v, 16) && v == 0xff);
+
+	v = 0xabcd;
+	spa_assert(!spa_atou32("-1", &v, 0) && v == 0xabcd);
+	spa_assert(!spa_atou32("-1234", &v, 0) && v == 0xabcd);
+	spa_assert(!spa_atou32("-2147483648", &v, 0) && v == 0xabcd);
+	spa_assert(!spa_atou32("0xff", &v, 10) && v == 0xabcd);
+	spa_assert(!spa_atou32("fabc", &v, 10) && v == 0xabcd);
+	spa_assert(!spa_atou32("fabc", &v, 0) && v == 0xabcd);
+
+	spa_assert(!spa_atou32("124bogus", &v, 0) && v == 0xabcd);
+	spa_assert(!spa_atou32("124bogus", &v, 10) && v == 0xabcd);
+	spa_assert(!spa_atou32("124bogus", &v, 16) && v == 0xabcd);
+	spa_assert(!spa_atou32("0xbogus", &v, 0) && v == 0xabcd);
+	spa_assert(!spa_atou32("bogus", &v, 10) && v == 0xabcd);
+	spa_assert(!spa_atou32("bogus", &v, 16) && v == 0xabcd);
+	spa_assert(!spa_atou32("", &v, 0) && v == 0xabcd);
+	spa_assert(!spa_atou32("", &v, 10) && v == 0xabcd);
+	spa_assert(!spa_atou32("", &v, 16) && v == 0xabcd);
+	spa_assert(!spa_atou32("  ", &v, 0) && v == 0xabcd);
+	spa_assert(!spa_atou32(" ", &v, 0) && v == 0xabcd);
+
+	spa_assert(!spa_atou32("-2147483649", &v, 0) && v == 0xabcd);
+	spa_assert(!spa_atou32("4294967296", &v, 0) && v == 0xabcd);
+	spa_assert(!spa_atou32("9223372036854775807", &v, 0) && v == 0xabcd);
+	spa_assert(!spa_atou32("-9223372036854775808", &v, 0) && v == 0xabcd);
+	spa_assert(!spa_atou32("9223372036854775808999", &v, 0) && v == 0xabcd);
+
+	spa_assert(!spa_atou32(NULL, &v, 0) && v == 0xabcd);
+	spa_assert(!spa_atou32(NULL, &v, 10) && v == 0xabcd);
+	spa_assert(!spa_atou32(NULL, &v, 16) && v == 0xabcd);
+}
+
+static void test_strtoll(void)
+{
+	int64_t v;
+
+	spa_assert(spa_atoi64("0", &v, 0) && v == 0);
+	spa_assert(spa_atoi64("0", &v, 16) && v == 0);
+	spa_assert(spa_atoi64("0", &v, 32) && v == 0);
+	spa_assert(spa_atoi64("-1", &v, 0) && v == -1);
+	spa_assert(spa_atoi64("-1234", &v, 0) && v == -1234);
+	spa_assert(spa_atoi64("-2147483648", &v, 0) && v == -2147483648);
+	spa_assert(spa_atoi64("+1", &v, 0) && v == 1);
+	spa_assert(spa_atoi64("+1234", &v, 0) && v == 1234);
+	spa_assert(spa_atoi64("+2147483647", &v, 0) && v == 2147483647);
+	spa_assert(spa_atoi64("65535", &v, 0) && v == 0xffff);
+	spa_assert(spa_atoi64("65535", &v, 10) && v == 0xffff);
+	spa_assert(spa_atoi64("65535", &v, 16) && v == 0x65535);
+	spa_assert(spa_atoi64("0xff", &v, 0) && v == 0xff);
+	spa_assert(spa_atoi64("0xff", &v, 16) && v == 0xff);
+	spa_assert(spa_atoi64("9223372036854775807", &v, 0) && v == 0x7fffffffffffffff);
+	spa_assert(spa_atoi64("-9223372036854775808", &v, 0) && (uint64_t)v == 0x8000000000000000);
+
+	v = 0xabcd;
+	spa_assert(!spa_atoi64("0xff", &v, 10) && v == 0xabcd);
+	spa_assert(!spa_atoi64("fabc", &v, 10) && v == 0xabcd);
+	spa_assert(!spa_atoi64("fabc", &v, 0) && v == 0xabcd);
+
+	spa_assert(!spa_atoi64("124bogus", &v, 0) && v == 0xabcd);
+	spa_assert(!spa_atoi64("124bogus", &v, 10) && v == 0xabcd);
+	spa_assert(!spa_atoi64("124bogus", &v, 16) && v == 0xabcd);
+	spa_assert(!spa_atoi64("0xbogus", &v, 0) && v == 0xabcd);
+	spa_assert(!spa_atoi64("bogus", &v, 10) && v == 0xabcd);
+	spa_assert(!spa_atoi64("bogus", &v, 16) && v == 0xabcd);
+	spa_assert(!spa_atoi64("", &v, 0) && v == 0xabcd);
+	spa_assert(!spa_atoi64("", &v, 10) && v == 0xabcd);
+	spa_assert(!spa_atoi64("", &v, 16) && v == 0xabcd);
+	spa_assert(!spa_atoi64("  ", &v, 0) && v == 0xabcd);
+	spa_assert(!spa_atoi64(" ", &v, 0) && v == 0xabcd);
+
+	spa_assert(!spa_atoi64("9223372036854775808999", &v, 0) && v == 0xabcd);
+
+	spa_assert(!spa_atoi64(NULL, &v, 0) && v == 0xabcd);
+	spa_assert(!spa_atoi64(NULL, &v, 10) && v == 0xabcd);
+	spa_assert(!spa_atoi64(NULL, &v, 16) && v == 0xabcd);
+}
+
+static void test_strtof(void)
+{
+	float v;
+
+	spa_assert(spa_atof("0", &v) && v == 0.0f);
+	spa_assert(spa_atof("0.00", &v) && v == 0.0f);
+	spa_assert(spa_atof("1", &v) && v == 1.0f);
+	spa_assert(spa_atof("-1", &v) && v == -1.0f);
+	spa_assert(spa_atof("0x1", &v) && v == 1.0f);
+
+	v = 0xabcd;
+	spa_assert(!spa_atof("0,00", &v) && v == 0xabcd);
+	spa_assert(!spa_atof("fabc", &v) && v == 0xabcd);
+	spa_assert(!spa_atof("1.bogus", &v) && v == 0xabcd);
+	spa_assert(!spa_atof("1.0a", &v) && v == 0xabcd);
+	spa_assert(!spa_atof("  ", &v) && v == 0xabcd);
+	spa_assert(!spa_atof(" ", &v) && v == 0xabcd);
+	spa_assert(!spa_atof("", &v) && v == 0xabcd);
+	spa_assert(!spa_atof(NULL, &v) && v == 0xabcd);
+}
+
+static void test_strtod(void)
+{
+	double v;
+
+	spa_assert(spa_atod("0", &v) && v == 0.0);
+	spa_assert(spa_atod("0.00", &v) && v == 0.0);
+	spa_assert(spa_atod("1", &v) && v == 1.0);
+	spa_assert(spa_atod("-1", &v) && v == -1.0);
+	spa_assert(spa_atod("0x1", &v) && v == 1.0);
+
+	v = 0xabcd;
+	spa_assert(!spa_atod("0,00", &v) && v == 0xabcd);
+	spa_assert(!spa_atod("fabc", &v) && v == 0xabcd);
+	spa_assert(!spa_atod("1.bogus", &v) && v == 0xabcd);
+	spa_assert(!spa_atod("1.0a", &v) && v == 0xabcd);
+	spa_assert(!spa_atod("  ", &v) && v == 0xabcd);
+	spa_assert(!spa_atod(" ", &v) && v == 0xabcd);
+	spa_assert(!spa_atod("", &v) && v == 0xabcd);
+	spa_assert(!spa_atod(NULL, &v) && v == 0xabcd);
+}
+
 static void test_streq(void)
 {
 	spa_assert(spa_streq(NULL, NULL));
@@ -592,6 +729,7 @@ static void test_ansi(void)
 
 int main(int argc, char *argv[])
 {
+    setlocale(LC_NUMERIC, "C"); /* For decimal number parsing */
     test_abi();
     test_macros();
     test_result();
@@ -600,6 +738,10 @@ int main(int argc, char *argv[])
     test_hook();
     test_ringbuffer();
     test_strtol();
+    test_strtoul();
+    test_strtoll();
+    test_strtof();
+    test_strtod();
     test_streq();
     return 0;
 }
#     test_streq();
#     test_atob();
#     test_ansi();
-- 
GitLab

