From e05149b7450c17785abd0b8c0ebef69622efcea9 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 6 Feb 2019 15:50:52 -0600 Subject: [PATCH] libs/libc/stdio: Add newlib-nano fprintf as an option. This fprintf code was originally adapted from AVR libc and provides improved floating point output support, including 'g' mode and making 'f' mode include digits past the decimal by default. However, this version does not have any long long support. On a demonstration cortex M4 build, this version saves about 5kB of ROM. The newlib version can be found here: https://salsa.debian.org/electronics-team/toolchains/newlib-nano . That project is GPL overall, but the newlib-nano printf portions are BSD licensed as described in the COPYING.NEWLIB file. --- libs/libc/stdio/Kconfig | 22 + libs/libc/stdio/Make.defs | 18 +- libs/libc/stdio/nano_dtoa_data.c | 276 +++++++ libs/libc/stdio/nano_dtoa_engine.c | 169 +++++ libs/libc/stdio/nano_dtoa_engine.h | 189 +++++ libs/libc/stdio/nano_libvsprintf.c | 1037 +++++++++++++++++++++++++++ libs/libc/stdio/nano_ultoa_invert.c | 79 ++ libs/libc/stdio/nano_ultoa_invert.h | 62 ++ 8 files changed, 1851 insertions(+), 1 deletion(-) create mode 100644 libs/libc/stdio/nano_dtoa_data.c create mode 100644 libs/libc/stdio/nano_dtoa_engine.c create mode 100644 libs/libc/stdio/nano_dtoa_engine.h create mode 100644 libs/libc/stdio/nano_libvsprintf.c create mode 100644 libs/libc/stdio/nano_ultoa_invert.c create mode 100644 libs/libc/stdio/nano_ultoa_invert.h diff --git a/libs/libc/stdio/Kconfig b/libs/libc/stdio/Kconfig index ae5ab4efb6..893a8c0317 100644 --- a/libs/libc/stdio/Kconfig +++ b/libs/libc/stdio/Kconfig @@ -108,4 +108,26 @@ config MEMCPY_VIK Select this option for improved performance at the expense of increased size. See licensing information in the top-level COPYING file. +config NANO_PRINTF + bool "Use nano printf code" + default n + ---help--- + Replace printf code with version from newlib-nano. This version + provides improved floating point output support, including 'g' mode + as well as making the default 'f' format include digits past the + decimal point. However, it does not include 'long long' support. + +config NANO_PRINTLEVEL + int "Nano printf support level" + default 2 + range 1 2 if !LIBC_FLOATINGPOINT + range 1 3 if LIBC_FLOATINGPOINT + depends on NANO_PRINTF + ---help--- + Nano printf can be built into more than one support level. The + default is to offer the usual modifiers and integer formatting + support (level 2). Level 1 maintains a minimal version that just + offers integer formatting, but no modifier support whatsoever. + Level 3 is intented for floating point support. + endmenu #Standard C I/O diff --git a/libs/libc/stdio/Make.defs b/libs/libc/stdio/Make.defs index 6db6433cb6..c7804402d7 100644 --- a/libs/libc/stdio/Make.defs +++ b/libs/libc/stdio/Make.defs @@ -39,12 +39,28 @@ CSRCS += lib_fileno.c lib_printf.c lib_sprintf.c lib_asprintf.c CSRCS += lib_snprintf.c lib_libsprintf.c lib_vsprintf.c lib_vasprintf.c -CSRCS += lib_vsnprintf.c lib_libvsprintf.c lib_dprintf.c lib_vdprintf.c +CSRCS += lib_vsnprintf.c lib_dprintf.c lib_vdprintf.c CSRCS += lib_meminstream.c lib_memoutstream.c lib_memsistream.c CSRCS += lib_memsostream.c lib_lowoutstream.c CSRCS += lib_zeroinstream.c lib_nullinstream.c lib_nulloutstream.c CSRCS += lib_sscanf.c lib_libnoflush.c lib_libsnoflush.c +ifeq ($(CONFIG_NANO_PRINTF),y) + +CSRCS += nano_libvsprintf.c nano_ultoa_invert.c + +ifeq ($(CONFIG_NANO_PRINTLEVEL),3) + +CSRCS += nano_dtoa_engine.c nano_dtoa_data.c + +endif + +else + +CSRCS += lib_libvsprintf.c + +endif + # The remaining sources files depend upon file descriptors ifneq ($(CONFIG_NFILE_DESCRIPTORS),0) diff --git a/libs/libc/stdio/nano_dtoa_data.c b/libs/libc/stdio/nano_dtoa_data.c new file mode 100644 index 0000000000..cb8d5ec3a9 --- /dev/null +++ b/libs/libc/stdio/nano_dtoa_data.c @@ -0,0 +1,276 @@ +/**************************************************************************** + * libs/libc/stdio/nano_dtoa_data.c + * + * Copyright © 2018, Keith Packard + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "nano_dtoa_engine.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifndef DBL_MAX_10_EXP +# error DBL_MAX_10_EXP +#endif + +#ifndef DBL_MIN_10_EXP +# error DBL_MIN_10_EXP +#endif + +#ifndef DBL_DIG +# error DBL_DIG +#endif + +/* Make sure the computed sizes of the arrays match the actual sizes + * by declaring an array which is legal if the sizes match and illegal + * if they do not + */ + +#define count_of(n) (sizeof (n) / sizeof (n[0])) +#define match(array,size) (count_of(array) == size) +#define check_match(array,size) (match(array, size) ? 1 : -1) + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +const double __dtoa_scale_up[] = +{ +#if DBL_MAX_10_EXP >= 1 + 1e1, +#endif +#if DBL_MAX_10_EXP >= 2 + 1e2, +#endif +#if DBL_MAX_10_EXP >= 4 + 1e4, +#endif +#if DBL_MAX_10_EXP >= 8 + 1e8, +#endif +#if DBL_MAX_10_EXP >= 16 + 1e16, +#endif +#if DBL_MAX_10_EXP >= 32 + 1e32, +#endif +#if DBL_MAX_10_EXP >= 64 + 1e64, +#endif +#if DBL_MAX_10_EXP >= 128 + 1e128, +#endif +#if DBL_MAX_10_EXP >= 256 + 1e256, +#endif +#if DBL_MAX_10_EXP >= 512 + 1e512, +#endif +#if DBL_MAX_10_EXP >= 1024 + 1e1024, +#endif +#if DBL_MAX_10_EXP >= 2048 + 1e2048, +#endif +#if DBL_MAX_10_EXP >= 4096 + 1e4096, +#endif +#if DBL_MAX_10_EXP >= 8192 + 1e8192, +#endif +#if DBL_MAX_10_EXP >= 16384 + 1e16384, +#endif +#if DBL_MAX_10_EXP >= 32768 + 1e32768, +#endif +#if DBL_MAX_10_EXP >= 65536 + 1e65536, +#endif +}; + +const double __dtoa_scale_down[] = +{ +#if DBL_MIN_10_EXP <= -1 + 1e-1, +#endif +#if DBL_MIN_10_EXP <= -2 + 1e-2, +#endif +#if DBL_MIN_10_EXP <= -4 + 1e-4, +#endif +#if DBL_MIN_10_EXP <= -8 + 1e-8, +#endif +#if DBL_MIN_10_EXP <= -16 + 1e-16, +#endif +#if DBL_MIN_10_EXP <= -32 + 1e-32, +#endif +#if DBL_MIN_10_EXP <= -64 + 1e-64, +#endif +#if DBL_MIN_10_EXP <= -128 + 1e-128, +#endif +#if DBL_MIN_10_EXP <= -256 + 1e-256, +#endif +#if DBL_MIN_10_EXP <= -512 + 1e-512, +#endif +#if DBL_MIN_10_EXP <= -1024 + 1e-1024, +#endif +#if DBL_MIN_10_EXP <= -2048 + 1e-2048, +#endif +#if DBL_MIN_10_EXP <= -4096 + 1e-4096, +#endif +#if DBL_MIN_10_EXP <= -8192 + 1e-8192, +#endif +#if DBL_MIN_10_EXP <= -16384 + 1e-16384, +#endif +#if DBL_MIN_10_EXP <= -32768 + 1e-32768, +#endif +#if DBL_MIN_10_EXP <= -65536 + 1e-65536, +#endif +}; + +const double __dtoa_round[] = +{ +#if DBL_DIG >= 30 + 5e30, +#endif +#if DBL_DIG >= 29 + 5e29, +#endif +#if DBL_DIG >= 28 + 5e28, +#endif +#if DBL_DIG >= 27 + 5e27, +#endif +#if DBL_DIG >= 26 + 5e26, +#endif +#if DBL_DIG >= 25 + 5e25, +#endif +#if DBL_DIG >= 24 + 5e24, +#endif +#if DBL_DIG >= 23 + 5e23, +#endif +#if DBL_DIG >= 22 + 5e22, +#endif +#if DBL_DIG >= 21 + 5e21, +#endif +#if DBL_DIG >= 20 + 5e20, +#endif +#if DBL_DIG >= 19 + 5e19, +#endif +#if DBL_DIG >= 18 + 5e18, +#endif +#if DBL_DIG >= 17 + 5e17, +#endif +#if DBL_DIG >= 16 + 5e16, +#endif +#if DBL_DIG >= 15 + 5e15, +#endif +#if DBL_DIG >= 14 + 5e14, +#endif +#if DBL_DIG >= 13 + 5e13, +#endif +#if DBL_DIG >= 12 + 5e12, +#endif +#if DBL_DIG >= 11 + 5e11, +#endif +#if DBL_DIG >= 10 + 5e10, +#endif +#if DBL_DIG >= 9 + 5e9, +#endif +#if DBL_DIG >= 8 + 5e8, +#endif +#if DBL_DIG >= 7 + 5e7, +#endif +#if DBL_DIG >= 6 + 5e6, +#endif +#if DBL_DIG >= 5 + 5e5, +#endif +#if DBL_DIG >= 4 + 5e4, +#endif +#if DBL_DIG >= 3 + 5e3, +#endif +#if DBL_DIG >= 2 + 5e2, +#endif +#if DBL_DIG >= 1 + 5e1, +#endif +#if DBL_DIG >= 0 + 5e0, +#endif +}; diff --git a/libs/libc/stdio/nano_dtoa_engine.c b/libs/libc/stdio/nano_dtoa_engine.c new file mode 100644 index 0000000000..dc4d3578d5 --- /dev/null +++ b/libs/libc/stdio/nano_dtoa_engine.c @@ -0,0 +1,169 @@ +/**************************************************************************** + * libs/libc/stdio/nanoo_dtoa_engine.c + * + * Copyright © 2018, Keith Packard + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "nano_dtoa_engine.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* A bit of CPP trickery -- construct the floating-point value 10 ** DBL_DIG + * by pasting the value of DBL_DIG onto '1e' to + */ + +#define PASTE(a) 1e##a +#define SUBSTITUTE(a) PASTE(a) +#define MIN_MANT (SUBSTITUTE(DBL_DIG)) +#define MAX_MANT (10.0 * MIN_MANT) +#define MIN_MANT_INT ((uint64_t)MIN_MANT) +#define MIN_MANT_EXP DBL_DIG + +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int __dtoa_engine(double x, FAR struct dtoa *dtoa, int max_digits, + int max_decimals) +{ + int32_t exp = 0; + uint8_t flags = 0; + int i; + + if (__builtin_signbit(x)) + { + flags |= DTOA_MINUS; + x = -x; + } + + if (x == 0) + { + flags |= DTOA_ZERO; + for (i = 0; i < max_digits; i++) + dtoa->digits[i] = '0'; + } + else if (isnan(x)) + { + flags |= DTOA_NAN; + } + else if (isinf(x)) + { + flags |= DTOA_INF; + } + else + { + double y; + + exp = MIN_MANT_EXP; + + /* Bring x within range MIN_MANT <= x < MAX_MANT while computing + * exponent value + */ + + if (x < MIN_MANT) + { + for (i = DTOA_SCALE_UP_NUM - 1; i >= 0; i--) + { + y = x * __dtoa_scale_up[i]; + if (y < MAX_MANT) + { + x = y; + exp -= (1 << i); + } + } + } + else + { + for (i = DTOA_SCALE_DOWN_NUM - 1; i >= 0; i--) + { + y = x * __dtoa_scale_down[i]; + if (y >= MIN_MANT) + { + x = y; + exp += (1 << i); + } + } + } + + /* If limiting decimals, then limit the max digits to no more than the + * number of digits left of the decimal plus the number of digits right + * of the decimal + */ + + if (max_decimals != 0) + { + max_digits = MIN(max_digits, max_decimals + MAX(exp + 1, 1)); + } + + /* Round nearest by adding 1/2 of the last digit before converting to + * int. Check for overflow and adjust mantissa and exponent values + */ + + x = x + __dtoa_round[max_digits]; + + if (x >= MAX_MANT) + { + x /= 10.0; + exp++; + } + + /* Now convert mantissa to decimal. */ + + uint64_t mant = (uint64_t) x; + uint64_t decimal = MIN_MANT_INT; + + /* Compute digits */ + + for (i = 0; i < max_digits; i++) + { + dtoa->digits[i] = mant / decimal + '0'; + mant %= decimal; + decimal /= 10; + } + } + + dtoa->digits[max_digits] = '\0'; + dtoa->flags = flags; + dtoa->exp = exp; + return max_digits; +} diff --git a/libs/libc/stdio/nano_dtoa_engine.h b/libs/libc/stdio/nano_dtoa_engine.h new file mode 100644 index 0000000000..9f2b54a468 --- /dev/null +++ b/libs/libc/stdio/nano_dtoa_engine.h @@ -0,0 +1,189 @@ +/**************************************************************************** + * libs/libc/stdio/nano_dtoa_engine.h + * + * Copyright © 2018, Keith Packard + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __LIBS_LIBC_STDIO_NANO_DTOA_ENGINE_H +#define __LIBS_LIBC_STDIO_NANO_DTOA_ENGINE_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define DTOA_MAX_DIG DBL_DIG + +#define DTOA_MINUS 1 +#define DTOA_ZERO 2 +#define DTOA_INF 4 +#define DTOA_NAN 8 +#define DTOA_CARRY 16 /* Carry was to master position. */ + +#if DBL_MAX_10_EXP >= 1 && DBL_MAX_10_EXP < 2 +# define DTOA_SCALE_UP_NUM 1 +#endif +#if DBL_MAX_10_EXP >= 2 && DBL_MAX_10_EXP < 4 +# define DTOA_SCALE_UP_NUM 2 +#endif +#if DBL_MAX_10_EXP >= 4 && DBL_MAX_10_EXP < 8 +# define DTOA_SCALE_UP_NUM 3 +#endif +#if DBL_MAX_10_EXP >= 8 && DBL_MAX_10_EXP < 16 +# define DTOA_SCALE_UP_NUM 4 +#endif +#if DBL_MAX_10_EXP >= 16 && DBL_MAX_10_EXP < 32 +# define DTOA_SCALE_UP_NUM 5 +#endif +#if DBL_MAX_10_EXP >= 32 && DBL_MAX_10_EXP < 64 +# define DTOA_SCALE_UP_NUM 6 +#endif +#if DBL_MAX_10_EXP >= 64 && DBL_MAX_10_EXP < 128 +# define DTOA_SCALE_UP_NUM 7 +#endif +#if DBL_MAX_10_EXP >= 128 && DBL_MAX_10_EXP < 256 +# define DTOA_SCALE_UP_NUM 8 +#endif +#if DBL_MAX_10_EXP >= 256 && DBL_MAX_10_EXP < 512 +# define DTOA_SCALE_UP_NUM 9 +#endif +#if DBL_MAX_10_EXP >= 512 && DBL_MAX_10_EXP < 1024 +# define DTOA_SCALE_UP_NUM 10 +#endif +#if DBL_MAX_10_EXP >= 1024 && DBL_MAX_10_EXP < 2048 +# define DTOA_SCALE_UP_NUM 11 +#endif +#if DBL_MAX_10_EXP >= 2048 && DBL_MAX_10_EXP < 4096 +# define DTOA_SCALE_UP_NUM 12 +#endif +#if DBL_MAX_10_EXP >= 4096 && DBL_MAX_10_EXP < 8192 +# define DTOA_SCALE_UP_NUM 13 +#endif +#if DBL_MAX_10_EXP >= 8192 && DBL_MAX_10_EXP < 16384 +# define DTOA_SCALE_UP_NUM 14 +#endif +#if DBL_MAX_10_EXP >= 16384 && DBL_MAX_10_EXP < 32768 +# define DTOA_SCALE_UP_NUM 15 +#endif +#if DBL_MAX_10_EXP >= 32768 && DBL_MAX_10_EXP < 65536 +# define DTOA_SCALE_UP_NUM 16 +#endif +#if DBL_MAX_10_EXP >= 65536 && DBL_MAX_10_EXP < 131072 +# define DTOA_SCALE_UP_NUM 17 +#endif +#if DBL_MIN_10_EXP <= -1 && DBL_MIN_10_EXP > -2 +# define DTOA_SCALE_DOWN_NUM 1 +#endif +#if DBL_MIN_10_EXP <= -2 && DBL_MIN_10_EXP > -4 +# define DTOA_SCALE_DOWN_NUM 2 +#endif +#if DBL_MIN_10_EXP <= -4 && DBL_MIN_10_EXP > -8 +# define DTOA_SCALE_DOWN_NUM 3 +#endif +#if DBL_MIN_10_EXP <= -8 && DBL_MIN_10_EXP > -16 +# define DTOA_SCALE_DOWN_NUM 4 +#endif +#if DBL_MIN_10_EXP <= -16 && DBL_MIN_10_EXP > -32 +# define DTOA_SCALE_DOWN_NUM 5 +#endif +#if DBL_MIN_10_EXP <= -32 && DBL_MIN_10_EXP > -64 +# define DTOA_SCALE_DOWN_NUM 6 +#endif +#if DBL_MIN_10_EXP <= -64 && DBL_MIN_10_EXP > -128 +# define DTOA_SCALE_DOWN_NUM 7 +#endif +#if DBL_MIN_10_EXP <= -128 && DBL_MIN_10_EXP > -256 +# define DTOA_SCALE_DOWN_NUM 8 +#endif +#if DBL_MIN_10_EXP <= -256 && DBL_MIN_10_EXP > -512 +# define DTOA_SCALE_DOWN_NUM 9 +#endif +#if DBL_MIN_10_EXP <= -512 && DBL_MIN_10_EXP > -1024 +# define DTOA_SCALE_DOWN_NUM 10 +#endif +#if DBL_MIN_10_EXP <= -1024 && DBL_MIN_10_EXP > -2048 +# define DTOA_SCALE_DOWN_NUM 11 +#endif +#if DBL_MIN_10_EXP <= -2048 && DBL_MIN_10_EXP > -4096 +# define DTOA_SCALE_DOWN_NUM 12 +#endif +#if DBL_MIN_10_EXP <= -4096 && DBL_MIN_10_EXP > -8192 +# define DTOA_SCALE_DOWN_NUM 13 +#endif +#if DBL_MIN_10_EXP <= -8192 && DBL_MIN_10_EXP > -16384 +# define DTOA_SCALE_DOWN_NUM 14 +#endif +#if DBL_MIN_10_EXP <= -16384 && DBL_MIN_10_EXP > -32768 +# define DTOA_SCALE_DOWN_NUM 15 +#endif +#if DBL_MIN_10_EXP <= -32768 && DBL_MIN_10_EXP > -65536 +# define DTOA_SCALE_DOWN_NUM 16 +#endif +#if DBL_MIN_10_EXP <= -65536 && DBL_MIN_10_EXP > -131072 +# define DTOA_SCALE_DOWN_NUM 17 +#endif + +#define DTOA_ROUND_NUM (DBL_DIG + 1) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +struct dtoa +{ + int32_t exp; + uint8_t flags; + char digits[DTOA_MAX_DIG + 1]; +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern const double __dtoa_scale_up[]; +extern const double __dtoa_scale_down[]; +extern const double __dtoa_round[]; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +int __dtoa_engine(double x, FAR struct dtoa *dtoa, int max_digits, + int max_decimals); + +#endif /* __LIBS_LIBC_STDIO_NANO_DTOA_ENGINE_H */ diff --git a/libs/libc/stdio/nano_libvsprintf.c b/libs/libc/stdio/nano_libvsprintf.c new file mode 100644 index 0000000000..f934b7b22d --- /dev/null +++ b/libs/libc/stdio/nano_libvsprintf.c @@ -0,0 +1,1037 @@ +/**************************************************************************** + * libs/libc/stdio/nano_libvsprintf.c + * + * Copyright (c) 2002, Alexander Popov (sasho@vip.bg) + * Copyright (c) 2002,2004,2005 Joerg Wunsch + * Copyright (c) 2005, Helmut Wallner + * Copyright (c) 2007, Dmitry Xmelkov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include + +#include "nano_dtoa_engine.h" +#include "nano_ultoa_invert.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration options for nano-printf PRINTF_LEVEL */ + +#define PRINTF_MIN 1 +#define PRINTF_STD 2 +#define PRINTF_FLT 3 + +/* This file can be compiled into more than one flavour. The default + * is to offer the usual modifiers and integer formatting support + * (level 2). Level 1 maintains a minimal version that just offers + * integer formatting, but no modifier support whatsoever. Level 3 is + * intented for floating point support. + */ + +#if defined(CONFIG_NANO_PRINTLEVEL) +# define PRINTF_LEVEL CONFIG_NANO_PRINTLEVEL +#elif defined(CONFIG_LIBC_FLOATINGPOINT) +# define PRINTF_LEVEL PRINTF_FLT +#else +# define PRINTF_LEVEL PRINTF_STD +#endif + +#if PRINTF_LEVEL == PRINTF_MIN || PRINTF_LEVEL == PRINTF_STD || \ + PRINTF_LEVEL == PRINTF_FLT + +/* OK */ + +#else +# error "Not a known printf level." +#endif + +#ifdef putc +# undef putc +#endif + +#define putc(c,stream) (total_len++, (stream)->put(stream, c)) + +#if PRINTF_LEVEL <= PRINTF_MIN + +# define FL_ALTHEX 0x04 +# define FL_ALT 0x10 +# define FL_ALTLWR 0x20 +# define FL_NEGATIVE 0x40 +# define FL_LONG 0x80 + +#else /* i.e. PRINTF_LEVEL > PRINTF_MIN */ + +/* Order is relevant here and matches order in format string */ + +# define FL_ZFILL 0x0001 +# define FL_PLUS 0x0002 +# define FL_SPACE 0x0004 +# define FL_LPAD 0x0008 +# define FL_ALT 0x0010 + +# define FL_WIDTH 0x0020 +# define FL_PREC 0x0040 + +# define FL_LONG 0x0080 +# define FL_LONGLONG 0x0100 +# define FL_SHORT 0x0200 + +# define FL_NEGATIVE 0x0400 + +# define FL_ALTUPP 0x0800 +# define FL_ALTHEX 0x1000 + +# define FL_FLTUPP 0x2000 +# define FL_FLTEXP 0x4000 +# define FL_FLTFIX 0x8000 + +#endif + +/* Support special access to CODE-space strings for Harvard architectures */ + +#ifdef CONFIG_ARCH_ROMGETC +# define fmt_char(fmt) up_romgetc((fmt)++) +#else +# define fmt_char(fmt) (*(fmt)++) +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +#if PRINTF_LEVEL <= PRINTF_MIN +int lib_vsprintf(FAR struct lib_outstream_s *stream, + FAR const IPTR char *fmt, va_list ap) +{ + unsigned char c; /* Holds a char from the format string */ + unsigned char flags; + unsigned char buf[11]; /* Size for -1 in octal, without '\0' */ + int total_len = 0; + + for (; ; ) + { + for (; ; ) + { + c = fmt_char(fmt); + if (!c) + { + goto ret; + } + + if (c == '%') + { + c = fmt_char(fmt); + if (c != '%') + { + break; + } + } + + putc(c, stream); + } + + for (flags = 0; !(flags & FL_LONG); /* 'll' will detect as error */ + c = fmt_char(fmt)) + { + if (c && strchr(" +-.0123456789h", c)) + { + continue; + } + + if (c == '#') + { + flags |= FL_ALT; + continue; + } + + if (c == 'l') + { + flags |= FL_LONG; + continue; + } + + break; + } + + /* Only a format character is valid. */ + + if (c && strchr("EFGefg", c)) + { + (void)va_arg(ap, double); + putc('?', stream); + continue; + } + + { + const char *pnt; + + switch (c) + { + case 'c': + putc(va_arg(ap, int), stream); + continue; + + case 'S': + + /* FALLTHROUGH */ + + case 's': + pnt = va_arg(ap, char *); + while ((c = *pnt++) != 0) + { + putc(c, stream); + } + + continue; + } + } + + if (c == 'd' || c == 'i') + { + long x = (flags & FL_LONG) ? va_arg(ap, long) : va_arg(ap, int); + flags &= ~FL_ALT; + if (x < 0) + { + x = -x; + + /* `putc ('-', stream)' will considarably inlarge stack size. So + * flag is used. + */ + + flags |= FL_NEGATIVE; + } + + c = __ultoa_invert(x, (char *)buf, 10) - (char *)buf; + } + else + { + int base; + + switch (c) + { + case 'u': + flags &= ~FL_ALT; + base = 10; + sign = 0; + goto ultoa; + + case 'o': + base = 8; + goto ultoa; + + case 'p': + flags |= FL_ALT; + + /* no break */ + + case 'x': + flags |= (FL_ALTHEX | FL_ALTLWR); + base = 16; + goto ultoa; + + case 'X': + flags |= FL_ALTHEX; + base = 16 | XTOA_UPPER; + + ultoa: + c = __ultoa_invert((flags & FL_LONG) + ? va_arg(ap, unsigned long) + : va_arg(ap, unsigned int), + (char *)buf, base) - (char *)buf; + break; + + default: + goto ret; + } + } + + /* Integer number output. */ + + if (flags & FL_NEGATIVE) + { + putc('-', stream); + } + + if ((flags & FL_ALT) && (buf[c - 1] != '0')) + { + putc('0', stream); + if (flags & FL_ALTHEX) + { +# if FL_ALTLWR != 'x' - 'X' +# error +# endif + putc('X' + (flags & FL_ALTLWR), stream); + } + } + + do + { + putc(buf[--c], stream); + } + while (c); + } + +ret: + return total_len; +} + +#else /* i.e. PRINTF_LEVEL > PRINTF_MIN */ + +int lib_vsprintf(FAR struct lib_outstream_s *stream, + FAR const IPTR char *fmt, va_list ap) +{ + unsigned char c; /* Holds a char from the format string */ + uint16_t flags; + int width; + int prec; + union + { + unsigned char __buf[11]; /* Size for -1 in octal, without '\0' */ +# if PRINTF_LEVEL >= PRINTF_FLT + struct dtoa __dtoa; +# endif + } u; + const char *pnt; + size_t size; + unsigned char len; + int total_len = 0; + +# define buf (u.__buf) +# define _dtoa (u.__dtoa) + + for (; ; ) + { + for (; ; ) + { + c = fmt_char(fmt); + if (!c) + { + goto ret; + } + + if (c == '%') + { + c = fmt_char(fmt); + if (c != '%') + { + break; + } + } + + putc(c, stream); + } + + flags = 0; + width = 0; + prec = 0; + + do + { + if (flags < FL_WIDTH) + { + switch (c) + { + case '0': + flags |= FL_ZFILL; + continue; + + case '+': + flags |= FL_PLUS; + + /* FALLTHROUGH */ + + case ' ': + flags |= FL_SPACE; + continue; + + case '-': + flags |= FL_LPAD; + continue; + + case '#': + flags |= FL_ALT; + continue; + } + } + + if (flags < FL_LONG) + { + if (c >= '0' && c <= '9') + { + c -= '0'; + if (flags & FL_PREC) + { + prec = 10 * prec + c; + continue; + } + + width = 10 * width + c; + flags |= FL_WIDTH; + continue; + } + + if (c == '*') + { + if (flags & FL_PREC) + { + prec = va_arg(ap, int); + if (prec < 0) + { + prec = 0; + } + } + else + { + width = va_arg(ap, int); + flags |= FL_WIDTH; + if (width < 0) + { + width = -width; + flags |= FL_LPAD; + } + } + + continue; + } + + if (c == '.') + { + if (flags & FL_PREC) + { + goto ret; + } + + flags |= FL_PREC; + continue; + } + } + + if (c == 'l') + { + if (flags & FL_LONG) + { + flags |= FL_LONGLONG; + } + + flags |= FL_LONG; + flags &= ~FL_SHORT; + continue; + } + + if (c == 'h') + { + flags |= FL_SHORT; + flags &= ~FL_LONG; + continue; + } + + break; + } + while ((c = fmt_char(fmt)) != 0); + + /* Only a format character is valid. */ + +# if 'F' != 'E'+1 || 'G' != 'F'+1 || 'f' != 'e'+1 || 'g' != 'f'+1 +# error +# endif + +# if PRINTF_LEVEL >= PRINTF_FLT + if (c >= 'E' && c <= 'G') + { + flags |= FL_FLTUPP; + c += 'e' - 'E'; + goto flt_oper; + } + else if (c >= 'e' && c <= 'g') + { + int exp; /* Exponent of master decimal digit */ + int n; + uint8_t sign; /* Sign character (or 0) */ + uint8_t ndigs; /* Number of digits to convert */ + uint8_t ndecimal; /* Digits after decimal (for 'f' format), 0 if + * no limit */ + + flags &= ~FL_FLTUPP; + + flt_oper: + ndigs = 0; + if (!(flags & FL_PREC)) + { + prec = 6; + } + + flags &= ~(FL_FLTEXP | FL_FLTFIX); + + if (c == 'e') + { + ndigs = prec + 1; + ndecimal = 0; + flags |= FL_FLTEXP; + } + else if (c == 'f') + { + ndigs = DTOA_MAX_DIG; + ndecimal = prec; + flags |= FL_FLTFIX; + } + else + { + ndigs = prec; + ndecimal = 0; + } + + if (ndigs > DTOA_MAX_DIG) + { + ndigs = DTOA_MAX_DIG; + } + + ndigs = __dtoa_engine(va_arg(ap, double), &_dtoa, ndigs, ndecimal); + exp = _dtoa.exp; + + sign = 0; + if ((_dtoa.flags & DTOA_MINUS) && !(_dtoa.flags & DTOA_NAN)) + { + sign = '-'; + } + else if (flags & FL_PLUS) + { + sign = '+'; + } + else if (flags & FL_SPACE) + { + sign = ' '; + } + + if (_dtoa.flags & (DTOA_NAN | DTOA_INF)) + { + const char *p; + + ndigs = sign ? 4 : 3; + if (width > ndigs) + { + width -= ndigs; + if (!(flags & FL_LPAD)) + { + do + { + putc(' ', stream); + } + while (--width); + } + } + else + { + width = 0; + } + + if (sign) + { + putc(sign, stream); + } + + p = "inf"; + if (_dtoa.flags & DTOA_NAN) + { + p = "nan"; + } + +# if ('I'-'i' != 'N'-'n') || ('I'-'i' != 'F'-'f') || ('I'-'i' != 'A'-'a') +# error +# endif + while ((ndigs = *p) != 0) + { + if (flags & FL_FLTUPP) + { + ndigs += 'I' - 'i'; + } + + putc(ndigs, stream); + p++; + } + + goto tail; + } + + if (!(flags & (FL_FLTEXP | FL_FLTFIX))) + { + /* 'g(G)' format */ + + prec = ndigs; + + /* Remove trailing zeros */ + + while (ndigs > 0 && _dtoa.digits[ndigs - 1] == '0') + { + ndigs--; + } + + if (-4 <= exp && exp < prec) + { + flags |= FL_FLTFIX; + + if (exp < 0 || ndigs > exp) + { + prec = ndigs - (exp + 1); + } + else + { + prec = 0; + } + } + else + { + /* Limit displayed precision to available precision */ + + prec = ndigs - 1; + } + } + + /* Conversion result length, width := free space length */ + + if (flags & FL_FLTFIX) + { + n = (exp > 0 ? exp + 1 : 1); + } + else + { + n = 5; /* 1e+00 */ + } + + if (sign) + { + n += 1; + } + + if (prec) + { + n += prec + 1; + } + else if (flags & FL_ALT) + { + n += 1; + } + + width = width > n ? width - n : 0; + + /* Output before first digit */ + + if (!(flags & (FL_LPAD | FL_ZFILL))) + { + while (width) + { + putc(' ', stream); + width--; + } + } + + if (sign) + { + putc(sign, stream); + } + + if (!(flags & FL_LPAD)) + { + while (width) + { + putc('0', stream); + width--; + } + } + + if (flags & FL_FLTFIX) + { + /* 'f' format */ + + char out; + + /* At this point, we should have exp exponent of leftmost digit + * in _dtoa.digits ndigs number of buffer digits to print prec + * number of digits after decimal In the loop, 'n' walks over + * the exponent value + */ + + n = exp > 0 ? exp : 0; /* Exponent of left digit */ + do + { + + /* Insert decimal point at correct place */ + + if (n == -1) + { + putc('.', stream); + } + + /* Pull digits from buffer when in-range, otherwise use 0 */ + + if (0 <= exp - n && exp - n < ndigs) + { + out = _dtoa.digits[exp - n]; + } + else + { + out = '0'; + } + + if (--n < -prec) + { + if ((flags & FL_ALT) && n == -1) + { + putc('.', stream); + } + + break; + } + + putc(out, stream); + } + while (1); + + if (n == exp && (_dtoa.digits[0] > '5' || + (_dtoa.digits[0] == '5' && !(_dtoa.flags & DTOA_CARRY)))) + { + out = '1'; + } + + putc(out, stream); + } + else + { + /* 'e(E)' format + * + * Mantissa + */ + + if (_dtoa.digits[0] != '1') + { + _dtoa.flags &= ~DTOA_CARRY; + } + + putc(_dtoa.digits[0], stream); + if (prec > 0) + { + putc('.', stream); + uint8_t pos = 1; + for (pos = 1; pos < 1 + prec; pos++) + putc(pos < ndigs ? _dtoa.digits[pos] : '0', stream); + } + else if (flags & FL_ALT) + { + putc('.', stream); + } + + /* Exponent */ + + putc(flags & FL_FLTUPP ? 'E' : 'e', stream); + ndigs = '+'; + if (exp < 0 || (exp == 0 && (_dtoa.flags & DTOA_CARRY) != 0)) + { + exp = -exp; + ndigs = '-'; + } + + putc(ndigs, stream); + for (ndigs = '0'; exp >= 10; exp -= 10) + { + ndigs += 1; + } + + putc(ndigs, stream); + putc('0' + exp, stream); + } + + goto tail; + } + +# else /* to: PRINTF_LEVEL >= PRINTF_FLT */ + if ((c >= 'E' && c <= 'G') || (c >= 'e' && c <= 'g')) + { + (void)va_arg(ap, double); + pnt = "*float*"; + size = sizeof("*float*") - 1; + goto str_lpad; + } +# endif + + switch (c) + { + + case 'c': + buf[0] = va_arg(ap, int); + pnt = (char *)buf; + size = 1; + goto str_lpad; + + case 's': + case 'S': + pnt = va_arg(ap, char *); + size = strnlen(pnt, (flags & FL_PREC) ? prec : ~0); + + str_lpad: + if (!(flags & FL_LPAD)) + { + while (size < width) + { + putc(' ', stream); + width--; + } + } + + while (size) + { + putc(*pnt++, stream); + if (width) + { + width -= 1; + } + + size -= 1; + } + + goto tail; + } + + if (c == 'd' || c == 'i') + { + long x; + + if (flags & FL_LONG) + { + x = va_arg(ap, long); + } + else + { + x = va_arg(ap, int); + if (flags & FL_SHORT) + { + x = (short)x; + } + } + + flags &= ~(FL_NEGATIVE | FL_ALT); + if (x < 0) + { + x = -x; + flags |= FL_NEGATIVE; + } + + if ((flags & FL_PREC) && prec == 0 && x == 0) + { + c = 0; + } + else + { + c = __ultoa_invert(x, (char *)buf, 10) - (char *)buf; + } + } + else + { + int base; + unsigned long x; + + if (flags & FL_LONG) + { + x = va_arg(ap, unsigned long); + } + else + { + x = va_arg(ap, unsigned int); + if (flags & FL_SHORT) + x = (unsigned short)x; + } + + flags &= ~(FL_PLUS | FL_SPACE); + + switch (c) + { + case 'u': + flags &= ~FL_ALT; + base = 10; + break; + + case 'o': + base = 8; + break; + + case 'p': + flags |= FL_ALT; + + /* no break */ + + case 'x': + if (flags & FL_ALT) + flags |= FL_ALTHEX; + base = 16; + break; + + case 'X': + if (flags & FL_ALT) + flags |= (FL_ALTHEX | FL_ALTUPP); + base = 16 | XTOA_UPPER; + break; + + default: + putc('%', stream); + putc(c, stream); + continue; + } + + if ((flags & FL_PREC) && prec == 0 && x == 0) + { + c = 0; + } + else + { + c = __ultoa_invert(x, (char *)buf, base) - (char *)buf; + } + + flags &= ~FL_NEGATIVE; + } + + len = c; + + if (flags & FL_PREC) + { + flags &= ~FL_ZFILL; + if (len < prec) + { + len = prec; + if ((flags & FL_ALT) && !(flags & FL_ALTHEX)) + { + flags &= ~FL_ALT; + } + } + } + + if (flags & FL_ALT) + { + if (buf[c - 1] == '0') + { + flags &= ~(FL_ALT | FL_ALTHEX | FL_ALTUPP); + } + else + { + len += 1; + if (flags & FL_ALTHEX) + { + len += 1; + } + } + } + else if (flags & (FL_NEGATIVE | FL_PLUS | FL_SPACE)) + { + len += 1; + } + + if (!(flags & FL_LPAD)) + { + if (flags & FL_ZFILL) + { + prec = c; + if (len < width) + { + prec += width - len; + len = width; + } + } + + while (len < width) + { + putc(' ', stream); + len++; + } + } + + width = (len < width) ? width - len : 0; + + if (flags & FL_ALT) + { + putc('0', stream); + if (flags & FL_ALTHEX) + { + putc(flags & FL_ALTUPP ? 'X' : 'x', stream); + } + } + else if (flags & (FL_NEGATIVE | FL_PLUS | FL_SPACE)) + { + unsigned char z = ' '; + if (flags & FL_PLUS) + { + z = '+'; + } + + if (flags & FL_NEGATIVE) + { + z = '-'; + } + + putc(z, stream); + } + + while (prec > c) + { + putc('0', stream); + prec--; + } + + while (c) + { + putc(buf[--c], stream); + } + + tail: + + /* Tail is possible. */ + + while (width) + { + putc(' ', stream); + width--; + } + } + +ret: + return total_len; +} + +#endif /* PRINTF_LEVEL > PRINTF_MIN */ diff --git a/libs/libc/stdio/nano_ultoa_invert.c b/libs/libc/stdio/nano_ultoa_invert.c new file mode 100644 index 0000000000..6bd28e9067 --- /dev/null +++ b/libs/libc/stdio/nano_ultoa_invert.c @@ -0,0 +1,79 @@ +/**************************************************************************** + * libs/libc/stdio/nano_ultoa_invert.c + * + * Copyright © 2017, Keith Packard + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "nano_ultoa_invert.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +FAR char *__ultoa_invert(unsigned long val, FAR char *str, int base) +{ + int upper = 0; + + if (base & XTOA_UPPER) + { + upper = 1; + base &= ~XTOA_UPPER; + } + do + { + int v; + + v = val % base; + val = val / base; + + if (v <= 9) + { + v += '0'; + } + else + { + if (upper) + v += 'A' - 10; + else + v += 'a' - 10; + } + + *str++ = v; + } + while (val); + + return str; +} diff --git a/libs/libc/stdio/nano_ultoa_invert.h b/libs/libc/stdio/nano_ultoa_invert.h new file mode 100644 index 0000000000..0c07cb96c1 --- /dev/null +++ b/libs/libc/stdio/nano_ultoa_invert.h @@ -0,0 +1,62 @@ +/**************************************************************************** + * libs/libc/stdio/nano_ultoa_invert.h + * + * Copyright (c) 2005, Dmitry Xmelkov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __LIBS_LIBC_STDIO_NANO_ULTOA_INVERT_H +#define __LIBS_LIBC_STDIO_NANO_ULTOA_INVERT_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Next flags are to use with `base'. Unused fields are reserved. */ + +#define XTOA_PREFIX 0x0100 /* Put prefix for octal or hex */ +#define XTOA_UPPER 0x0200 /* Use upper case letters */ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Internal function for use from `printf'. */ + +FAR char *__ultoa_invert(unsigned long val, FAR char *s, int base); + +#endif /* __LIBS_LIBC_STDIO_NANO_ULTOA_INVERT_H */