This is a tiny but **fully loaded** printf, sprintf and snprintf implementation.
This is a tiny but **fully loaded** printf, sprintf and (v)snprintf implementation.
Primarily designed for usage in embedded systems, where printf is not available due to memory issues or in avoidance of linking against libc.
Using the standard libc printf may pull **a lot** of unwanted library stuff and can bloat code size about 20k or is not 100% thread safe. In this cases the following implementation can be used.
Absolutely **NO dependencies** are required, *printf.c* brings all necessary routines, even its own fast `ftoa`, `ntoa` conversion.
Absolutely **NO dependencies** are required, *printf.c* brings all necessary routines, even its own fast `ftoa` (float), `ntoa` (decimal) conversion.
If memory footprint is really a critical issue, floating point and 'long long' support and can be turned off via the `PRINTF_FLOAT_SUPPORT` and `PRINTF_LONG_LONG_SUPPORT` compiler switches.
When using printf (instead of sprintf) you have to provide your own `_putchar()` low level function as console/serial output.
When using printf (instead of sprintf/snprintf) you have to provide your own `_putchar()` low level function as console/serial output.
## Highligths and design goals
There is a boatload of so called 'tiny' printf implementations around. So why this one?
I've tested many implementations, but most of them have very limited flag/specifier support, a lot of other dependencies or are just not standard compliant and failing the test suite.
Therefore I decided to write an own implementation which meets the following items:
I've tested many implementations, but most of them have very limited flag/specifier support, a lot of other dependencies or are just not standard compliant and failing most of the test suite.
Therefore I decided to write an own, final implementation which meets the following items:
- Very small implementation (<500codelines)
- Very small implementation (around 500 code lines)
- NO dependencies, no libs, just one module file
- Support of all important flags, width and precision sub-specifiers (see below)
- Support of dec/float number representation (with an own fast itoa/ftoa)
- Support of decimal/floating number representation (with an own fast itoa/ftoa)
@ -49,10 +49,11 @@ Usage is 1:1 like the according stdio.h library version:
int printf(const char* format, ...);
int sprintf(char* buffer, const char* format, ...);
int snprintf(char* buffer, size_t count, const char* format, ...);
int vsnprintf(char* buffer, size_t count, const char* format, va_list va);
```
**Due to genaral security reasons it is highly recommended to use `snprintf` (with the max buffer size as `count` parameter) only.**
`sprintf` has no buffer limitation, so when necessary - use it with care!
**Due to genaral security reasons it is highly recommended to use `snprintf` (with the max buffer size as `count` parameter) instead of `sprintf`.**
`sprintf` has no buffer limitation, so when needed - use it really with care!
## Format specifiers
@ -69,10 +70,9 @@ The following format specifiers are supported:
| u | Unsigned decimal integer |
| b | Unsigned binary |
| o | Unsigned octal |
| x | Unsigned hexadecimal integer |
| x | Unsigned hexadecimal integer (lowercase) |
| X | Unsigned hexadecimal integer (uppercase) |
| f | Decimal floating point, lowercase |
| F | Decimal floating point, uppercase |
| f or F | Decimal floating point |
| c | Single character |
| s | String of characters |
| p | Pointer address |
@ -90,14 +90,6 @@ The following format specifiers are supported:
| 0 | Left-pads the number with zeroes (0) instead of spaces when padding is specified (see width sub-specifier). |
### Supported precision
| Precision | Description |
|-----------|-------------|
| .number | For integer specifiers (d, i, o, u, x, X): precision specifies the minimum number of digits to be written. If the value to be written is shorter than this number, the result is padded with leading zeros. The value is not truncated even if the result is longer. A precision of 0 means that no character is written for the value 0.<br>For f and F specifiers: this is the number of digits to be printed after the decimal point. By default, this is 6, maximum is 9.<br>For s: this is the maximum number of characters to be printed. By default all characters are printed until the ending null character is encountered.<br>If the period is specified without an explicit value for precision, 0 is assumed. |
| .* | The precision is not specified in the format string, but as an additional integer value argument preceding the argument that has to be formatted. |
### Supported width
| Width | Description |
@ -106,6 +98,14 @@ The following format specifiers are supported:
| * | The width is not specified in the format string, but as an additional integer value argument preceding the argument that has to be formatted. |
### Supported precision
| Precision | Description |
|-----------|-------------|
| .number | For integer specifiers (d, i, o, u, x, X): precision specifies the minimum number of digits to be written. If the value to be written is shorter than this number, the result is padded with leading zeros. The value is not truncated even if the result is longer. A precision of 0 means that no character is written for the value 0.<br>For f and F specifiers: this is the number of digits to be printed after the decimal point. By default, this is 6, maximum is 9.<br>For s: this is the maximum number of characters to be printed. By default all characters are printed until the ending null character is encountered.<br>If the period is specified without an explicit value for precision, 0 is assumed. |
| .* | The precision is not specified in the format string, but as an additional integer value argument preceding the argument that has to be formatted. |
### Supported length
The length sub-specifier modifies the length of the data type.
@ -114,38 +114,52 @@ The length sub-specifier modifies the length of the data type.
|--------|------|---------|
| (none) | int | unsigned int |
| l | long int | unsigned long int |
| ll | long long int | unsigned long long int |
| ll | long long int | unsigned long long int (if PRINTF_LONG_LONG_SUPPORT is defined) |
| z | size_t int | unsigned size_t int |
### Return value
Upon successful return, all functions return the number of characters _written_, _excluding_ the null byte used to end the string.
Functions `snprintf()` and `vsnprintf()` don't write more than `count` bytes, _including_ the terminating null byte ('\0'). If the output was truncated
due to this limit, the return value is `count`, which indicates any truncation.
If an output error (invalid buffer etc.) is encountered, `-1` is returned.
## Caveats
Currently `snprintf()` and `vsnprintf()` don't support `(v)snprintf(nullptr, 0, "Some text")` to get the length of the formatted string only. An error value of `-1` is returned.
## Compiler switches/defines
| Name | Default value | Description |
|------|---------------|-------------|
| PRINTF_BUFFER_SIZE | 128 | The buffer size used for printf |
| NTOA_BUFFER_SIZE | 32 | ntoa (integer) conversion buffer size. This must be big enough to hold one converted numeric number, normally 32 is a sufficient value. |
| FTOA_BUFFER_SIZE | 32 | ftoa (float) conversion buffer size. This must be big enough to hold one converted float number, normally 32 is a sufficient value. |
| PRINTF_FLOAT_SUPPORT | defined | Define this to enable floating point (%f) support |
| PRINTF_LONG_LONG_SUPPORT | defined | Define this to enable long long (%ll) support |
| PRINTF_BUFFER_SIZE | 128 | The buffer size used for the printf() function (not for sprintf/snprintf). Set to 0 if the printf() function is unused (just using sprintf/snprintf) |
| PRINTF_NTOA_BUFFER_SIZE | 32 | ntoa (integer) conversion buffer size. This must be big enough to hold one converted numeric number_including_ leading zeros, normally 32 is a sufficient value |
| PRINTF_FTOA_BUFFER_SIZE | 32 | ftoa (float) conversion buffer size. This must be big enough to hold one converted float number_including_ leading zeros, normally 32 is a sufficient value |
| PRINTF_FLOAT_SUPPORT | undefined | Define this to enable floating point (%f) support |
| PRINTF_LONG_LONG_SUPPORT | undefined | Define this to enable long long (%ll) support |
## Test suite
For testing just compile, build and run the test suite located in `test/test_suite.cpp`. This uses the [catch](https://github.com/philsquared/Catch) framework for unit-tests, which is auto-adding main().
For testing just compile, build and run the test suite located in `test/test_suite.cpp`. This uses the [catch](https://github.com/catchorg/Catch2) framework for unit-tests, which is auto-adding main().
Running with the `--wait-for-keypress exit` option waits for the enter key after test end.
## Projects using printf
- [turnkey-board](https://github.com/mpaland/turnkey-board) uses printf as log and generic display formatting/output.
- [turnkey-board](https://github.com/mpaland/turnkey-board) uses printf as log and generic display formatting/output.
(Just send me a mail/issue to get your project listed here)