diff --git a/sys/fmt/fmt.c b/sys/fmt/fmt.c index a921f287c4f28f442887ad645f731a9b282c40a0..b2aadef8b91f7420b1528f63ccf11282865616ba 100644 --- a/sys/fmt/fmt.c +++ b/sys/fmt/fmt.c @@ -62,6 +62,18 @@ size_t fmt_byte_hex(char *out, uint8_t byte) return 2; } +size_t fmt_bytes_hex(char *out, const uint8_t *ptr, size_t n) +{ + size_t len = n * 2; + if (out) { + while (n--) { + out += fmt_byte_hex(out, *ptr++); + } + } + + return len; +} + size_t fmt_strlen(const char *str) { const char *tmp = str; @@ -95,6 +107,39 @@ size_t fmt_bytes_hex_reverse(char *out, const uint8_t *ptr, size_t n) return (n<<1); } +static uint8_t _byte_mod25(uint8_t x) +{ + for (unsigned divisor = 200; divisor >= 25; divisor >>= 1) { + if (x >= divisor) { + x -= divisor; + } + } + + return x; +} + +static uint8_t _hex_nib(uint8_t nib) +{ + return _byte_mod25((nib & 0x1f) + 9); +} + +size_t fmt_hex_bytes(uint8_t *out, const char *hex) +{ + size_t len = fmt_strlen(hex); + + if (len & 1) { + out = NULL; + return 0; + } + + size_t final_len = len >> 1; + for (size_t i = 0, j = 0; j < final_len; i += 2, j++) { + out[j] = (_hex_nib(hex[i]) << 4) | _hex_nib(hex[i+1]); + } + + return final_len; +} + 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 e1d0dfb38c5c81920937373b5cb36694afa19572..ceb98a5cf227018d57fafb7b1ff72dd3691b261d 100644 --- a/sys/include/fmt.h +++ b/sys/include/fmt.h @@ -61,6 +61,21 @@ extern "C" { */ size_t fmt_byte_hex(char *out, uint8_t byte); +/** + * @brief Formats a sequence of bytes as hex bytes + * + * Will write 2*n 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] ptr Pointer to bytes to convert + * @param[in] n Number of bytes to convert + * + * @return 2*n + */ +size_t fmt_bytes_hex(char *out, const uint8_t *ptr, size_t n); + /** * @brief Formats a sequence of bytes as hex bytes, starting with the last byte * @@ -76,6 +91,23 @@ size_t fmt_byte_hex(char *out, uint8_t byte); */ size_t fmt_bytes_hex_reverse(char *out, const uint8_t *ptr, size_t n); +/** + * @brief Converts a sequence of hex bytes to an array of bytes + * + * The sequence of hex characters must have an even length: + * 2 hex character => 1 byte. If the sequence of hex has an odd length, this + * function returns 0 and an empty @p out. + * + * The hex characters sequence must contain valid hexadecimal characters + * otherwise the result in @p out is undefined. + * + * @param[out] out Pointer to converted bytes, or NULL + * @param[in] hex Pointer to input buffer + * @returns strlen(hex) / 2 when length of @p hex was even + * @returns 0 otherwise + */ +size_t fmt_hex_bytes(uint8_t *out, const char *hex); + /** * @brief Convert a uint32 value to hex string. * diff --git a/tests/unittests/tests-fmt/tests-fmt.c b/tests/unittests/tests-fmt/tests-fmt.c index b69261935ad5e28ff48d04e6040738e368d7487c..df7f6ef4a1ad3f76f39c4402d184f4f0da0120a6 100644 --- a/tests/unittests/tests-fmt/tests-fmt.c +++ b/tests/unittests/tests-fmt/tests-fmt.c @@ -34,6 +34,53 @@ static void test_fmt_byte_hex(void) TEST_ASSERT_EQUAL_STRING("FF", (char *) out); } +static void test_fmt_bytes_hex(void) +{ + char out[15] = "--------------"; + uint8_t val[7] = { 0xAA, 9, 8, 7, 6, 0xA8, 0xEF}; + uint8_t bytes = 0; + + bytes = fmt_bytes_hex(out, val, 0); + out[bytes] = '\0'; + TEST_ASSERT_EQUAL_INT(0, bytes); + TEST_ASSERT_EQUAL_STRING("", (char *) out); + + bytes = fmt_bytes_hex(out, val, 1); + out[bytes] = '\0'; + TEST_ASSERT_EQUAL_INT(2, bytes); + TEST_ASSERT_EQUAL_STRING("AA", (char *) out); + + bytes = fmt_bytes_hex(out, val, 2); + out[bytes] = '\0'; + TEST_ASSERT_EQUAL_INT(4, bytes); + TEST_ASSERT_EQUAL_STRING("AA09", (char *) out); + + bytes = fmt_bytes_hex(out, val, 3); + out[bytes] = '\0'; + TEST_ASSERT_EQUAL_INT(6, bytes); + TEST_ASSERT_EQUAL_STRING("AA0908", (char *) out); + + bytes = fmt_bytes_hex(out, val, 4); + out[bytes] = '\0'; + TEST_ASSERT_EQUAL_INT(8, bytes); + TEST_ASSERT_EQUAL_STRING("AA090807", (char *) out); + + bytes = fmt_bytes_hex(out, val, 5); + out[bytes] = '\0'; + TEST_ASSERT_EQUAL_INT(10, bytes); + TEST_ASSERT_EQUAL_STRING("AA09080706", (char *) out); + + bytes = fmt_bytes_hex(out, val, 6); + out[bytes] = '\0'; + TEST_ASSERT_EQUAL_INT(12, bytes); + TEST_ASSERT_EQUAL_STRING("AA09080706A8", (char *) out); + + bytes = fmt_bytes_hex(out, val, 7); + out[bytes] = '\0'; + TEST_ASSERT_EQUAL_INT(14, bytes); + TEST_ASSERT_EQUAL_STRING("AA09080706A8EF", (char *) out); +} + static void test_fmt_bytes_hex_reverse(void) { char out[9] = "--------"; @@ -61,6 +108,53 @@ static void test_fmt_bytes_hex_reverse(void) TEST_ASSERT_EQUAL_STRING("06070809", (char *) out); } +static void test_fmt_hex_bytes(void) +{ + uint8_t val = 0; + uint8_t bytes = fmt_hex_bytes(&val, ""); + TEST_ASSERT_EQUAL_INT(0, bytes); + TEST_ASSERT_EQUAL_INT(0, val); + + bytes = fmt_hex_bytes(&val, "A"); + TEST_ASSERT_EQUAL_INT(0, val); + TEST_ASSERT_EQUAL_INT(0, bytes); + + char hex2[3] = "00"; + uint8_t val1[1] = { 0 }; + bytes = fmt_hex_bytes(val1, hex2); + TEST_ASSERT_EQUAL_INT(1, bytes); + TEST_ASSERT_EQUAL_INT(0, val1[0]); + + memcpy(hex2, "AB", 2); + hex2[2] = '\0'; + val1[0] = 0; + bytes = fmt_hex_bytes(val1, hex2); + TEST_ASSERT_EQUAL_INT(1, bytes); + TEST_ASSERT_EQUAL_INT(0xAB, val1[0]); + + memcpy(hex2, "CD", 2); + hex2[2] = '\0'; + val1[0] = 0; + bytes = fmt_hex_bytes(val1, hex2); + TEST_ASSERT_EQUAL_INT(1, bytes); + TEST_ASSERT_EQUAL_INT(0xCD, val1[0]); + + memcpy(hex2, "EF", 2); + hex2[2] = '\0'; + val1[0] = 0; + bytes = fmt_hex_bytes(val1, hex2); + TEST_ASSERT_EQUAL_INT(1, bytes); + TEST_ASSERT_EQUAL_INT(0xEF, val1[0]); + + char hex6[] = "0102aF"; + uint8_t val3[3]; + bytes = fmt_hex_bytes(val3, hex6); + TEST_ASSERT_EQUAL_INT(3, bytes); + TEST_ASSERT_EQUAL_INT(1, val3[0]); + TEST_ASSERT_EQUAL_INT(2, val3[1]); + TEST_ASSERT_EQUAL_INT(0xAF, val3[2]); +} + static void test_fmt_u32_hex(void) { char out[9] = "--------"; @@ -376,7 +470,9 @@ Test *tests_fmt_tests(void) { EMB_UNIT_TESTFIXTURES(fixtures) { new_TestFixture(test_fmt_byte_hex), + new_TestFixture(test_fmt_bytes_hex), new_TestFixture(test_fmt_bytes_hex_reverse), + new_TestFixture(test_fmt_hex_bytes), new_TestFixture(test_fmt_u32_hex), new_TestFixture(test_fmt_u64_hex), new_TestFixture(test_fmt_u32_dec),