Change in libosmocore[master]: add osmo_float_str_to_micros and osmo_micros_to_float_str

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

neels gerrit-no-reply at lists.osmocom.org
Thu Oct 1 01:40:19 UTC 2020


neels has uploaded this change for review. ( https://gerrit.osmocom.org/c/libosmocore/+/20331 )


Change subject: add osmo_float_str_to_micros and osmo_micros_to_float_str
......................................................................

add osmo_float_str_to_micros and osmo_micros_to_float_str

This will be useful to handle latitude and longitude numbers for GAD, which is
the location estimate representation used for LCS (Location Services).

The VTY user interface will provide floating-point strings like "23.456" while
GAD stores them as micro-degress 23456000. The osmo_gad_to_str* will also
convert latitude and longitude to floating-point string.

Change-Id: Ib9aee749cd331712a4dcdadfb6a2dfa4c26da957
---
M include/osmocom/core/utils.h
M src/utils.c
M tests/utils/utils_test.c
M tests/utils/utils_test.ok
4 files changed, 279 insertions(+), 0 deletions(-)



  git pull ssh://gerrit.osmocom.org:29418/libosmocore refs/changes/31/20331/1

diff --git a/include/osmocom/core/utils.h b/include/osmocom/core/utils.h
index 8619120..5e48ab8 100644
--- a/include/osmocom/core/utils.h
+++ b/include/osmocom/core/utils.h
@@ -279,6 +279,10 @@
 
 bool osmo_str_startswith(const char *str, const char *startswith_str);
 
+int osmo_float_str_to_micros(int32_t *val, const char *str);
+int osmo_micros_to_float_str_buf(char *buf, size_t buflen, int32_t val);
+char *osmo_micros_to_float_str_c(void *ctx, int32_t val);
+
 /*! Translate a buffer function to a talloc context function.
  * This is the full function body of a char *foo_name_c(void *ctx, val...) function, implemented by an
  * int foo_name_buf(buf, buflen, val...) function:
diff --git a/src/utils.c b/src/utils.c
index 772b671..d97f835 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -30,6 +30,7 @@
 #include <errno.h>
 #include <stdio.h>
 #include <inttypes.h>
+#include <limits.h>
 
 #include <osmocom/core/utils.h>
 #include <osmocom/core/bit64gen.h>
@@ -1208,4 +1209,127 @@
 	return strncmp(str, startswith_str, strlen(startswith_str)) == 0;
 }
 
+/*! Convert a string of a floating point number to a signed int with factor of a million.
+ * For example, convert "-1.23" to -1230000.
+ * The usable range is "-2147.483648" .. "2147.483647", resulting in -2147483648 .. 2147483647.
+ * \param[out] val  Returned integer value.
+ * \param[in] str  String of a float, like '-12.345'.
+ * \returns 0 on success, negative on error.
+ */
+int osmo_float_str_to_micros(int32_t *val, const char *str)
+{
+	const char *point;
+	char *endptr;
+	int32_t sign = 1;
+	int32_t integer = 0;
+	int32_t micros = 0;
+
+	OSMO_ASSERT(val);
+	*val = 0;
+
+	if (!str || !str[0])
+		return -EINVAL;
+	point = strchr(str, '.');
+
+	if (str[0] == '-') {
+		str = str + 1;
+		sign = -1;
+	} else if (str[0] == '+') {
+		str = str + 1;
+	}
+	if (strchr(str, '-') || strchr(str, '+'))
+		return -EINVAL;
+
+	if (!point || point > str) {
+		errno = 0;
+		integer = strtol(str, &endptr, 10);
+		if ((errno == ERANGE && (integer == LONG_MAX || integer == LONG_MIN))
+		    || (errno != 0 && integer == 0))
+			return -ERANGE;
+
+		if ((point && endptr != point)
+		    || (!point && *endptr))
+			return -EINVAL;
+	}
+
+	if (point && point[1] != '\0') {
+		/* limit the number of digits parsed to 6 */
+		char decimal[7];
+		int i;
+		OSMO_STRLCPY_ARRAY(decimal, point+1);
+
+		/* fill with zeros to make exactly 6 digits */
+		for (i = strlen(decimal); i < 6; i++)
+			decimal[i] = '0';
+		decimal[6] = '\0';
+
+		errno = 0;
+		micros = strtol(decimal, &endptr, 10);
+		if ((errno == ERANGE && (micros == LONG_MAX || micros == LONG_MIN))
+		    || (errno != 0 && micros == 0))
+			return -ERANGE;
+
+		if (*endptr)
+			return -EINVAL;
+	}
+
+	/* Do not surpass the resulting int32_t range of -2147483648..2147483647,
+	 * i.e. -2147.483648 .. 2147.483647 */
+	if (integer > 2147)
+		return -ERANGE;
+	if (integer == 2147 && micros > 483647) {
+		/* Special case for INT32_MIN, because "-1 * 2147483648" can't be calculated in int32_t. */
+		if (sign < 0 && micros == 483648) {
+			*val = -2147483648;
+			return 0;
+		}
+		return -ERANGE;
+	}
+
+	*val = sign * (integer * 1000000 + micros);
+	return 0;
+}
+
+/*! Convert an integer with a factor of a million to a floating point string.
+ * For example, convert -1230000 to "-1.23".
+ * \param[out] buf  Buffer to write string to.
+ * \param[in] buflen  sizeof(buf).
+ * \param[in] val  Value to convert to float.
+ * \returns number of chars that would be written, like snprintf().
+ */
+int osmo_micros_to_float_str_buf(char *buf, size_t buflen, int32_t val)
+{
+	struct osmo_strbuf sb = { .buf = buf, .len = buflen };
+	/* -INT32_MIN > INT32_MAX, so use uint32_t */
+	int64_t v = val;
+	if (v < 0) {
+		OSMO_STRBUF_PRINTF(sb, "-");
+		v = -v;
+	}
+
+	OSMO_STRBUF_PRINTF(sb, "%" PRId64, v / 1000000);
+	v %= 1000000;
+	if (v) {
+		/* skip trailing zeros */
+		int w = 6;
+		while (!(v % 10)) {
+			v /= 10;
+			w--;
+		}
+		OSMO_STRBUF_PRINTF(sb, ".%0*" PRId64, w, v);
+	}
+	return sb.chars_needed;
+}
+
+/*! Convert an integer with a factor of a million to a floating point string.
+ * For example, convert -1230000 to "-1.23".
+ * \param[in] ctx  Talloc ctx to allocate string buffer from.
+ * \param[in] val  Value to convert to float.
+ * \returns resulting string, dynamically allocated.
+ */
+char *osmo_micros_to_float_str_c(void *ctx, int32_t val)
+{
+	OSMO_NAME_C_IMPL(ctx, 16, "ERROR", osmo_micros_to_float_str_buf, val)
+}
+
 /*! @} */
diff --git a/tests/utils/utils_test.c b/tests/utils/utils_test.c
index 9ff023b..ecf13a8 100644
--- a/tests/utils/utils_test.c
+++ b/tests/utils/utils_test.c
@@ -1443,6 +1443,105 @@
 	}
 }
 
+struct float_str_to_micros_test {
+	const char *str;
+	int32_t expect_val;
+	int expect_err;
+};
+struct float_str_to_micros_test float_str_to_micros_tests[] = {
+	{ "0", 0 },
+	{ "1", 1000000 },
+	{ "12.345", 12345000 },
+	{ "+12.345", 12345000 },
+	{ "-12.345", -12345000 },
+	{ "0.345", 345000 },
+	{ ".345", 345000 },
+	{ "-0.345", -345000 },
+	{ "-.345", -345000 },
+	{ "12.", 12000000 },
+	{ "-180", -180000000 },
+	{ "180", 180000000 },
+	{ "360", 360000000 },
+	{ "123.4567890123", 123456789 },
+	{ "123.4567890123456789012345", 123456789 },
+	{ "2147.483647", 2147483647 },
+	{ "-2147.483647", -2147483647 },
+	{ "-2147.483648", -2147483648 },
+	{ "2147.483648", .expect_err = -ERANGE },
+	{ "-2147.483649", .expect_err = -ERANGE },
+	{ "10000", .expect_err = -ERANGE },
+	{ "-10000", .expect_err = -ERANGE },
+	{ "9999999999999", .expect_err = -ERANGE },
+	{ "-9999999999999", .expect_err = -ERANGE },
+	{ "1.2.3", .expect_err = -EINVAL },
+	{ "foo", .expect_err = -EINVAL },
+	{ "1.foo", .expect_err = -EINVAL },
+	{ "1.foo", .expect_err = -EINVAL },
+	{ "12.-345", .expect_err = -EINVAL },
+	{ "-12.-345", .expect_err = -EINVAL },
+	{ "12.+345", .expect_err = -EINVAL },
+	{ "+12.+345", .expect_err = -EINVAL },
+	{ "", .expect_err = -EINVAL },
+	{ NULL, .expect_err = -EINVAL },
+};
+void test_float_str_to_micros()
+{
+	const struct float_str_to_micros_test *t;
+	printf("--- %s\n", __func__);
+	for (t = float_str_to_micros_tests;
+	     (t - float_str_to_micros_tests) < ARRAY_SIZE(float_str_to_micros_tests);
+	     t++) {
+		int rc;
+		int32_t val;
+		rc = osmo_float_str_to_micros(&val, t->str);
+		printf("osmo_float_str_to_micros(%s) -> rc=%d val=%d\n", osmo_quote_str(t->str, -1), rc, val);
+
+		if (rc != t->expect_err)
+			printf("  ERROR: expected rc=%d\n", t->expect_err);
+		if (val != t->expect_val)
+			printf("  ERROR: expected val=%d\n", t->expect_val);
+	}
+}
+
+struct micros_to_float_str_test {
+	int32_t val;
+	const char *expect_str;
+};
+struct micros_to_float_str_test micros_to_float_str_tests[] = {
+	{ 0, "0" },
+	{ 1000000, "1" },
+	{ -1000000, "-1" },
+	{ 1000001, "1.000001" },
+	{ -1000001, "-1.000001" },
+	{ 1000100, "1.0001" },
+	{ -1010000, "-1.01" },
+	{ 1100000, "1.1" },
+	{ 10000000, "10" },
+	{ -10000000, "-10" },
+	{ 100000000, "100" },
+	{ -100000000, "-100" },
+	{ 2147483647, "2147.483647" },
+	{ -2147483648, "-2147.483648" },
+};
+void test_micros_to_float_str()
+{
+	const struct micros_to_float_str_test *t;
+	printf("--- %s\n", __func__);
+	for (t = micros_to_float_str_tests;
+	     (t - micros_to_float_str_tests) < ARRAY_SIZE(micros_to_float_str_tests);
+	     t++) {
+		char buf[128];
+		int rc;
+		rc = osmo_micros_to_float_str_buf(buf, sizeof(buf), t->val);
+		printf("osmo_micros_to_float_str_buf(%d) -> rc=%d str=%s\n", t->val, rc, osmo_quote_str(buf, -1));
+
+		if (rc != strlen(buf))
+			printf("  ERROR: expected rc=%zu\n", strlen(buf));
+		if (strcmp(buf, t->expect_str))
+			printf("  ERROR: expected str=%s\n", osmo_quote_str(t->expect_str, -1));
+	}
+}
+
 int main(int argc, char **argv)
 {
 	static const struct log_info log_info = {};
@@ -1468,5 +1567,7 @@
 	name_c_impl_test();
 	osmo_print_n_test();
 	osmo_strnchr_test();
+	test_float_str_to_micros();
+	test_micros_to_float_str();
 	return 0;
 }
diff --git a/tests/utils/utils_test.ok b/tests/utils/utils_test.ok
index f1b07fa..50972be 100644
--- a/tests/utils/utils_test.ok
+++ b/tests/utils/utils_test.ok
@@ -505,3 +505,53 @@
 osmo_strnchr("foo=bar", 0, '=') -> -1
 osmo_strnchr("foo", 9, '=') -> -1
 osmo_strnchr("foo", 9, '\0') -> 3
+--- test_float_str_to_micros
+osmo_float_str_to_micros("0") -> rc=0 val=0
+osmo_float_str_to_micros("1") -> rc=0 val=1000000
+osmo_float_str_to_micros("12.345") -> rc=0 val=12345000
+osmo_float_str_to_micros("+12.345") -> rc=0 val=12345000
+osmo_float_str_to_micros("-12.345") -> rc=0 val=-12345000
+osmo_float_str_to_micros("0.345") -> rc=0 val=345000
+osmo_float_str_to_micros(".345") -> rc=0 val=345000
+osmo_float_str_to_micros("-0.345") -> rc=0 val=-345000
+osmo_float_str_to_micros("-.345") -> rc=0 val=-345000
+osmo_float_str_to_micros("12.") -> rc=0 val=12000000
+osmo_float_str_to_micros("-180") -> rc=0 val=-180000000
+osmo_float_str_to_micros("180") -> rc=0 val=180000000
+osmo_float_str_to_micros("360") -> rc=0 val=360000000
+osmo_float_str_to_micros("123.4567890123") -> rc=0 val=123456789
+osmo_float_str_to_micros("123.4567890123456789012345") -> rc=0 val=123456789
+osmo_float_str_to_micros("2147.483647") -> rc=0 val=2147483647
+osmo_float_str_to_micros("-2147.483647") -> rc=0 val=-2147483647
+osmo_float_str_to_micros("-2147.483648") -> rc=0 val=-2147483648
+osmo_float_str_to_micros("2147.483648") -> rc=-34 val=0
+osmo_float_str_to_micros("-2147.483649") -> rc=-34 val=0
+osmo_float_str_to_micros("10000") -> rc=-34 val=0
+osmo_float_str_to_micros("-10000") -> rc=-34 val=0
+osmo_float_str_to_micros("9999999999999") -> rc=-34 val=0
+osmo_float_str_to_micros("-9999999999999") -> rc=-34 val=0
+osmo_float_str_to_micros("1.2.3") -> rc=-22 val=0
+osmo_float_str_to_micros("foo") -> rc=-22 val=0
+osmo_float_str_to_micros("1.foo") -> rc=-22 val=0
+osmo_float_str_to_micros("1.foo") -> rc=-22 val=0
+osmo_float_str_to_micros("12.-345") -> rc=-22 val=0
+osmo_float_str_to_micros("-12.-345") -> rc=-22 val=0
+osmo_float_str_to_micros("12.+345") -> rc=-22 val=0
+osmo_float_str_to_micros("+12.+345") -> rc=-22 val=0
+osmo_float_str_to_micros("") -> rc=-22 val=0
+osmo_float_str_to_micros(NULL) -> rc=-22 val=0
+--- test_micros_to_float_str
+osmo_micros_to_float_str_buf(0) -> rc=1 str="0"
+osmo_micros_to_float_str_buf(1000000) -> rc=1 str="1"
+osmo_micros_to_float_str_buf(-1000000) -> rc=2 str="-1"
+osmo_micros_to_float_str_buf(1000001) -> rc=8 str="1.000001"
+osmo_micros_to_float_str_buf(-1000001) -> rc=9 str="-1.000001"
+osmo_micros_to_float_str_buf(1000100) -> rc=6 str="1.0001"
+osmo_micros_to_float_str_buf(-1010000) -> rc=5 str="-1.01"
+osmo_micros_to_float_str_buf(1100000) -> rc=3 str="1.1"
+osmo_micros_to_float_str_buf(10000000) -> rc=2 str="10"
+osmo_micros_to_float_str_buf(-10000000) -> rc=3 str="-10"
+osmo_micros_to_float_str_buf(100000000) -> rc=3 str="100"
+osmo_micros_to_float_str_buf(-100000000) -> rc=4 str="-100"
+osmo_micros_to_float_str_buf(2147483647) -> rc=11 str="2147.483647"
+osmo_micros_to_float_str_buf(-2147483648) -> rc=12 str="-2147.483648"

-- 
To view, visit https://gerrit.osmocom.org/c/libosmocore/+/20331
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: libosmocore
Gerrit-Branch: master
Gerrit-Change-Id: Ib9aee749cd331712a4dcdadfb6a2dfa4c26da957
Gerrit-Change-Number: 20331
Gerrit-PatchSet: 1
Gerrit-Owner: neels <nhofmeyr at sysmocom.de>
Gerrit-MessageType: newchange
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20201001/cd91ddad/attachment.htm>


More information about the gerrit-log mailing list