From ea48b48dd3d40981a7ec8e9774d82e11e58b0e59 Mon Sep 17 00:00:00 2001 From: Martine Lenders Date: Wed, 5 Aug 2015 16:55:41 +0200 Subject: [PATCH 1/2] timex: make timex_to_str more efficient --- sys/include/timex.h | 35 ++++++------------ sys/shell/commands/sc_ipv6_nc.c | 1 + sys/timex/timex_to_str.c | 65 +++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 24 deletions(-) create mode 100644 sys/timex/timex_to_str.c diff --git a/sys/include/timex.h b/sys/include/timex.h index 317fdf85b4..897bb7adad 100644 --- a/sys/include/timex.h +++ b/sys/include/timex.h @@ -22,22 +22,12 @@ #define __TIMEX_H #include -#include #include #ifdef __cplusplus extern "C" { #endif -/** - * @brief Formater for unsigned 32 bit values - * - * mspgcc bug : PRIxxx macros not defined before mid-2011 versions - */ -#ifndef PRIu32 -#define PRIu32 "lu" -#endif - /** * @brief The number of microseconds per second */ @@ -56,7 +46,14 @@ extern "C" { /** * @brief The maximum length of the string representation of a timex timestamp */ -#define TIMEX_MAX_STR_LEN (18) +#define TIMEX_MAX_STR_LEN (20) +/* 20 = + * + 10 byte: 2^32-1 for seconds + * + 1 byte: decimal point + * + 6 byte: microseconds (normalized) + * + 2 byte: " s" (unit) + * + 1 byte: '\0' + */ /** * @brief A timex timestamp @@ -166,26 +163,16 @@ static inline timex_t timex_from_uint64(const uint64_t timestamp) /** * @brief Converts a timex timestamp to a string * + * @pre memory at timestamp >= TIMEX_MAX_STR_LEN + * * @param[in] t The timestamp to convert * @param[out] timestamp The output char buffer for the converted timestamp * * @note The timestamp will be normalized - * @note The buffer must have a size of TIMEX_MAX_STR_LEN characters * * @return A pointer to the string representation of the timestamp - * - * @todo replace call to snprintf by something more efficient */ -static inline const char *timex_to_str(timex_t t, char *timestamp) -{ - timex_normalize(&t); - /* 2^32 seconds have maximum 10 digits, microseconds are always < 1000000 - * in a normalized timestamp, plus two chars for the point and terminator - * => 10 + 6 + 2 = 20 */ - snprintf(timestamp, TIMEX_MAX_STR_LEN, "%" PRIu32 ".%06" PRIu32 " s", - t.seconds, t.microseconds); - return timestamp; -} +const char *timex_to_str(timex_t t, char *timestamp); #ifdef __cplusplus } diff --git a/sys/shell/commands/sc_ipv6_nc.c b/sys/shell/commands/sc_ipv6_nc.c index 9f8dc160e8..cd41b04540 100644 --- a/sys/shell/commands/sc_ipv6_nc.c +++ b/sys/shell/commands/sc_ipv6_nc.c @@ -15,6 +15,7 @@ * @author Martine Lenders */ +#include #include #include "kernel_types.h" diff --git a/sys/timex/timex_to_str.c b/sys/timex/timex_to_str.c new file mode 100644 index 0000000000..eb96ef1bb9 --- /dev/null +++ b/sys/timex/timex_to_str.c @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2015 Martine Lenders + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @{ + * + * @file + */ + +#include + +#include "timex.h" + +#define RADIX (10) + +static unsigned int u32_to_str(char *str, uint32_t u32) +{ + int len; + int i = 0; + if (u32 == 0) { + str[i] = '0'; + return 1; + } + for (; u32 > 0; u32 /= RADIX) { + char digit = (char)(u32 % RADIX); + str[i++] = '0' + digit; + } + len = i; + i--; /* go back to last character */ + for (int j = 0; j < i; j++, i--) { + char tmp = str[i]; + str[i] = str[j]; + str[j] = tmp; + } + return len; +} + +const char *timex_to_str(timex_t t, char *timestamp) +{ + timex_normalize(&t); + char *dec; + uint8_t offset = 6; + /* get seconds and set terminating '\0' byte */ + dec = ×tamp[u32_to_str(timestamp, t.seconds)]; + *(dec++) = '.'; /* set decimal point */ + for (uint32_t i = 100000; i > 1; i /= 10) { /* timex_normalize() ensures that i < 1000000 */ + if (t.microseconds < i) { /* pad with 0's */ + *(dec++) = '0'; + offset--; + } + } + u32_to_str(dec, t.microseconds); /* convert microseconds */ + dec += offset; + *(dec++) = ' '; /* append unit */ + *(dec++) = 's'; + *(dec) = '\0'; + return timestamp; +} + +/** @} */ From 2a4fd4dd75f27b832fb4bef3eea15dbedad13af0 Mon Sep 17 00:00:00 2001 From: Martine Lenders Date: Wed, 5 Aug 2015 18:36:22 +0200 Subject: [PATCH 2/2] unittests: add test for timex_to_str() --- tests/unittests/tests-timex/tests-timex.c | 24 +++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/unittests/tests-timex/tests-timex.c b/tests/unittests/tests-timex/tests-timex.c index f48810cc94..1e92647468 100644 --- a/tests/unittests/tests-timex/tests-timex.c +++ b/tests/unittests/tests-timex/tests-timex.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 Philipp Rosenkranz, Daniel Jentsch + * Copyright (C) 2015 Martine Lenders * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level @@ -44,6 +45,28 @@ static void test_timex_from_uint64(void) TEST_ASSERT(time.microseconds == 1000); } +static void test_timex_to_str(void) +{ + timex_t t = { 0, 0 }; + char t_str[TIMEX_MAX_STR_LEN]; + + TEST_ASSERT_EQUAL_STRING("0.000000 s", timex_to_str(t, t_str)); + t.seconds = 1; + TEST_ASSERT_EQUAL_STRING("1.000000 s", timex_to_str(t, t_str)); + t.seconds = 9; + t.microseconds = 2640288149; + TEST_ASSERT_EQUAL_STRING("2649.288149 s", timex_to_str(t, t_str)); + t.seconds = 1689940699; + t.microseconds = 4361; + TEST_ASSERT_EQUAL_STRING("1689940699.004361 s", timex_to_str(t, t_str)); + t.seconds = 100; + t.microseconds = 101010; + TEST_ASSERT_EQUAL_STRING("100.101010 s", timex_to_str(t, t_str)); + t.seconds = UINT32_MAX; + t.microseconds = 999999; /* should be .999999 */ + TEST_ASSERT_EQUAL_STRING("4294967295.999999 s", timex_to_str(t, t_str)); +} + Test *tests_timex_tests(void) { EMB_UNIT_TESTFIXTURES(fixtures) { @@ -51,6 +74,7 @@ Test *tests_timex_tests(void) new_TestFixture(test_timex_add), new_TestFixture(test_timex_sub), new_TestFixture(test_timex_from_uint64), + new_TestFixture(test_timex_to_str), }; EMB_UNIT_TESTCALLER(timex_tests, NULL, NULL, fixtures);