Import Mbed OS hard-float snapshot

This commit is contained in:
Beslan
2026-06-01 20:15:04 +03:00
commit d3738e2f89
16278 changed files with 10628036 additions and 0 deletions

View File

@@ -0,0 +1,163 @@
# Minimal printf and snprintf
Library supports both printf and snprintf in around 1300 bytes of flash.
Prints directly to stdio/UART without using malloc. All flags and precision modifiers are ignored.
There is no error handling if a writing error occurs.
Supports:
* %d: signed integer [h, hh, (none), l, ll, z, j, t].
* %i: signed integer [h, hh, (none), l, ll, z, j, t].
* %u: unsigned integer [h, hh, (none), l, ll, z, j, t].
* %x: unsigned integer [h, hh, (none), l, ll, z, j, t], printed as hexadecimal number (e.g., ff).
* %X: unsigned integer [h, hh, (none), l, ll, z, j, t], printed as hexadecimal number (e.g., FF).
* %f: floating point (disabled by default).
* %F: floating point (disabled by default, treated as %f).
* %g: floating point (disabled by default, treated as %f).
* %G: floating point (disabled by default, treated as %f).
* %c: character.
* %s: string.
* %p: pointer (e.g. 0x00123456).
Note that support for:
* 64b modifiers is only present when `minimal-printf-enable-64-bit` config is set to `true` (default).
* Floating point parameters is only present when `minimal-printf-enable-floating-point` config is set to `true` (disabled by default).
Unrecognized format specifiers are treated as ordinary characters.
Floating point limitations:
* All floating points are treated as %f.
* No support for inf, infinity or nan
## Usage
As of Mbed OS 6.0 this is enabled by default. To replace the standard implementation of the printf functions with the ones in this library for older versions of Mbed:
Modify your application configuration file to override the parameter `target.printf_lib` with the value `minimal-printf` as shown below:
```json
{
"target_overrides": {
"*": {
"target.printf_lib": "minimal-printf"
}
}
}
```
If your application requires more advanced functionality, you'll need to revert to using standard version of printf/snprintf. Please note that it will result in significant ROM usage increase. In case you are using minimal version of standard C library advanced functionality may not be present.
Modify your application configuration in `mbed_app.json` file to override the parameter `target.printf_lib` with the value `std` as shown below:
```json
"target_overrides": {
"*": {
"target.printf_lib": "std"
}
}
```
## Configuration
Minimal printf is configured by the following parameters defined in `platform/mbed_lib.json`:
```json
{
"name": "platform",
"config": {
"minimal-printf-enable-64-bit": {
"help": "Enable printing 64 bit integers when using minimal printf library",
"value": true
},
"minimal-printf-enable-floating-point": {
"help": "Enable floating point printing when using minimal printf library",
"value": false
},
"minimal-printf-set-floating-point-max-decimals": {
"help": "Maximum number of decimals to be printed when using minimal printf library",
"value": 6
}
}
}
```
By default, 64 bit integers support is enabled, but floating point support is disabled to increase memory savings.
If your application needs to override the default configuration add following section to your `mbed_app.json`:
```json
"target_overrides": {
"*": {
"target.printf_lib": "minimal-printf",
"platform.minimal-printf-enable-floating-point": false,
"platform.minimal-printf-set-floating-point-max-decimals": 6,
"platform.minimal-printf-enable-64-bit": false
}
}
```
## Size comparison
### Blinky application
https://github.com/ARMmbed/mbed-os-example-blinky application compiled with the different toolchains.
Blinky application size on K64F/GCC_ARM
| | Floating point | 64 bit integers | Flash | RAM |
| - | - | - | - | - |
| mbed-printf | | | 32,972 | 11,608 |
| mbed-printf | | X | 33,116 | 11,608 |
| mbed-printf | X | X | 35,856 | 11,608 |
| std printf | X | X | 55,766 | 12,104 |
Blinky application size on K64F/ARMC6
| | Floating point | 64 bit integers | Flash | RAM |
| - | - | - | - | - |
| mbed-printf | | | 33,585 | xxxxx |
| mbed-printf | | X | 33,679 | xxxxx |
| mbed-printf | X | X | 36,525 | xxxxx |
| std printf | X | X | 39,128 | xxxxx |
Blinky application size on K64F/IAR
| | Floating point | 64 bit integers | Flash | RAM |
| - | - | - | - | - |
| mbed-printf | | | 31,439 | 8,493 |
| mbed-printf | | X | 31,579 | 8,493 |
| mbed-printf | X | X | 33,387 | 8,493 |
| std printf | X | X | 36,643 | 8,553 |
### Blinky bare metal application
https://github.com/ARMmbed/mbed-os-example-blinky-baremetal application compiled with the different toolchains.
Blinky application size on K64F/GCC_ARM
| | Floating point | 64 bit integers | Flash | RAM |
| - | - | - | - | - |
| mbed-printf | | | 19,660 | 5,368 |
| mbed-printf | | X | 19,804 | 5,368 |
| mbed-printf | X | X | 22,548 | 5,368 |
| std printf | X | X | 35,292 | 5,864 |
Blinky application size on K64F/ARMC6
| | Floating point | 64 bit integers | Flash | RAM |
| - | - | - | - | - |
| mbed-printf | | | 18,764 | xxxxx |
| mbed-printf | | X | 18,764 | xxxxx |
| mbed-printf | X | X | 18,764 | xxxxx |
| std printf | X | X | 25,403 | xxxxx |
Blinky application size on K64F/IAR
| | Floating point | 64 bit integers | Flash | RAM |
| - | - | - | - | - |
| mbed-printf | | | 19,623 | 1,737 |
| mbed-printf | | X | 19,763 | 1,737 |
| mbed-printf | X | X | 21,571 | 1,737 |
| std printf | X | X | 18,059 | 1,281 |

View File

@@ -0,0 +1,391 @@
/* mbed Microcontroller Library
* Copyright (c) 2019 ARM Limited
* SPDX-License-Identifier: Apache-2.0
*
* 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.
*/
/**
* Arm Compiler uses dedicated functions for each format specifier used by
* [sn/v/vsn]printf. When minimal-printf overwrites [sn/v/vsn]printf the
* linker is unable to remove unused functions related to printf.
*
* The following stubs replace the built-in functions and helps the linker
* to resolve dependencies and correctly remove unused functions.
*/
#ifdef MBED_MINIMAL_PRINTF
#if defined(TOOLCHAIN_ARM)
#include "mbed_printf_implementation.h"
#include <limits.h>
#include <stdio.h>
#include <stdarg.h>
/**
* Arm Compiler uses __2[s/sn/vsn]printf internally.
*/
int $Sub$$__2printf(const char *format, ...)
{
va_list arguments;
va_start(arguments, format);
int result = mbed_minimal_formatted_string(NULL, LONG_MAX, format, arguments, stdout);
va_end(arguments);
return result;
}
int $Sub$$__2sprintf(char *buffer, const char *format, ...)
{
va_list arguments;
va_start(arguments, format);
int result = mbed_minimal_formatted_string(buffer, LONG_MAX, format, arguments, NULL);
va_end(arguments);
return result;
}
int $Sub$$__2snprintf(char *buffer, size_t length, const char *format, ...)
{
va_list arguments;
va_start(arguments, format);
int result = mbed_minimal_formatted_string(buffer, length, format, arguments, NULL);
va_end(arguments);
return result;
}
int $Sub$$__2vprintf(const char *format, va_list arguments)
{
return mbed_minimal_formatted_string(NULL, LONG_MAX, format, arguments, stdout);
}
int $Sub$$__2vsprintf(char *buffer, const char *format, va_list arguments)
{
return mbed_minimal_formatted_string(buffer, LONG_MAX, format, arguments, NULL);
}
int $Sub$$__2vsnprintf(char *buffer, size_t length, const char *format, va_list arguments)
{
return mbed_minimal_formatted_string(buffer, length, format, arguments, NULL);
}
int $Sub$$__2fprintf(FILE *stream, const char *format, ...)
{
va_list arguments;
va_start(arguments, format);
int result = mbed_minimal_formatted_string(NULL, LONG_MAX, format, arguments, stream);
va_end(arguments);
return result;
}
int $Sub$$__2vfprintf(FILE *stream, const char *format, va_list arguments)
{
return mbed_minimal_formatted_string(NULL, LONG_MAX, format, arguments, stream);
}
/**
* Replace the built-in functions which the linker is unable to prune with dummy stubs
* that take up less space.
*/
int $Sub$$_printf_a(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_c(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_char(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_char_common(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_char_file_locked(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_charcount(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_cs_common(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_d(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_dec(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_e(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_f(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_fp_dec(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_fp_dec_real(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_fp_hex(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_fp_hex_real(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_fp_infnan(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_g(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_hex_int_ll_ptr(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_hex_ptr(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_i(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_int_common(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_int_dec(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_int_hex(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_int_oct(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_l(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_lc(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_lcs_common(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_ll(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_ll_hex(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_ll_oct(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_lld(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_lli(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_llo(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_llu(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_llx(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_longlong_dec(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_longlong_hex(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_longlong_oct(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_ls(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_n(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_o(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_oct_int_ll(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_p(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_pad(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_percent(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_percent_end(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_s(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_str(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_string(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_truncate(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_truncate_signed(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_truncate_unsigned(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_u(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_wchar(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_wctomb(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_wstring(const char *format, ...)
{
return 0;
}
int $Sub$$_printf_x(const char *format, ...)
{
return 0;
}
#endif
#endif // MBED_MINIMAL_PRINTF

View File

@@ -0,0 +1,646 @@
/* mbed Microcontroller Library
* Copyright (c) 2017-2020 ARM Limited
* SPDX-License-Identifier: Apache-2.0
*
* 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_implementation.h"
#include <stdbool.h>
#include <limits.h>
#include <stdint.h>
#include <stddef.h>
#if !TARGET_LIKE_MBED
/* Linux implementation is for debug only */
#define MBED_CONF_PLATFORM_MINIMAL_PRINTF_ENABLE_FLOATING_POINT 1
#define MBED_CONF_PLATFORM_MINIMAL_PRINTF_SET_FLOATING_POINT_MAX_DECIMALS 6
#define MBED_CONF_PLATFORM_MINIMAL_PRINTF_ENABLE_64_BIT 1
#endif
#ifndef MBED_CONF_PLATFORM_MINIMAL_PRINTF_ENABLE_FLOATING_POINT
#define MBED_CONF_PLATFORM_MINIMAL_PRINTF_ENABLE_FLOATING_POINT 0
#endif
#ifndef MBED_CONF_PLATFORM_MINIMAL_PRINTF_SET_FLOATING_POINT_MAX_DECIMALS
#define MBED_CONF_PLATFORM_MINIMAL_PRINTF_SET_FLOATING_POINT_MAX_DECIMALS 6
#endif
#ifndef MBED_CONF_PLATFORM_MINIMAL_PRINTF_ENABLE_64_BIT
#define MBED_CONF_PLATFORM_MINIMAL_PRINTF_ENABLE_64_BIT 1
#endif
/**
* Check architecture and choose storage data type.
* On 32 bit machines, the default storage type is 32 bit wide
* unless 64 bit integers are enabled in the configuration.
*/
#if INTPTR_MAX == INT32_MAX
#define MBED_SIGNED_NATIVE_TYPE int32_t
#define MBED_UNSIGNED_NATIVE_TYPE uint32_t
#if MBED_CONF_PLATFORM_MINIMAL_PRINTF_ENABLE_64_BIT
#define MBED_SIGNED_STORAGE int64_t
#define MBED_UNSIGNED_STORAGE uint64_t
#else
#define MBED_SIGNED_STORAGE int32_t
#define MBED_UNSIGNED_STORAGE uint32_t
#endif
#elif INTPTR_MAX == INT64_MAX
#define MBED_SIGNED_NATIVE_TYPE int64_t
#define MBED_UNSIGNED_NATIVE_TYPE uint64_t
#define MBED_SIGNED_STORAGE int64_t
#define MBED_UNSIGNED_STORAGE uint64_t
#else
#error unsupported architecture
#endif
/**
* Precision defines
*/
#define PRECISION_DEFAULT (INT_MAX)
/**
* Enum for storing width modifier.
*/
typedef enum {
LENGTH_NONE = 0x00,
LENGTH_H = 0x11,
LENGTH_L = 0x21,
LENGTH_J = 0x31,
LENGTH_Z = 0x41,
LENGTH_T = 0x51,
LENGTH_CAPITAL_L = 0x61,
LENGTH_HH = 0x72,
LENGTH_LL = 0x82
} length_t;
/**
* Prototypes
*/
static void mbed_minimal_formatted_string_signed(char *buffer, size_t length, int *result, MBED_SIGNED_STORAGE value, FILE *stream);
static void mbed_minimal_formatted_string_unsigned(char *buffer, size_t length, int *result, MBED_UNSIGNED_STORAGE value, FILE *stream);
static void mbed_minimal_formatted_string_hexadecimal(char *buffer, size_t length, int *result, MBED_UNSIGNED_STORAGE value, FILE *stream, bool upper);
static void mbed_minimal_formatted_string_void_pointer(char *buffer, size_t length, int *result, const void *value, FILE *stream);
static void mbed_minimal_formatted_string_string(char *buffer, size_t length, int *result, const char *string, size_t precision, FILE *stream);
/**
* @brief Print a single character, checking for buffer and size overflows.
*
* @param buffer The buffer to store output (NULL for stdout).
* @param[in] length The length of the buffer.
* @param result The current output location.
* @param[in] data The char to be printed.
*/
static void mbed_minimal_putchar(char *buffer, size_t length, int *result, char data, FILE *stream)
{
/* only continue if 'result' doesn't overflow */
if ((*result >= 0) && (*result <= INT_MAX - 1)) {
if (stream) {
if (fputc(data, stream) == EOF) {
*result = EOF;
} else {
*result += 1;
}
} else {
if (buffer) {
/* write data only if there's enough space */
if ((size_t)*result < length) {
buffer[*result] = data;
}
}
/* increment 'result' even if data was not written. This ensures that
'mbed_minimal_formatted_string' returns the correct value. */
*result += 1;
}
}
}
/**
* @brief Print signed integer.
*
* @param buffer The buffer to store output (NULL for stdout).
* @param[in] length The length of the buffer.
* @param result The current output location.
* @param[in] value The value to be printed.
*/
static void mbed_minimal_formatted_string_signed(char *buffer, size_t length, int *result, MBED_SIGNED_STORAGE value, FILE *stream)
{
MBED_UNSIGNED_STORAGE new_value = 0;
/* if value is negative print sign and treat as positive number */
if (value < 0) {
/* write sign */
mbed_minimal_putchar(buffer, length, result, '-', stream);
/* get absolute value using two's complement */
new_value = ~((MBED_UNSIGNED_STORAGE) value) + 1;
} else {
new_value = value;
}
/* use unsigned long int function */
mbed_minimal_formatted_string_unsigned(buffer, length, result, new_value, stream);
}
/**
* @brief Print unsigned integer.
*
* @param buffer The buffer to store output (NULL for stdout).
* @param[in] length The length of the buffer.
* @param result The current output location.
* @param[in] value The value to be printed.
*/
static void mbed_minimal_formatted_string_unsigned(char *buffer, size_t length, int *result, MBED_UNSIGNED_STORAGE value, FILE *stream)
{
/* treat 0 as a corner case */
if (value == 0) {
mbed_minimal_putchar(buffer, length, result, '0', stream);
} else {
/* allocate 3 digits per byte */
char scratch[sizeof(MBED_UNSIGNED_STORAGE) * 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 (; index > 0; index--) {
mbed_minimal_putchar(buffer, length, result, scratch[index - 1], stream);
}
}
}
/**
* @brief Print hexadecimal.
*
* @param buffer The buffer to store output (NULL for stdout).
* @param[in] length The length of the buffer.
* @param result The current output location.
* @param[in] value The value to be printed.
* @param upper Flag to print the hexadecimal in upper or lower case.
*/
static void mbed_minimal_formatted_string_hexadecimal(char *buffer, size_t length, int *result, MBED_UNSIGNED_STORAGE value, FILE *stream, bool upper)
{
bool print_leading_zero = false;
for (int index = 7; index >= 0; index--) {
/* get most significant byte */
uint8_t output = value >> (8 * index);
/* only print leading zeros when set */
if (print_leading_zero || (output != 0) || (index == 0)) {
unsigned int nibble_one = (output >> 4);
unsigned int nibble_two = (output & 0x0F);
static const char int2hex_lower[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
};
static const char int2hex_upper[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};
const char *int2hex = upper ? int2hex_upper : int2hex_lower;
if (print_leading_zero || nibble_one != 0) {
mbed_minimal_putchar(buffer, length, result, int2hex[nibble_one], stream);
}
mbed_minimal_putchar(buffer, length, result, int2hex[nibble_two], stream);
/* print zeroes after the first non-zero byte */
print_leading_zero = true;
}
}
}
/**
* @brief Print pointer.
*
* @param buffer The buffer to store output (NULL for stdout).
* @param[in] length The length of the buffer.
* @param result The current output location.
* @param[in] value The pointer to be printed.
*/
static void mbed_minimal_formatted_string_void_pointer(char *buffer, size_t length, int *result, const void *value, FILE *stream)
{
/* write leading 0x */
mbed_minimal_putchar(buffer, length, result, '0', stream);
mbed_minimal_putchar(buffer, length, result, 'x', stream);
/* write rest as a regular hexadecimal number */
mbed_minimal_formatted_string_hexadecimal(buffer, length, result, (ptrdiff_t) value, stream, true);
}
#if MBED_CONF_PLATFORM_MINIMAL_PRINTF_ENABLE_FLOATING_POINT
/**
* @brief Write double.
*
* @param buffer The buffer to store output (NULL for stdout).
* @param[in] length The length of the buffer.
* @param result The current output location.
* @param[in] value The value to be printed.
*/
static void mbed_minimal_formatted_string_double(char *buffer, size_t length, int *result, double value, FILE *stream)
{
/* get integer part */
MBED_SIGNED_STORAGE integer = value;
/* write integer part */
mbed_minimal_formatted_string_signed(buffer, length, result, integer, stream);
/* write decimal point */
mbed_minimal_putchar(buffer, length, result, '.', stream);
/* get decimal part */
double precision = 1.0;
for (size_t index = 0; index < MBED_CONF_PLATFORM_MINIMAL_PRINTF_SET_FLOATING_POINT_MAX_DECIMALS; index++) {
precision *= 10;
}
value = (value - integer) * precision;
/* convert to unsigned integer */
MBED_UNSIGNED_STORAGE decimal = 0;
if (value < 0) {
MBED_SIGNED_STORAGE temp = value;
decimal = ~((MBED_UNSIGNED_STORAGE) temp) + 1;
} else {
decimal = value;
}
/* round up or down */
value -= decimal;
if (!((value > -0.5) && (value < 0.5))) {
decimal++;
}
/* convert precision to unsigned integer */
MBED_UNSIGNED_STORAGE precision_in_uint = precision;
precision_in_uint /= 10;
/* ensure that leading zeros are printed if decimal equals 0 */
MBED_UNSIGNED_STORAGE val = decimal ? decimal : decimal + 1;
while (precision_in_uint > val) {
/* print leading zeros */
mbed_minimal_putchar(buffer, length, result, '0', stream);
precision_in_uint /= 10;
}
/* write decimal part */
mbed_minimal_formatted_string_unsigned(buffer, length, result, decimal, stream);
}
#endif
/**
* @brief Print string with precision.
*
* @param buffer The buffer to store output (NULL for stdout).
* @param[in] length The length of the buffer.
* @param result The current output location.
* @param[in] value The string to be printed.
* @param[in] precision The maximum number of characters to be printed.
*/
static void mbed_minimal_formatted_string_string(char *buffer, size_t length, int *result, const char *string, size_t precision, FILE *stream)
{
while ((*string != '\0') && (precision)) {
mbed_minimal_putchar(buffer, length, result, *string, stream);
string++;
precision--;
}
}
/**
* @brief Parse formatted string and invoke write handlers based on type.
*
* @param buffer The buffer to write to, write to stdout if NULL.
* @param[in] length The length of the buffer.
* @param[in] format The formatted string.
* @param[in] arguments The va_list arguments.
*
* @return Number of characters written.
*/
int mbed_minimal_formatted_string(char *buffer, size_t length, const char *format, va_list arguments, FILE *stream)
{
int result = 0;
bool empty_buffer = false;
/* ensure that function wasn't called with an empty buffer, or with or with
a buffer size that is larger than the maximum 'int' value, or with
a NULL format specifier */
if (format && length <= INT_MAX) {
/* Make sure that there's always space for the NULL terminator */
if (length > 0) {
length --;
} else {
/* the buffer is empty, there's no place to write the terminator */
empty_buffer = true;
}
/* parse string */
for (size_t index = 0; format[index] != '\0'; index++) {
/* format specifier begin */
if (format[index] == '%') {
size_t next_index = index + 1;
/**************************************************************
* skip and ignore flags [-+(space)#0]
*************************************************************/
if ((format[next_index] == '-') ||
(format[next_index] == '+') ||
(format[next_index] == ' ') ||
(format[next_index] == '#') ||
(format[next_index] == '0')) {
/* skip to next character */
next_index++;
}
/**************************************************************
* skip and ignore width [(number)*]
*************************************************************/
if (format[next_index] == '*') {
/* skip to next character */
next_index++;
/* discard argument */
va_arg(arguments, MBED_SIGNED_NATIVE_TYPE);
} else {
while ((format[next_index] >= '0') &&
(format[next_index] <= '9')) {
/* skip to next character */
next_index++;
}
}
/**************************************************************
* look for precision modifier
*************************************************************/
int precision = PRECISION_DEFAULT;
if ((format[next_index] == '.') &&
(format[next_index + 1] == '*')) {
next_index += 2;
/* read precision from argument list */
precision = va_arg(arguments, MBED_SIGNED_NATIVE_TYPE);
} else if (format[next_index] == '.') {
/* precision modifier found, reset default to 0 and increment index */
next_index++;
precision = 0;
/* parse precision until not a decimal */
size_t inner_index = 0;
while ((format[next_index + inner_index] >= '0') &&
(format[next_index + inner_index] <= '9')) {
precision = precision * 10 + (format[next_index + inner_index] - '0');
inner_index++;
}
/* move index forward to point at next character */
next_index += inner_index;
}
/**************************************************************
* look for length modifier, default to NONE
*************************************************************/
length_t length_modifier = LENGTH_NONE;
/* look for two character length modifier */
if ((format[next_index] == 'h') && (format[next_index + 1] == 'h')) {
length_modifier = LENGTH_HH;
} else if ((format[next_index] == 'l') && (format[next_index + 1] == 'l')) {
length_modifier = LENGTH_LL;
}
/* look for one character length modifier if two character search failed */
else if (format[next_index] == 'h') {
length_modifier = LENGTH_H;
} else if (format[next_index] == 'l') {
length_modifier = LENGTH_L;
} else if (format[next_index] == 'j') {
length_modifier = LENGTH_J;
} else if (format[next_index] == 'z') {
length_modifier = LENGTH_Z;
} else if (format[next_index] == 't') {
length_modifier = LENGTH_T;
} else if (format[next_index] == 'L') {
length_modifier = LENGTH_CAPITAL_L;
}
/* increment index, length is encoded in modifier enum */
next_index += (length_modifier & 0x0F);
/**************************************************************
* read out character - this is a supported format character,
* '\0', or a not suported character
*************************************************************/
char next = format[next_index];
/* signed integer */
if ((next == 'd') || (next == 'i')) {
MBED_SIGNED_STORAGE value = 0;
#if MBED_CONF_PLATFORM_MINIMAL_PRINTF_ENABLE_64_BIT
/* if 64 bit is enabled and the integer types are larger than the native type */
if (((length_modifier == LENGTH_LL) && (sizeof(long long int) > sizeof(MBED_SIGNED_NATIVE_TYPE))) ||
((length_modifier == LENGTH_L) && (sizeof(long int) > sizeof(MBED_SIGNED_NATIVE_TYPE))) ||
((length_modifier == LENGTH_NONE) && (sizeof(int) > sizeof(MBED_SIGNED_NATIVE_TYPE)))) {
/* use 64 bit storage type for readout */
value = va_arg(arguments, MBED_SIGNED_STORAGE);
} else
#else
/* If 64 bit is not enabled, print %ll[di] rather than truncated value */
if (length_modifier == LENGTH_LL) {
mbed_minimal_putchar(buffer, length, &result, '%', stream);
if (next == '%') {
// Continue printing loop after `%`
index = next_index;
}
continue;
}
#endif
{
/* use native storage type (which can be 32 or 64 bit) */
value = va_arg(arguments, MBED_SIGNED_NATIVE_TYPE);
}
/* constrict value based on length modifier */
switch (length_modifier) {
case LENGTH_NONE:
value = (int) value;
break;
case LENGTH_HH:
value = (signed char) value;
break;
case LENGTH_H:
value = (short int) value;
break;
case LENGTH_L:
value = (long int) value;
break;
case LENGTH_LL:
value = (long long int) value;
break;
case LENGTH_J:
value = (intmax_t) value;
break;
case LENGTH_T:
value = (ptrdiff_t) value;
break;
default:
break;
}
index = next_index;
mbed_minimal_formatted_string_signed(buffer, length, &result, value, stream);
}
/* unsigned integer */
else if ((next == 'u') || (next == 'x') || (next == 'X')) {
MBED_UNSIGNED_STORAGE value = 0;
#if MBED_CONF_PLATFORM_MINIMAL_PRINTF_ENABLE_64_BIT
/* if 64 bit is enabled and the integer types are larger than the native type */
if (((length_modifier == LENGTH_LL) && (sizeof(unsigned long long int) > sizeof(MBED_UNSIGNED_NATIVE_TYPE))) ||
((length_modifier == LENGTH_L) && (sizeof(unsigned long int) > sizeof(MBED_UNSIGNED_NATIVE_TYPE))) ||
((length_modifier == LENGTH_NONE) && (sizeof(unsigned int) > sizeof(MBED_UNSIGNED_NATIVE_TYPE)))) {
/* use 64 bit storage type for readout */
value = va_arg(arguments, MBED_UNSIGNED_STORAGE);
} else
#else
/* If 64 bit is not enabled, print %ll[uxX] rather than truncated value */
if (length_modifier == LENGTH_LL) {
mbed_minimal_putchar(buffer, length, &result, '%', stream);
if (next == '%') {
// Continue printing loop after `%`
index = next_index;
}
continue;
}
#endif
{
/* use native storage type (which can be 32 or 64 bit) */
value = va_arg(arguments, MBED_UNSIGNED_NATIVE_TYPE);
}
/* constrict value based on length modifier */
switch (length_modifier) {
case LENGTH_NONE:
value = (unsigned int) value;
break;
case LENGTH_HH:
value = (unsigned char) value;
break;
case LENGTH_H:
value = (unsigned short int) value;
break;
case LENGTH_L:
value = (unsigned long int) value;
break;
case LENGTH_LL:
value = (unsigned long long int) value;
break;
case LENGTH_J:
value = (uintmax_t) value;
break;
case LENGTH_Z:
value = (size_t) value;
break;
case LENGTH_T:
value = (ptrdiff_t) value;
break;
default:
break;
}
index = next_index;
/* write unsigned or hexadecimal */
if (next == 'u') {
mbed_minimal_formatted_string_unsigned(buffer, length, &result, value, stream);
} else {
mbed_minimal_formatted_string_hexadecimal(buffer, length, &result, value, stream, next == 'X');
}
}
#if MBED_CONF_PLATFORM_MINIMAL_PRINTF_ENABLE_FLOATING_POINT
/* treat all floating points the same */
else if ((next == 'f') || (next == 'F') || (next == 'g') || (next == 'G')) {
double value = va_arg(arguments, double);
index = next_index;
mbed_minimal_formatted_string_double(buffer, length, &result, value, stream);
}
#endif
/* character */
else if (next == 'c') {
char value = va_arg(arguments, MBED_SIGNED_NATIVE_TYPE);
index = next_index;
mbed_minimal_putchar(buffer, length, &result, value, stream);
}
/* string */
else if (next == 's') {
char *value = va_arg(arguments, char *);
index = next_index;
mbed_minimal_formatted_string_string(buffer, length, &result, value, precision, stream);
}
/* pointer */
else if (next == 'p') {
void *value = va_arg(arguments, void *);
index = next_index;
mbed_minimal_formatted_string_void_pointer(buffer, length, &result, value, stream);
} else {
// Unrecognised, or `%%`. Print the `%` that led us in.
mbed_minimal_putchar(buffer, length, &result, '%', stream);
if (next == '%') {
// Continue printing loop after `%%`
index = next_index;
}
// Otherwise we continue the printing loop after the leading `%`, so an
// unrecognised thing like "Blah = %a" will just come out as "Blah = %a"
}
} else
/* not a format specifier */
{
/* write normal character */
mbed_minimal_putchar(buffer, length, &result, format[index], stream);
}
}
if (buffer && !empty_buffer) {
/* NULL-terminate the buffer no matter what. We use '<=' to compare instead of '<'
because we know that we initially reserved space for '\0' by decrementing length */
if ((size_t)result <= length) {
buffer[result] = '\0';
} else {
buffer[length] = '\0';
}
}
}
return result;
}

View File

@@ -0,0 +1,25 @@
/* mbed Microcontroller Library
* Copyright (c) 2017 ARM Limited
* SPDX-License-Identifier: Apache-2.0
*
* 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_IMPLEMENTATION_H
#define MBED_PRINTF_IMPLEMENTATION_H
#include <stdio.h>
#include <stdarg.h>
int mbed_minimal_formatted_string(char *buffer, size_t length, const char *format, va_list arguments, FILE *stream);
#endif

View File

@@ -0,0 +1,93 @@
/* mbed Microcontroller Library
* Copyright (c) 2017 ARM Limited
* SPDX-License-Identifier: Apache-2.0
*
* 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.
*/
#ifdef MBED_MINIMAL_PRINTF
#include "mbed_printf_implementation.h"
#include <limits.h>
#if defined(__ARMCC_VERSION) || defined(__ICCARM__)
# define PREFIX(x) $Sub$$##x
#elif defined(__GNUC__)
# define PREFIX(x) __wrap_##x
#else
#warning "This compiler is not yet supported."
#endif
int PREFIX(printf)(const char *format, ...)
{
va_list arguments;
va_start(arguments, format);
int result = mbed_minimal_formatted_string(NULL, LONG_MAX, format, arguments, stdout);
va_end(arguments);
return result;
}
int PREFIX(sprintf)(char *buffer, const char *format, ...)
{
va_list arguments;
va_start(arguments, format);
int result = mbed_minimal_formatted_string(buffer, LONG_MAX, format, arguments, NULL);
va_end(arguments);
return result;
}
int PREFIX(snprintf)(char *buffer, size_t length, const char *format, ...)
{
va_list arguments;
va_start(arguments, format);
int result = mbed_minimal_formatted_string(buffer, length, format, arguments, NULL);
va_end(arguments);
return result;
}
int PREFIX(vprintf)(const char *format, va_list arguments)
{
return mbed_minimal_formatted_string(NULL, LONG_MAX, format, arguments, stdout);
}
int PREFIX(vsprintf)(char *buffer, const char *format, va_list arguments)
{
return mbed_minimal_formatted_string(buffer, LONG_MAX, format, arguments, NULL);
}
int PREFIX(vsnprintf)(char *buffer, size_t length, const char *format, va_list arguments)
{
return mbed_minimal_formatted_string(buffer, length, format, arguments, NULL);
}
int PREFIX(fprintf)(FILE *stream, const char *format, ...)
{
va_list arguments;
va_start(arguments, format);
int result = mbed_minimal_formatted_string(NULL, LONG_MAX, format, arguments, stream);
va_end(arguments);
return result;
}
int PREFIX(vfprintf)(FILE *stream, const char *format, va_list arguments)
{
return mbed_minimal_formatted_string(NULL, LONG_MAX, format, arguments, stream);
}
#endif // MBED_MINIMAL_PRINTF