diff --git a/CMake/Modules/FindNF_CoreCLR.cmake b/CMake/Modules/FindNF_CoreCLR.cmake index 415b8da8d7..f415d8b4da 100644 --- a/CMake/Modules/FindNF_CoreCLR.cmake +++ b/CMake/Modules/FindNF_CoreCLR.cmake @@ -22,6 +22,7 @@ list(APPEND NF_CoreCLR_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/targets/${RTOS}/_include list(APPEND NF_CoreCLR_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/src/CLR/Diagnostics) list(APPEND NF_CoreCLR_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/src/CLR/Debugger) list(APPEND NF_CoreCLR_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/src/CLR/Helpers/NanoRingBuffer) +list(APPEND NF_CoreCLR_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/src/CLR/Helpers/nanoprintf) list(APPEND NF_CoreCLR_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/src/CLR/Helpers/Base64) list(APPEND NF_CoreCLR_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/src/nanoFramework.Runtime.Events) list(APPEND NF_CoreCLR_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/src/nanoFramework.Runtime.Native) @@ -142,6 +143,7 @@ set(NF_CoreCLR_SRCS Messaging_stub.cpp # Helpers + nanoprintf.c nanoRingBuffer.c Info_Safeprintf.cpp @@ -266,6 +268,7 @@ foreach(SRC_FILE ${NF_CoreCLR_SRCS}) ${CMAKE_SOURCE_DIR}/src/CLR/Messaging # Helpers + ${CMAKE_SOURCE_DIR}/src/CLR/Helpers/nanoprintf ${CMAKE_SOURCE_DIR}/src/CLR/Helpers/NanoRingBuffer ${CMAKE_SOURCE_DIR}/src/CLR/Helpers/Base64 ${CMAKE_SOURCE_DIR}/src/CLR/Diagnostics diff --git a/src/CLR/CorLib/CorLib.h b/src/CLR/CorLib/CorLib.h index 537ba9e1bc..8f457748ff 100644 --- a/src/CLR/CorLib/CorLib.h +++ b/src/CLR/CorLib/CorLib.h @@ -14,4 +14,8 @@ #include "corlib_native.h" #include "nanoCRT.h" +#ifndef VIRTUAL_DEVICE +#include +#endif // VIRTUAL_DEVICE + #endif // NANOCLR_CORLIB_NATIVE_H diff --git a/src/CLR/CorLib/CorLib.vcxproj b/src/CLR/CorLib/CorLib.vcxproj index 0b0dfd2560..a581b79054 100644 --- a/src/CLR/CorLib/CorLib.vcxproj +++ b/src/CLR/CorLib/CorLib.vcxproj @@ -152,7 +152,7 @@ Level3 Disabled _DEBUG;_LIB;%(PreprocessorDefinitions) - ..\..\..\targets\win32\Include;..\Include;..\..\HAL\Include;..\..\PAL\Include;..\..\CLR\Helpers\Base64;%(AdditionalIncludeDirectories) + ..\..\..\targets\win32\Include;..\..\CLR\Helpers\nanoprintf;..\Include;..\..\HAL\Include;..\..\PAL\Include;..\..\CLR\Helpers\Base64;%(AdditionalIncludeDirectories) Windows @@ -165,7 +165,7 @@ Level3 Disabled _DEBUG;_LIB;%(PreprocessorDefinitions) - ..\..\..\targets\win32\Include;..\Include;..\..\HAL\Include;..\..\PAL\Include;..\..\CLR\Helpers\Base64;%(AdditionalIncludeDirectories) + ..\..\..\targets\win32\Include;..\..\CLR\Helpers\nanoprintf;..\Include;..\..\HAL\Include;..\..\PAL\Include;..\..\CLR\Helpers\Base64;%(AdditionalIncludeDirectories) Windows @@ -180,7 +180,7 @@ true true NDEBUG;_LIB;%(PreprocessorDefinitions) - ..\..\..\targets\win32\Include;..\Include;..\..\HAL\Include;..\..\PAL\Include;..\..\CLR\Helpers\Base64;%(AdditionalIncludeDirectories) + ..\..\..\targets\win32\Include;..\..\CLR\Helpers\nanoprintf;..\Include;..\..\HAL\Include;..\..\PAL\Include;..\..\CLR\Helpers\Base64;%(AdditionalIncludeDirectories) Windows @@ -197,7 +197,7 @@ true true NDEBUG;_LIB;%(PreprocessorDefinitions) - ..\..\..\targets\win32\Include;..\Include;..\..\HAL\Include;..\..\PAL\Include;..\..\CLR\Helpers\Base64;%(AdditionalIncludeDirectories) + ..\..\..\targets\win32\Include;..\..\CLR\Helpers\nanoprintf;..\Include;..\..\HAL\Include;..\..\PAL\Include;..\..\CLR\Helpers\Base64;%(AdditionalIncludeDirectories) Windows @@ -208,4 +208,4 @@ - + \ No newline at end of file diff --git a/src/CLR/CorLib/corlib_native_System_Number.cpp b/src/CLR/CorLib/corlib_native_System_Number.cpp index de86b016d8..34a9939a03 100644 --- a/src/CLR/CorLib/corlib_native_System_Number.cpp +++ b/src/CLR/CorLib/corlib_native_System_Number.cpp @@ -1,9 +1,10 @@ -// +// // Copyright (c) .NET Foundation and Contributors // Portions Copyright (c) Microsoft Corporation. All rights reserved. // See LICENSE file in the project root for full license information. // #include "CorLib.h" +#include // must be big enough to fit the biggest number // decorated with negative signs, group separators, etc. @@ -360,11 +361,22 @@ int Library_corlib_native_System_Number::Format_G( defaultPrecision = 9; break; case DATATYPE_R8: + { // from .NET documentation: // When used with a Double value, the "G17" format specifier ensures that the original Double value // successfully round-trips. defaultPrecision = 17; + + CLR_DOUBLE_TEMP_CAST number = (CLR_DOUBLE_TEMP_CAST)value->NumericByRef().r8; + + // check if number is an integer + if (number == (CLR_INT64_TEMP_CAST)number) + { + // this is an integer, set precision to a value achievable by the library + defaultPrecision = 15; + } break; + } default: break; } diff --git a/src/CLR/Core/Core.vcxproj b/src/CLR/Core/Core.vcxproj index 2f1ed1a326..f9504978cb 100644 --- a/src/CLR/Core/Core.vcxproj +++ b/src/CLR/Core/Core.vcxproj @@ -19,6 +19,7 @@ + @@ -217,4 +218,4 @@ - + \ No newline at end of file diff --git a/src/CLR/Core/Core.vcxproj.filters b/src/CLR/Core/Core.vcxproj.filters index 87f474053c..486b2a7481 100644 --- a/src/CLR/Core/Core.vcxproj.filters +++ b/src/CLR/Core/Core.vcxproj.filters @@ -132,6 +132,9 @@ Source Files + + Source Files + Source Files diff --git a/src/CLR/Diagnostics/Diagnostics.h b/src/CLR/Diagnostics/Diagnostics.h index 8ecd0e0d4c..2b4eb4d522 100644 --- a/src/CLR/Diagnostics/Diagnostics.h +++ b/src/CLR/Diagnostics/Diagnostics.h @@ -11,4 +11,7 @@ // #include #include +#ifndef VIRTUAL_DEVICE +#include +#endif // VIRTUAL_DEVICE // #include diff --git a/src/CLR/Diagnostics/Diagnostics.vcxproj b/src/CLR/Diagnostics/Diagnostics.vcxproj index b889cfda8d..7dcba88a8c 100644 --- a/src/CLR/Diagnostics/Diagnostics.vcxproj +++ b/src/CLR/Diagnostics/Diagnostics.vcxproj @@ -105,7 +105,7 @@ Level3 Disabled _DEBUG;_LIB;%(PreprocessorDefinitions) - ..\..\..\targets\win32\Include;..\Include;..\CorLib;..\..\HAL\Include;..\..\PAL\Include;%(AdditionalIncludeDirectories) + ..\Helpers\nanoprintf;..\..\..\targets\win32\Include;..\Include;..\CorLib;..\..\HAL\Include;..\..\PAL\Include;%(AdditionalIncludeDirectories) Windows @@ -118,7 +118,7 @@ Level3 Disabled _DEBUG;_LIB;%(PreprocessorDefinitions) - ..\..\..\targets\win32\Include;..\Include;..\CorLib;..\..\HAL\Include;..\..\PAL\Include;%(AdditionalIncludeDirectories) + ..\Helpers\nanoprintf;..\..\..\targets\win32\Include;..\Include;..\CorLib;..\..\HAL\Include;..\..\PAL\Include;%(AdditionalIncludeDirectories) Windows @@ -133,7 +133,7 @@ true true NDEBUG;_LIB;%(PreprocessorDefinitions) - ..\..\..\targets\win32\Include;..\Include;..\CorLib;..\..\HAL\Include;..\..\PAL\Include;%(AdditionalIncludeDirectories) + ..\Helpers\nanoprintf;..\..\..\targets\win32\Include;..\Include;..\CorLib;..\..\HAL\Include;..\..\PAL\Include;%(AdditionalIncludeDirectories) Windows @@ -150,7 +150,7 @@ true true NDEBUG;_LIB;%(PreprocessorDefinitions) - ..\..\..\targets\win32\Include;..\Include;..\CorLib;..\..\HAL\Include;..\..\PAL\Include;%(AdditionalIncludeDirectories) + ..\Helpers\nanoprintf;..\..\..\targets\win32\Include;..\Include;..\CorLib;..\..\HAL\Include;..\..\PAL\Include;%(AdditionalIncludeDirectories) Windows diff --git a/src/CLR/Diagnostics/Info_Safeprintf.cpp b/src/CLR/Diagnostics/Info_Safeprintf.cpp index 909a92af78..ff705e8808 100644 --- a/src/CLR/Diagnostics/Info_Safeprintf.cpp +++ b/src/CLR/Diagnostics/Info_Safeprintf.cpp @@ -5,22 +5,19 @@ // #include +#include bool CLR_SafeSprintfV(char *&szBuffer, size_t &iBuffer, const char *format, va_list arg) { NATIVE_PROFILE_CLR_DIAGNOSTICS(); - size_t chars = vsnprintf(szBuffer, iBuffer, format, arg); - bool fRes = (chars < iBuffer); + int chars = vsnprintf(szBuffer, iBuffer, format, arg); + bool fRes = (chars >= 0); - if (!fRes) - { - // If the buffer was too small, set chars to iBuffer - 1 to ensure null-termination - chars = iBuffer - 1; - } + if (fRes == false) + chars = (int)iBuffer; szBuffer += chars; - // Null-terminate the buffer szBuffer[0] = 0; iBuffer -= chars; diff --git a/src/CLR/Helpers/nanoprintf/nanoprintf.c b/src/CLR/Helpers/nanoprintf/nanoprintf.c new file mode 100644 index 0000000000..e500132815 --- /dev/null +++ b/src/CLR/Helpers/nanoprintf/nanoprintf.c @@ -0,0 +1,1278 @@ +// +// Copyright (c) .NET Foundation and Contributors +// Portions Copyright (c) 2006 - 2021 Skirrid Systems. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +// clang-format off + +// System and config header files are included by the parent stub file. +#include +#include +#include "nanoprintf.h" +#include + +/* Define default macro to access the format string using a pointer. */ +#ifndef GET_FORMAT + #define GET_FORMAT(p) (*(p)) +#endif + +/* Define default function for printf output. */ +#ifndef PUTCHAR_FUNC + #define PUTCHAR_FUNC putchar +#endif + +/* Renames the functions if they have been defined as macros in printf.h */ +#ifdef printf + #undef printf + #define printf _prntf +#endif + +#ifdef sprintf + #undef sprintf + #define sprintf _sprntf +#endif + +// Macro used to check presence of a feature flag. +// These are defined in print_cfg.h +#define FEATURE(flag) ((FEATURE_FLAGS) & (flag)) + +// Size of buffer for formatting numbers into. +// Use the smallest buffer we can get away with to conserve RAM. +#if FEATURE(USE_BINARY) + #if FEATURE(USE_LONG) + #define BUFMAX 32 + #elif FEATURE(USE_FLOAT) + #define BUFMAX 30 + #else + #define BUFMAX 16 + #endif +#else + #if FEATURE(USE_FLOAT) + #define BUFMAX 30 + #elif FEATURE(USE_LONG_LONG) + #define BUFMAX 22 + #else + #define BUFMAX 16 + #endif +#endif + +// Bit definitions in the flags variable (integer and general) +#if FEATURE(USE_LEFT_JUST) + #define FL_LEFT_JUST (1<<0) +#else + #define FL_LEFT_JUST 0 +#endif +#define FL_ZERO_PAD (1<<1) +#define FL_SPECIAL (1<<2) +#define FL_PLUS (1<<3) +#define FL_SPACE (1<<4) +#define FL_NEG (1<<5) +#define FL_LONG (1<<6) +#define FL_FSTR (1<<7) + +// Bit definitions in the fflags variable (mostly floating point) +#define FF_UCASE (1<<0) +#define FF_ECVT (1<<1) +#define FF_FCVT (1<<2) +#define FF_GCVT (1<<3) +#define FF_NRND (1<<4) +#define FF_XLONG (1<<5) + +// Check whether integer or octal support is needed. +#define HEX_CONVERT_ONLY !(FEATURE(USE_SIGNED) || FEATURE(USE_SIGNED_I) || FEATURE(USE_UNSIGNED) || \ + FEATURE(USE_OCTAL) || FEATURE(USE_BINARY)) + +/***************************************************************************** +Floating point +******************************************************************************/ +#if FEATURE(USE_FLOAT) + +// Most compilers can support double precision +#ifndef NO_DOUBLE_PRECISION + #define DP_LIMIT 310 + #define MAX_POWER 256 +// [NF_CHANGE] + #define FLOAT_DIGITS 19 +// [END_NF_CHANGE] +#else + #define DP_LIMIT 40 + #define MAX_POWER 32 + #define FLOAT_DIGITS 8 +#endif + +#if !FEATURE(USE_SMALL_FLOAT) + // Floating point normalisation tables for fast normalisation. + // smalltable[] is used for value < 1.0 + static const double smalltable[] = { + #ifndef NO_DOUBLE_PRECISION + 1e-256, 1e-128, 1e-64, + #endif + 1e-32, 1e-16, 1e-8, 1e-4, 1e-2, 1e-1, 1.0 + }; + // large table[] is used for value >= 10.0 + static const double largetable[] = { + #ifndef NO_DOUBLE_PRECISION + 1e+256, 1e+128, 1e+64, + #endif + 1e+32, 1e+16, 1e+8, 1e+4, 1e+2, 1e+1 + }; +#endif + +#ifdef NO_DOUBLE_PRECISION + // Double precision not supported by compiler. + // Single precision numbers up to 10^38 require only 8-bit exponent. + typedef signed char flt_width_t; +#else + // Double precision numbers up to 10^308 require 16-bit exponents. + typedef signed short flt_width_t; +#endif + +// These define the maximum number of digits that can be output into the buffer. +// %f requires space for sign, point and rounding digit. +#define DIGITS_MAX_F (BUFMAX-3) +// %e requires space for sign, point and up to 3 digit exponent (E+123). +// Rounding happens in the place where the exponent will go. +#define DIGITS_MAX_E (BUFMAX-7) + +// Function to trim trailing zeros and DP in 'g' mode. +// Return pointer to new string terminator position. +static char *trim_zeros(char *p) +{ + // Trim trailing 0's in 'g' mode. + while (*p == '0') + p--; + // If last non-zero is DP, trim that as well. + if (*p != '.') ++p; + return p; +} + +/* --------------------------------------------------------------------------- +Function: format_float() +Called from the main doprnt function to handle formatting of floating point +values. The general process is: +1. Check for non-numbers and just display the error code. +2. Convert number to positive form. +3. Normalise the number to lie in the range 1.0 <= number < 10.0 +4. Work out where the DP lies and optimum format. +5. Output the digits in reverse order. +--------------------------------------------------------------------------- */ +static char *format_float(double number, flt_width_t ndigits, flt_width_t width, + unsigned char flags, unsigned char fflags, char *buf) +{ + flt_width_t decpt; + flt_width_t nzero; + unsigned char i; + char *p = buf + 2; + char *pend; + +#ifndef NO_ISNAN_ISINF + // Handle special values which need no formatting + if (isinf(number)) + { + buf[0] = 'I'; + buf[1] = 'n'; + buf[2] = 'f'; + buf[3] = '\0'; + return buf; + } + if (isnan(number)) + { + buf[0] = 'N'; + buf[1] = 'a'; + buf[2] = 'N'; + buf[3] = '\0'; + return buf; + } +#endif + + // Handle all numbers as if they were positive. + if (number < 0) + { + number = -number; + flags |= FL_NEG; + } + + if (fflags & FF_FCVT) + { + // Number of DP cannot be negative. + if (ndigits < 0) + ndigits = 0; + } + else + { + // Significant digits must be at least 1. + if (ndigits < 1) + ndigits = 1; + } + + // Prefill buffer with zeros. + for (i = 0; i < BUFMAX; i++) + buf[i] = '0'; + + if (number == 0) + { + // Special case to correct number of decimals, significant figures, + // and avoid printing 0E-00. + decpt = 1; + } + else + { + // Normalise the number such that it lies in the range 1 <= n < 10. +#if FEATURE(USE_SMALL_FLOAT) + /* Normalise using a linear search. This code is simple and uses + * least code space. It also eliminates the lookup tables which may + * otherwise take up RAM space. However, it can take many more operations + * to execute for the full range of floating point values, and may + * introduce rounding errors. Use only when space is critical. + */ + // First make small numbers bigger. + decpt = 1; + while (number < 1.0) + { + number *= 10.0; + --decpt; + } + // Then make big numbers smaller. + while (number >= 10.0) + { + number /= 10.0; + ++decpt; +#ifdef NO_ISNAN_ISINF + // Avoid this loop hanging on infinity. + if (decpt > DP_LIMIT) + { + buf[0] = 'I'; + buf[1] = 'n'; + buf[2] = 'f'; + buf[3] = '\0'; + return buf; + } +#endif + } +#else + flt_width_t power10 = MAX_POWER; + decpt = 1; + + if(number == DBL_MAX) + { + number = 1.7976931348623157; + decpt = 309; + } + else if(number == DBL_MIN) + { + number = -1.7976931348623157; + decpt = -309; + } + else + { + /* Normalise using a binary search, making the largest possible + * adjustment first and getting progressively smaller. This gets + * to the answer in the fastest time, with the minimum number of + * operations to introduce rounding errors. + */ + + // Normalise the number such that it lies in the range 1 <= n < 10. + // First make small numbers bigger. + + i = 0; + while (number < 1.0) + { + while (number < smalltable[i + 1]) + { + number /= smalltable[i]; + decpt -= power10; + } + power10 >>= 1; + i++; + } + // Then make big numbers smaller. + power10 = MAX_POWER; + i = 0; + while (number >= 10.0) + { + while (number >= largetable[i]) + { + number /= largetable[i]; + decpt += power10; +#ifdef NO_ISNAN_ISINF + // Avoid this loop hanging on infinity. + if (decpt > DP_LIMIT) + { + buf[0] = 'I'; + buf[1] = 'n'; + buf[2] = 'f'; + buf[3] = '\0'; + return buf; + } +#endif + } + power10 >>= 1; + i++; + } + } +#endif + } + + // For g conversions determine whether to use e or f mode. + if (fflags & FF_GCVT) + { + /* 'g' format uses 'f' notation where it can and + * 'e' notation where the exponent is more extreme. + * Some references indicate that it uses the more + * compact form but the ANSI standard give an explict + * definition: Use 'e' when the exponent is < -4 + * or where the exponent is >= ndigits. + * The exponent is equal to decpt - 1 so this translates + * to decpt <= -4 || decpt > ndigits + * http://www.acm.uiuc.edu/webmonkeys/book/c_guide/2.12.html#printf + * http://www.mkssoftware.com/docs/man3/printf.3.asp + */ + if (decpt > -4 && decpt <= ndigits) + fflags |= FF_FCVT; + } + + // Sanitise ndigits making sure it fits buffer space. + if (fflags & FF_FCVT) + { + if (!(fflags & FF_GCVT)) + { + // For fcvt operation the number of digits is used to + // refer to decimal places rather than significant digits. + ndigits += decpt; + // When there are no significant digits, + // avoid printing too many 0's. + if (ndigits < 0) + { + decpt -= ndigits; + } + } + /* For 'f' conversions with positive DP value that would overflow + * the buffer space, there is no way to display this so fall back + * to 'e' format. 'f' conversion needs space for sign, point and + * rounding digit. The point may be forced using the special flag + * and the rounding digit may be included in the case of maximum + * overflow. + */ + if (decpt > DIGITS_MAX_F) + { + fflags &= ~FF_FCVT; + } + } + + nzero = 0; + if (fflags & FF_FCVT) + { + if (decpt < 1) + { + // Values < 1 require leading zeros before the real digits. + nzero = 1 - decpt; + } + // Check for buffer fit. Zeros take precedence. + if (nzero > DIGITS_MAX_F) + { + nzero = DIGITS_MAX_F; + ndigits = -1; + } + if (ndigits < 0) + { + // First significant digit is below rounding range. + fflags |= FF_NRND; + ndigits = 0; + } + else if (ndigits + nzero > DIGITS_MAX_F) + { + ndigits = DIGITS_MAX_F - nzero; + } + p += nzero; + } + else + { + // Allow space for sign, point exponent. + if (ndigits > DIGITS_MAX_E) + ndigits = DIGITS_MAX_E; + } + + // Format digits one-by-one into the output string. + // One extra digit is required for rounding. + for (i = 0; i <= ndigits; i++) + { + // Ignore digits beyond the supported precision. + if (i >= FLOAT_DIGITS) + { + *p++ = '0'; + } + else + { + // number is normalised to a positive value between 0 and 9. + int n = number; + *p++ = n + '0'; + number = (number - n) * 10; + } + } + // Store a pointer to the last (rounding) digit. + pend = --p; + // Round the result directly in the string buffer. + // Only use the calculated digits, not trailing 0's. + if (i > FLOAT_DIGITS) + { + p -= (i - FLOAT_DIGITS); + } + if (!(fflags & FF_NRND) && *p >= '5') + { + for (;;) + { + if (i == 0) + { + // The rounding has rippled all the way through to + // the first digit. i.e. 9.999..9 -> 10.0 + // Just replace the first 0 with a 1 and shift the DP. + *p = '1'; + ++decpt; + // This increases the displayed digits for 'f' only. + if ((fflags & (FF_FCVT|FF_GCVT)) == FF_FCVT) + { + ++ndigits; + ++pend; + } + break; + } + // Previous digit was a rollover + *p-- = '0'; + // Increment next digit and break out unless there is a rollover. + if (*p != '9') + { + (*p)++; + break; + } + } + } + + // Insert the decimal point + if (fflags & FF_FCVT) + { + flt_width_t num; + num = (decpt > 1) ? decpt : 1; + p = buf + 1; + for (i = 0; i < num; i++) + { + *p = *(p+1); + ++p; + } + if (p + 1 < pend || (flags & FL_SPECIAL)) + { + // There are digits after the DP or DP is forced. + *p = '.'; + // Trim trailing 0's in 'g' mode. + if ((fflags & FF_GCVT) && !(flags & FL_SPECIAL)) + { + pend = trim_zeros(pend - 1); + } + } + else + { + // DP at end is not required. + --pend; + } + // Leave p pointing to start of digit string. + p = buf + 1; + } + else + { + // Leave p pointing to start of digit string. + if (ndigits > 1 || (flags & FL_SPECIAL)) + { + // Decimal point is always after first digit. + // Shift digit and insert point. + buf[1] = buf[2]; + buf[2] = '.'; + p = buf + 1; + } + else + { + // No decimal point needed. + p = buf + 2; + } + // Trim trailing 0's in 'g' mode. + if ((fflags & FF_GCVT) && !(flags & FL_SPECIAL)) + { + pend = trim_zeros(pend - 1); + } + // Add exponent + *pend++ = (fflags & FF_UCASE) ? 'E' : 'e'; + if (--decpt < 0) + { + *pend++ = '-'; + decpt = -decpt; + } + else + { + *pend++ = '+'; + } +#ifndef EXP_3_DIGIT + // Optional 3rd digit of exponent + if (decpt > 99) +#endif + { + *pend++ = decpt / 100 + '0'; + decpt %= 100; + } + // Always print at least 2 digits of exponent. + *pend++ = decpt / 10 + '0'; + *pend++ = decpt % 10 + '0'; + } + // Add the sign prefix. + if (flags & FL_NEG) *--p = '-'; +#if FEATURE(USE_PLUS_SIGN) + else if (flags & FL_PLUS) *--p = '+'; +#endif +#if FEATURE(USE_SPACE_SIGN) + else if (flags & FL_SPACE) *--p = ' '; +#endif + + *pend = '\0'; // Add null terminator + +#if FEATURE(USE_ZERO_PAD) + if (flags & FL_ZERO_PAD) + { + /* Implement leading zero padding by shifting the formatted buffer. + * This is not the most efficient but at any other point it is hard + * to know what the exact buffer length will be. The processing time + * pales into insignificance next to the floating point operations. + */ + flt_width_t fwidth = pend - p; + if (width > BUFMAX) + width = BUFMAX; + if (fwidth < width) + { + // Set pointer to location after the new end point. + // It will be predecremented. + char *pnew = pend + width - fwidth + 1; + ++pend; + // Do not shift the sign/space if used. + if (flags & (FL_NEG | FL_PLUS | FL_SPACE)) + ++p; + // p now points to the last character to move. + // Shift the buffer rightwards + do *--pnew = *--pend; + while (pend != p); + // Fill the remainder with 0s. + do *--pnew = '0'; + while (pnew != p); + // Restore the former value of p. + if (flags & (FL_NEG | FL_PLUS | FL_SPACE)) + --p; + } + } +#endif + + return p; // Start of string +} +#endif // End of floating point section + +/***************************************************************************** +Integer, character and string +******************************************************************************/ + +#if FEATURE(USE_SPACE_PAD) +/* --------------------------------------------------------------------------- +Function: p_len() +Helper function to find length of string. +This offers a small space saving over strlen and allows for reading strings +from flash where the micro uses different semantics to access program memory. +This is used on the AVR processor. +--------------------------------------------------------------------------- */ +#if FEATURE(USE_FSTRING) +static width_t p_len(char *p, unsigned char flags) +#else +static width_t p_len(char *p) +#endif +{ + width_t len = 0; +#if FEATURE(USE_FSTRING) + if (flags & FL_FSTR) + { + while (GET_FORMAT(p)) + { + ++p; + ++len; + } + } + else +#endif + { + while (*p++) ++len; + } + return len; +} +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic push +// -Wimplicit-fallthrough option was added in GCC 7 +#if (__GNUC__ >= 7) +#pragma GCC diagnostic ignored "-Wimplicit-fallthrough" +#endif +#endif + +/* --------------------------------------------------------------------------- +Function: doprnt() +This is the main worker function which does all the formatting. +The output function must always be provided. +Unless BASIC_PRINTF is defined it also needs the context variable, +which tells the output function where to write. +--------------------------------------------------------------------------- */ +#ifdef BASIC_PRINTF_ONLY +static printf_t doprnt(void (*func)(char c), const char *fmt, va_list ap) +#else +static printf_t doprnt(void *context, void (*func)(char c, void *context), size_t n, const char *fmt, va_list ap) +#endif +{ +#if FEATURE(USE_LONG_LONG) + unsigned long long uvalue; +#elif FEATURE(USE_LONG) + unsigned long uvalue; +#else + unsigned uvalue; +#endif +#if !HEX_CONVERT_ONLY + unsigned base; +#endif +#if FEATURE(USE_SPACE_PAD) || FEATURE(USE_ZERO_PAD) || FEATURE(USE_FLOAT) + width_t width; + width_t fwidth; +#endif +#if FEATURE(USE_PRECISION) || FEATURE(USE_FLOAT) + width_t precision; +#else + #define precision -1 +#endif + char convert, c; + char *p; + char buffer[BUFMAX+1]; +#ifdef PRINTF_T + printf_t count = 0; +#endif + unsigned char flags; +#if FEATURE(USE_FLOAT) || FEATURE(USE_LONG_LONG) + unsigned char fflags; +#endif +#if FEATURE(USE_FLOAT) + double fvalue; +#endif + + buffer[BUFMAX] = '\0'; + + for (;;) + { + convert = GET_FORMAT(fmt); + if (convert == 0) break; + if (convert == '%') + { + p = buffer + BUFMAX; +#if FEATURE(USE_PRECISION) || FEATURE(USE_FLOAT) + precision = -1; +#endif +#if FEATURE(USE_SPACE_PAD) || FEATURE(USE_ZERO_PAD) || FEATURE(USE_FLOAT) + width = 0; +#endif +#if FEATURE(USE_FLOAT) || FEATURE(USE_LONG_LONG) + fflags = 0; +#endif + flags = 0; + + // Extract flag chars + for (;;) + { + convert = GET_FORMAT(++fmt); +#if FEATURE(USE_ZERO_PAD) + if (convert == '0') + { + flags |= FL_ZERO_PAD; + } + else +#endif +#if FEATURE(USE_PLUS_SIGN) + if (convert == '+') + { + flags |= FL_PLUS; + } + else +#endif +#if FEATURE(USE_LEFT_JUST) + if (convert == '-') + { + flags |= FL_LEFT_JUST; + } + else +#endif +#if FEATURE(USE_SPACE_SIGN) + if (convert == ' ') + { + flags |= FL_SPACE; + } + else +#endif +#if FEATURE(USE_SPECIAL) + if (convert == '#') + { + flags |= FL_SPECIAL; + } + else +#endif + break; + } +#if FEATURE(USE_SPACE_PAD) || FEATURE(USE_ZERO_PAD) + // Extract width + #if FEATURE(USE_INDIRECT) + if (convert == '*') + { + width = va_arg(ap, int); + convert = GET_FORMAT(++fmt); + } + else + #endif + // cppcheck-suppress knownConditionTrueFalse // False positive + while (convert >= '0' && convert <= '9') + { + width = width * 10 + convert - '0'; + convert = GET_FORMAT(++fmt); + } +#endif +#if FEATURE(USE_PRECISION) + // Extract precision + if (convert == '.') + { + precision = 0; + convert = GET_FORMAT(++fmt); + #if FEATURE(USE_INDIRECT) + if (convert == '*') + { + precision = va_arg(ap, int); + convert = GET_FORMAT(++fmt); + } + else + #endif + while (convert >= '0' && convert <= '9') + { + precision = precision * 10 + convert - '0'; + convert = GET_FORMAT(++fmt); + } + } +#endif +#if FEATURE(USE_LONG) + // Extract length modifier + if (convert == 'l') + { + convert = GET_FORMAT(++fmt); + #if FEATURE(USE_LONG_LONG) + if (convert == 'l') + { + fflags |= FF_XLONG; + convert = GET_FORMAT(++fmt); + } + else + #endif + flags |= FL_LONG; + } +#endif + switch (convert) + { +#if FEATURE(USE_CHAR) + case 'c': + #if FEATURE(USE_SPACE_PAD) + width = 0; + #endif + *--p = (char) va_arg(ap, int); + break; +#endif +#if FEATURE(USE_SIGNED) + case 'd': +#endif +#if FEATURE(USE_SIGNED_I) + case 'i': +#endif +#if FEATURE(USE_SIGNED) || FEATURE(USE_SIGNED_I) + flags |= FL_NEG; // Flag possible negative value, to be determined later + base = 10; + goto number; +#endif +#if FEATURE(USE_UNSIGNED) + case 'u': + base = 10; + goto number; +#endif +#if FEATURE(USE_OCTAL) + case 'o': + base = 8; + goto number; +#endif +#if FEATURE(USE_BINARY) + case 'b': + base = 2; + goto number; +#endif +#if FEATURE(USE_HEX_LOWER) + case 'x': +#endif +#if FEATURE(USE_HEX_UPPER) + case 'X': +#endif +#if FEATURE(USE_HEX_LOWER) || FEATURE(USE_HEX_UPPER) + #if !HEX_CONVERT_ONLY + base = 16; + #endif +#endif +#if !HEX_CONVERT_ONLY + number: +#endif + /* Using separate va_arg() calls for signed and unsigned types is expensive. + Instead, values are read as unsigned, regardless of signed/unsigned type. + Signed values then need to be sign-extended + and this is fixed after the check for negative numbers. + */ +#if FEATURE(USE_LONG) + #if FEATURE(USE_LONG_LONG) + if (fflags & FF_XLONG) + uvalue = va_arg(ap, unsigned long long); + else + #endif + if (flags & FL_LONG) + uvalue = va_arg(ap, unsigned long); + else +#endif + uvalue = va_arg(ap, unsigned int); +#if FEATURE(USE_SIGNED) || FEATURE(USE_SIGNED_I) + // FL_NEG was used temporarily to indicate signed type + if (flags & FL_NEG) + { + // Values may need to be sign extended if not the widest type. + #if FEATURE(USE_LONG) + #if FEATURE(USE_LONG_LONG) + if (!(fflags & FF_XLONG)) + { + if (!(flags & FL_LONG)) + uvalue = (int) uvalue; + else + uvalue = (long) uvalue; + } + #else + if (!(flags & FL_LONG)) + uvalue = (int) uvalue; + #endif + #endif + // Check whether this is a negative value + #if FEATURE(USE_LONG) + #if FEATURE(USE_LONG_LONG) + if ((long long) uvalue < 0) + #else + if ((long) uvalue < 0) + #endif + #else + if ((int) uvalue < 0) + #endif + { + uvalue = -uvalue; // Yes, it's negative + } + else + { + flags &= ~FL_NEG; // No, it's positive + } + } +#endif +#if FEATURE(USE_PRECISION) + // Set default precision + if (precision == -1) precision = 1; +#endif + // Make sure options are valid. +#if HEX_CONVERT_ONLY + #if FEATURE(USE_PLUS_SIGN) || FEATURE(USE_SPACE_SIGN) + flags &= ~(FL_PLUS|FL_SPACE); + #endif +#else + if (base != 10) flags &= ~(FL_PLUS|FL_NEG|FL_SPACE); + #if FEATURE(USE_SPECIAL) + else flags &= ~FL_SPECIAL; + #endif +#endif + // Generate the number without any prefix yet. +#if FEATURE(USE_ZERO_PAD) + fwidth = width; + // Avoid formatting buffer overflow. + if (fwidth > BUFMAX) fwidth = BUFMAX; +#endif +#if FEATURE(USE_LONG_LONG) && FEATURE(USE_BINARY) + // 64-bit binary output is impractical for reading and requires a huge buffer. + // Restrict to 32 bits in binary mode. + if ((base == 2) && (fflags & FF_XLONG)) + { + uvalue = (unsigned long) uvalue; + } +#endif +#if FEATURE(USE_PRECISION) + while (uvalue || precision > 0) +#else + if (uvalue == 0) + { + // Avoid printing 0 as ' ' + *--p = '0'; + #if FEATURE(USE_ZERO_PAD) + --fwidth; + #endif + } + while (uvalue) +#endif + { +#if HEX_CONVERT_ONLY + c = (char) ((uvalue & 0x0f) + '0'); +#else + c = (char) ((uvalue % base) + '0'); +#endif +#if FEATURE(USE_HEX_LOWER) || FEATURE(USE_HEX_UPPER) + if (c > '9') + { + // Hex digits + #if FEATURE(USE_HEX_LOWER) && FEATURE(USE_HEX_UPPER) + if (convert == 'X') c += 'A' - '0' - 10; + else c += 'a' - '0' - 10; + #elif FEATURE(USE_HEX_UPPER) || FEATURE(USE_HEX_UPPER_L) + c += 'A' - '0' - 10; + #else + c += 'a' - '0' - 10; + #endif + } +#endif + *--p = c; +#if HEX_CONVERT_ONLY + uvalue >>= 4; +#else + uvalue /= base; +#endif +#if FEATURE(USE_ZERO_PAD) + --fwidth; +#endif +#if FEATURE(USE_PRECISION) + --precision; +#endif + } +#if FEATURE(USE_ZERO_PAD) + // Allocate space for the sign bit. + if (flags & (FL_PLUS|FL_NEG|FL_SPACE)) --fwidth; + #if FEATURE(USE_SPECIAL) + // Allocate space for special chars if required. + if (flags & FL_SPECIAL) + { + if (convert == 'o') fwidth -= 1; + else fwidth -= 2; + } + #endif + // Add leading zero padding if required. + if ((flags & FL_ZERO_PAD) && !(flags & FL_LEFT_JUST)) + { + while (fwidth > 0) + { + *--p = '0'; + --fwidth; + } + } +#endif +#if FEATURE(USE_SPECIAL) + // Add special prefix if required. + if (flags & FL_SPECIAL) + { + if (convert != 'o') *--p = convert; + *--p = '0'; + } +#endif + // Add the sign prefix + if (flags & FL_NEG) *--p = '-'; +#if FEATURE(USE_PLUS_SIGN) + else if (flags & FL_PLUS) *--p = '+'; +#endif +#if FEATURE(USE_SPACE_SIGN) + else if (flags & FL_SPACE) *--p = ' '; +#endif +#if FEATURE(USE_PRECISION) + // Precision is not used to limit number output. + precision = -1; +#endif + break; +#if FEATURE(USE_FLOAT) + case 'f': + fflags = FF_FCVT; + goto fp_number; + case 'E': + fflags = FF_UCASE; + case 'e': + fflags |= FF_ECVT; + goto fp_number; + case 'G': + fflags = FF_UCASE; + case 'g': + fflags |= FF_GCVT; + fp_number: + // Set default precision + if (precision == -1) precision = 6; + // Need one extra digit precision in E mode + if (fflags & FF_ECVT) ++precision; + fvalue = va_arg(ap, double); + p = format_float(fvalue, precision, width, flags, fflags, buffer); + // Precision is not used to limit number output. + precision = -1; + break; +#endif +#if FEATURE(USE_STRING) + #if FEATURE(USE_FSTRING) + case 'S': + flags |= FL_FSTR; + // fall through + #endif + case 's': + p = va_arg(ap, char *); + break; +#endif + default: + *--p = convert; + break; + } + +#if FEATURE(USE_SPACE_PAD) + // Check width of formatted text. + #if FEATURE(USE_FSTRING) + fwidth = p_len(p, flags); + #else + fwidth = p_len(p); + #endif + // Copy formatted text with leading or trailing space. + // A positive value for precision will limit the length of p used. + for (;;) + { + #if FEATURE(USE_FSTRING) + if (flags & FL_FSTR) + c = GET_FORMAT(p); + else + #endif + c = *p; + if ((c == '\0' || precision == 0) && width <= 0) break; + if (((flags & FL_LEFT_JUST) || width <= fwidth) && c && precision != 0) ++p; + else c = ' '; + // for loop continues after #endif +#else + for (;;) + { + #if FEATURE(USE_FSTRING) + if (flags & FL_FSTR) + c = GET_FORMAT(p); + else + #endif + c = *p; + #if FEATURE(USE_PRECISION) + // A positive value for precision will limit the length of p used. + if (c == '\0' || precision == 0) break; + #else + if (c == '\0') break; + #endif + ++p; + // for loop continues after #endif +#endif + // for loop continues here from either of the USE_SPACE_PAD cases. +#ifdef BASIC_PRINTF_ONLY + func(c); // Basic output function. +#else + func(c, context); // Output function. +#endif +#ifdef PRINTF_T + ++count; + +// [NF_CHANGE] + if (count >= n) + { + break; + } +// [END NF_CHANGE] + +#endif +#if FEATURE(USE_SPACE_PAD) + --width; +#endif +#if FEATURE(USE_PRECISION) + if (precision > 0) --precision; +#endif + } + } + else + { +#ifdef BASIC_PRINTF_ONLY + func(convert); // Basic output function. +#else + func(convert, context); // Output function. +#endif +#ifdef PRINTF_T + ++count; +#endif + } + ++fmt; + } + +#ifdef PRINTF_T + return count; +#endif +} + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +/* --------------------------------------------------------------------------- +Function: putout() +This is the output function used for printf. +The context is normally required, but is not used at present. It could be +extended to include streams or to avoid output mixing in multi-threaded use. +If using BASIC_PRINTF, context is not supported. +--------------------------------------------------------------------------- */ +#ifdef BASIC_PRINTF_ONLY +static void putout(char c) +{ +#else +static void putout(char c, void *context) +{ + (void) context; // Suppress compiler warning about unused argument. +#endif + PUTCHAR_FUNC(c); +} + +/* --------------------------------------------------------------------------- +Function: printf() +Replacement for library printf - writes to output (normally serial) +It uses the output function putout() to update the serial output. +If PRINTF_T is defined then the number of characters generated is returned. +--------------------------------------------------------------------------- */ +printf_t printf_(const char *fmt, ...) +{ + va_list ap; +#ifdef PRINTF_T + int Count; +#endif + + va_start(ap, fmt); +#ifdef PRINTF_T + #ifdef BASIC_PRINTF_ONLY + Count = doprnt(putout, fmt, ap); + #else + Count = doprnt((void *)0, putout, BUFMAX, fmt, ap); + #endif +#else + #ifdef BASIC_PRINTF_ONLY + doprnt(putout, fmt, ap); + #else + doprnt((void *)0, putout, fmt, ap); + #endif +#endif + va_end(ap); + +#ifdef PRINTF_T + return Count; +#endif +} + +#ifndef BASIC_PRINTF_ONLY +/* --------------------------------------------------------------------------- +Function: putbuf() +This is the output function used for sprintf. +Here the context is a pointer to a pointer to the buffer. +Double indirection allows the function to increment the buffer pointer. +--------------------------------------------------------------------------- */ +static void putbuf(char c, void *context) +{ + char *buf = *((char **) context); + *buf++ = c; + *((char **) context) = buf; +} + +/* --------------------------------------------------------------------------- +Function: sprintf() +Replacement for library sprintf - writes into buffer supplied. +Normally it uses the output function putout() to update the buffer. +sprintf is not supported when using BASIC_PRINTF +If PRINTF_T is defined then the number of characters generated is returned. +--------------------------------------------------------------------------- */ +printf_t sprintf_(char *buf, const char *fmt, ... ) +{ + va_list ap; +#ifdef PRINTF_T + int Count; +#endif + + va_start(ap, fmt); +#ifdef PRINTF_T + Count = doprnt(&buf, putbuf, BUFMAX, fmt, ap); +#else + doprnt(&buf, putbuf, fmt, ap); +#endif + va_end(ap); + // Append null terminator. + *buf = '\0'; + +#ifdef PRINTF_T + return Count; +#endif +} +#endif + +// [NF_CHANGE] +printf_t snprintf_(char *buf, size_t n,const char *fmt, ... ) +{ + va_list ap; + int count; + + va_start(ap, fmt); + count = doprnt(&buf, putbuf, n - 1, fmt, ap); + va_end(ap); + + // Append null terminator if there's space. + if (n > 0) + { + *buf = '\0'; + } + + return count; +} +// [END_NF_CHANGE] + +// [NF_CHANGE] +void putchar_(char character) +{ + (void)character; +} +// [END_NF_CHANGE] + +// [NF_CHANGE] +printf_t vsnprintf_(char *buffer, size_t bufsz, char const *format, va_list vlist) +{ + size_t count; + va_list vlistCopy; + + // Create a copy of the variable argument list + va_copy(vlistCopy, vlist); + + // Perform the actual formatting operation + count = doprnt(&buffer, putbuf, bufsz, format, vlistCopy); + + // append null terminator + if(count < bufsz) + { + buffer[count] = '\0'; + } + else + { + buffer[bufsz - 1] = '\0'; + } + + // Clean up the copied variable argument list + va_end(vlistCopy); + + return count; +} +// [END_NF_CHANGE] + +// clang-format on diff --git a/src/CLR/Helpers/nanoprintf/nanoprintf.h b/src/CLR/Helpers/nanoprintf/nanoprintf.h new file mode 100644 index 0000000000..8d0571d690 --- /dev/null +++ b/src/CLR/Helpers/nanoprintf/nanoprintf.h @@ -0,0 +1,317 @@ +// +// Copyright (c) .NET Foundation and Contributors +// Portions Copyright (c) 2006 - 2021 Skirrid Systems. All rights reserved. +// See LICENSE file in the project root for full license information. +// + +#ifndef NANOPRINTF_H +#define NANOPRINTF_H + +#include + +// clang-format off + +/************************************************************************* +Number of chars output + +Traditionally printf returns the number of chars output. If you are not +interested in that value you can leave PRINTF_T undefined. +On a small micro you can define the return type as unsigned char if you +are sure the total output width will never exceed 255, or unsigned short. +*************************************************************************/ + +#define PRINTF_T size_t + +/************************************************************************* +Memory access definitions + +Some micros such as the AVR can only support storing strings in flash +memory by wrapping the string in a macro. To make this transparent we can +define the printf function itself as a macro which performs the wrap and +calls a renamed version of printf with an _ suffix and no i. +*************************************************************************/ + +/* +Example for AVR micros using GCC toolchain from WinAVR or Atmel Studio + +#define sprintf(buf, format, args...) _sprntf(buf, PSTR(format), ## args) +#define printf(format, args...) _prntf(PSTR(format), ## args) + +extern printf_t _sprntf(char *, const char *, ...); +extern printf_t _prntf(const char *, ...); +*/ + +/************************************************************************* +End of customisations - Stop Editing! + +The remainder of this file contains the function declarations. +*************************************************************************/ + +// Create a type definition for the return value +#ifndef PRINTF_T +typedef void printf_t; +#else +typedef PRINTF_T printf_t; +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +// Function declarations, unless macros have been defined above +printf_t printf_(const char *, ...); +printf_t sprintf_(char *, const char *, ...); +printf_t snprintf_(char *, size_t n, const char *, ...); +printf_t vsnprintf_(char *, size_t n, const char *, va_list); + +#ifdef __cplusplus +} +#endif + + // from config + +/************************************************************************* +Basic printf only + +The code is designed to support a variety of printf-related functions. +If simple serial output is all you want then you can save some space by +defining BASIC_PRINTF_ONLY which allows the internal API to be simplified. +Note that sprintf will not be supported in this case. +*************************************************************************/ + +// #define BASIC_PRINTF_ONLY + +/************************************************************************* +Memory access definitions + +Some micros such as the AVR can only support storing and accessing strings +in flash memory using special macros and functions. This section can be +used to specify those methods. You may also need to modify printf.h +to get the compiler to place the format strings in flash memory. + +The GET_FORMAT(ptr) macro is used to access a character in the printf +format string. By default this does a normal memory pointer access, but +you can configure it to access flash memory if needed. +*************************************************************************/ + +/* +Example for AVR micros using GCC toolchain from WinAVR or Atmel Studio + +#include +#include +#define GET_FORMAT(p) pgm_read_byte(p) +*/ + +/************************************************************************* +Output configuration + +By default printf will use the putchar function. If this is not defined +in your system you can set your own function here by defining +PUTCHAR_FUNC to be the name of that function. +*************************************************************************/ + +void putchar_(char character); +#define PUTCHAR_FUNC putchar_ + +/************************************************************************* +Compiler capability configuration + +Set some options that the C pre-processor will not tell us about. +*************************************************************************/ + +// Does the compiler support double precision or silently degrade to single? +//#define NO_DOUBLE_PRECISION + +// Does the compiler support isnan and isinf floating point functions? +// #define NO_ISNAN_ISINF + +/************************************************************************* +Formatted item width + +Since it is extremely unlikely that you will ever want to use a formatted +width for a single item of more than 127 chars (i.e. the expanded and +padded size of a single % expression), the width variables can normally +be restricted to 8 bits. On small micros this saves a lot of code and +variable space. On a 32-bit RISC it may increase code size due to type +conversions. Choose the variable type to suit your CPU. +Note that a signed type is required. +*************************************************************************/ + +typedef signed char width_t; + +/************************************************************************* +Feature configuration + +This section defines the individual feature flags. +These are combined as needed to produce the FEATURE_FLAGS macro. +*************************************************************************/ + +// Include floating point number support +#define USE_FLOAT (1 << 0) + +// Include support for long integers +#define USE_LONG (1 << 1) + +// Include support for octal formatting +#define USE_OCTAL (1 << 2) + +// Include support for %d decimal formatting +#define USE_SIGNED (1 << 3) + +// Include support for the %i synonym for %d +#define USE_SIGNED_I (1 << 4) + +// Include support for the %u unsigned decimal specifier +#define USE_UNSIGNED (1 << 5) + +// Include support for the %x hex specifier (lowercase output) +#define USE_HEX_LOWER (1 << 6) + +// Include support for the %X hex specifier (uppercase output) +#define USE_HEX_UPPER (1 << 7) + +// Force uppercase output with %x. +// Used in conjunction with USE_HEX_LOWER. +// Ignored if USE_HEX_UPPER is also set. +#define USE_HEX_UPPER_L (1 << 8) + +// Include support for %c single character +#define USE_CHAR (1 << 9) + +// Include support for %s string +#define USE_STRING (1 << 10) + +// Include support for %S string in flash memory +// Only needed for architectures which cannot access program memory using normal pointers. +// If you have not defined the GET_FORMAT() macro above then you don't need this option. +#define USE_FSTRING (1 << 11) + +// Include support for %b binary specifier +#define USE_BINARY (1 << 12) + +// Include precision support when floating point is not present. +// Precision is automatically enabled when floating point support is used. +#define USE_PRECISION (1UL << 16) + +// Allow use of leading zero padding e.g. "%03d" +#define USE_ZERO_PAD (1UL << 17) + +// Allow use of space padding e.g. "%3d" or "%12s" +#define USE_SPACE_PAD (1UL << 18) + +// Include indirect width/precision support e.g. "%*d" +#define USE_INDIRECT (1UL << 19) + +// Allow forcing a leading plus sign e.g. "%+3d" +#define USE_PLUS_SIGN (1UL << 20) + +// Allow forcing a leading space (instead of + or -) in front of zero e.g. "% 3d" +#define USE_SPACE_SIGN (1UL << 21) + +// Include support for the left-justify '-' flag. +#define USE_LEFT_JUST (1UL << 22) + +// Include support for the special '#' flag. +#define USE_SPECIAL (1UL << 23) + +// Use smaller but less efficient floating point normalisation. +// This is not recommended unless code space is critically low. +#define USE_SMALL_FLOAT (1UL << 24) + +// Include support for 64-bit integers e.g. "%lld" +#define USE_LONG_LONG (1UL << 25) + +/************************************************************************* +Pre-defined feature sets + +This section provides some commonly used combinations of features. +*************************************************************************/ + +// Lowercase hex integers only. This really is the bare minimum. +#define HEX_INT (USE_HEX_LOWER) + +// Decimal and lowercase hex only. +#define MINIMAL_INT (USE_SIGNED | USE_HEX_LOWER) + +// Signed and unsigned decimal, lower case hex, zero & space padding, plus char and string +#define BASIC_INT (USE_CHAR | USE_STRING | USE_SIGNED | USE_UNSIGNED | USE_HEX_LOWER | USE_ZERO_PAD | USE_SPACE_PAD) + +// All short integer features except octal, binary, %i, indirection and specials. +#define SHORT_INT \ + (USE_CHAR | USE_STRING | USE_SIGNED | USE_UNSIGNED | USE_HEX_LOWER | USE_HEX_UPPER | USE_PRECISION | \ + USE_ZERO_PAD | USE_SPACE_PAD | USE_PLUS_SIGN | USE_SPACE_SIGN | USE_LEFT_JUST) + +// As above, but also supports long integers. +#define LONG_INT (USE_LONG | SHORT_INT) + +// As above, but also supports long-long integers. +#define LONG_LONG_INT (USE_LONG_LONG | LONG_INT) + +// All possible integer features. +#define FULL_INT (USE_BINARY | USE_OCTAL | USE_SIGNED_I | USE_INDIRECT | USE_SPECIAL | LONG_LONG_INT) + +// All available features including floating point. +#define FULL_FLOAT (USE_FLOAT | FULL_INT) + +/************************************************************************* +Features included in your build of printf. Use only the features you need +to keep code size and execution time to a minimum. + +You can use the custom set, with anything you don't want commented out, +or you can use one of the pre-defined sets. + +Examples: + +#define FEATURE_FLAGS CUSTOM_SET +#define FEATURE_FLAGS SHORT_INT + +Features and pre-defined sets are set out in the following sections. +*************************************************************************/ + +// Custom feature set. Comment out features you don't want. +#define CUSTOM_SET \ + (0 | USE_FLOAT | USE_LONG | USE_BINARY | USE_OCTAL | USE_SIGNED | USE_SIGNED_I | USE_UNSIGNED | USE_HEX_LOWER | \ + USE_HEX_UPPER | USE_HEX_UPPER_L | USE_CHAR | USE_STRING | USE_FSTRING | USE_PRECISION | USE_ZERO_PAD | \ + USE_SPACE_PAD | USE_INDIRECT | USE_PLUS_SIGN | USE_SPACE_SIGN | USE_LEFT_JUST | USE_SPECIAL | USE_SMALL_FLOAT | \ + USE_LONG_LONG) + +#define NANOFRAMEWORK_SET ( 0 \ + | USE_FLOAT \ + | USE_LONG \ + | USE_BINARY \ + | USE_OCTAL \ + | USE_SIGNED \ + | USE_SIGNED_I \ + | USE_UNSIGNED \ + | USE_HEX_LOWER \ + | USE_HEX_UPPER \ + | USE_HEX_UPPER_L \ + | USE_CHAR \ + | USE_STRING \ + | USE_FSTRING \ + | USE_PRECISION \ + | USE_ZERO_PAD \ + | USE_SPACE_PAD \ + | USE_INDIRECT \ + | USE_PLUS_SIGN \ + | USE_SPACE_SIGN \ + | USE_LEFT_JUST \ + | USE_SPECIAL \ + | USE_LONG_LONG \ +) + +#define FEATURE_FLAGS NANOFRAMEWORK_SET + +/************************************************************************* +End of customisations - Stop Editing! +*************************************************************************/ + +#define printf printf_ +#define sprintf sprintf_ +#define snprintf snprintf_ +#define vsnprintf vsnprintf_ + +#endif // NANOPRINTF_H + +// clang-format on diff --git a/targets/AzureRTOS/_common/nanoCLR/nanoCRT.cpp b/targets/AzureRTOS/_common/nanoCLR/nanoCRT.cpp index 7a64e26ccf..dad201afa3 100644 --- a/targets/AzureRTOS/_common/nanoCLR/nanoCRT.cpp +++ b/targets/AzureRTOS/_common/nanoCLR/nanoCRT.cpp @@ -6,6 +6,7 @@ #include #include +#include //--// /* STDIO stubs */ diff --git a/targets/ChibiOS/_nanoCLR/nanoCRT.cpp b/targets/ChibiOS/_nanoCLR/nanoCRT.cpp index ec2ab722f4..4297bd9a2a 100644 --- a/targets/ChibiOS/_nanoCLR/nanoCRT.cpp +++ b/targets/ChibiOS/_nanoCLR/nanoCRT.cpp @@ -5,6 +5,7 @@ // #include +#include //--// /* STDIO stubs */ diff --git a/targets/ESP32/_Network/NF_ESP32_OpenThread.cpp b/targets/ESP32/_Network/NF_ESP32_OpenThread.cpp index 7193c5760d..6d520a55e9 100644 --- a/targets/ESP32/_Network/NF_ESP32_OpenThread.cpp +++ b/targets/ESP32/_Network/NF_ESP32_OpenThread.cpp @@ -10,6 +10,7 @@ #include #include +#include #include "esp_openthread.h" #include "esp_openthread_cli.h" diff --git a/targets/ESP32/_nanoCLR/nanoCRT.cpp b/targets/ESP32/_nanoCLR/nanoCRT.cpp index 3bdc81db78..0968b35c6c 100644 --- a/targets/ESP32/_nanoCLR/nanoCRT.cpp +++ b/targets/ESP32/_nanoCLR/nanoCRT.cpp @@ -6,6 +6,7 @@ #include #include +#include //--// /* STDIO stubs */ diff --git a/targets/FreeRTOS/_common/nanoCLR/nanoCRT.cpp b/targets/FreeRTOS/_common/nanoCLR/nanoCRT.cpp index ec2ab722f4..4297bd9a2a 100644 --- a/targets/FreeRTOS/_common/nanoCLR/nanoCRT.cpp +++ b/targets/FreeRTOS/_common/nanoCLR/nanoCRT.cpp @@ -5,6 +5,7 @@ // #include +#include //--// /* STDIO stubs */ diff --git a/targets/TI_SimpleLink/_nanoCLR/nanoCRT.cpp b/targets/TI_SimpleLink/_nanoCLR/nanoCRT.cpp index ec2ab722f4..4297bd9a2a 100644 --- a/targets/TI_SimpleLink/_nanoCLR/nanoCRT.cpp +++ b/targets/TI_SimpleLink/_nanoCLR/nanoCRT.cpp @@ -5,6 +5,7 @@ // #include +#include //--// /* STDIO stubs */