From a542e954cf79b51a427b4ceee14795ec47b11147 Mon Sep 17 00:00:00 2001 From: Vincent Dupont Date: Thu, 23 Aug 2018 13:59:48 +0200 Subject: [PATCH 1/6] fmt: add fmt_u16_hex() --- sys/fmt/fmt.c | 5 +++++ sys/include/fmt.h | 14 ++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/sys/fmt/fmt.c b/sys/fmt/fmt.c index 58aa01483b..2bd4da0229 100644 --- a/sys/fmt/fmt.c +++ b/sys/fmt/fmt.c @@ -153,6 +153,11 @@ size_t fmt_hex_bytes(uint8_t *out, const char *hex) return final_len; } +size_t fmt_u16_hex(char *out, uint16_t val) +{ + return fmt_bytes_hex_reverse(out, (uint8_t*) &val, 2); +} + size_t fmt_u32_hex(char *out, uint32_t val) { return fmt_bytes_hex_reverse(out, (uint8_t*) &val, 4); diff --git a/sys/include/fmt.h b/sys/include/fmt.h index 7176494d30..3e3d3c8fb2 100644 --- a/sys/include/fmt.h +++ b/sys/include/fmt.h @@ -119,6 +119,20 @@ uint8_t fmt_hex_byte(const char *hex); */ size_t fmt_hex_bytes(uint8_t *out, const char *hex); +/** + * @brief Convert a uint16 value to hex string. + * + * Will write 4 bytes to @p out. + * If @p out is NULL, will only return the number of bytes that would have + * been written. + * + * @param[out] out Pointer to output buffer, or NULL + * @param[in] val Value to convert + * + * @return 4 + */ +size_t fmt_u16_hex(char *out, uint16_t val); + /** * @brief Convert a uint32 value to hex string. * From 71455b692fe08129ce42e2c25565bb88a87ce969 Mon Sep 17 00:00:00 2001 From: Vincent Dupont Date: Thu, 23 Aug 2018 14:00:32 +0200 Subject: [PATCH 2/6] fmt: add fmt_to_lower() --- sys/fmt/fmt.c | 31 +++++++++++++++++++++++++++ sys/include/fmt.h | 8 +++++++ tests/unittests/tests-fmt/tests-fmt.c | 11 ++++++++++ 3 files changed, 50 insertions(+) diff --git a/sys/fmt/fmt.c b/sys/fmt/fmt.c index 2bd4da0229..1ec54890f7 100644 --- a/sys/fmt/fmt.c +++ b/sys/fmt/fmt.c @@ -53,6 +53,16 @@ static inline int _is_digit(char c) return (c >= '0' && c <= '9'); } +static inline int _is_upper(char c) +{ + return (c >= 'A' && c <= 'Z'); +} + +static inline char _to_lower(char c) +{ + return 'a' + (c - 'A'); +} + size_t fmt_byte_hex(char *out, uint8_t byte) { if (out) { @@ -405,6 +415,27 @@ size_t fmt_lpad(char *out, size_t in_len, size_t pad_len, char pad_char) return pad_len; } +{ +size_t fmt_to_lower(char *out, const char *str) +{ + size_t len = 0; + + while (str && *str) { + if (_is_upper(*str)) { + if (out) { + *out++ = _to_lower(*str); + } + } + else if (out) { + *out++ = *str; + } + str++; + len++; + } + + return len; +} + uint32_t scn_u32_dec(const char *str, size_t n) { uint32_t res = 0; diff --git a/sys/include/fmt.h b/sys/include/fmt.h index 3e3d3c8fb2..8f927d5c35 100644 --- a/sys/include/fmt.h +++ b/sys/include/fmt.h @@ -343,6 +343,14 @@ size_t fmt_strnlen(const char *str, size_t maxlen); */ size_t fmt_str(char *out, const char *str); +/** + * @brief Copy null-terminated string to a lowercase string (excluding terminating \0) + * + * @param[out] out Pointer to output buffer, or NULL + * @param[in] str Pointer to null-terminated source string + */ +size_t fmt_to_lower(char *out, const char *str); + /** * @brief Convert digits to uint32 * diff --git a/tests/unittests/tests-fmt/tests-fmt.c b/tests/unittests/tests-fmt/tests-fmt.c index 0a7d537828..576444b9f8 100644 --- a/tests/unittests/tests-fmt/tests-fmt.c +++ b/tests/unittests/tests-fmt/tests-fmt.c @@ -749,6 +749,16 @@ static void test_fmt_str(void) TEST_ASSERT_EQUAL_STRING(string1, &string2[0]); } +static void test_fmt_to_lower(void) +{ + const char string_up[] = "AbCdeFGHijkLM"; + char string[] = "zzzzzzzzzzzzzzz"; + + TEST_ASSERT_EQUAL_INT(fmt_strlen(string_up), fmt_to_lower(string, string_up)); + string[fmt_strlen(string_up)] = '\0'; + TEST_ASSERT_EQUAL_STRING("abcdefghijklm", &string[0]); +} + static void test_scn_u32_dec(void) { const char *string1 = "123456789"; @@ -817,6 +827,7 @@ Test *tests_fmt_tests(void) new_TestFixture(test_fmt_strlen), new_TestFixture(test_fmt_strnlen), new_TestFixture(test_fmt_str), + new_TestFixture(test_fmt_to_lower), new_TestFixture(test_scn_u32_dec), new_TestFixture(test_fmt_lpad), }; From db4ce5eff2dd3e85f4162faa8725135b5590eb09 Mon Sep 17 00:00:00 2001 From: Vincent Dupont Date: Thu, 23 Aug 2018 14:00:44 +0200 Subject: [PATCH 3/6] uuid: add uuid_to_string() --- Makefile.dep | 1 + sys/include/uuid.h | 10 ++++++++++ sys/uuid/uuid.c | 18 ++++++++++++++++++ tests/unittests/tests-uuid/tests-uuid.c | 21 +++++++++++++++++++++ 4 files changed, 50 insertions(+) diff --git a/Makefile.dep b/Makefile.dep index 5cbbf1db90..fec1eac125 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -780,6 +780,7 @@ endif ifneq (,$(filter uuid,$(USEMODULE))) USEMODULE += hashes USEMODULE += random + USEMODULE += fmt endif # Enable periph_gpio when periph_gpio_irq is enabled diff --git a/sys/include/uuid.h b/sys/include/uuid.h index 2616ebd3b4..1c8acb41a9 100644 --- a/sys/include/uuid.h +++ b/sys/include/uuid.h @@ -38,6 +38,8 @@ extern "C" { #define UUID_NODE_LEN (6U) /**< Size of the node identifier in bytes */ +#define UUID_STR_LEN (36U) /**< Size of a string UUID without null character */ + /** * @name UUID version identifiers * @{ @@ -140,6 +142,14 @@ static inline bool uuid_equal(uuid_t *uuid1, uuid_t *uuid2) return (memcmp(uuid1, uuid2, sizeof(uuid_t)) == 0); } +/** + * @brief Generate an UUID string from an UUID structure + * + * @param[in] uuid UUID + * @param[out] str null-terminated UUID string, must be at least UUID_STR_LEN + 1 bytes + */ +void uuid_to_string(const uuid_t *uuid, char *str); + #ifdef __cplusplus } #endif diff --git a/sys/uuid/uuid.c b/sys/uuid/uuid.c index a7c9c95e40..aeb94e28b4 100644 --- a/sys/uuid/uuid.c +++ b/sys/uuid/uuid.c @@ -23,6 +23,7 @@ #include "hashes/sha1.h" #include "random.h" #include "uuid.h" +#include "fmt.h" const uuid_t uuid_namespace_dns = { /* 6ba7b810-9dad-11d1-80b4-00c04fd430c8 */ .time_low.u8 = { 0x6b, 0xa7, 0xb8, 0x10 }, @@ -110,3 +111,20 @@ void uuid_v5(uuid_t *uuid, const uuid_t *ns, const uint8_t *name, size_t len) _set_version(uuid, UUID_V5); _set_reserved(uuid); } + +void uuid_to_string(const uuid_t *uuid, char *str) +{ + char *p = str; + p += fmt_u32_hex(p, byteorder_ntohl(uuid->time_low)); + p += fmt_char(p, '-'); + p += fmt_u16_hex(p, byteorder_ntohs(uuid->time_mid)); + p += fmt_char(p, '-'); + p += fmt_u16_hex(p, byteorder_ntohs(uuid->time_hi)); + p += fmt_char(p, '-'); + p += fmt_byte_hex(p, uuid->clk_seq_hi_res); + p += fmt_byte_hex(p, uuid->clk_seq_low); + p += fmt_char(p, '-'); + p += fmt_bytes_hex(p, uuid->node, UUID_NODE_LEN); + *p = '\0'; + fmt_to_lower(str, str); +} diff --git a/tests/unittests/tests-uuid/tests-uuid.c b/tests/unittests/tests-uuid/tests-uuid.c index 0552d24ae8..aceadf3982 100644 --- a/tests/unittests/tests-uuid/tests-uuid.c +++ b/tests/unittests/tests-uuid/tests-uuid.c @@ -95,12 +95,33 @@ void test_uuid_v5(void) TEST_ASSERT_EQUAL_INT(uuid_version(&uuid_next), UUID_V5); } +void test_uuid_str(void) +{ + char str[40]; + const char dns[] = "6ba7b810-9dad-11d1-80b4-00c04fd430c8"; + uuid_to_string(&uuid_namespace_dns, str); + TEST_ASSERT_EQUAL_INT(0, memcmp(dns, str, sizeof(dns))); + + const char url[] = "6ba7b811-9dad-11d1-80b4-00c04fd430c8"; + uuid_to_string(&uuid_namespace_url, str); + TEST_ASSERT_EQUAL_INT(0, memcmp(url, str, sizeof(dns))); + + const char iso[] = "6ba7b812-9dad-11d1-80b4-00c04fd430c8"; + uuid_to_string(&uuid_namespace_iso, str); + TEST_ASSERT_EQUAL_INT(0, memcmp(iso, str, sizeof(dns))); + + const char x500[] = "6ba7b814-9dad-11d1-80b4-00c04fd430c8"; + uuid_to_string(&uuid_namespace_x500, str); + TEST_ASSERT_EQUAL_INT(0, memcmp(x500, str, sizeof(dns))); +} + Test *tests_uuid_all(void) { EMB_UNIT_TESTFIXTURES(fixtures) { new_TestFixture(test_uuid_v3), new_TestFixture(test_uuid_v4), new_TestFixture(test_uuid_v5), + new_TestFixture(test_uuid_str), }; EMB_UNIT_TESTCALLER(uuid_tests, NULL, NULL, fixtures); From 8c5ffa0a8d0a6cff0345422a4d3ac62b8d9ef1b1 Mon Sep 17 00:00:00 2001 From: Vincent Dupont Date: Thu, 20 Sep 2018 19:12:17 +0200 Subject: [PATCH 4/6] fmt: add fmt_char --- sys/fmt/fmt.c | 8 ++++++++ sys/include/fmt.h | 13 +++++++++++++ tests/unittests/tests-fmt/tests-fmt.c | 11 +++++++++++ 3 files changed, 32 insertions(+) diff --git a/sys/fmt/fmt.c b/sys/fmt/fmt.c index 1ec54890f7..066f92c93b 100644 --- a/sys/fmt/fmt.c +++ b/sys/fmt/fmt.c @@ -415,7 +415,15 @@ size_t fmt_lpad(char *out, size_t in_len, size_t pad_len, char pad_char) return pad_len; } +size_t fmt_char(char *out, char c) { + if (out) { + *out = c; + } + + return 1; +} + size_t fmt_to_lower(char *out, const char *str) { size_t len = 0; diff --git a/sys/include/fmt.h b/sys/include/fmt.h index 8f927d5c35..627256220f 100644 --- a/sys/include/fmt.h +++ b/sys/include/fmt.h @@ -310,6 +310,19 @@ size_t fmt_s32_dfp(char *out, int32_t val, int fp_digits); */ size_t fmt_float(char *out, float f, unsigned precision); +/** + * @brief Copy @p in char to string (without terminating '\0') + * + * If @p out is NULL, will only return the number of bytes that would have + * been written. + * + * @param[out] out string to write to (or NULL) + * @param[in] c char value to append + * + * @return nr of bytes the function did or would write to out + */ +size_t fmt_char(char *out, char c); + /** * @brief Count characters until '\0' (exclusive) in @p str * diff --git a/tests/unittests/tests-fmt/tests-fmt.c b/tests/unittests/tests-fmt/tests-fmt.c index 576444b9f8..ed9d2209a4 100644 --- a/tests/unittests/tests-fmt/tests-fmt.c +++ b/tests/unittests/tests-fmt/tests-fmt.c @@ -749,6 +749,16 @@ static void test_fmt_str(void) TEST_ASSERT_EQUAL_STRING(string1, &string2[0]); } +static void test_fmt_char(void) +{ + char string[] = "zzzzzzzzz"; + + TEST_ASSERT_EQUAL_INT(1, fmt_char(NULL, 'c')); + TEST_ASSERT_EQUAL_INT(1, fmt_char(string, 'c')); + string[1] = '\0'; + TEST_ASSERT_EQUAL_STRING("c", &string[0]); +} + static void test_fmt_to_lower(void) { const char string_up[] = "AbCdeFGHijkLM"; @@ -827,6 +837,7 @@ Test *tests_fmt_tests(void) new_TestFixture(test_fmt_strlen), new_TestFixture(test_fmt_strnlen), new_TestFixture(test_fmt_str), + new_TestFixture(test_fmt_char), new_TestFixture(test_fmt_to_lower), new_TestFixture(test_scn_u32_dec), new_TestFixture(test_fmt_lpad), From 695a94449a8f0cc8395834dc8c3e905239ed7637 Mon Sep 17 00:00:00 2001 From: Vincent Dupont Date: Thu, 20 Sep 2018 19:13:15 +0200 Subject: [PATCH 5/6] fmt: add scn_u32_hex() --- sys/fmt/fmt.c | 24 ++++++++++++++++++++++++ sys/include/fmt.h | 12 ++++++++++++ tests/unittests/tests-fmt/tests-fmt.c | 12 ++++++++++++ 3 files changed, 48 insertions(+) diff --git a/sys/fmt/fmt.c b/sys/fmt/fmt.c index 066f92c93b..df79a40f4e 100644 --- a/sys/fmt/fmt.c +++ b/sys/fmt/fmt.c @@ -460,6 +460,30 @@ uint32_t scn_u32_dec(const char *str, size_t n) return res; } +uint32_t scn_u32_hex(const char *str, size_t n) +{ + uint32_t res = 0; + + while (n--) { + char c = *str++; + if (!_is_digit(c)) { + if (_is_upper(c)) { + c = _to_lower(c); + } + if (c == '\0' || c > 'f') { + break; + } + res <<= 4; + res |= c - 'a' + 0xa; + } + else { + res <<= 4; + res |= c - '0'; + } + } + return res; +} + void print(const char *s, size_t n) { #ifdef __WITH_AVRLIBC__ diff --git a/sys/include/fmt.h b/sys/include/fmt.h index 627256220f..b1e0a286bb 100644 --- a/sys/include/fmt.h +++ b/sys/include/fmt.h @@ -376,6 +376,18 @@ size_t fmt_to_lower(char *out, const char *str); */ uint32_t scn_u32_dec(const char *str, size_t n); +/** + * @brief Convert hexadecimal characters to uin32_t + * + * Will convert up to @p n char. Stop at any non-hexadecimal or '\0' character + * + * @param[in] str Pointer to tring to read from + * @param[in] n Maximum number of characters to consider + * + * @return converted uint32_t value + */ +uint32_t scn_u32_hex(const char *str, size_t n); + /** * @brief Print string to stdout * diff --git a/tests/unittests/tests-fmt/tests-fmt.c b/tests/unittests/tests-fmt/tests-fmt.c index ed9d2209a4..8126beafcc 100644 --- a/tests/unittests/tests-fmt/tests-fmt.c +++ b/tests/unittests/tests-fmt/tests-fmt.c @@ -779,6 +779,17 @@ static void test_scn_u32_dec(void) TEST_ASSERT_EQUAL_INT(val2, scn_u32_dec(string1, 5)); } +static void test_scn_u32_hex(void) +{ + const char *string1 = "aB12cE4F"; + uint32_t val1 = 0xab12ce4f; + uint32_t val2 = 0xab1; + + TEST_ASSERT_EQUAL_INT(val1, scn_u32_hex(string1, 8)); + TEST_ASSERT_EQUAL_INT(val2, scn_u32_hex(string1, 3)); + TEST_ASSERT_EQUAL_INT(val1, scn_u32_hex(string1, 9)); +} + static void test_fmt_lpad(void) { const char base[] = "abcd"; @@ -840,6 +851,7 @@ Test *tests_fmt_tests(void) new_TestFixture(test_fmt_char), new_TestFixture(test_fmt_to_lower), new_TestFixture(test_scn_u32_dec), + new_TestFixture(test_scn_u32_hex), new_TestFixture(test_fmt_lpad), }; From 73e971fc56eaee59c7f9d8f171511c478ca2989d Mon Sep 17 00:00:00 2001 From: Vincent Dupont Date: Thu, 20 Sep 2018 19:21:11 +0200 Subject: [PATCH 6/6] uuid: add uuid_from_string() --- sys/include/uuid.h | 14 +++++++-- sys/uuid/uuid.c | 39 +++++++++++++++++++++++++ tests/unittests/tests-uuid/tests-uuid.c | 13 +++++++++ 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/sys/include/uuid.h b/sys/include/uuid.h index 1c8acb41a9..ac050278aa 100644 --- a/sys/include/uuid.h +++ b/sys/include/uuid.h @@ -122,7 +122,7 @@ void uuid_v5(uuid_t *uuid, const uuid_t *ns, const uint8_t *name, size_t len); * * @return Version number */ -static inline unsigned uuid_version(uuid_t *uuid) +static inline unsigned uuid_version(const uuid_t *uuid) { uint16_t time_hi_vers = byteorder_ntohs(uuid->time_hi); @@ -137,7 +137,7 @@ static inline unsigned uuid_version(uuid_t *uuid) * * @return True when equal */ -static inline bool uuid_equal(uuid_t *uuid1, uuid_t *uuid2) +static inline bool uuid_equal(const uuid_t *uuid1, const uuid_t *uuid2) { return (memcmp(uuid1, uuid2, sizeof(uuid_t)) == 0); } @@ -150,6 +150,16 @@ static inline bool uuid_equal(uuid_t *uuid1, uuid_t *uuid2) */ void uuid_to_string(const uuid_t *uuid, char *str); +/** + * @brief Populate an UUID structure from an UUID string + * + * @param[out] uuid out UUID + * @param[in] str null-terminated input UUID string, must be UUID_STR_LEN bytes + * + * @return 0 on succes, < 0 if @p str is not valid + */ +int uuid_from_string(uuid_t *uuid, const char *str); + #ifdef __cplusplus } #endif diff --git a/sys/uuid/uuid.c b/sys/uuid/uuid.c index aeb94e28b4..464586dcbe 100644 --- a/sys/uuid/uuid.c +++ b/sys/uuid/uuid.c @@ -128,3 +128,42 @@ void uuid_to_string(const uuid_t *uuid, char *str) *p = '\0'; fmt_to_lower(str, str); } + +int uuid_from_string(uuid_t *uuid, const char *str) +{ + uint32_t tmp; + if (fmt_strlen(str) < UUID_STR_LEN) { + return -1; + } + tmp = scn_u32_hex(str, 8); + uuid->time_low = byteorder_htonl(tmp); + str += 8; + if (*str++ != '-') { + return -2; + } + tmp = scn_u32_hex(str, 4); + uuid->time_mid = byteorder_htons(tmp); + str += 4; + if (*str++ != '-') { + return -2; + } + tmp = scn_u32_hex(str, 4); + uuid->time_hi = byteorder_htons(tmp); + str += 4; + if (*str++ != '-') { + return -2; + } + uuid->clk_seq_hi_res = scn_u32_hex(str, 2); + str += 2; + uuid->clk_seq_low = scn_u32_hex(str, 2); + str += 2; + if (*str++ != '-') { + return -2; + } + for (unsigned i = 0; i < UUID_NODE_LEN; i++) { + uuid->node[i] = scn_u32_hex(str, 2); + str += 2; + } + + return 0; +} diff --git a/tests/unittests/tests-uuid/tests-uuid.c b/tests/unittests/tests-uuid/tests-uuid.c index aceadf3982..f0b98bd8c9 100644 --- a/tests/unittests/tests-uuid/tests-uuid.c +++ b/tests/unittests/tests-uuid/tests-uuid.c @@ -113,6 +113,19 @@ void test_uuid_str(void) const char x500[] = "6ba7b814-9dad-11d1-80b4-00c04fd430c8"; uuid_to_string(&uuid_namespace_x500, str); TEST_ASSERT_EQUAL_INT(0, memcmp(x500, str, sizeof(dns))); + + uuid_t uuid; + TEST_ASSERT_EQUAL_INT(0, uuid_from_string(&uuid, dns)); + TEST_ASSERT_EQUAL_INT(true, uuid_equal(&uuid, &uuid_namespace_dns)); + + TEST_ASSERT_EQUAL_INT(0, uuid_from_string(&uuid, url)); + TEST_ASSERT_EQUAL_INT(true, uuid_equal(&uuid, &uuid_namespace_url)); + + TEST_ASSERT_EQUAL_INT(0, uuid_from_string(&uuid, iso)); + TEST_ASSERT_EQUAL_INT(true, uuid_equal(&uuid, &uuid_namespace_iso)); + + TEST_ASSERT_EQUAL_INT(0, uuid_from_string(&uuid, x500)); + TEST_ASSERT_EQUAL_INT(true, uuid_equal(&uuid, &uuid_namespace_x500)); } Test *tests_uuid_all(void)