Merge pull request #4161 from kaspar030/add_fmt

sys: fmt: initial commit of simple string formatting library
This commit is contained in:
Cenk Gündoğan 2015-10-30 18:51:29 +01:00
commit 26e67b86b5
7 changed files with 612 additions and 0 deletions

1
sys/fmt/Makefile Normal file
View File

@ -0,0 +1 @@
include $(RIOTBASE)/Makefile.base

181
sys/fmt/fmt.c Normal file
View File

@ -0,0 +1,181 @@
/**
* Copyright (C) 2015 Kaspar Schleiser <kaspar@schleiser.de>
*
* 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.
*
* @ingroup sys_fmt
* @{
* @file
* @brief String formatting library implementation
* @author Kaspar Schleiser <kaspar@schleiser.de>
* @}
*/
#include <assert.h>
#include <stdarg.h>
#include <stdint.h>
#include <unistd.h>
/* work around broken sys/posix/unistd.h */
ssize_t write(int fildes, const void *buf, size_t nbyte);
#include "div.h"
#include "fmt.h"
static const char _hex_chars[16] = "0123456789ABCDEF";
static inline int _is_digit(char c)
{
return (c >= '0' && c <= '9');
}
size_t fmt_byte_hex(char *out, uint8_t byte)
{
if (out) {
*out++ = _hex_chars[byte >> 4];
*out = _hex_chars[byte & 0x0F];
}
return 2;
}
size_t fmt_strlen(const char *str)
{
const char *tmp = str;
while(*tmp) {
tmp++;
}
return (tmp - str);
}
size_t fmt_str(char *out, const char *str)
{
int len = 0;
if (!out) {
len = fmt_strlen(str);
} else {
char c;
while ((c = *str++)) {
*out++ = c;
len++;
}
}
return len;
}
size_t fmt_bytes_hex_reverse(char *out, const uint8_t *ptr, size_t n)
{
size_t i = n;
while (i--) {
out += fmt_byte_hex(out, ptr[i]);
}
return (n<<1);
}
size_t fmt_u32_hex(char *out, uint32_t val)
{
return fmt_bytes_hex_reverse(out, (uint8_t*) &val, 4);
}
size_t fmt_u64_hex(char *out, uint64_t val)
{
return fmt_bytes_hex_reverse(out, (uint8_t*) &val, 8);
}
size_t fmt_u32_dec(char *out, uint32_t val)
{
size_t len = 1;
/* count needed characters */
for (uint32_t tmp = val; (tmp > 9); len++) {
tmp = div_u32_by_10(tmp);
}
if (out) {
char *ptr = out + len;
while(val) {
*--ptr = div_u32_mod_10(val) + '0';
val = div_u32_by_10(val);
}
}
return len;
}
size_t fmt_u16_dec(char *out, uint16_t val)
{
return fmt_u32_dec(out, val);
}
size_t fmt_s32_dec(char *out, int32_t val)
{
int negative = (val < 0);
if (negative) {
if (out) {
*out++ = '-';
}
val *= -1;
}
return fmt_u32_dec(out, val) + negative;
}
uint32_t scn_u32_dec(const char *str, size_t n)
{
uint32_t res = 0;
while(n--) {
char c = *str++;
if (!_is_digit(c)) {
break;
}
else {
res *= 10;
res += (c - '0');
}
}
return res;
}
void print(const char *s, size_t n)
{
while (n > 0) {
ssize_t written = write(STDOUT_FILENO, s, n);
if (written < 0) {
break;
}
n -= written;
s += written;
}
}
void print_u32_dec(uint32_t val)
{
char buf[10];
size_t len = fmt_u32_dec(buf, val);
print(buf, len);
}
void print_s32_dec(int32_t val)
{
char buf[11];
size_t len = fmt_s32_dec(buf, val);
print(buf, len);
}
void print_u32_hex(uint32_t val)
{
char buf[8];
fmt_u32_hex(buf, val);
print(buf, sizeof(buf));
}
void print_u64_hex(uint64_t val)
{
print_u32_hex(val>>32);
print_u32_hex(val);
}
void print_str(const char* str)
{
print(str, fmt_strlen(str));
}

214
sys/include/fmt.h Normal file
View File

@ -0,0 +1,214 @@
/*
* Copyright (C) 2015 Kaspar Schleiser <kaspar@schleiser.de>
*
* 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.
*/
/**
* @defgroup sys_fmt string formatting
* @ingroup sys
* @brief Provides simple string formatting functions
*
* @{
* @file
* @brief string formatting API
* @author Kaspar Schleiser <kaspar@schleiser.de>
*/
#ifndef FMT_H_
#define FMT_H_
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Format a byte value as hex
*
* E.g., converts byte value 0 to the string 00, 255 to the string FF.
*
* Will write two 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] byte Byte value to convert
*
* @return 2
*/
size_t fmt_byte_hex(char *out, uint8_t byte);
/**
* @brief Formats a sequence of bytes as hex bytes, starting with the last byte
*
* 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_reverse(char *out, const uint8_t *ptr, size_t n);
/**
* @brief Convert a uint32 value to hex string.
*
* Will write 8 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 8
*/
size_t fmt_u32_hex(char *out, uint32_t val);
/**
* @brief Convert a uint64 value to hex string.
*
* Will write 16 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 16
*/
size_t fmt_u64_hex(char *out, uint64_t val);
/**
* @brief Convert a uint32 value to decimal string.
*
* 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 nr of digits written to (or needed in) @p out
*/
size_t fmt_u32_dec(char *out, uint32_t val);
/**
* @brief Convert a uint16 value to decimal string.
*
* 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 nr of digits written to (or needed in) @p out
*/
size_t fmt_u16_dec(char *out, uint16_t val);
/**
* @brief Convert a int32 value to decimal string.
*
* Will add a leading "-" if @p val is negative.
*
* 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 nr of characters written to (or needed in) @p out
*/
size_t fmt_s32_dec(char *out, int32_t val);
/**
* @brief Count characters until '\0' (exclusive) in @p str
*
* @param[in] str Pointer to string
*
* @return nr of characters in string @p str points to
*/
size_t fmt_strlen(const char *str);
/**
* @brief Copy null-terminated string (excluding terminating \0)
*
* 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] str Pointer to null-terminated source string
*
* @return nr of characters written to (or needed in) @p out
*/
size_t fmt_str(char *out, const char *str);
/**
* @brief Convert digits to uint32
*
* Will convert up to @p n digits. Stops at any non-digit or '\0' character.
*
* @param[out] str Pointer to string to read from
* @param[in] n Maximum nr of characters to consider
*
* @return nr of digits read
*/
uint32_t scn_u32_dec(const char *str, size_t n);
/**
* @brief Print string to stdout
*
* Writes @p n bytes from @p s to STDOUT_FILENO.
*
* @param[out] s Pointer to string to print
* @param[in] n Number of bytes to print
*/
void print(const char* s, size_t n);
/**
* @brief Print uint32 value to stdout
*
* @param[in] val Value to print
*/
void print_u32_dec(uint32_t val);
/**
* @brief Print int32 value to stdout
*
* @param[in] val Value to print
*/
void print_s32_dec(int32_t val);
/**
* @brief Print uint32 value as hex to stdout
*
* @param[in] val Value to print
*/
void print_u32_hex(uint32_t val);
/**
* @brief Print uint64 value as hex to stdout
*
* @param[in] val Value to print
*/
void print_u64_hex(uint64_t val);
/**
* @brief Print null-terminated string to stdout
*
* @param[in] str Pointer to string to print
*/
void print_str(const char* str);
#ifdef __cplusplus
}
#endif
/** @} */
#endif /* FMT_H_ */

View File

@ -0,0 +1 @@
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1 @@
USEMODULE += fmt

View File

@ -0,0 +1,177 @@
/*
* Copyright (C) 2015 Cenk Gündoğan <cnkgndgn@gmail.com>
*
* 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 <errno.h>
#include <stdint.h>
#include "embUnit/embUnit.h"
#include "fmt.h"
#include "tests-fmt.h"
static void test_fmt_byte_hex(void)
{
char out[3] = "--";
TEST_ASSERT_EQUAL_INT(2, fmt_byte_hex(out, 0));
TEST_ASSERT_EQUAL_STRING("00", (char *) out);
TEST_ASSERT_EQUAL_INT(2, fmt_byte_hex(out, 128));
TEST_ASSERT_EQUAL_STRING("80", (char *) out);
TEST_ASSERT_EQUAL_INT(2, fmt_byte_hex(out, 255));
TEST_ASSERT_EQUAL_STRING("FF", (char *) out);
}
static void test_fmt_bytes_hex_reverse(void)
{
char out[10];
uint8_t val[4] = { 9, 8, 7, 6 };
uint8_t bytes = 0;
bytes = fmt_bytes_hex_reverse(out, val, 1);
out[bytes] = '\0';
TEST_ASSERT_EQUAL_INT(2, bytes);
TEST_ASSERT_EQUAL_STRING("09", (char *) out);
bytes = fmt_bytes_hex_reverse(out, val, 2);
out[bytes] = '\0';
TEST_ASSERT_EQUAL_INT(4, bytes);
TEST_ASSERT_EQUAL_STRING("0809", (char *) out);
bytes = fmt_bytes_hex_reverse(out, val, 3);
out[bytes] = '\0';
TEST_ASSERT_EQUAL_INT(6, bytes);
TEST_ASSERT_EQUAL_STRING("070809", (char *) out);
bytes = fmt_bytes_hex_reverse(out, val, 4);
out[bytes] = '\0';
TEST_ASSERT_EQUAL_INT(8, bytes);
TEST_ASSERT_EQUAL_STRING("06070809", (char *) out);
}
static void test_fmt_u32_hex(void)
{
char out[9] = "--------";
uint32_t val = 3735928559;
TEST_ASSERT_EQUAL_INT(8, fmt_u32_hex(out, val));
TEST_ASSERT_EQUAL_STRING("DEADBEEF", (char *) out);
}
static void test_fmt_u64_hex(void)
{
char out[17] = "----------------";
uint64_t val = 1002843385516306400;
TEST_ASSERT_EQUAL_INT(16, fmt_u64_hex(out, val));
TEST_ASSERT_EQUAL_STRING("0DEAD0BEEF0CAFE0", (char *) out);
}
static void test_fmt_u32_dec(void)
{
char out[9] = "--------";
uint32_t val = 12345678;
uint8_t chars = 0;
chars = fmt_u32_dec(out, val);
TEST_ASSERT_EQUAL_INT(8, chars);
out[chars] = '\0';
TEST_ASSERT_EQUAL_STRING("12345678", (char *) out);
}
static void test_fmt_u16_dec(void)
{
char out[5] = "----";
uint16_t val = 6556;
uint8_t chars = 0;
chars = fmt_u16_dec(out, val);
TEST_ASSERT_EQUAL_INT(4, chars);
out[chars] = '\0';
TEST_ASSERT_EQUAL_STRING("6556", (char *) out);
}
static void test_fmt_s32_dec(void)
{
char out[8] = "--------";
int32_t val = 9876;
uint8_t chars = 0;
chars = fmt_s32_dec(out, val);
TEST_ASSERT_EQUAL_INT(4, chars);
out[chars] = '\0';
TEST_ASSERT_EQUAL_STRING("9876", (char *) out);
val = -9876;
chars = fmt_s32_dec(out, val);
TEST_ASSERT_EQUAL_INT(5, chars);
out[chars] = '\0';
TEST_ASSERT_EQUAL_STRING("-9876", (char *) out);
}
static void test_fmt_strlen(void)
{
const char *empty_str = "";
const char *short_str = "short";
const char *long_str = "this is a long string";
TEST_ASSERT_EQUAL_INT(0, fmt_strlen(empty_str));
TEST_ASSERT_EQUAL_INT(5, fmt_strlen(short_str));
TEST_ASSERT_EQUAL_INT(21, fmt_strlen(long_str));
}
static void test_fmt_str(void)
{
const char *string1 = "string1";
char string2[] = "StRiNg2";
TEST_ASSERT_EQUAL_INT(fmt_strlen(string1), fmt_str(&string2[0], string1));
TEST_ASSERT_EQUAL_STRING(string1, &string2[0]);
}
static void test_scn_u32_dec(void)
{
const char *string1 = "123456789";
uint32_t val1 = 123456789;
uint32_t val2 = 12345;
TEST_ASSERT_EQUAL_INT(val1, scn_u32_dec(string1, 9));
TEST_ASSERT_EQUAL_INT(val2, scn_u32_dec(string1, 5));
}
Test *tests_fmt_tests(void)
{
EMB_UNIT_TESTFIXTURES(fixtures) {
new_TestFixture(test_fmt_byte_hex),
new_TestFixture(test_fmt_bytes_hex_reverse),
new_TestFixture(test_fmt_u32_hex),
new_TestFixture(test_fmt_u64_hex),
new_TestFixture(test_fmt_u32_dec),
new_TestFixture(test_fmt_u16_dec),
new_TestFixture(test_fmt_s32_dec),
new_TestFixture(test_fmt_strlen),
new_TestFixture(test_fmt_str),
new_TestFixture(test_scn_u32_dec),
};
EMB_UNIT_TESTCALLER(fmt_tests, NULL, NULL, fixtures);
return (Test *)&fmt_tests;
}
void tests_fmt(void)
{
TESTS_RUN(tests_fmt_tests());
}
/** @} */

View File

@ -0,0 +1,37 @@
/*
* Copyright (C) 2015 Cenk Gündoğan <cnkgndgn@gmail.com>
*
* 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.
*/
/**
* @addtogroup unittests
* @{
*
* @file
* @brief Unittests for the ``fmt`` module
*
* @author Cenk Gündoğan <cnkgndgn@gmail.com>
*/
#ifndef TESTS_FMT_H_
#define TESTS_FMT_H_
#include "embUnit.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief The entry point of this test suite.
*/
void tests_fmt(void);
#ifdef __cplusplus
}
#endif
#endif /* TESTS_FMT_H_ */
/** @} */