From 9a21fe521b706e4a62377f673c8f4fd22e20fe57 Mon Sep 17 00:00:00 2001 From: Marcus Chang Date: Sat, 14 Oct 2017 19:16:50 -0700 Subject: [PATCH] Minimal printf Supports %d %u %X %p %s Doesn't use malloc --- features/minimal-printf/mbed_printf.c | 352 ++++++++++++++++++++++++++ features/minimal-printf/mbed_printf.h | 58 +++++ 2 files changed, 410 insertions(+) create mode 100755 features/minimal-printf/mbed_printf.c create mode 100755 features/minimal-printf/mbed_printf.h diff --git a/features/minimal-printf/mbed_printf.c b/features/minimal-printf/mbed_printf.c new file mode 100755 index 0000000000..a43ae0ebb2 --- /dev/null +++ b/features/minimal-printf/mbed_printf.c @@ -0,0 +1,352 @@ +/* mbed Microcontroller Library + * Copyright (c) 2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mbed_printf.h" + +#include +#include +#include +#include + +#if TARGET_LIKE_MBED +#define MBED_INITIALIZE_PRINT(x) { init_serial(); } +#define MBED_PRINT_CHARACTER(x) { serial_putc(&stdio_uart, x); } +#else +#define MBED_INITIALIZE_PRINT(x) { ; } +#define MBED_PRINT_CHARACTER(x) { printf("%c", x); } +#endif + +static void mbed_minimal_formatted_string_long_int(char* buffer, size_t length, int* result, long int value); +static void mbed_minimal_formatted_string_unsigned_long_int(char* buffer, size_t length, int* result, unsigned long int value); +static void mbed_minimal_formatted_string_unsigned_char(char* buffer, size_t length, int* result, int value); +static void mbed_minimal_formatted_string_void_pointer(char* buffer, size_t length, int* result, void* value); +static void mbed_minimal_formatted_string_character(char* buffer, size_t length, int* result, int character); +static void mbed_minimal_formatted_string_string(char* buffer, size_t length, int* result, char* string); + +/* module variable for keeping track of initialization */ +static bool not_initialized = true; + +static void mbed_minimal_formatted_string_long_int(char* buffer, size_t length, int* result, long int value) +{ + /* only continue if buffer can fit at least 1 characters */ + if ((*result + 1) <= length) + { + unsigned long int new_value = 0; + + /* if value is negative print sign and treat as positive number */ + if (value < 0) + { + /* write sign */ + if (buffer) + { + buffer[*result] = '-'; + } + else + { + MBED_PRINT_CHARACTER('-'); + } + + *result += 1; + + /* get absolute value */ + new_value = (unsigned long int) (-1 * value); + } + else + { + new_value = (unsigned long int) value; + } + + /* use unsigned long int function */ + mbed_minimal_formatted_string_unsigned_long_int(buffer, length, result, new_value); + } +} + +static void mbed_minimal_formatted_string_unsigned_long_int(char* buffer, size_t length, int* result, unsigned long int value) +{ + /* only continue if buffer can fit at least 1 characters */ + if ((*result + 1) <= length) + { + /* treat 0 as a corner case */ + if (value == 0) + { + if (buffer) + { + buffer[*result] = '0'; + } + else + { + MBED_PRINT_CHARACTER('0'); + } + + *result += 1; + } + else + { + char scratch[sizeof(unsigned long int) * 3] = { 0 }; + + size_t index = 0; + + /* write numbers in reverse order to scratch pad */ + for ( ; value > 0; index++) + { + /* use '0' as base and add digit */ + scratch[index] = '0' + (value % 10); + + /* shift value one decimal position */ + value = value / 10; + } + + /* write scratch pad to buffer or output */ + for ( ; (*result < length) && (index > 0); index--) + { + if (buffer) + { + buffer[*result] = scratch[index - 1]; + } + else + { + MBED_PRINT_CHARACTER(scratch[index - 1]); + } + + *result += 1; + } + } + } +} + +static void mbed_minimal_formatted_string_unsigned_char(char* buffer, size_t length, int* result, int value) +{ + /* only continue if buffer can fit at least 2 characters */ + if ((*result + 2) <= length) + { + char nibble_one = (value & 0xFF) >> 4; + char nibble_two = (value & 0x0F); + + const char int2hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + + if (buffer) + { + buffer[*result] = int2hex[nibble_one]; + buffer[*result + 1] = int2hex[nibble_two]; + } + else + { + MBED_PRINT_CHARACTER(int2hex[nibble_one]); + MBED_PRINT_CHARACTER(int2hex[nibble_two]); + } + + *result += 2; + } +} + +static void mbed_minimal_formatted_string_void_pointer(char* buffer, size_t length, int* result, void* value) +{ + /* only continue if buffer can fit '0x' and twice the size of a void* */ + if ((*result + 2 + 2 * sizeof(void*)) <= length) + { + if (buffer) + { + buffer[*result] = '0'; + buffer[*result + 1] = 'x'; + } + else + { + MBED_PRINT_CHARACTER('0'); + MBED_PRINT_CHARACTER('x'); + } + + *result += 2; + + for (size_t index = 0; index < sizeof(void*); index++) + { + unsigned char byte = (unsigned char) (((unsigned long int) value) >> (8 * (sizeof(void*) - index - 1))); + + mbed_minimal_formatted_string_unsigned_char(buffer, length, result, byte); + } + } +} + +static void mbed_minimal_formatted_string_character(char* buffer, size_t length, int* result, int character) +{ + /* only continue if the buffer can fit 1 character */ + if ((*result + 1) <= length) + { + if (buffer) + { + buffer[*result] = (char) character; + } + else + { + MBED_PRINT_CHARACTER((char) character); + } + + *result += 1; + } +} + +static void mbed_minimal_formatted_string_string(char* buffer, size_t length, int* result, char* string) +{ + /* only continue if the buffer can fit at least 1 character */ + if ((*result + 1) <= length) + { + /* count characters in string */ + size_t remaining = length - *result; + size_t string_length = 0; + + /* only count characters that will fit into buffer */ + while ((string[string_length] != '\0') && (string_length < remaining)) + { + string_length++; + } + + /* copy string to buffer */ + if (buffer) + { + for (size_t index = 0; index < string_length; index++) + { + buffer[*result + index] = string[index]; + } + } + /* print string */ + else + { + for (size_t index = 0; index < string_length; index++) + { + MBED_PRINT_CHARACTER(string[index]); + } + } + + /* add length to counter */ + *result += string_length; + } +} + +static int mbed_minimal_formatted_string(char* buffer, size_t length, const char* format, va_list arguments) +{ + int result = 0; + + /* if writing to buffer, reserve space for NULL termination */ + if (buffer) + { + length--; + } + + /* parse string */ + for (size_t index = 0; format[index] != '\0'; index++) + { + /* format specifier begin */ + if (format[index] == '%') + { + char next = format[index + 1]; + + if (next == 'd') + { + long int value = va_arg(arguments, long int); + index++; + + mbed_minimal_formatted_string_long_int(buffer, length, &result, value); + } + else if (next == 'u') + { + unsigned long int value = va_arg(arguments, unsigned long int); + index++; + + mbed_minimal_formatted_string_unsigned_long_int(buffer, length, &result, value); + } + else if (next == 'X') + { + int value = va_arg(arguments, int); + index++; + + mbed_minimal_formatted_string_unsigned_char(buffer, length, &result, value); + } + else if (next == 'p') + { + void* value = va_arg(arguments, void*); + index++; + + mbed_minimal_formatted_string_void_pointer(buffer, length, &result, value); + } + else if (next == 'c') + { + int value = va_arg(arguments, int); + index++; + + mbed_minimal_formatted_string_character(buffer, length, &result, value); + } + else if (next == 's') + { + char* value = va_arg(arguments, char*); + index++; + + mbed_minimal_formatted_string_string(buffer, length, &result, value); + } + else if (next == '\0') + { + mbed_minimal_formatted_string_character(buffer, length, &result, '%'); + } + else + { + mbed_minimal_formatted_string_character(buffer, length, &result, '%'); + } + } + else + { + mbed_minimal_formatted_string_character(buffer, length, &result, format[index]); + } + } + + /* if writing to buffer, NULL terminate string in reserved space*/ + if (buffer) + { + buffer[result] = '\0'; + } + + return result; +} + +int mbed_printf(const char *format, ...) +{ + if (not_initialized) + { + not_initialized = false; + MBED_INITIALIZE_PRINT(); + } + + va_list arguments; + va_start(arguments, format); + int result = mbed_minimal_formatted_string(NULL, ULONG_MAX, format, arguments); + va_end(arguments); + + return result; +} + +int mbed_snprintf(char* buffer, size_t length, const char* format, ...) +{ + if (not_initialized) + { + not_initialized = false; + MBED_INITIALIZE_PRINT(); + } + + va_list arguments; + va_start(arguments, format); + int result = mbed_minimal_formatted_string(buffer, length, format, arguments); + va_end(arguments); + + return result; +} diff --git a/features/minimal-printf/mbed_printf.h b/features/minimal-printf/mbed_printf.h new file mode 100755 index 0000000000..76b3f5e09d --- /dev/null +++ b/features/minimal-printf/mbed_printf.h @@ -0,0 +1,58 @@ +/* mbed Microcontroller Library + * Copyright (c) 2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBED_PRINTF_H +#define MBED_PRINTF_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Minimal printf + * + * Prints directly to stdio/UART without using malloc. + * Doesn't suppoet any modifiers. + * + * Supports: + * %d: signed long int + * %u: unsigned long int + * %p: void* (e.g. 0x00123456) + * %s: string + * %X: unsigned char printed as hexadecimal number (e.g., FF) + */ +int mbed_printf(const char *format, ...); + +/** + * Minimal snprintf + * + * Prints directly to buffer without using malloc. + * Doesn't suppoet any modifiers. + * + * Supports: + * %d: signed long int + * %u: unsigned long int + * %p: void* (e.g. 0x00123456) + * %s: string + * %X: unsigned char printed as hexadecimal number (e.g., FF) + */ +int mbed_snprintf(char* buffer, size_t length, const char* format, ...); + +#ifdef __cplusplus +} +#endif + +#endif // MBED_PRINTF_H