diff --git a/Makefile b/Makefile index 7b28a1a1..6cfb430e 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ APP = test_suite # bar/file3 # ----------------------------------------------------------------------------- -FILES_PRJ = test/test_suite +FILES_PRJ = test/test_suite printf # ------------------------------------------------------------------------------ @@ -50,9 +50,9 @@ FILES_PRJ = test/test_suite # -Iinclude_path3 \ # ------------------------------------------------------------------------------ -C_INCLUDES = +C_INCLUDES = -C_DEFINES = +C_DEFINES = # ------------------------------------------------------------------------------ @@ -65,7 +65,7 @@ TRG = $(PATH_BIN)/$(APP) # object files # ------------------------------------------------------------------------------ FILES_TMP = $(FILES_PRJ) -FILES_O = $(addsuffix .o, $(FILES_TMP)) +FILES_O = $(addprefix $(PATH_OBJ)/, $(addsuffix .o, $(notdir $(FILES_TMP)))) # ------------------------------------------------------------------------------ @@ -85,7 +85,7 @@ VPATH := $(sort $(dir $(FILES_TMP))) # ------------------------------------------------------------------------------ AR = $(PATH_TOOLS_CC)ar AS = $(PATH_TOOLS_CC)g++ -CC = $(PATH_TOOLS_CC)g++ +CC = $(PATH_TOOLS_CC)gcc CL = $(PATH_TOOLS_CC)g++ NM = $(PATH_TOOLS_CC)nm GCOV = $(PATH_TOOLS_CC)gcov @@ -101,13 +101,15 @@ RM = $(PATH_TOOLS_UTIL)rm SED = $(PATH_TOOLS_UTIL)sed +# convert GCC output for the VisualStudio(R) output window +#PRINT_ERR = $(SED) -e 's|.h:\([0-9]*\),|.h(\1) :|' -e 's|:\([0-9]*\):|(\1) :|' +PRINT_ERR = cat # ------------------------------------------------------------------------------ # Compiler flags for the target architecture # ------------------------------------------------------------------------------ GCCFLAGS = $(C_INCLUDES) \ $(C_DEFINES) \ - -std=c++11 \ -g \ -Wall \ -pedantic \ @@ -124,10 +126,8 @@ GCCFLAGS = $(C_INCLUDES) \ -Winit-self \ -Wdouble-promotion \ -gdwarf-2 \ - -fno-exceptions \ - -O2 \ + -Og \ -ffunction-sections \ - -ffat-lto-objects \ -fdata-sections \ -fverbose-asm \ -Wextra \ @@ -135,12 +135,12 @@ GCCFLAGS = $(C_INCLUDES) \ -Wfloat-equal CFLAGS = $(GCCFLAGS) \ - -Wunsuffixed-float-constants \ -x c \ -std=c99 CPPFLAGS = $(GCCFLAGS) \ -x c++ \ + -std=c++11 \ -fno-rtti \ -fstrict-enums \ -fno-use-cxa-atexit \ @@ -225,11 +225,11 @@ version: # ------------------------------------------------------------------------------ # Link/locate application # ------------------------------------------------------------------------------ -$(TRG) : $(FILES_O) +$(TRG) : $(FILES_O) Makefile @-$(ECHO) +++ linkink application to generate: $(TRG) - @-$(CL) $(LFLAGS) -L. -lc $(PATH_OBJ)/*.o -Wl,-Map,$(TRG).map -o $(TRG) + -$(CL) $(LFLAGS) -L. -lc $(FILES_O) -Wl,-Map,$(TRG).map -o $(TRG) # profiling - @-$(CL) $(LFLAGS) -L. -lc $(PATH_COV)/*.o --coverage -o $(PATH_COV)/$(APP) +# @-$(CL) $(LFLAGS) -L. -lc $(PATH_COV)/*.o --coverage -o $(PATH_COV)/$(APP) # ------------------------------------------------------------------------------ @@ -244,7 +244,8 @@ $(TRG)_nm.txt : $(TRG) @-$(SIZE) -A -t $(TRG) > $(TRG)_size.txt -%.o : %.cpp + +$(PATH_OBJ)/%.o : %.cpp @$(ECHO) +++ compile: $< # Compile the source file # ...and Reformat (using sed) any possible error/warning messages for the VisualStudio(R) output window @@ -252,13 +253,13 @@ $(TRG)_nm.txt : $(TRG) # ...and Generate a dependency file (using the -MM flag) @-$(CL) $(CPPFLAGS) $< -E -o $(PATH_PRE)/$(basename $(@F)).pre @-$(CL) $(CPPFLAGS) $< -c -o $(PATH_OBJ)/$(basename $(@F)).o 2> $(PATH_ERR)/$(basename $(@F)).err - @-$(SED) -e 's|.h:\([0-9]*\),|.h(\1) :|' -e 's|:\([0-9]*\):|(\1) :|' $(PATH_ERR)/$(basename $(@F)).err + @-$(PRINT_ERR) $(PATH_ERR)/$(basename $(@F)).err @-$(OBJDUMP) --disassemble --line-numbers -S $(PATH_OBJ)/$(basename $(@F)).o > $(PATH_LST)/$(basename $(@F)).lst @-$(CL) $(CPPFLAGS) $< -MM > $(PATH_OBJ)/$(basename $(@F)).d # profiling - @-$(CL) $(CPPFLAGS) -O0 --coverage $< -c -o $(PATH_COV)/$(basename $(@F)).o 2> $(PATH_NUL) +# @-$(CL) $(CPPFLAGS) -O0 --coverage $< -c -o $(PATH_COV)/$(basename $(@F)).o 2> $(PATH_NUL) -%.o : %.c +$(PATH_OBJ)/%.o : %.c @$(ECHO) +++ compile: $< # Compile the source file # ...and Reformat (using sed) any possible error/warning messages for the VisualStudio(R) output window @@ -266,6 +267,10 @@ $(TRG)_nm.txt : $(TRG) # ...and Generate a dependency file (using the -MM flag) @-$(CL) $(CFLAGS) $< -E -o $(PATH_PRE)/$(basename $(@F)).pre @-$(CC) $(CFLAGS) $< -c -o $(PATH_OBJ)/$(basename $(@F)).o 2> $(PATH_ERR)/$(basename $(@F)).err - @-$(SED) -e 's|.h:\([0-9]*\),|.h(\1) :|' -e 's|:\([0-9]*\):|(\1) :|' $(PATH_ERR)/$(basename $(@F)).err + @-$(PRINT_ERR) $(PATH_ERR)/$(basename $(@F)).err @-$(OBJDUMP) -S $(PATH_OBJ)/$(basename $(@F)).o > $(PATH_LST)/$(basename $(@F)).lst @-$(CC) $(CFLAGS) $< -MM > $(PATH_OBJ)/$(basename $(@F)).d + +$(FILES_O) : Makefile + +-include $(FILES_O:.o=.d) diff --git a/printf.c b/printf.c index 8a700add..8e867483 100644 --- a/printf.c +++ b/printf.c @@ -32,6 +32,8 @@ #include #include +#include +#include #include "printf.h" @@ -45,19 +47,12 @@ // 'ntoa' conversion buffer size, this must be big enough to hold one converted -// numeric number including padded zeros (dynamically created on stack) +// numeric/float number including padded zeros (dynamically created on stack) // default: 32 byte #ifndef PRINTF_NTOA_BUFFER_SIZE #define PRINTF_NTOA_BUFFER_SIZE 32U #endif -// 'ftoa' conversion buffer size, this must be big enough to hold one converted -// float number including padded zeros (dynamically created on stack) -// default: 32 byte -#ifndef PRINTF_FTOA_BUFFER_SIZE -#define PRINTF_FTOA_BUFFER_SIZE 32U -#endif - // support for the floating point type (%f) // default: activated #ifndef PRINTF_DISABLE_SUPPORT_FLOAT @@ -79,7 +74,7 @@ // define the largest float suitable to print with %f // default: 1e9 #ifndef PRINTF_MAX_FLOAT -#define PRINTF_MAX_FLOAT 1e9 +#define PRINTF_MAX_FLOAT 1e9f #endif // support for the long long types (%llu or %p) @@ -95,6 +90,18 @@ #define PRINTF_SUPPORT_PTRDIFF_T #endif +#define PRINTF_EXACT_ROUNDING + +//#define PRINTF_FLOAT_MATH +//#define PRINTF_FLOAT_ONLY + +//#undef PRINTF_SUPPORT_EXPONENTIAL + +// sanity check +#if defined(PRINTF_SUPPORT_FLOAT) && defined(PRINTF_FLOAT_MATH) && defined(PRINTF_SUPPORT_EXPONENTIAL) +# error "PRINTF_SUPPORT_EXPONENTIAL is not supported in combination with PRINTF_FLOAT_MATH" +#endif + /////////////////////////////////////////////////////////////////////////////// // internal flag definitions @@ -110,62 +117,173 @@ #define FLAGS_LONG_LONG (1U << 9U) #define FLAGS_PRECISION (1U << 10U) #define FLAGS_ADAPT_EXP (1U << 11U) - +#define FLAGS_NEGATIVE (1U << 12U) // import float.h for DBL_MAX #if defined(PRINTF_SUPPORT_FLOAT) #include -#endif +# if defined(PRINTF_FLOAT_MATH) +typedef float real; +#define REAL_MAX FLT_MAX +# else +typedef double real; +#define REAL_MAX DBL_MAX +# endif -// output function type -typedef void (*out_fct_type)(char character, void* buffer, size_t idx, size_t maxlen); +static inline float va_int2float(uint32_t in) { return *(float*)∈ } -// wrapper (used as buffer) for output function type -typedef struct { - void (*fct)(char character, void* arg); - void* arg; -} out_fct_wrap_type; +# if defined(PRINTF_EXACT_ROUNDING) +// implement fmsub without math library +// used code from https://stackoverflow.com/questions/28630864/how-is-fma-implemented +// c is close to a*b, so this algorithm should work correctly +// FPU may have fma instruction, using it (through libm) will be much faster +# if !defined(USE_MATH_H) +# if defined(PRINTF_FLOAT_MATH) -// internal buffer output -static inline void _out_buffer(char character, void* buffer, size_t idx, size_t maxlen) -{ - if (idx < maxlen) { - ((char*)buffer)[idx] = character; - } +struct floatfloat { float hi; float lo; }; +struct uflt {float f; uint32_t i; } ; + +static struct floatfloat split(float a) { + union uflt lo, hi = {a}; + hi.i &= ~(((uint32_t)1U << (FLT_MANT_DIG / 2)) - 1); // mask low-order mantissa bits + lo.f = a - hi.f; + return (struct floatfloat){hi.f,lo.f}; } +real fmsub(real a, real b, real c) { + struct floatfloat as = split(a), bs = split(b); + return ((as.hi*bs.hi - c) + as.hi*bs.lo + as.lo*bs.hi) + as.lo*bs.lo; +} +# else + +struct doubledouble { double hi; double lo; }; +union udbl { double f; uint64_t i;} ; +static struct doubledouble split(double a) { + union udbl lo, hi = {a}; + hi.i &= ~(((uint64_t)1U << (DBL_MANT_DIG / 2)) - 1); // mask low-order mantissa bits + lo.f = a - hi.f; + return (struct doubledouble){hi.f,lo.f}; +} -// internal null output -static inline void _out_null(char character, void* buffer, size_t idx, size_t maxlen) +real fmsub(real a, real b, real c) { + struct doubledouble as = split(a), bs = split(b); + return ((as.hi*bs.hi - c) + as.hi*bs.lo + as.lo*bs.hi) + as.lo*bs.lo; +} +# endif /* defined(PRINTF_FLOAT_MATH) */ +# else /* !defined(USE_MATH_H) */ + +#include +real fmsub(real a, real b, real c) { { - (void)character; (void)buffer; (void)idx; (void)maxlen; +# if defined(PRINTF_FLOAT_MATH) + return fmaf(a, b, -c); +# else + return fma(a, b, -c); +# endif +} +# endif /* ifndef USE_MATH_H */ +# endif /* defined(PRINTF_EXACT_ROUNDING) */ +#endif /* defined(PRINTF_SUPPORT_FLOAT) */ + +#if !defined(__GNUC__) +// ISO C version, but no type checking +#define container_of(ptr, type, member) \ + ((type *) ((char *)(ptr) - offsetof(type, member))) +#else +// non ISO variant from linux kernel; checks ptr type, but triggers 'ISO C forbids braced-groups within expressions [-Wpedantic]' +// __extension__ is here to disable this warning +#define container_of(ptr, type, member) ( __extension__ ({ \ + const __typeof__( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );})) \ + /**/ +#endif + +#ifndef MAX +# define MAX(a,b) ((a) < (b) ? (b) : (a)) +#endif +#ifndef MIN +# define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + + +// type used for index, used as buffer pointer for s(n)printf +typedef uintptr_t idx_t; + +// isnan is C99 macro, use it if defined +#ifndef isnan +// avoid gcc warning +# define isnan(v) ((v) != (v)) +#endif + +#if 0 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +bool iszero(real v) { + return v == 0; } +#pragma GCC diagnostic pop +#else +#define iszero(v) ( \ + __extension__ ({ \ + _Pragma("GCC diagnostic push"); \ + _Pragma("GCC diagnostic ignored \"-Wfloat-equal\""); \ + ((v) == 0); \ + _Pragma("GCC diagnostic pop"); \ + })) \ +/**/ +#endif +// output function structure, passed as fisrt argument on ofn call +// see how out_userfct augments this structure +struct out_base { + void (*ofn)(struct out_base const* out, char character, idx_t idx); +}; + +// state used in printf processing +// can't be merged with out_base, it would disable a lot of optimizations +struct printf_state { + const struct out_base* out; + idx_t maxidx; + unsigned short width; + unsigned short base; + unsigned short prec; + unsigned short flags; +}; + + +// sprintf/snprinf out function. idx is reused as pointer into buffer +static void _out_buffer_fn(struct out_base const* out, char character, idx_t idx) +{ + (void)out; + *(char*)idx = character; +} +static const struct out_base out_buffer = { _out_buffer_fn }; // internal _putchar wrapper -static inline void _out_char(char character, void* buffer, size_t idx, size_t maxlen) +static void _out_putchar_fn(struct out_base const* out, char character, idx_t idx) { - (void)buffer; (void)idx; (void)maxlen; - if (character) { - _putchar(character); - } + (void)out; (void)idx; + _putchar(character); } +static const struct out_base out_putchar = { _out_putchar_fn }; -// internal output function wrapper -static inline void _out_fct(char character, void* buffer, size_t idx, size_t maxlen) +struct out_userfct { + struct out_base out_base; + void (*fct)(char character, void* arg); + void* arg; +}; + +static void _out_userfct_fn(struct out_base const* out, char character, idx_t idx) { - (void)idx; (void)maxlen; - if (character) { - // buffer is the output fct pointer - ((out_fct_wrap_type*)buffer)->fct(character, ((out_fct_wrap_type*)buffer)->arg); - } + struct out_userfct* self = container_of(out, struct out_userfct, out_base); + (void)idx; + self->fct(character, self->arg); } - // internal secure strlen // \return The length of the string (excluding the terminating 0) limited by 'maxsize' static inline unsigned int _strnlen_s(const char* str, size_t maxsize) @@ -183,7 +301,6 @@ static inline bool _is_digit(char ch) return (ch >= '0') && (ch <= '9'); } - // internal ASCII string to unsigned int conversion static unsigned int _atoi(const char** str) { @@ -194,29 +311,48 @@ static unsigned int _atoi(const char** str) return i; } +static inline void _out(struct printf_state* st, char character, idx_t idx) +{ + if (idx < st->maxidx) { + st->out->ofn(st->out, character, idx); + } +} -// output the specified string in reverse, taking care of any zero-padding -static size_t _out_rev(out_fct_type out, char* buffer, size_t idx, size_t maxlen, const char* buf, size_t len, unsigned int width, unsigned int flags) +// add count padchar characters, return adjusted index +// count can be negative +static idx_t _out_pad(struct printf_state* st, char padchar, idx_t idx, int count) { - const size_t start_idx = idx; + if (count <= 0) + return idx; + + const idx_t top = idx + (unsigned)count; + const idx_t end = MIN(top, st->maxidx); + const struct out_base * const out = st->out; // cache it in register + while(idx < end) + out->ofn(out, padchar, idx++); + return top; +} + +// output the specified string in reverse, taking care of any space-padding +// zero-padding is already performed in buf +static idx_t _out_rev(struct printf_state* st, idx_t idx, const char* buf, size_t len) +{ + const int pad = st->width - len; // pad spaces up to given width - if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) { - for (size_t i = len; i < width; i++) { - out(' ', buffer, idx++, maxlen); - } + if (!(st->flags & FLAGS_LEFT)) { + idx = _out_pad(st, ' ', idx, pad); } // reverse string + // TODO - optimize this loop like out_pad while (len) { - out(buf[--len], buffer, idx++, maxlen); + _out(st, buf[--len], idx++); } // append pad spaces up to given width - if (flags & FLAGS_LEFT) { - while (idx - start_idx < width) { - out(' ', buffer, idx++, maxlen); - } + if ((st->flags & FLAGS_LEFT)) { + idx = _out_pad(st, ' ', idx, pad); } return idx; @@ -224,105 +360,100 @@ static size_t _out_rev(out_fct_type out, char* buffer, size_t idx, size_t maxlen // internal itoa format -static size_t _ntoa_format(out_fct_type out, char* buffer, size_t idx, size_t maxlen, char* buf, size_t len, bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags) +static idx_t _ntoa_format(struct printf_state* st, idx_t idx, char* buf, size_t len) { // pad leading zeros - if (!(flags & FLAGS_LEFT)) { - if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { - width--; - } - while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) { - buf[len++] = '0'; + int zeropad = 0; + if (st->prec || st->flags & FLAGS_PRECISION) { + // no other source of zero-padding if prec is specified (FLAGS_PRECISION may be unset) + zeropad = st->prec; + } + else if (st->flags & FLAGS_ZEROPAD) { + zeropad = st->width; + if (st->flags & (FLAGS_NEGATIVE | FLAGS_PLUS | FLAGS_SPACE)) { + // keep one space for sign + zeropad--; } - while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) { - buf[len++] = '0'; + if (st->flags & FLAGS_HASH) { + // keep space for 0x / 0b (only for width, not precision) + // octal is handled separatelly + if (st->base == 16U || st->base == 2U) { + zeropad -= 2; + } } } + while ((int)len < MIN(zeropad, (int)PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + // handle hash - if (flags & FLAGS_HASH) { - if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) { - len--; - if (len && (base == 16U)) { - len--; - } - } - if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { - buf[len++] = 'x'; + if (st->flags & FLAGS_HASH) { + if (st->base == 2 && len < PRINTF_NTOA_BUFFER_SIZE - 1) { + buf[len++] = 'b'; + buf[len++] = '0'; } - else if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { - buf[len++] = 'X'; + // pesky octal case. Add 0 prefix only if following digit is not zero (both from value and zero-padding) + if (st->base == 8 && len < PRINTF_NTOA_BUFFER_SIZE && (!len || buf[len - 1] != '0')) { + buf[len++] = '0'; } - else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) { - buf[len++] = 'b'; - } - if (len < PRINTF_NTOA_BUFFER_SIZE) { - buf[len++] = '0'; + if (st->base == 16 && len < PRINTF_NTOA_BUFFER_SIZE - 1) { + buf[len++] = (st->flags & FLAGS_UPPERCASE) ? 'X' : 'x'; + buf[len++] = '0'; } } - + // handle '+', '-', ' ' if (len < PRINTF_NTOA_BUFFER_SIZE) { - if (negative) { + if (st->flags & FLAGS_NEGATIVE) { buf[len++] = '-'; } - else if (flags & FLAGS_PLUS) { + else if (st->flags & FLAGS_PLUS) { buf[len++] = '+'; // ignore the space if the '+' exists } - else if (flags & FLAGS_SPACE) { + else if (st->flags & FLAGS_SPACE) { buf[len++] = ' '; } } - return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); + return _out_rev(st, idx, buf, len); } +// long/long long itoa code +#define NTOA_CODE \ + char buf[PRINTF_NTOA_BUFFER_SIZE]; \ + size_t len = 0U; \ + \ + /* no hash for 0 values, except octal 0 */ \ + if (!value && st->base != 8) { \ + st->flags &= ~FLAGS_HASH; \ + } \ + \ + /* if precision is specified and zero, don't print zero value */ \ + /* if precision > 0, zero padding code will supply zeroes */ \ + if (!(st->flags & FLAGS_PRECISION) || value) { \ + unsigned base = st->base; \ + do { \ + const char digit = (char)(value % base); \ + buf[len++] = digit < 10 ? '0' + digit : \ + (st->flags & FLAGS_UPPERCASE ? 'A' : 'a') -10 + digit; \ + value /= base; \ + } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); \ + } \ + return _ntoa_format(st, idx, buf, len); \ + /**/ + // internal itoa for 'long' type -static size_t _ntoa_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long value, bool negative, unsigned long base, unsigned int prec, unsigned int width, unsigned int flags) +static idx_t _ntoa_long(struct printf_state* st, idx_t idx, unsigned long value) { - char buf[PRINTF_NTOA_BUFFER_SIZE]; - size_t len = 0U; - - // no hash for 0 values - if (!value) { - flags &= ~FLAGS_HASH; - } - - // write if precision != 0 and value is != 0 - if (!(flags & FLAGS_PRECISION) || value) { - do { - const char digit = (char)(value % base); - buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; - value /= base; - } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); - } - - return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); + NTOA_CODE; } - -// internal itoa for 'long long' type #if defined(PRINTF_SUPPORT_LONG_LONG) -static size_t _ntoa_long_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long long value, bool negative, unsigned long long base, unsigned int prec, unsigned int width, unsigned int flags) +// internal itoa for 'long long' type +static idx_t _ntoa_long_long(struct printf_state* st, idx_t idx, unsigned long long value) { - char buf[PRINTF_NTOA_BUFFER_SIZE]; - size_t len = 0U; - - // no hash for 0 values - if (!value) { - flags &= ~FLAGS_HASH; - } - - // write if precision != 0 and value is != 0 - if (!(flags & FLAGS_PRECISION) || value) { - do { - const char digit = (char)(value % base); - buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; - value /= base; - } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); - } - - return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); + NTOA_CODE; } #endif // PRINTF_SUPPORT_LONG_LONG @@ -331,156 +462,151 @@ static size_t _ntoa_long_long(out_fct_type out, char* buffer, size_t idx, size_t #if defined(PRINTF_SUPPORT_EXPONENTIAL) // forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT -static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags); +static idx_t _etoa(struct printf_state* st, idx_t idx, double value); #endif // internal ftoa for fixed decimal floating point -static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) +static idx_t _ftoa(struct printf_state* st, idx_t idx, real value) { - char buf[PRINTF_FTOA_BUFFER_SIZE]; + char buf[PRINTF_NTOA_BUFFER_SIZE]; size_t len = 0U; - double diff = 0.0; // powers of 10 - static const double pow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; + static const real pow10[] = { 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9 }; // test for special values - if (value != value) - return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags); - if (value < -DBL_MAX) - return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags); - if (value > DBL_MAX) - return _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags); + if (isnan(value)) + return _out_rev(st, idx, "nan", 3); + if (value < -REAL_MAX) + return _out_rev(st, idx, "fni-", 4); + if (value > REAL_MAX) + return _out_rev(st, idx, (st->flags & FLAGS_PLUS) ? "fni+" : "fni", (st->flags & FLAGS_PLUS) ? 4U : 3U); // test for very large values // standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad + // TODO - emit string indicating overflow (possibly as +-inf) if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) { #if defined(PRINTF_SUPPORT_EXPONENTIAL) - return _etoa(out, buffer, idx, maxlen, value, prec, width, flags); + return _etoa(st, idx, value); #else return 0U; #endif } // test for negative - bool negative = false; - if (value < 0) { - negative = true; + // if FLAGS_NEGATIVE was passed, print value as negative (used in _etoa) + const bool negative = value < 0; + if (negative) { value = 0 - value; + st->flags |= FLAGS_NEGATIVE; } - // set default precision, if not set explicitly - if (!(flags & FLAGS_PRECISION)) { - prec = PRINTF_DEFAULT_FLOAT_PRECISION; - } - // limit precision to 9, cause a prec >= 10 can lead to overflow errors - while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) { + unsigned prec = (st->flags & FLAGS_PRECISION) ? st->prec : PRINTF_DEFAULT_FLOAT_PRECISION; + // limit precision to 9, cause a prec >= 10 can lead to overflow errors (if using 32bit integer type) + while ((len < PRINTF_NTOA_BUFFER_SIZE) && (prec > 9U)) { buf[len++] = '0'; prec--; } - int whole = (int)value; - double tmp = (value - whole) * pow10[prec]; - unsigned long frac = (unsigned long)tmp; - diff = tmp - frac; + // use signed value for double/int conversions, some CPUs don't support unsigned conversion, making the code larger + unsigned long whole = (unsigned long)(long)value; + // calculation tmp/frac is safe - only whole part is subtracted + real fracdbl = value - (long)whole; + unsigned long frac = (unsigned long)(long)(fracdbl * pow10[prec]); + // we need better accuracy to calculate diff - * pow10 provides inaccurate result + // using fused multiply accumulate gets correct value +#if defined(PRINTF_EXACT_ROUNDING) + real diff = fmsub(fracdbl, pow10[prec], (real)(long)frac + 0.5f); +#else + real diff = fracdbl * pow10[prec] - ((real)(long)frac + 0.5f); +#endif - if (diff > 0.5) { - ++frac; - // handle rollover, e.g. case 0.99 with prec 1 is 1.0 - if (frac >= pow10[prec]) { - frac = 0; - ++whole; - } + if (diff < 0) { + // round down } - else if (diff < 0.5) { - } - else if ((frac == 0U) || (frac & 1U)) { - // if halfway, round up if odd OR if last digit is 0 + else if (diff > 0) { ++frac; } - - if (prec == 0U) { - diff = value - (double)whole; - if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) { - // exactly 0.5 and ODD, then round up - // 1.5 -> 2, but 2.5 -> 2 - ++whole; + else { + // half-way, round to even + if (prec == 0U) { + whole = (whole + 1) & ~1U; // round whole to even + } else { + frac = (frac + 1) & ~1U; // round frac to even } } - else { + // handle rollover, e.g. case 0.99 with prec 1 is 1.0 + if (frac >= pow10[prec]) { + frac = 0; + ++whole; + } + + if (prec > 0U) { unsigned int count = prec; // now do fractional part, as an unsigned number - while (len < PRINTF_FTOA_BUFFER_SIZE) { + // digits(frac) <= prec + while (len < PRINTF_NTOA_BUFFER_SIZE && frac) { --count; - buf[len++] = (char)(48U + (frac % 10U)); - if (!(frac /= 10U)) { - break; - } + buf[len++] = (char)('0' + (frac % 10U)); + frac /= 10U; } // add extra 0s - while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) { + while ((len < PRINTF_NTOA_BUFFER_SIZE) && (count-- > 0U)) { buf[len++] = '0'; } - if (len < PRINTF_FTOA_BUFFER_SIZE) { + if (len < PRINTF_NTOA_BUFFER_SIZE) { // add decimal buf[len++] = '.'; } + } else if (st->flags & FLAGS_HASH) { + if (len < PRINTF_NTOA_BUFFER_SIZE) { + // add decimal point if precision is zero and hash flag is set + buf[len++] = '.'; + } } // do whole part, number is reversed - while (len < PRINTF_FTOA_BUFFER_SIZE) { - buf[len++] = (char)(48 + (whole % 10)); - if (!(whole /= 10)) { + while (len < PRINTF_NTOA_BUFFER_SIZE) { + buf[len++] = (char)('0' + (whole % 10U)); + if (!(whole /= 10U)) { // output at least one zero break; } } - - // pad leading zeros - if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) { - if (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { - width--; - } - while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) { - buf[len++] = '0'; - } - } - - if (len < PRINTF_FTOA_BUFFER_SIZE) { - if (negative) { - buf[len++] = '-'; - } - else if (flags & FLAGS_PLUS) { - buf[len++] = '+'; // ignore the space if the '+' exists + // maybe we printed trailing zeroes for %g (not %#g) - erase them + char *bp = buf; + if ((st->flags & FLAGS_ADAPT_EXP) && !(st->flags & FLAGS_HASH)) { + while(len && (*bp == '0')) { + bp++; len--; } - else if (flags & FLAGS_SPACE) { - buf[len++] = ' '; + // '.' too + if(len && (*bp == '.')) { + bp++; len--; } } - - return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); + st->flags &= ~(FLAGS_HASH | FLAGS_PRECISION); + st->prec = 0; // precision is not used for decimal point, not zero padding in %f + return _ntoa_format(st, idx, bp, len); } #if defined(PRINTF_SUPPORT_EXPONENTIAL) // internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse -static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) +static size_t _etoa(struct printf_state* st, idx_t idx, double value) { // check for NaN and special values - if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) { - return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags); + if (isnan(value) || (value > DBL_MAX) || (value < -DBL_MAX)) { + return _ftoa(st, idx, value); } // determine the sign const bool negative = value < 0; if (negative) { value = -value; + st->flags |= FLAGS_NEGATIVE; } - // default precision - if (!(flags & FLAGS_PRECISION)) { - prec = PRINTF_DEFAULT_FLOAT_PRECISION; - } + unsigned prec = (st->flags & FLAGS_PRECISION) ? st->prec : PRINTF_DEFAULT_FLOAT_PRECISION; // determine the decimal exponent // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) @@ -488,61 +614,71 @@ static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, d uint64_t U; double F; } conv; - - conv.F = value; - int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2 - conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2) - // now approximate log10 from the log2 integer part and an expansion of ln around 1.5 - int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168); - // now we want to compute 10^expval but we want to be sure it won't overflow - exp2 = (int)(expval * 3.321928094887362 + 0.5); - const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453; - const double z2 = z * z; - conv.U = (uint64_t)(exp2 + 1023) << 52U; - // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex - conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); - // correct for rounding errors - if (value < conv.F) { - expval--; - conv.F /= 10; + int expval; + + if (!iszero(value)) { + conv.F = value; + int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2 + conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2) + // now approximate log10 from the log2 integer part and an expansion of ln around 1.5 + expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168); + // now we want to compute 10^expval but we want to be sure it won't overflow + exp2 = (int)(expval * 3.321928094887362 + 0.5); + const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453; + const double z2 = z * z; + conv.U = (uint64_t)(exp2 + 1023) << 52U; + // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex + conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); + // correct for rounding errors + if (value < conv.F) { + expval--; + conv.F /= 10; + } + } else { + // treat zero as 0.0e0 + conv.F = 0; + expval = 0; } // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters - unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U; + unsigned int minwidth = ((expval > -100) && (expval < 100)) ? 4U : 5U; // in "%g" mode, "prec" is the number of *significant figures* not decimals - if (flags & FLAGS_ADAPT_EXP) { + if (st->flags & FLAGS_ADAPT_EXP) { + st->flags |= FLAGS_PRECISION; // make sure _ftoa respects precision (1 digit of default precision is taken for exp format) // do we want to fall-back to "%f" mode? - if ((value >= 1e-4) && (value < 1e6)) { + // check if value is between 1e-4 and 1e6 or it can be printed with precision digits (and within ftoa limit) + // handles 0.0 too (exp is 0) + if ((expval >= -4) && ((expval < 6) || ((expval < (int)prec) && (expval <= 9)))) { if ((int)prec > expval) { prec = (unsigned)((int)prec - expval - 1); } else { prec = 0; } - flags |= FLAGS_PRECISION; // make sure _ftoa respects precision // no characters in exponent minwidth = 0U; expval = 0; } else { - // we use one sigfig for the whole part - if ((prec > 0) && (flags & FLAGS_PRECISION)) { + // we use one sigfig for the whole part (even with default precision) + if (prec > 0) { --prec; } } } // will everything fit? + unsigned int width = st->width; // remember original width here, state is modified unsigned int fwidth = width; if (width > minwidth) { - // we didn't fall-back so subtract the characters required for the exponent + // we didn't fall-back so subtract the characters required for the exponent fwidth -= minwidth; } else { // not enough characters, so go back to default sizing fwidth = 0U; } - if ((flags & FLAGS_LEFT) && minwidth) { + if ((st->flags & FLAGS_LEFT) && minwidth) { // if we're padding on the right, DON'T pad the floating part fwidth = 0U; } @@ -554,17 +690,30 @@ static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, d // output the floating part const size_t start_idx = idx; - idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP); + st->prec = prec; + st->width = fwidth; + //st->flags &= ~FLAGS_ADAPT_EXP; // TODO + // ftoa respects FLAGS_NEGATIVE + idx = _ftoa(st, idx, value); // output the exponent part if (minwidth) { + // keep value before overwriting it + const bool rpad = st->flags & FLAGS_LEFT; // output the exponential symbol - out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen); + _out(st, (st->flags & FLAGS_UPPERCASE) ? 'E' : 'e', idx++); // output the exponent value - idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth-1, FLAGS_ZEROPAD | FLAGS_PLUS); + st->prec = 0; + st->width = minwidth - 1; + st->flags = FLAGS_ZEROPAD | FLAGS_PLUS; + if(expval < 0) { + st->flags |= FLAGS_NEGATIVE; + expval = -expval; + } + idx = _ntoa_long(st, idx, (unsigned)expval); // might need to right-pad spaces - if (flags & FLAGS_LEFT) { - while (idx - start_idx < width) out(' ', buffer, idx++, maxlen); + if (rpad) { + idx = _out_pad(st, ' ', idx, width - (idx - start_idx)); } } return idx; @@ -574,22 +723,15 @@ static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, d // internal vsnprintf -static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const char* format, va_list va) +static int _vsnprintf(struct printf_state* st, idx_t idx, const char* format, va_list va) { - unsigned int flags, width, precision, n; - size_t idx = 0U; + idx_t start_idx = idx; - if (!buffer) { - // use null output function - out = _out_null; - } - - while (*format) - { + while (*format) { // format specifier? %[flags][width][.precision][length] if (*format != '%') { // no - out(*format, buffer, idx++, maxlen); + _out(st, *format, idx++); format++; continue; } @@ -599,20 +741,19 @@ static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const } // evaluate flags - flags = 0U; - do { + unsigned flags = 0U; + for (bool cont = true; cont; ) { switch (*format) { - case '0': flags |= FLAGS_ZEROPAD; format++; n = 1U; break; - case '-': flags |= FLAGS_LEFT; format++; n = 1U; break; - case '+': flags |= FLAGS_PLUS; format++; n = 1U; break; - case ' ': flags |= FLAGS_SPACE; format++; n = 1U; break; - case '#': flags |= FLAGS_HASH; format++; n = 1U; break; - default : n = 0U; break; + case '0': flags |= FLAGS_ZEROPAD; format++; break; + case '-': flags |= FLAGS_LEFT; format++; break; + case '+': flags |= FLAGS_PLUS; format++; break; + case ' ': flags |= FLAGS_SPACE; format++; break; + case '#': flags |= FLAGS_HASH; format++; break; + default : cont = false; break; } - } while (n); - + } // evaluate width field - width = 0U; + unsigned width = 0U; if (_is_digit(*format)) { width = _atoi(&format); } @@ -629,7 +770,7 @@ static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const } // evaluate precision field - precision = 0U; + unsigned precision = 0U; if (*format == '.') { flags |= FLAGS_PRECISION; format++; @@ -638,7 +779,12 @@ static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const } else if (*format == '*') { const int prec = (int)va_arg(va, int); - precision = prec > 0 ? (unsigned int)prec : 0U; + if (prec >= 0) { + precision = (unsigned)prec; + } else { + // negative precision is like no precision specifier at all + flags &= ~FLAGS_PRECISION; + } format++; } } @@ -713,42 +859,63 @@ static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const flags &= ~(FLAGS_PLUS | FLAGS_SPACE); } - // ignore '0' flag when precision is given - if (flags & FLAGS_PRECISION) { + // ignore '0' flag when precision is given or padding to left + if (flags & (FLAGS_PRECISION | FLAGS_LEFT)) { flags &= ~FLAGS_ZEROPAD; } + // store values into state + st->width = width; + st->base = base; + st->prec = precision; + // flags are stored later + // convert the integer if ((*format == 'i') || (*format == 'd')) { // signed if (flags & FLAGS_LONG_LONG) { #if defined(PRINTF_SUPPORT_LONG_LONG) - const long long value = va_arg(va, long long); - idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); + long long value = va_arg(va, long long); + if(value < 0) { + flags |= FLAGS_NEGATIVE; + } + st->flags = flags; + idx = _ntoa_long_long(st, idx, value < 0 ? 0L-(unsigned long long)value : (unsigned long long)value); #endif } else if (flags & FLAGS_LONG) { - const long value = va_arg(va, long); - idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); + long value = va_arg(va, long); + if(value < 0) { + flags |= FLAGS_NEGATIVE; + } + st->flags = flags; + // // -LONG_MIN is undefined in C (overflow), convert to unsigned first + idx = _ntoa_long(st, idx, value < 0 ? 0U-(unsigned long)value : (unsigned long)value); } else { - const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) : va_arg(va, int); - idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); + int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) : va_arg(va, int); + if (value < 0) { + flags |= FLAGS_NEGATIVE; + value = -value; + } + st->flags = flags; + idx = _ntoa_long(st, idx, value < 0 ? 0U-(unsigned int)value : (unsigned int)value); } } else { // unsigned + st->flags = flags; if (flags & FLAGS_LONG_LONG) { #if defined(PRINTF_SUPPORT_LONG_LONG) - idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, precision, width, flags); + idx = _ntoa_long_long(st, idx, va_arg(va, unsigned long long)); #endif } else if (flags & FLAGS_LONG) { - idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, base, precision, width, flags); + idx = _ntoa_long(st, idx, va_arg(va, unsigned long)); } else { const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) : va_arg(va, unsigned int); - idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags); + idx = _ntoa_long(st, idx, value); } } format++; @@ -757,8 +924,21 @@ static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const #if defined(PRINTF_SUPPORT_FLOAT) case 'f' : case 'F' : + if (flags & FLAGS_LEFT) { + flags &= ~FLAGS_ZEROPAD; + } if (*format == 'F') flags |= FLAGS_UPPERCASE; - idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); + st->width = width; + st->base = 10U; + st->prec = precision; + st->flags = flags; +#if defined(PRINTF_FLOAT_ONLY) + // TODO - pass NaN without %hf + real val = (flags & FLAGS_SHORT) ? (real)va_int2float(va_arg(va, uint32_t)) : 0; +#else + real val = (flags & FLAGS_SHORT) ? (real)va_int2float(va_arg(va, uint32_t)) : (real)va_arg(va, double); +#endif + idx = _ftoa(st, idx, val); format++; break; #if defined(PRINTF_SUPPORT_EXPONENTIAL) @@ -768,26 +948,29 @@ static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const case 'G': if ((*format == 'g')||(*format == 'G')) flags |= FLAGS_ADAPT_EXP; if ((*format == 'E')||(*format == 'G')) flags |= FLAGS_UPPERCASE; - idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); + if (flags & FLAGS_LEFT) { + flags &= ~FLAGS_ZEROPAD; + } + st->width = width; + st->base = 10U; + st->prec = precision; + st->flags = flags; + + idx = _etoa(st, idx, va_arg(va, double)); format++; break; #endif // PRINTF_SUPPORT_EXPONENTIAL #endif // PRINTF_SUPPORT_FLOAT case 'c' : { - unsigned int l = 1U; // pre padding if (!(flags & FLAGS_LEFT)) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } + idx = _out_pad(st, ' ', idx, (int)width - 1); } // char output - out((char)va_arg(va, int), buffer, idx++, maxlen); + _out(st, (char)va_arg(va, int), idx++); // post padding if (flags & FLAGS_LEFT) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } + idx = _out_pad(st, ' ', idx, (int)width - 1); } format++; break; @@ -795,41 +978,38 @@ static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const case 's' : { const char* p = va_arg(va, char*); - unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1); + unsigned int toprint = _strnlen_s(p, (flags & FLAGS_PRECISION) ? precision : (size_t)-1); // pre padding - if (flags & FLAGS_PRECISION) { - l = (l < precision ? l : precision); - } if (!(flags & FLAGS_LEFT)) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } + idx = _out_pad(st, ' ', idx, (int)(width - toprint)); } // string output - while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) { - out(*(p++), buffer, idx++, maxlen); + for (unsigned int i = 0; i < toprint; i++) { + _out(st, *(p++), idx++); } // post padding if (flags & FLAGS_LEFT) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } + idx = _out_pad(st, ' ', idx, (int)(width - toprint)); } format++; break; } case 'p' : { - width = sizeof(void*) * 2U; flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE; + + st->width = sizeof(void*) * 2U; + st->base = 16U; + st->prec = precision; + st->flags = flags; #if defined(PRINTF_SUPPORT_LONG_LONG) const bool is_ll = sizeof(uintptr_t) == sizeof(long long); if (is_ll) { - idx = _ntoa_long_long(out, buffer, idx, maxlen, (uintptr_t)va_arg(va, void*), false, 16U, precision, width, flags); + idx = _ntoa_long_long(st, idx, (uintptr_t)va_arg(va, void*)); } else { #endif - idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va_arg(va, void*)), false, 16U, precision, width, flags); + idx = _ntoa_long(st, idx, (unsigned long)((uintptr_t)va_arg(va, void*))); #if defined(PRINTF_SUPPORT_LONG_LONG) } #endif @@ -838,43 +1018,67 @@ static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const } case '%' : - out('%', buffer, idx++, maxlen); + _out(st, '%', idx++); format++; break; default : - out(*format, buffer, idx++, maxlen); + // TODO - remember %, ouptut full printf specifier + _out(st, *format, idx++); format++; break; } } - // termination - out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen); + // termination is done in calling function, for string output only - // return written chars without terminating \0 - return (int)idx; + // return written chars + return (int)(idx - start_idx); } /////////////////////////////////////////////////////////////////////////////// +int vprintf_(const char* format, va_list va) +{ + struct printf_state st = { .out = &out_putchar, .maxidx = UINTPTR_MAX}; + return _vsnprintf(&st, 0, format, va); +} + int printf_(const char* format, ...) { va_list va; va_start(va, format); - char buffer[1]; - const int ret = _vsnprintf(_out_char, buffer, (size_t)-1, format, va); + const int ret = vprintf_(format, va); va_end(va); return ret; } +int vsnprintf_(char* buffer, size_t count, const char* format, va_list va) +{ + struct printf_state st = { .out = &out_buffer}; + if (buffer && count) { // avoid case when there is no space for '\0' in buffer + idx_t idx = (uintptr_t)buffer; + idx_t eidx = idx + count; + if (eidx < idx) { // overflow + eidx = UINTPTR_MAX; + } + st.maxidx = eidx; + int ret = _vsnprintf(&st, idx, format, va); + // terminate string + _out(&st, '\0', MIN(idx + (unsigned)ret, eidx - 1)); + return ret; + } else { + st.maxidx = 0; + return _vsnprintf(&st, 0, format, va); + } +} int sprintf_(char* buffer, const char* format, ...) { va_list va; va_start(va, format); - const int ret = _vsnprintf(_out_buffer, buffer, (size_t)-1, format, va); + const int ret = vsnprintf_(buffer, SIZE_MAX, format, va); va_end(va); return ret; } @@ -884,31 +1088,18 @@ int snprintf_(char* buffer, size_t count, const char* format, ...) { va_list va; va_start(va, format); - const int ret = _vsnprintf(_out_buffer, buffer, count, format, va); + const int ret = vsnprintf_(buffer, count, format, va); va_end(va); return ret; } - -int vprintf_(const char* format, va_list va) -{ - char buffer[1]; - return _vsnprintf(_out_char, buffer, (size_t)-1, format, va); -} - - -int vsnprintf_(char* buffer, size_t count, const char* format, va_list va) -{ - return _vsnprintf(_out_buffer, buffer, count, format, va); -} - - -int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...) +int fctprintf(void (*outfn)(char character, void* arg), void* arg, const char* format, ...) { va_list va; va_start(va, format); - const out_fct_wrap_type out_fct_wrap = { out, arg }; - const int ret = _vsnprintf(_out_fct, (char*)(uintptr_t)&out_fct_wrap, (size_t)-1, format, va); + const struct out_userfct usr = {.out_base = {_out_userfct_fn}, .fct = outfn, .arg = arg}; + struct printf_state st = {.out = &usr.out_base, .maxidx = UINTPTR_MAX}; + const int ret = _vsnprintf(&st, 0, format, va); va_end(va); return ret; } diff --git a/test/catch.hpp b/test/catch.hpp index 1850fff1..52ed8e5b 100644 --- a/test/catch.hpp +++ b/test/catch.hpp @@ -9602,6 +9602,7 @@ namespace Catch { size_t start = 0; std::stack openings; + bool isquoted = false; for (size_t pos = 0; pos < names.size(); ++pos) { char c = names[pos]; switch (c) { @@ -9619,8 +9620,11 @@ namespace Catch { // case '>': openings.pop(); break; + case '"': + isquoted = !isquoted; + break; case ',': - if (start != pos && openings.size() == 0) { + if (start != pos && openings.size() == 0 && !isquoted) { m_messages.emplace_back(macroName, lineInfo, resultType); m_messages.back().message = trimmed(start, pos); m_messages.back().message += " := "; diff --git a/test/test_suite.cpp b/test/test_suite.cpp index 5507c3bd..c9308e23 100644 --- a/test/test_suite.cpp +++ b/test/test_suite.cpp @@ -33,20 +33,30 @@ #include #include #include +#include +#include +using Catch::Matchers::Equals; +// store pointers to library functions before they are masked by macro +int (*lib_sprintf)(char * buffer, const char* fmt, ...) = sprintf; +int (*lib_snprintf)(char * buffer, size_t count, const char* fmt, ...) = snprintf; + +#if 0 namespace test { // use functions in own test namespace to avoid stdio conflicts #include "../printf.h" #include "../printf.c" } // namespace test - +#else + #include "../printf.h" +#endif // dummy putchar static char printf_buffer[100]; static size_t printf_idx = 0U; -void test::_putchar(char character) +void /*test::*/_putchar(char character) { printf_buffer[printf_idx++] = character; } @@ -60,8 +70,8 @@ void _out_fct(char character, void* arg) TEST_CASE("printf", "[]" ) { printf_idx = 0U; - memset(printf_buffer, 0xCC, 100U); - REQUIRE(test::printf("% d", 4232) == 5); + memset(printf_buffer, 0xCC, sizeof(printf_buffer)); + REQUIRE(/*test::*/printf("% d", 4232) == 5); REQUIRE(printf_buffer[5] == (char)0xCC); printf_buffer[5] = 0; REQUIRE(!strcmp(printf_buffer, " 4232")); @@ -70,28 +80,17 @@ TEST_CASE("printf", "[]" ) { TEST_CASE("fctprintf", "[]" ) { printf_idx = 0U; - memset(printf_buffer, 0xCC, 100U); - test::fctprintf(&_out_fct, nullptr, "This is a test of %X", 0x12EFU); + memset(printf_buffer, 0xCC, sizeof(printf_buffer)); + /*test::*/fctprintf(&_out_fct, nullptr, "This is a test of %X", 0x12EFU); REQUIRE(!strncmp(printf_buffer, "This is a test of 12EF", 22U)); REQUIRE(printf_buffer[22] == (char)0xCC); } - -TEST_CASE("snprintf", "[]" ) { - char buffer[100]; - - test::snprintf(buffer, 100U, "%d", -1000); - REQUIRE(!strcmp(buffer, "-1000")); - - test::snprintf(buffer, 3U, "%d", -1000); - REQUIRE(!strcmp(buffer, "-1")); -} - static void vprintf_builder_1(char* buffer, ...) { va_list args; va_start(args, buffer); - test::vprintf("%d", args); + /*test::*/vprintf("%d", args); va_end(args); } @@ -99,7 +98,7 @@ static void vsnprintf_builder_1(char* buffer, ...) { va_list args; va_start(args, buffer); - test::vsnprintf(buffer, 100U, "%d", args); + /*test::*/vsnprintf(buffer, 100U, "%d", args); va_end(args); } @@ -107,7 +106,7 @@ static void vsnprintf_builder_3(char* buffer, ...) { va_list args; va_start(args, buffer); - test::vsnprintf(buffer, 100U, "%d %d %s", args); + /*test::*/vsnprintf(buffer, 100U, "%d %d %s", args); va_end(args); } @@ -122,1114 +121,577 @@ TEST_CASE("vprintf", "[]" ) { REQUIRE(!strcmp(printf_buffer, "2345")); } - TEST_CASE("vsnprintf", "[]" ) { char buffer[100]; vsnprintf_builder_1(buffer, -1); - REQUIRE(!strcmp(buffer, "-1")); + REQUIRE_THAT(buffer, Equals("-1")); vsnprintf_builder_3(buffer, 3, -1000, "test"); - REQUIRE(!strcmp(buffer, "3 -1000 test")); + REQUIRE_THAT(buffer, Equals("3 -1000 test")); } -TEST_CASE("space flag", "[]" ) { - char buffer[100]; - - test::sprintf(buffer, "% d", 42); - REQUIRE(!strcmp(buffer, " 42")); - - test::sprintf(buffer, "% d", -42); - REQUIRE(!strcmp(buffer, "-42")); - - test::sprintf(buffer, "% 5d", 42); - REQUIRE(!strcmp(buffer, " 42")); - - test::sprintf(buffer, "% 5d", -42); - REQUIRE(!strcmp(buffer, " -42")); - - test::sprintf(buffer, "% 15d", 42); - REQUIRE(!strcmp(buffer, " 42")); - - test::sprintf(buffer, "% 15d", -42); - REQUIRE(!strcmp(buffer, " -42")); - - test::sprintf(buffer, "% 15d", -42); - REQUIRE(!strcmp(buffer, " -42")); - - test::sprintf(buffer, "% 15.3f", -42.987); - REQUIRE(!strcmp(buffer, " -42.987")); - - test::sprintf(buffer, "% 15.3f", 42.987); - REQUIRE(!strcmp(buffer, " 42.987")); - - test::sprintf(buffer, "% s", "Hello testing"); - REQUIRE(!strcmp(buffer, "Hello testing")); - - test::sprintf(buffer, "% d", 1024); - REQUIRE(!strcmp(buffer, " 1024")); - - test::sprintf(buffer, "% d", -1024); - REQUIRE(!strcmp(buffer, "-1024")); - - test::sprintf(buffer, "% i", 1024); - REQUIRE(!strcmp(buffer, " 1024")); +// wrapper macros to compare our implementation with library one +// variables with result are available for further test cases +#define TEST_DEF \ + char buffer[100]; \ + int ret; \ + struct dummy \ + /**/ + +#define CMP_DEF \ + TEST_DEF; \ + char buffer_ref[100]; \ + int ret_ref; \ + struct dummy \ + /**/ + +#define CMP_SPRINTF(...) \ + do { \ + CAPTURE(__VA_ARGS__); \ + ret = /*test::*/sprintf(buffer, __VA_ARGS__); \ + ret_ref = lib_sprintf(buffer_ref, __VA_ARGS__); \ + CHECK_THAT(buffer, Equals(buffer_ref)); \ + CHECK(ret == ret_ref); \ + } while (0) \ + /**/ + +#define CMP_SNPRINTF(...) \ + do { \ + CAPTURE(__VA_ARGS__); \ + ret = /*test::*/snprintf(buffer, __VA_ARGS__); \ + ret_ref = lib_snprintf(buffer_ref, __VA_ARGS__); \ + CHECK_THAT(buffer, Equals(buffer_ref)); \ + CHECK(ret == ret_ref); \ + } while (0) \ + /**/ + +#define TEST_SPRINTF(matcher, ...) \ + do { \ + CAPTURE(__VA_ARGS__); \ + ret = /*test::*/sprintf(buffer, __VA_ARGS__); \ + CHECK_THAT(buffer, matcher); \ + (void)ret; \ + } while (0) \ + /**/ + +#define TEST(str, retval, ...) \ + do { \ + CAPTURE(__VA_ARGS__); \ + ret = /*test::*/sprintf(buffer, __VA_ARGS__); \ + CHECK_THAT(buffer, Equals(str)); \ + CHECK(retval == ret); \ + } while (0) \ + /**/ - test::sprintf(buffer, "% i", -1024); - REQUIRE(!strcmp(buffer, "-1024")); - - test::sprintf(buffer, "% u", 1024); - REQUIRE(!strcmp(buffer, "1024")); - - test::sprintf(buffer, "% u", 4294966272U); - REQUIRE(!strcmp(buffer, "4294966272")); - - test::sprintf(buffer, "% o", 511); - REQUIRE(!strcmp(buffer, "777")); - - test::sprintf(buffer, "% o", 4294966785U); - REQUIRE(!strcmp(buffer, "37777777001")); - - test::sprintf(buffer, "% x", 305441741); - REQUIRE(!strcmp(buffer, "1234abcd")); - - test::sprintf(buffer, "% x", 3989525555U); - REQUIRE(!strcmp(buffer, "edcb5433")); +TEST_CASE("snprintf", "[]" ) { + CMP_DEF; - test::sprintf(buffer, "% X", 305441741); - REQUIRE(!strcmp(buffer, "1234ABCD")); + CMP_SNPRINTF(100U, "%d", -1000); + CMP_SNPRINTF(3U, "%d", -1000); +} - test::sprintf(buffer, "% X", 3989525555U); - REQUIRE(!strcmp(buffer, "EDCB5433")); - test::sprintf(buffer, "% c", 'x'); - REQUIRE(!strcmp(buffer, "x")); +TEST_CASE("space flag", "[]" ) { + CMP_DEF; + + CMP_SPRINTF("% d", 42); + CMP_SPRINTF("% d", -42); + CMP_SPRINTF("% 5d", 42); + CMP_SPRINTF("% 5d", -42); + CMP_SPRINTF("% 15d", 42); + CMP_SPRINTF("% 15d", -42); + CMP_SPRINTF("% 15d", -42); + CMP_SPRINTF("% 15.3f", -42.987); + CMP_SPRINTF("% 15.3f", 42.987); + CMP_SPRINTF("% s", "Hello testing"); + CMP_SPRINTF("% d", 1024); + CMP_SPRINTF("% d", -1024); + CMP_SPRINTF("% i", 1024); + CMP_SPRINTF("% i", -1024); + CMP_SPRINTF("% u", 1024); + CMP_SPRINTF("% u", 4294966272U); + CMP_SPRINTF("% o", 511); + CMP_SPRINTF("% o", 4294966785U); + CMP_SPRINTF("% x", 305441741); + CMP_SPRINTF("% x", 3989525555U); + CMP_SPRINTF("% X", 305441741); + CMP_SPRINTF("% X", 3989525555U); + CMP_SPRINTF("% c", 'x'); } TEST_CASE("+ flag", "[]" ) { - char buffer[100]; - - test::sprintf(buffer, "%+d", 42); - REQUIRE(!strcmp(buffer, "+42")); - - test::sprintf(buffer, "%+d", -42); - REQUIRE(!strcmp(buffer, "-42")); - - test::sprintf(buffer, "%+5d", 42); - REQUIRE(!strcmp(buffer, " +42")); - - test::sprintf(buffer, "%+5d", -42); - REQUIRE(!strcmp(buffer, " -42")); - - test::sprintf(buffer, "%+15d", 42); - REQUIRE(!strcmp(buffer, " +42")); - - test::sprintf(buffer, "%+15d", -42); - REQUIRE(!strcmp(buffer, " -42")); - - test::sprintf(buffer, "%+s", "Hello testing"); - REQUIRE(!strcmp(buffer, "Hello testing")); - - test::sprintf(buffer, "%+d", 1024); - REQUIRE(!strcmp(buffer, "+1024")); - - test::sprintf(buffer, "%+d", -1024); - REQUIRE(!strcmp(buffer, "-1024")); - - test::sprintf(buffer, "%+i", 1024); - REQUIRE(!strcmp(buffer, "+1024")); - - test::sprintf(buffer, "%+i", -1024); - REQUIRE(!strcmp(buffer, "-1024")); - - test::sprintf(buffer, "%+u", 1024); - REQUIRE(!strcmp(buffer, "1024")); - - test::sprintf(buffer, "%+u", 4294966272U); - REQUIRE(!strcmp(buffer, "4294966272")); - - test::sprintf(buffer, "%+o", 511); - REQUIRE(!strcmp(buffer, "777")); - - test::sprintf(buffer, "%+o", 4294966785U); - REQUIRE(!strcmp(buffer, "37777777001")); - - test::sprintf(buffer, "%+x", 305441741); - REQUIRE(!strcmp(buffer, "1234abcd")); - - test::sprintf(buffer, "%+x", 3989525555U); - REQUIRE(!strcmp(buffer, "edcb5433")); - - test::sprintf(buffer, "%+X", 305441741); - REQUIRE(!strcmp(buffer, "1234ABCD")); - - test::sprintf(buffer, "%+X", 3989525555U); - REQUIRE(!strcmp(buffer, "EDCB5433")); - - test::sprintf(buffer, "%+c", 'x'); - REQUIRE(!strcmp(buffer, "x")); - - test::sprintf(buffer, "%+.0d", 0); - REQUIRE(!strcmp(buffer, "+")); + CMP_DEF; + + CMP_SPRINTF("%+d", 42); + CMP_SPRINTF("%+d", -42); + CMP_SPRINTF("%+5d", 42); + CMP_SPRINTF("%+5d", -42); + CMP_SPRINTF("%+15d", 42); + CMP_SPRINTF("%+15d", -42); + CMP_SPRINTF("%+s", "Hello testing"); + CMP_SPRINTF("%+d", 1024); + CMP_SPRINTF("%+d", -1024); + CMP_SPRINTF("%+i", 1024); + CMP_SPRINTF("%+i", -1024); + CMP_SPRINTF("%+u", 1024); + CMP_SPRINTF("%+u", 4294966272U); + CMP_SPRINTF("%+o", 511); + CMP_SPRINTF("%+o", 4294966785U); + CMP_SPRINTF("%+x", 305441741); + CMP_SPRINTF("%+x", 3989525555U); + CMP_SPRINTF("%+X", 305441741); + CMP_SPRINTF("%+X", 3989525555U); + CMP_SPRINTF("%+c", 'x'); + CMP_SPRINTF("%+.0d", 0); } TEST_CASE("0 flag", "[]" ) { - char buffer[100]; - - test::sprintf(buffer, "%0d", 42); - REQUIRE(!strcmp(buffer, "42")); - - test::sprintf(buffer, "%0ld", 42L); - REQUIRE(!strcmp(buffer, "42")); - - test::sprintf(buffer, "%0d", -42); - REQUIRE(!strcmp(buffer, "-42")); - - test::sprintf(buffer, "%05d", 42); - REQUIRE(!strcmp(buffer, "00042")); - - test::sprintf(buffer, "%05d", -42); - REQUIRE(!strcmp(buffer, "-0042")); - - test::sprintf(buffer, "%015d", 42); - REQUIRE(!strcmp(buffer, "000000000000042")); - - test::sprintf(buffer, "%015d", -42); - REQUIRE(!strcmp(buffer, "-00000000000042")); - - test::sprintf(buffer, "%015.2f", 42.1234); - REQUIRE(!strcmp(buffer, "000000000042.12")); - - test::sprintf(buffer, "%015.3f", 42.9876); - REQUIRE(!strcmp(buffer, "00000000042.988")); - - test::sprintf(buffer, "%015.5f", -42.9876); - REQUIRE(!strcmp(buffer, "-00000042.98760")); + CMP_DEF; + + CMP_SPRINTF("%0d", 42); + CMP_SPRINTF("%0ld", 42L); + CMP_SPRINTF("%0d", -42); + CMP_SPRINTF("%05d", 42); + CMP_SPRINTF("%05d", -42); + CMP_SPRINTF("%015d", 42); + CMP_SPRINTF("%015d", -42); + CMP_SPRINTF("%015.2f", 42.1234); + CMP_SPRINTF("%015.3f", 42.9876); + CMP_SPRINTF("%015.5f", -42.9876); } TEST_CASE("- flag", "[]" ) { - char buffer[100]; - - test::sprintf(buffer, "%-d", 42); - REQUIRE(!strcmp(buffer, "42")); - - test::sprintf(buffer, "%-d", -42); - REQUIRE(!strcmp(buffer, "-42")); - - test::sprintf(buffer, "%-5d", 42); - REQUIRE(!strcmp(buffer, "42 ")); - - test::sprintf(buffer, "%-5d", -42); - REQUIRE(!strcmp(buffer, "-42 ")); - - test::sprintf(buffer, "%-15d", 42); - REQUIRE(!strcmp(buffer, "42 ")); - - test::sprintf(buffer, "%-15d", -42); - REQUIRE(!strcmp(buffer, "-42 ")); - - test::sprintf(buffer, "%-0d", 42); - REQUIRE(!strcmp(buffer, "42")); - - test::sprintf(buffer, "%-0d", -42); - REQUIRE(!strcmp(buffer, "-42")); - - test::sprintf(buffer, "%-05d", 42); - REQUIRE(!strcmp(buffer, "42 ")); - - test::sprintf(buffer, "%-05d", -42); - REQUIRE(!strcmp(buffer, "-42 ")); - - test::sprintf(buffer, "%-015d", 42); - REQUIRE(!strcmp(buffer, "42 ")); - - test::sprintf(buffer, "%-015d", -42); - REQUIRE(!strcmp(buffer, "-42 ")); - - test::sprintf(buffer, "%0-d", 42); - REQUIRE(!strcmp(buffer, "42")); - - test::sprintf(buffer, "%0-d", -42); - REQUIRE(!strcmp(buffer, "-42")); - - test::sprintf(buffer, "%0-5d", 42); - REQUIRE(!strcmp(buffer, "42 ")); - - test::sprintf(buffer, "%0-5d", -42); - REQUIRE(!strcmp(buffer, "-42 ")); - - test::sprintf(buffer, "%0-15d", 42); - REQUIRE(!strcmp(buffer, "42 ")); - - test::sprintf(buffer, "%0-15d", -42); - REQUIRE(!strcmp(buffer, "-42 ")); - - test::sprintf(buffer, "%0-15.3e", -42.); + CMP_DEF; + + CMP_SPRINTF("%-d", 42); + CMP_SPRINTF("%-d", -42); + CMP_SPRINTF("%-5d", 42); + CMP_SPRINTF("%-5d", -42); + CMP_SPRINTF("%-15d", 42); + CMP_SPRINTF("%-15d", -42); + CMP_SPRINTF("%-0d", 42); + CMP_SPRINTF("%-0d", -42); + CMP_SPRINTF("%-05d", 42); + CMP_SPRINTF("%-05d", -42); + CMP_SPRINTF("%-015d", 42); + CMP_SPRINTF("%-015d", -42); + CMP_SPRINTF("%0-d", 42); + CMP_SPRINTF("%0-d", -42); + CMP_SPRINTF("%0-5d", 42); + CMP_SPRINTF("%0-5d", -42); + CMP_SPRINTF("%0-15d", 42); + CMP_SPRINTF("%0-15d", -42); #ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL - REQUIRE(!strcmp(buffer, "-4.200e+01 ")); + CMP_SPRINTF("%0-15.3e", -42.); #else - REQUIRE(!strcmp(buffer, "e")); + TEST_SPRINTF(Equals("e"), "%0-15.3e", -42.); #endif - test::sprintf(buffer, "%0-15.3g", -42.); #ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL - REQUIRE(!strcmp(buffer, "-42.0 ")); + TEST_SPRINTF(Equals("-42.0 "), "%0-15.3g", -42.); #else - REQUIRE(!strcmp(buffer, "g")); + TEST_SPRINTF(Equals("g"), "%0-15.3g", -42.); #endif } TEST_CASE("# flag", "[]" ) { - char buffer[100]; - - test::sprintf(buffer, "%#.0x", 0); - REQUIRE(!strcmp(buffer, "")); - test::sprintf(buffer, "%#.1x", 0); - REQUIRE(!strcmp(buffer, "0")); - test::sprintf(buffer, "%#.0llx", (long long)0); - REQUIRE(!strcmp(buffer, "")); - test::sprintf(buffer, "%#.8x", 0x614e); - REQUIRE(!strcmp(buffer, "0x0000614e")); - test::sprintf(buffer,"%#b", 6); - REQUIRE(!strcmp(buffer, "0b110")); + CMP_DEF; + + CMP_SPRINTF("%#.0x", 0); + CMP_SPRINTF("%#.1x", 0); + CMP_SPRINTF("%#.0llx", (long long)0); + CMP_SPRINTF("%#.8x", 0x614e); + TEST_SPRINTF(Equals("0b110"), "%#b", 6); + TEST_SPRINTF(Equals("0b110"), "%#5b", 6); } TEST_CASE("specifier", "[]" ) { - char buffer[100]; - - test::sprintf(buffer, "Hello testing"); - REQUIRE(!strcmp(buffer, "Hello testing")); - - test::sprintf(buffer, "%s", "Hello testing"); - REQUIRE(!strcmp(buffer, "Hello testing")); - - test::sprintf(buffer, "%d", 1024); - REQUIRE(!strcmp(buffer, "1024")); - - test::sprintf(buffer, "%d", -1024); - REQUIRE(!strcmp(buffer, "-1024")); - - test::sprintf(buffer, "%i", 1024); - REQUIRE(!strcmp(buffer, "1024")); - - test::sprintf(buffer, "%i", -1024); - REQUIRE(!strcmp(buffer, "-1024")); - - test::sprintf(buffer, "%u", 1024); - REQUIRE(!strcmp(buffer, "1024")); - - test::sprintf(buffer, "%u", 4294966272U); - REQUIRE(!strcmp(buffer, "4294966272")); - - test::sprintf(buffer, "%o", 511); - REQUIRE(!strcmp(buffer, "777")); - - test::sprintf(buffer, "%o", 4294966785U); - REQUIRE(!strcmp(buffer, "37777777001")); - - test::sprintf(buffer, "%x", 305441741); - REQUIRE(!strcmp(buffer, "1234abcd")); - - test::sprintf(buffer, "%x", 3989525555U); - REQUIRE(!strcmp(buffer, "edcb5433")); - - test::sprintf(buffer, "%X", 305441741); - REQUIRE(!strcmp(buffer, "1234ABCD")); - - test::sprintf(buffer, "%X", 3989525555U); - REQUIRE(!strcmp(buffer, "EDCB5433")); - - test::sprintf(buffer, "%%"); - REQUIRE(!strcmp(buffer, "%")); + CMP_DEF; + + CMP_SPRINTF("Hello testing"); + CMP_SPRINTF("%s", "Hello testing"); + CMP_SPRINTF("%d", 1024); + CMP_SPRINTF("%d", -1024); + CMP_SPRINTF("%i", 1024); + CMP_SPRINTF("%i", -1024); + CMP_SPRINTF("%u", 1024); + CMP_SPRINTF("%u", 4294966272U); + CMP_SPRINTF("%o", 511); + CMP_SPRINTF("%o", 4294966785U); + CMP_SPRINTF("%x", 305441741); + CMP_SPRINTF("%x", 3989525555U); + CMP_SPRINTF("%X", 305441741); + CMP_SPRINTF("%X", 3989525555U); + CMP_SPRINTF("%%"); } TEST_CASE("width", "[]" ) { - char buffer[100]; - - test::sprintf(buffer, "%1s", "Hello testing"); - REQUIRE(!strcmp(buffer, "Hello testing")); - - test::sprintf(buffer, "%1d", 1024); - REQUIRE(!strcmp(buffer, "1024")); - - test::sprintf(buffer, "%1d", -1024); - REQUIRE(!strcmp(buffer, "-1024")); - - test::sprintf(buffer, "%1i", 1024); - REQUIRE(!strcmp(buffer, "1024")); - - test::sprintf(buffer, "%1i", -1024); - REQUIRE(!strcmp(buffer, "-1024")); - - test::sprintf(buffer, "%1u", 1024); - REQUIRE(!strcmp(buffer, "1024")); - - test::sprintf(buffer, "%1u", 4294966272U); - REQUIRE(!strcmp(buffer, "4294966272")); - - test::sprintf(buffer, "%1o", 511); - REQUIRE(!strcmp(buffer, "777")); - - test::sprintf(buffer, "%1o", 4294966785U); - REQUIRE(!strcmp(buffer, "37777777001")); - - test::sprintf(buffer, "%1x", 305441741); - REQUIRE(!strcmp(buffer, "1234abcd")); - - test::sprintf(buffer, "%1x", 3989525555U); - REQUIRE(!strcmp(buffer, "edcb5433")); - - test::sprintf(buffer, "%1X", 305441741); - REQUIRE(!strcmp(buffer, "1234ABCD")); - - test::sprintf(buffer, "%1X", 3989525555U); - REQUIRE(!strcmp(buffer, "EDCB5433")); - - test::sprintf(buffer, "%1c", 'x'); - REQUIRE(!strcmp(buffer, "x")); + CMP_DEF; + + CMP_SPRINTF("%1s", "Hello testing"); + CMP_SPRINTF("%1d", 1024); + CMP_SPRINTF("%1d", -1024); + CMP_SPRINTF("%1i", 1024); + CMP_SPRINTF("%1i", -1024); + CMP_SPRINTF("%1u", 1024); + CMP_SPRINTF("%1u", 4294966272U); + CMP_SPRINTF("%1o", 511); + CMP_SPRINTF("%1o", 4294966785U); + CMP_SPRINTF("%1x", 305441741); + CMP_SPRINTF("%1x", 3989525555U); + CMP_SPRINTF("%1X", 305441741); + CMP_SPRINTF("%1X", 3989525555U); + CMP_SPRINTF("%1c", 'x'); } TEST_CASE("width 20", "[]" ) { - char buffer[100]; - - test::sprintf(buffer, "%20s", "Hello"); - REQUIRE(!strcmp(buffer, " Hello")); - - test::sprintf(buffer, "%20d", 1024); - REQUIRE(!strcmp(buffer, " 1024")); - - test::sprintf(buffer, "%20d", -1024); - REQUIRE(!strcmp(buffer, " -1024")); - - test::sprintf(buffer, "%20i", 1024); - REQUIRE(!strcmp(buffer, " 1024")); - - test::sprintf(buffer, "%20i", -1024); - REQUIRE(!strcmp(buffer, " -1024")); - - test::sprintf(buffer, "%20u", 1024); - REQUIRE(!strcmp(buffer, " 1024")); - - test::sprintf(buffer, "%20u", 4294966272U); - REQUIRE(!strcmp(buffer, " 4294966272")); - - test::sprintf(buffer, "%20o", 511); - REQUIRE(!strcmp(buffer, " 777")); - - test::sprintf(buffer, "%20o", 4294966785U); - REQUIRE(!strcmp(buffer, " 37777777001")); - - test::sprintf(buffer, "%20x", 305441741); - REQUIRE(!strcmp(buffer, " 1234abcd")); - - test::sprintf(buffer, "%20x", 3989525555U); - REQUIRE(!strcmp(buffer, " edcb5433")); - - test::sprintf(buffer, "%20X", 305441741); - REQUIRE(!strcmp(buffer, " 1234ABCD")); - - test::sprintf(buffer, "%20X", 3989525555U); - REQUIRE(!strcmp(buffer, " EDCB5433")); - - test::sprintf(buffer, "%20c", 'x'); - REQUIRE(!strcmp(buffer, " x")); + CMP_DEF; + + CMP_SPRINTF("%20s", "Hello"); + CMP_SPRINTF("%20d", 1024); + CMP_SPRINTF("%20d", -1024); + CMP_SPRINTF("%20i", 1024); + CMP_SPRINTF("%20i", -1024); + CMP_SPRINTF("%20u", 1024); + CMP_SPRINTF("%20u", 4294966272U); + CMP_SPRINTF("%20o", 511); + CMP_SPRINTF("%20o", 4294966785U); + CMP_SPRINTF("%20x", 305441741); + CMP_SPRINTF("%20x", 3989525555U); + CMP_SPRINTF("%20X", 305441741); + CMP_SPRINTF("%20X", 3989525555U); + CMP_SPRINTF("%20c", 'x'); } TEST_CASE("width *20", "[]" ) { - char buffer[100]; - - test::sprintf(buffer, "%*s", 20, "Hello"); - REQUIRE(!strcmp(buffer, " Hello")); - - test::sprintf(buffer, "%*d", 20, 1024); - REQUIRE(!strcmp(buffer, " 1024")); - - test::sprintf(buffer, "%*d", 20, -1024); - REQUIRE(!strcmp(buffer, " -1024")); - - test::sprintf(buffer, "%*i", 20, 1024); - REQUIRE(!strcmp(buffer, " 1024")); - - test::sprintf(buffer, "%*i", 20, -1024); - REQUIRE(!strcmp(buffer, " -1024")); - - test::sprintf(buffer, "%*u", 20, 1024); - REQUIRE(!strcmp(buffer, " 1024")); - - test::sprintf(buffer, "%*u", 20, 4294966272U); - REQUIRE(!strcmp(buffer, " 4294966272")); - - test::sprintf(buffer, "%*o", 20, 511); - REQUIRE(!strcmp(buffer, " 777")); - - test::sprintf(buffer, "%*o", 20, 4294966785U); - REQUIRE(!strcmp(buffer, " 37777777001")); - - test::sprintf(buffer, "%*x", 20, 305441741); - REQUIRE(!strcmp(buffer, " 1234abcd")); - - test::sprintf(buffer, "%*x", 20, 3989525555U); - REQUIRE(!strcmp(buffer, " edcb5433")); - - test::sprintf(buffer, "%*X", 20, 305441741); - REQUIRE(!strcmp(buffer, " 1234ABCD")); - - test::sprintf(buffer, "%*X", 20, 3989525555U); - REQUIRE(!strcmp(buffer, " EDCB5433")); - - test::sprintf(buffer, "%*c", 20,'x'); - REQUIRE(!strcmp(buffer, " x")); + CMP_DEF; + + CMP_SPRINTF("%*s", 20, "Hello"); + CMP_SPRINTF("%*d", 20, 1024); + CMP_SPRINTF("%*d", 20, -1024); + CMP_SPRINTF("%*i", 20, 1024); + CMP_SPRINTF("%*i", 20, -1024); + CMP_SPRINTF("%*u", 20, 1024); + CMP_SPRINTF("%*u", 20, 4294966272U); + CMP_SPRINTF("%*o", 20, 511); + CMP_SPRINTF("%*o", 20, 4294966785U); + CMP_SPRINTF("%*x", 20, 305441741); + CMP_SPRINTF("%*x", 20, 3989525555U); + CMP_SPRINTF("%*X", 20, 305441741); + CMP_SPRINTF("%*X", 20, 3989525555U); + CMP_SPRINTF("%*c", 20,'x'); } TEST_CASE("width -20", "[]" ) { - char buffer[100]; - - test::sprintf(buffer, "%-20s", "Hello"); - REQUIRE(!strcmp(buffer, "Hello ")); - - test::sprintf(buffer, "%-20d", 1024); - REQUIRE(!strcmp(buffer, "1024 ")); - - test::sprintf(buffer, "%-20d", -1024); - REQUIRE(!strcmp(buffer, "-1024 ")); - - test::sprintf(buffer, "%-20i", 1024); - REQUIRE(!strcmp(buffer, "1024 ")); - - test::sprintf(buffer, "%-20i", -1024); - REQUIRE(!strcmp(buffer, "-1024 ")); - - test::sprintf(buffer, "%-20u", 1024); - REQUIRE(!strcmp(buffer, "1024 ")); - - test::sprintf(buffer, "%-20.4f", 1024.1234); - REQUIRE(!strcmp(buffer, "1024.1234 ")); - - test::sprintf(buffer, "%-20u", 4294966272U); - REQUIRE(!strcmp(buffer, "4294966272 ")); - - test::sprintf(buffer, "%-20o", 511); - REQUIRE(!strcmp(buffer, "777 ")); - - test::sprintf(buffer, "%-20o", 4294966785U); - REQUIRE(!strcmp(buffer, "37777777001 ")); - - test::sprintf(buffer, "%-20x", 305441741); - REQUIRE(!strcmp(buffer, "1234abcd ")); - - test::sprintf(buffer, "%-20x", 3989525555U); - REQUIRE(!strcmp(buffer, "edcb5433 ")); - - test::sprintf(buffer, "%-20X", 305441741); - REQUIRE(!strcmp(buffer, "1234ABCD ")); - - test::sprintf(buffer, "%-20X", 3989525555U); - REQUIRE(!strcmp(buffer, "EDCB5433 ")); - - test::sprintf(buffer, "%-20c", 'x'); - REQUIRE(!strcmp(buffer, "x ")); - - test::sprintf(buffer, "|%5d| |%-2d| |%5d|", 9, 9, 9); - REQUIRE(!strcmp(buffer, "| 9| |9 | | 9|")); - - test::sprintf(buffer, "|%5d| |%-2d| |%5d|", 10, 10, 10); - REQUIRE(!strcmp(buffer, "| 10| |10| | 10|")); - - test::sprintf(buffer, "|%5d| |%-12d| |%5d|", 9, 9, 9); - REQUIRE(!strcmp(buffer, "| 9| |9 | | 9|")); - - test::sprintf(buffer, "|%5d| |%-12d| |%5d|", 10, 10, 10); - REQUIRE(!strcmp(buffer, "| 10| |10 | | 10|")); + CMP_DEF; + + CMP_SPRINTF("%-20s", "Hello"); + CMP_SPRINTF("%-20d", 1024); + CMP_SPRINTF("%-20d", -1024); + CMP_SPRINTF("%-20i", 1024); + CMP_SPRINTF("%-20i", -1024); + CMP_SPRINTF("%-20u", 1024); + CMP_SPRINTF("%-20.4f", 1024.1234); + CMP_SPRINTF("%-20u", 4294966272U); + CMP_SPRINTF("%-20o", 511); + CMP_SPRINTF("%-20o", 4294966785U); + CMP_SPRINTF("%-20x", 305441741); + CMP_SPRINTF("%-20x", 3989525555U); + CMP_SPRINTF("%-20X", 305441741); + CMP_SPRINTF("%-20X", 3989525555U); + CMP_SPRINTF("%-20c", 'x'); + CMP_SPRINTF("|%5d| |%-2d| |%5d|", 9, 9, 9); + CMP_SPRINTF("|%5d| |%-2d| |%5d|", 10, 10, 10); + CMP_SPRINTF("|%5d| |%-12d| |%5d|", 9, 9, 9); + CMP_SPRINTF("|%5d| |%-12d| |%5d|", 10, 10, 10); } TEST_CASE("width 0-20", "[]" ) { - char buffer[100]; - - test::sprintf(buffer, "%0-20s", "Hello"); - REQUIRE(!strcmp(buffer, "Hello ")); - - test::sprintf(buffer, "%0-20d", 1024); - REQUIRE(!strcmp(buffer, "1024 ")); - - test::sprintf(buffer, "%0-20d", -1024); - REQUIRE(!strcmp(buffer, "-1024 ")); - - test::sprintf(buffer, "%0-20i", 1024); - REQUIRE(!strcmp(buffer, "1024 ")); - - test::sprintf(buffer, "%0-20i", -1024); - REQUIRE(!strcmp(buffer, "-1024 ")); - - test::sprintf(buffer, "%0-20u", 1024); - REQUIRE(!strcmp(buffer, "1024 ")); - - test::sprintf(buffer, "%0-20u", 4294966272U); - REQUIRE(!strcmp(buffer, "4294966272 ")); - - test::sprintf(buffer, "%0-20o", 511); - REQUIRE(!strcmp(buffer, "777 ")); - - test::sprintf(buffer, "%0-20o", 4294966785U); - REQUIRE(!strcmp(buffer, "37777777001 ")); - - test::sprintf(buffer, "%0-20x", 305441741); - REQUIRE(!strcmp(buffer, "1234abcd ")); - - test::sprintf(buffer, "%0-20x", 3989525555U); - REQUIRE(!strcmp(buffer, "edcb5433 ")); - - test::sprintf(buffer, "%0-20X", 305441741); - REQUIRE(!strcmp(buffer, "1234ABCD ")); - - test::sprintf(buffer, "%0-20X", 3989525555U); - REQUIRE(!strcmp(buffer, "EDCB5433 ")); - - test::sprintf(buffer, "%0-20c", 'x'); - REQUIRE(!strcmp(buffer, "x ")); + CMP_DEF; + + CMP_SPRINTF("%0-20s", "Hello"); + CMP_SPRINTF("%0-20d", 1024); + CMP_SPRINTF("%0-20d", -1024); + CMP_SPRINTF("%0-20i", 1024); + CMP_SPRINTF("%0-20i", -1024); + CMP_SPRINTF("%0-20u", 1024); + CMP_SPRINTF("%0-20u", 4294966272U); + CMP_SPRINTF("%0-20o", 511); + CMP_SPRINTF("%0-20o", 4294966785U); + CMP_SPRINTF("%0-20x", 305441741); + CMP_SPRINTF("%0-20x", 3989525555U); + CMP_SPRINTF("%0-20X", 305441741); + CMP_SPRINTF("%0-20X", 3989525555U); + CMP_SPRINTF("%0-20c", 'x'); } TEST_CASE("padding 20", "[]" ) { - char buffer[100]; - - test::sprintf(buffer, "%020d", 1024); - REQUIRE(!strcmp(buffer, "00000000000000001024")); - - test::sprintf(buffer, "%020d", -1024); - REQUIRE(!strcmp(buffer, "-0000000000000001024")); - - test::sprintf(buffer, "%020i", 1024); - REQUIRE(!strcmp(buffer, "00000000000000001024")); - - test::sprintf(buffer, "%020i", -1024); - REQUIRE(!strcmp(buffer, "-0000000000000001024")); - - test::sprintf(buffer, "%020u", 1024); - REQUIRE(!strcmp(buffer, "00000000000000001024")); - - test::sprintf(buffer, "%020u", 4294966272U); - REQUIRE(!strcmp(buffer, "00000000004294966272")); - - test::sprintf(buffer, "%020o", 511); - REQUIRE(!strcmp(buffer, "00000000000000000777")); - - test::sprintf(buffer, "%020o", 4294966785U); - REQUIRE(!strcmp(buffer, "00000000037777777001")); - - test::sprintf(buffer, "%020x", 305441741); - REQUIRE(!strcmp(buffer, "0000000000001234abcd")); - - test::sprintf(buffer, "%020x", 3989525555U); - REQUIRE(!strcmp(buffer, "000000000000edcb5433")); - - test::sprintf(buffer, "%020X", 305441741); - REQUIRE(!strcmp(buffer, "0000000000001234ABCD")); - - test::sprintf(buffer, "%020X", 3989525555U); - REQUIRE(!strcmp(buffer, "000000000000EDCB5433")); + CMP_DEF; + + CMP_SPRINTF("%020d", 1024); + CMP_SPRINTF("%020d", -1024); + CMP_SPRINTF("%020i", 1024); + CMP_SPRINTF("%020i", -1024); + CMP_SPRINTF("%020u", 1024); + CMP_SPRINTF("%020u", 4294966272U); + CMP_SPRINTF("%020o", 511); + CMP_SPRINTF("%020o", 4294966785U); + CMP_SPRINTF("%020x", 305441741); + CMP_SPRINTF("%020x", 3989525555U); + CMP_SPRINTF("%020X", 305441741); + CMP_SPRINTF("%020X", 3989525555U); } TEST_CASE("padding .20", "[]" ) { - char buffer[100]; - - test::sprintf(buffer, "%.20d", 1024); - REQUIRE(!strcmp(buffer, "00000000000000001024")); - - test::sprintf(buffer, "%.20d", -1024); - REQUIRE(!strcmp(buffer, "-00000000000000001024")); - - test::sprintf(buffer, "%.20i", 1024); - REQUIRE(!strcmp(buffer, "00000000000000001024")); - - test::sprintf(buffer, "%.20i", -1024); - REQUIRE(!strcmp(buffer, "-00000000000000001024")); - - test::sprintf(buffer, "%.20u", 1024); - REQUIRE(!strcmp(buffer, "00000000000000001024")); - - test::sprintf(buffer, "%.20u", 4294966272U); - REQUIRE(!strcmp(buffer, "00000000004294966272")); - - test::sprintf(buffer, "%.20o", 511); - REQUIRE(!strcmp(buffer, "00000000000000000777")); - - test::sprintf(buffer, "%.20o", 4294966785U); - REQUIRE(!strcmp(buffer, "00000000037777777001")); - - test::sprintf(buffer, "%.20x", 305441741); - REQUIRE(!strcmp(buffer, "0000000000001234abcd")); - - test::sprintf(buffer, "%.20x", 3989525555U); - REQUIRE(!strcmp(buffer, "000000000000edcb5433")); - - test::sprintf(buffer, "%.20X", 305441741); - REQUIRE(!strcmp(buffer, "0000000000001234ABCD")); - - test::sprintf(buffer, "%.20X", 3989525555U); - REQUIRE(!strcmp(buffer, "000000000000EDCB5433")); + CMP_DEF; + + CMP_SPRINTF("%.20d", 1024); + CMP_SPRINTF("%.20d", -1024); + CMP_SPRINTF("%.20i", 1024); + CMP_SPRINTF("%.20i", -1024); + CMP_SPRINTF("%.20u", 1024); + CMP_SPRINTF("%.20u", 4294966272U); + CMP_SPRINTF("%.20o", 511); + CMP_SPRINTF("%.20o", 4294966785U); + CMP_SPRINTF("%.20x", 305441741); + CMP_SPRINTF("%.20x", 3989525555U); + CMP_SPRINTF("%.20X", 305441741); + CMP_SPRINTF("%.20X", 3989525555U); } TEST_CASE("padding #020", "[]" ) { - char buffer[100]; - - test::sprintf(buffer, "%#020d", 1024); - REQUIRE(!strcmp(buffer, "00000000000000001024")); - - test::sprintf(buffer, "%#020d", -1024); - REQUIRE(!strcmp(buffer, "-0000000000000001024")); - - test::sprintf(buffer, "%#020i", 1024); - REQUIRE(!strcmp(buffer, "00000000000000001024")); - - test::sprintf(buffer, "%#020i", -1024); - REQUIRE(!strcmp(buffer, "-0000000000000001024")); - - test::sprintf(buffer, "%#020u", 1024); - REQUIRE(!strcmp(buffer, "00000000000000001024")); - - test::sprintf(buffer, "%#020u", 4294966272U); - REQUIRE(!strcmp(buffer, "00000000004294966272")); - - test::sprintf(buffer, "%#020o", 511); - REQUIRE(!strcmp(buffer, "00000000000000000777")); - - test::sprintf(buffer, "%#020o", 4294966785U); - REQUIRE(!strcmp(buffer, "00000000037777777001")); - - test::sprintf(buffer, "%#020x", 305441741); - REQUIRE(!strcmp(buffer, "0x00000000001234abcd")); - - test::sprintf(buffer, "%#020x", 3989525555U); - REQUIRE(!strcmp(buffer, "0x0000000000edcb5433")); + CMP_DEF; + + CMP_SPRINTF("%#020d", 1024); + CMP_SPRINTF("%#020d", -1024); + CMP_SPRINTF("%#020i", 1024); + CMP_SPRINTF("%#020i", -1024); + CMP_SPRINTF("%#020u", 1024); + CMP_SPRINTF("%#020u", 4294966272U); + CMP_SPRINTF("%#020o", 511); + CMP_SPRINTF("%#020o", 4294966785U); + CMP_SPRINTF("%#020x", 305441741); + CMP_SPRINTF("%#020x", 3989525555U); + CMP_SPRINTF("%#020X", 305441741); + CMP_SPRINTF("%#020X", 3989525555U); +} - test::sprintf(buffer, "%#020X", 305441741); - REQUIRE(!strcmp(buffer, "0X00000000001234ABCD")); +TEST_CASE("variable length padding", "[]") { + CMP_DEF; - test::sprintf(buffer, "%#020X", 3989525555U); - REQUIRE(!strcmp(buffer, "0X0000000000EDCB5433")); + for (int pad = 0; pad <= 20; pad++) + CMP_SPRINTF("%#*x", pad, 1234); } - TEST_CASE("padding #20", "[]" ) { - char buffer[100]; - - test::sprintf(buffer, "%#20d", 1024); - REQUIRE(!strcmp(buffer, " 1024")); - - test::sprintf(buffer, "%#20d", -1024); - REQUIRE(!strcmp(buffer, " -1024")); - - test::sprintf(buffer, "%#20i", 1024); - REQUIRE(!strcmp(buffer, " 1024")); - - test::sprintf(buffer, "%#20i", -1024); - REQUIRE(!strcmp(buffer, " -1024")); - - test::sprintf(buffer, "%#20u", 1024); - REQUIRE(!strcmp(buffer, " 1024")); - - test::sprintf(buffer, "%#20u", 4294966272U); - REQUIRE(!strcmp(buffer, " 4294966272")); - - test::sprintf(buffer, "%#20o", 511); - REQUIRE(!strcmp(buffer, " 0777")); - - test::sprintf(buffer, "%#20o", 4294966785U); - REQUIRE(!strcmp(buffer, " 037777777001")); - - test::sprintf(buffer, "%#20x", 305441741); - REQUIRE(!strcmp(buffer, " 0x1234abcd")); - - test::sprintf(buffer, "%#20x", 3989525555U); - REQUIRE(!strcmp(buffer, " 0xedcb5433")); - - test::sprintf(buffer, "%#20X", 305441741); - REQUIRE(!strcmp(buffer, " 0X1234ABCD")); - - test::sprintf(buffer, "%#20X", 3989525555U); - REQUIRE(!strcmp(buffer, " 0XEDCB5433")); + CMP_DEF; + + CMP_SPRINTF("%#20d", 1024); + CMP_SPRINTF("%#20d", -1024); + CMP_SPRINTF("%#20i", 1024); + CMP_SPRINTF("%#20i", -1024); + CMP_SPRINTF("%#20u", 1024); + CMP_SPRINTF("%#20u", 4294966272U); + CMP_SPRINTF("%#20o", 511); + CMP_SPRINTF("%#20o", 4294966785U); + CMP_SPRINTF("%#20x", 305441741); + CMP_SPRINTF("%#20x", 3989525555U); + CMP_SPRINTF("%#20X", 305441741); + CMP_SPRINTF("%#20X", 3989525555U); } TEST_CASE("padding 20.5", "[]" ) { - char buffer[100]; - - test::sprintf(buffer, "%20.5d", 1024); - REQUIRE(!strcmp(buffer, " 01024")); - - test::sprintf(buffer, "%20.5d", -1024); - REQUIRE(!strcmp(buffer, " -01024")); - - test::sprintf(buffer, "%20.5i", 1024); - REQUIRE(!strcmp(buffer, " 01024")); - - test::sprintf(buffer, "%20.5i", -1024); - REQUIRE(!strcmp(buffer, " -01024")); - - test::sprintf(buffer, "%20.5u", 1024); - REQUIRE(!strcmp(buffer, " 01024")); - - test::sprintf(buffer, "%20.5u", 4294966272U); - REQUIRE(!strcmp(buffer, " 4294966272")); - - test::sprintf(buffer, "%20.5o", 511); - REQUIRE(!strcmp(buffer, " 00777")); - - test::sprintf(buffer, "%20.5o", 4294966785U); - REQUIRE(!strcmp(buffer, " 37777777001")); - - test::sprintf(buffer, "%20.5x", 305441741); - REQUIRE(!strcmp(buffer, " 1234abcd")); - - test::sprintf(buffer, "%20.10x", 3989525555U); - REQUIRE(!strcmp(buffer, " 00edcb5433")); - - test::sprintf(buffer, "%20.5X", 305441741); - REQUIRE(!strcmp(buffer, " 1234ABCD")); - - test::sprintf(buffer, "%20.10X", 3989525555U); - REQUIRE(!strcmp(buffer, " 00EDCB5433")); + CMP_DEF; + + CMP_SPRINTF("%20.5d", 1024); + CMP_SPRINTF("%20.5d", -1024); + CMP_SPRINTF("%20.5i", 1024); + CMP_SPRINTF("%20.5i", -1024); + CMP_SPRINTF("%20.5u", 1024); + CMP_SPRINTF("%20.5u", 4294966272U); + CMP_SPRINTF("%20.5o", 511); + CMP_SPRINTF("%20.5o", 4294966785U); + CMP_SPRINTF("%20.5x", 305441741); + CMP_SPRINTF("%20.10x", 3989525555U); + CMP_SPRINTF("%20.5X", 305441741); + CMP_SPRINTF("%20.10X", 3989525555U); } TEST_CASE("padding neg numbers", "[]" ) { - char buffer[100]; + CMP_DEF; // space padding - test::sprintf(buffer, "% 1d", -5); - REQUIRE(!strcmp(buffer, "-5")); - - test::sprintf(buffer, "% 2d", -5); - REQUIRE(!strcmp(buffer, "-5")); - - test::sprintf(buffer, "% 3d", -5); - REQUIRE(!strcmp(buffer, " -5")); - - test::sprintf(buffer, "% 4d", -5); - REQUIRE(!strcmp(buffer, " -5")); - + CMP_SPRINTF("% 1d", -5); + CMP_SPRINTF("% 2d", -5); + CMP_SPRINTF("% 3d", -5); + CMP_SPRINTF("% 4d", -5); // zero padding - test::sprintf(buffer, "%01d", -5); - REQUIRE(!strcmp(buffer, "-5")); - - test::sprintf(buffer, "%02d", -5); - REQUIRE(!strcmp(buffer, "-5")); - - test::sprintf(buffer, "%03d", -5); - REQUIRE(!strcmp(buffer, "-05")); - - test::sprintf(buffer, "%04d", -5); - REQUIRE(!strcmp(buffer, "-005")); + CMP_SPRINTF("%01d", -5); + CMP_SPRINTF("%02d", -5); + CMP_SPRINTF("%03d", -5); + CMP_SPRINTF("%04d", -5); } TEST_CASE("float padding neg numbers", "[]" ) { - char buffer[100]; + CMP_DEF; // space padding - test::sprintf(buffer, "% 3.1f", -5.); - REQUIRE(!strcmp(buffer, "-5.0")); - - test::sprintf(buffer, "% 4.1f", -5.); - REQUIRE(!strcmp(buffer, "-5.0")); - - test::sprintf(buffer, "% 5.1f", -5.); - REQUIRE(!strcmp(buffer, " -5.0")); - + CMP_SPRINTF("% 3.1f", -5.); + CMP_SPRINTF("% 4.1f", -5.); + CMP_SPRINTF("% 5.1f", -5.); #ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL - test::sprintf(buffer, "% 6.1g", -5.); - REQUIRE(!strcmp(buffer, " -5")); - - test::sprintf(buffer, "% 6.1e", -5.); - REQUIRE(!strcmp(buffer, "-5.0e+00")); - - test::sprintf(buffer, "% 10.1e", -5.); - REQUIRE(!strcmp(buffer, " -5.0e+00")); + CMP_SPRINTF("% 6.1g", -5.); + CMP_SPRINTF("% 6.1e", -5.); + CMP_SPRINTF("% 10.1e", -5.); #endif // zero padding - test::sprintf(buffer, "%03.1f", -5.); - REQUIRE(!strcmp(buffer, "-5.0")); - - test::sprintf(buffer, "%04.1f", -5.); - REQUIRE(!strcmp(buffer, "-5.0")); - - test::sprintf(buffer, "%05.1f", -5.); - REQUIRE(!strcmp(buffer, "-05.0")); - + CMP_SPRINTF("%03.1f", -5.); + CMP_SPRINTF("%04.1f", -5.); + CMP_SPRINTF("%05.1f", -5.); // zero padding no decimal point - test::sprintf(buffer, "%01.0f", -5.); - REQUIRE(!strcmp(buffer, "-5")); - - test::sprintf(buffer, "%02.0f", -5.); - REQUIRE(!strcmp(buffer, "-5")); - - test::sprintf(buffer, "%03.0f", -5.); - REQUIRE(!strcmp(buffer, "-05")); - + CMP_SPRINTF("%01.0f", -5.); + CMP_SPRINTF("%02.0f", -5.); + CMP_SPRINTF("%03.0f", -5.); #ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL - test::sprintf(buffer, "%010.1e", -5.); - REQUIRE(!strcmp(buffer, "-005.0e+00")); - - test::sprintf(buffer, "%07.0E", -5.); - REQUIRE(!strcmp(buffer, "-05E+00")); - - test::sprintf(buffer, "%03.0g", -5.); - REQUIRE(!strcmp(buffer, "-05")); + CMP_SPRINTF("%010.1e", -5.); + CMP_SPRINTF("%07.0E", -5.); + CMP_SPRINTF("%03.0g", -5.); #endif } TEST_CASE("length", "[]" ) { - char buffer[100]; - - test::sprintf(buffer, "%.0s", "Hello testing"); - REQUIRE(!strcmp(buffer, "")); - - test::sprintf(buffer, "%20.0s", "Hello testing"); - REQUIRE(!strcmp(buffer, " ")); - - test::sprintf(buffer, "%.s", "Hello testing"); - REQUIRE(!strcmp(buffer, "")); - - test::sprintf(buffer, "%20.s", "Hello testing"); - REQUIRE(!strcmp(buffer, " ")); - - test::sprintf(buffer, "%20.0d", 1024); - REQUIRE(!strcmp(buffer, " 1024")); - - test::sprintf(buffer, "%20.0d", -1024); - REQUIRE(!strcmp(buffer, " -1024")); - - test::sprintf(buffer, "%20.d", 0); - REQUIRE(!strcmp(buffer, " ")); - - test::sprintf(buffer, "%20.0i", 1024); - REQUIRE(!strcmp(buffer, " 1024")); - - test::sprintf(buffer, "%20.i", -1024); - REQUIRE(!strcmp(buffer, " -1024")); - - test::sprintf(buffer, "%20.i", 0); - REQUIRE(!strcmp(buffer, " ")); - - test::sprintf(buffer, "%20.u", 1024); - REQUIRE(!strcmp(buffer, " 1024")); - - test::sprintf(buffer, "%20.0u", 4294966272U); - REQUIRE(!strcmp(buffer, " 4294966272")); - - test::sprintf(buffer, "%20.u", 0U); - REQUIRE(!strcmp(buffer, " ")); - - test::sprintf(buffer, "%20.o", 511); - REQUIRE(!strcmp(buffer, " 777")); - - test::sprintf(buffer, "%20.0o", 4294966785U); - REQUIRE(!strcmp(buffer, " 37777777001")); - - test::sprintf(buffer, "%20.o", 0U); - REQUIRE(!strcmp(buffer, " ")); - - test::sprintf(buffer, "%20.x", 305441741); - REQUIRE(!strcmp(buffer, " 1234abcd")); - - test::sprintf(buffer, "%50.x", 305441741); - REQUIRE(!strcmp(buffer, " 1234abcd")); - - test::sprintf(buffer, "%50.x%10.u", 305441741, 12345); - REQUIRE(!strcmp(buffer, " 1234abcd 12345")); - - test::sprintf(buffer, "%20.0x", 3989525555U); - REQUIRE(!strcmp(buffer, " edcb5433")); - - test::sprintf(buffer, "%20.x", 0U); - REQUIRE(!strcmp(buffer, " ")); - - test::sprintf(buffer, "%20.X", 305441741); - REQUIRE(!strcmp(buffer, " 1234ABCD")); - - test::sprintf(buffer, "%20.0X", 3989525555U); - REQUIRE(!strcmp(buffer, " EDCB5433")); - - test::sprintf(buffer, "%20.X", 0U); - REQUIRE(!strcmp(buffer, " ")); - - test::sprintf(buffer, "%02.0u", 0U); - REQUIRE(!strcmp(buffer, " ")); - - test::sprintf(buffer, "%02.0d", 0); - REQUIRE(!strcmp(buffer, " ")); + CMP_DEF; + + CMP_SPRINTF("%.0s", "Hello testing"); + CMP_SPRINTF("%20.0s", "Hello testing"); + CMP_SPRINTF("%.s", "Hello testing"); + CMP_SPRINTF("%20.s", "Hello testing"); + CMP_SPRINTF("%20.0d", 1024); + CMP_SPRINTF("%20.0d", -1024); + CMP_SPRINTF("%20.d", 0); + CMP_SPRINTF("%20.0i", 1024); + CMP_SPRINTF("%20.i", -1024); + CMP_SPRINTF("%20.i", 0); + CMP_SPRINTF("%20.u", 1024); + CMP_SPRINTF("%20.0u", 4294966272U); + CMP_SPRINTF("%20.u", 0U); + CMP_SPRINTF("%20.o", 511); + CMP_SPRINTF("%20.0o", 4294966785U); + CMP_SPRINTF("%20.o", 0U); + CMP_SPRINTF("%20.x", 305441741); + CMP_SPRINTF("%50.x", 305441741); + CMP_SPRINTF("%50.x%10.u", 305441741, 12345); + CMP_SPRINTF("%20.0x", 3989525555U); + CMP_SPRINTF("%20.x", 0U); + CMP_SPRINTF("%20.X", 305441741); + CMP_SPRINTF("%20.0X", 3989525555U); + CMP_SPRINTF("%20.X", 0U); + CMP_SPRINTF("%02.0u", 0U); + CMP_SPRINTF("%02.0d", 0); } TEST_CASE("float", "[]" ) { - char buffer[100]; + CMP_DEF; // test special-case floats using math.h macros - test::sprintf(buffer, "%8f", NAN); - REQUIRE(!strcmp(buffer, " nan")); - - test::sprintf(buffer, "%8f", INFINITY); - REQUIRE(!strcmp(buffer, " inf")); - - test::sprintf(buffer, "%-8f", -INFINITY); - REQUIRE(!strcmp(buffer, "-inf ")); - + CMP_SPRINTF("%8f", NAN); + CMP_SPRINTF("%8f", (double)INFINITY); + CMP_SPRINTF("%-8f", (double)-INFINITY); #ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL - test::sprintf(buffer, "%+8e", INFINITY); - REQUIRE(!strcmp(buffer, " +inf")); + CMP_SPRINTF("%+8e", INFINITY); #endif - test::sprintf(buffer, "%.4f", 3.1415354); - REQUIRE(!strcmp(buffer, "3.1415")); - - test::sprintf(buffer, "%.3f", 30343.1415354); - REQUIRE(!strcmp(buffer, "30343.142")); - - test::sprintf(buffer, "%.0f", 34.1415354); - REQUIRE(!strcmp(buffer, "34")); - - test::sprintf(buffer, "%.0f", 1.3); - REQUIRE(!strcmp(buffer, "1")); - - test::sprintf(buffer, "%.0f", 1.55); - REQUIRE(!strcmp(buffer, "2")); - - test::sprintf(buffer, "%.1f", 1.64); - REQUIRE(!strcmp(buffer, "1.6")); - - test::sprintf(buffer, "%.2f", 42.8952); - REQUIRE(!strcmp(buffer, "42.90")); - - test::sprintf(buffer, "%.9f", 42.8952); - REQUIRE(!strcmp(buffer, "42.895200000")); - - test::sprintf(buffer, "%.10f", 42.895223); - REQUIRE(!strcmp(buffer, "42.8952230000")); - - // this testcase checks, that the precision is truncated to 9 digits. + CMP_SPRINTF("%.4f", 3.1415354); + CMP_SPRINTF("%.3f", 30343.1415354); + CMP_SPRINTF("%.0f", 34.1415354); + CMP_SPRINTF("%.0f", 1.3); + CMP_SPRINTF("%.0f", 1.55); + CMP_SPRINTF("%.1f", 1.64); + CMP_SPRINTF("%.2f", 42.8952); + CMP_SPRINTF("%.9f", 42.8952); + CMP_SPRINTF("%.10f", 42.895223); + CMP_SPRINTF("%.1f", -.95); + CMP_SPRINTF("%.1f", .95); + CMP_SPRINTF("%.1f", .25); + CMP_SPRINTF("%.1f", .75); + // this testcase checks, that the precision is truncated to 9 digits. // a perfect working float should return the whole number - test::sprintf(buffer, "%.12f", 42.89522312345678); - REQUIRE(!strcmp(buffer, "42.895223123000")); - + TEST_SPRINTF(Equals("42.895223123000"), "%.12f", 42.89522312345678); // this testcase checks, that the precision is truncated AND rounded to 9 digits. // a perfect working float should return the whole number - test::sprintf(buffer, "%.12f", 42.89522387654321); - REQUIRE(!strcmp(buffer, "42.895223877000")); - - test::sprintf(buffer, "%6.2f", 42.8952); - REQUIRE(!strcmp(buffer, " 42.90")); - - test::sprintf(buffer, "%+6.2f", 42.8952); - REQUIRE(!strcmp(buffer, "+42.90")); - - test::sprintf(buffer, "%+5.1f", 42.9252); - REQUIRE(!strcmp(buffer, "+42.9")); - - test::sprintf(buffer, "%f", 42.5); - REQUIRE(!strcmp(buffer, "42.500000")); - - test::sprintf(buffer, "%.1f", 42.5); - REQUIRE(!strcmp(buffer, "42.5")); - - test::sprintf(buffer, "%f", 42167.0); - REQUIRE(!strcmp(buffer, "42167.000000")); - - test::sprintf(buffer, "%.9f", -12345.987654321); - REQUIRE(!strcmp(buffer, "-12345.987654321")); - - test::sprintf(buffer, "%.1f", 3.999); - REQUIRE(!strcmp(buffer, "4.0")); - - test::sprintf(buffer, "%.0f", 3.5); - REQUIRE(!strcmp(buffer, "4")); - - test::sprintf(buffer, "%.0f", 4.5); - REQUIRE(!strcmp(buffer, "4")); - - test::sprintf(buffer, "%.0f", 3.49); - REQUIRE(!strcmp(buffer, "3")); - - test::sprintf(buffer, "%.1f", 3.49); - REQUIRE(!strcmp(buffer, "3.5")); - - test::sprintf(buffer, "a%-5.1f", 0.5); - REQUIRE(!strcmp(buffer, "a0.5 ")); - - test::sprintf(buffer, "a%-5.1fend", 0.5); - REQUIRE(!strcmp(buffer, "a0.5 end")); - + TEST_SPRINTF(Equals("42.895223877000"), "%.12f", 42.89522387654321); + CMP_SPRINTF("%6.2f", 42.8952); + CMP_SPRINTF("%+6.2f", 42.8952); + CMP_SPRINTF("%+5.1f", 42.9252); + CMP_SPRINTF("%f", 42.5); + CMP_SPRINTF("%.1f", 42.5); + CMP_SPRINTF("%f", 42167.0); + CMP_SPRINTF("%.9f", -12345.987654321); + CMP_SPRINTF("%.1f", 3.999); + CMP_SPRINTF("%.0f", 3.5); + CMP_SPRINTF("%.0f", 4.5); + CMP_SPRINTF("%.0f", 3.49); + CMP_SPRINTF("%.1f", 3.49); + CMP_SPRINTF("a%-5.1f", 0.5); + CMP_SPRINTF("a%-5.1fend", 0.5); #ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL - test::sprintf(buffer, "%G", 12345.678); - REQUIRE(!strcmp(buffer, "12345.7")); - - test::sprintf(buffer, "%.7G", 12345.678); - REQUIRE(!strcmp(buffer, "12345.68")); - - test::sprintf(buffer, "%.5G", 123456789.); - REQUIRE(!strcmp(buffer, "1.2346E+08")); - - test::sprintf(buffer, "%.6G", 12345.); - REQUIRE(!strcmp(buffer, "12345.0")); - - test::sprintf(buffer, "%+12.4g", 123456789.); - REQUIRE(!strcmp(buffer, " +1.235e+08")); - - test::sprintf(buffer, "%.2G", 0.001234); - REQUIRE(!strcmp(buffer, "0.0012")); - - test::sprintf(buffer, "%+10.4G", 0.001234); - REQUIRE(!strcmp(buffer, " +0.001234")); - - test::sprintf(buffer, "%+012.4g", 0.00001234); - REQUIRE(!strcmp(buffer, "+001.234e-05")); - - test::sprintf(buffer, "%.3g", -1.2345e-308); - REQUIRE(!strcmp(buffer, "-1.23e-308")); - - test::sprintf(buffer, "%+.3E", 1.23e+308); - REQUIRE(!strcmp(buffer, "+1.230E+308")); + CMP_SPRINTF("%G", 12345.678); + CMP_SPRINTF("%.7G", 12345.678); + CMP_SPRINTF("%.5G", 123456789.); + TEST_SPRINTF(Equals("12345.0"), "%.6G", 12345.); + CMP_SPRINTF("%+12.4g", 123456789.); + CMP_SPRINTF("%.2G", 0.001234); + CMP_SPRINTF("%+10.4G", 0.001234); + CMP_SPRINTF("%+012.4g", 0.00001234); + CMP_SPRINTF("%.3g", -1.2345e-308); + CMP_SPRINTF("%+.3E", 1.23e+308); #endif // out of range for float: should switch to exp notation if supported, else empty - test::sprintf(buffer, "%.1f", 1E20); #ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL - REQUIRE(!strcmp(buffer, "1.0e+20")); + TEST_SPRINTF(Equals("1.0e+20"), "%.1f", 1E20); #else - REQUIRE(!strcmp(buffer, "")); + TEST_SPRINTF(Equals(""), "%.1f", 1E20); #endif // brute force float bool fail = false; std::stringstream str; str.precision(5); - for (float i = -100000; i < 100000; i += 1) { - test::sprintf(buffer, "%.5f", i / 10000); - str.str(""); - str << std::fixed << i / 10000; - fail = fail || !!strcmp(buffer, str.str().c_str()); + + for (double i = -200000; i < 200000; i += 1) { + CMP_SPRINTF("%.5f", i / 20000); } - REQUIRE(!fail); + for (double i = -1; i <= 1; i += .05) { + CMP_SPRINTF("%.1f", i); + } #ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL // brute force exp str.setf(std::ios::scientific, std::ios::floatfield); - for (float i = -1e20; i < 1e20; i += 1e15) { - test::sprintf(buffer, "%.5f", i); + for (float i = -1e20f; i < 1e20f; i += 1e15f) { + /*test::*/sprintf(buffer, "%.5f", (double)i); str.str(""); str << i; fail = fail || !!strcmp(buffer, str.str().c_str()); @@ -1240,274 +702,1118 @@ TEST_CASE("float", "[]" ) { TEST_CASE("types", "[]" ) { - char buffer[100]; - - test::sprintf(buffer, "%i", 0); - REQUIRE(!strcmp(buffer, "0")); - - test::sprintf(buffer, "%i", 1234); - REQUIRE(!strcmp(buffer, "1234")); - - test::sprintf(buffer, "%i", 32767); - REQUIRE(!strcmp(buffer, "32767")); - - test::sprintf(buffer, "%i", -32767); - REQUIRE(!strcmp(buffer, "-32767")); - - test::sprintf(buffer, "%li", 30L); - REQUIRE(!strcmp(buffer, "30")); - - test::sprintf(buffer, "%li", -2147483647L); - REQUIRE(!strcmp(buffer, "-2147483647")); - - test::sprintf(buffer, "%li", 2147483647L); - REQUIRE(!strcmp(buffer, "2147483647")); - - test::sprintf(buffer, "%lli", 30LL); - REQUIRE(!strcmp(buffer, "30")); - - test::sprintf(buffer, "%lli", -9223372036854775807LL); - REQUIRE(!strcmp(buffer, "-9223372036854775807")); - - test::sprintf(buffer, "%lli", 9223372036854775807LL); - REQUIRE(!strcmp(buffer, "9223372036854775807")); - - test::sprintf(buffer, "%lu", 100000L); - REQUIRE(!strcmp(buffer, "100000")); - - test::sprintf(buffer, "%lu", 0xFFFFFFFFL); - REQUIRE(!strcmp(buffer, "4294967295")); - - test::sprintf(buffer, "%llu", 281474976710656LLU); - REQUIRE(!strcmp(buffer, "281474976710656")); - - test::sprintf(buffer, "%llu", 18446744073709551615LLU); - REQUIRE(!strcmp(buffer, "18446744073709551615")); - - test::sprintf(buffer, "%zu", 2147483647UL); - REQUIRE(!strcmp(buffer, "2147483647")); - - test::sprintf(buffer, "%zd", 2147483647UL); - REQUIRE(!strcmp(buffer, "2147483647")); - + CMP_DEF; + + CMP_SPRINTF("%i", 0); + CMP_SPRINTF("%i", 1234); + CMP_SPRINTF("%i", 32767); + CMP_SPRINTF("%i", -32767); + CMP_SPRINTF("%li", 30L); + CMP_SPRINTF("%li", -2147483647L); + CMP_SPRINTF("%li", 2147483647L); + CMP_SPRINTF("%lli", 30LL); + CMP_SPRINTF("%lli", -9223372036854775807LL); + CMP_SPRINTF("%lli", 9223372036854775807LL); + CMP_SPRINTF("%lu", 100000L); + CMP_SPRINTF("%lu", 0xFFFFFFFFL); + CMP_SPRINTF("%llu", 281474976710656LLU); + CMP_SPRINTF("%llu", 18446744073709551615LLU); + CMP_SPRINTF("%zu", 2147483647UL); + CMP_SPRINTF("%zd", 2147483647UL); if (sizeof(size_t) == sizeof(long)) { - test::sprintf(buffer, "%zi", -2147483647L); - REQUIRE(!strcmp(buffer, "-2147483647")); + CMP_SPRINTF("%zi", -2147483647L); } else { - test::sprintf(buffer, "%zi", -2147483647LL); - REQUIRE(!strcmp(buffer, "-2147483647")); + CMP_SPRINTF("%zi", -2147483647LL); } - test::sprintf(buffer, "%b", 60000); - REQUIRE(!strcmp(buffer, "1110101001100000")); - - test::sprintf(buffer, "%lb", 12345678L); - REQUIRE(!strcmp(buffer, "101111000110000101001110")); - - test::sprintf(buffer, "%o", 60000); - REQUIRE(!strcmp(buffer, "165140")); - - test::sprintf(buffer, "%lo", 12345678L); - REQUIRE(!strcmp(buffer, "57060516")); - - test::sprintf(buffer, "%lx", 0x12345678L); - REQUIRE(!strcmp(buffer, "12345678")); - - test::sprintf(buffer, "%llx", 0x1234567891234567LLU); - REQUIRE(!strcmp(buffer, "1234567891234567")); - - test::sprintf(buffer, "%lx", 0xabcdefabL); - REQUIRE(!strcmp(buffer, "abcdefab")); - - test::sprintf(buffer, "%lX", 0xabcdefabL); - REQUIRE(!strcmp(buffer, "ABCDEFAB")); - - test::sprintf(buffer, "%c", 'v'); - REQUIRE(!strcmp(buffer, "v")); - - test::sprintf(buffer, "%cv", 'w'); - REQUIRE(!strcmp(buffer, "wv")); - - test::sprintf(buffer, "%s", "A Test"); - REQUIRE(!strcmp(buffer, "A Test")); - - test::sprintf(buffer, "%hhu", 0xFFFFUL); - REQUIRE(!strcmp(buffer, "255")); - - test::sprintf(buffer, "%hu", 0x123456UL); - REQUIRE(!strcmp(buffer, "13398")); - - test::sprintf(buffer, "%s%hhi %hu", "Test", 10000, 0xFFFFFFFF); - REQUIRE(!strcmp(buffer, "Test16 65535")); - - test::sprintf(buffer, "%tx", &buffer[10] - &buffer[0]); - REQUIRE(!strcmp(buffer, "a")); - + TEST_SPRINTF(Equals("1110101001100000"), "%b", 60000); + TEST_SPRINTF(Equals("101111000110000101001110"), "%lb", 12345678L); // size limit + CMP_SPRINTF("%o", 60000); + CMP_SPRINTF("%lo", 12345678L); + CMP_SPRINTF("%lx", 0x12345678L); + CMP_SPRINTF("%llx", 0x1234567891234567LLU); + CMP_SPRINTF("%lx", 0xabcdefabL); + CMP_SPRINTF("%lX", 0xabcdefabL); + CMP_SPRINTF("%c", 'v'); + CMP_SPRINTF("%cv", 'w'); + CMP_SPRINTF("%s", "A Test"); + CMP_SPRINTF("%hhu", 0xFFFFUL); + CMP_SPRINTF("%hu", 0x123456UL); + CMP_SPRINTF("%s%hhi %hu", "Test", 10000, 0xFFFFFFFF); + CMP_SPRINTF("%tx", &buffer[10] - &buffer[0]); // TBD if (sizeof(intmax_t) == sizeof(long)) { - test::sprintf(buffer, "%ji", -2147483647L); - REQUIRE(!strcmp(buffer, "-2147483647")); + CMP_SPRINTF("%ji", -2147483647L); } else { - test::sprintf(buffer, "%ji", -2147483647LL); - REQUIRE(!strcmp(buffer, "-2147483647")); + CMP_SPRINTF("%ji", -2147483647LL); } } TEST_CASE("pointer", "[]" ) { - char buffer[100]; + TEST_DEF; + + // gcc %p implementation is different (and implementatino-specific) - test::sprintf(buffer, "%p", (void*)0x1234U); if (sizeof(void*) == 4U) { - REQUIRE(!strcmp(buffer, "00001234")); + TEST_SPRINTF(Equals("00001234"), "%p", (void*)0x1234U); } else { - REQUIRE(!strcmp(buffer, "0000000000001234")); + TEST_SPRINTF(Equals("0000000000001234"), "%p", (void*)0x1234U); } - test::sprintf(buffer, "%p", (void*)0x12345678U); if (sizeof(void*) == 4U) { - REQUIRE(!strcmp(buffer, "12345678")); + TEST_SPRINTF(Equals("12345678"), "%p", (void*)0x12345678U); } else { - REQUIRE(!strcmp(buffer, "0000000012345678")); + TEST_SPRINTF(Equals("0000000012345678"), "%p", (void*)0x12345678U); } - test::sprintf(buffer, "%p-%p", (void*)0x12345678U, (void*)0x7EDCBA98U); if (sizeof(void*) == 4U) { - REQUIRE(!strcmp(buffer, "12345678-7EDCBA98")); + TEST_SPRINTF(Equals("12345678-7EDCBA98"), "%p-%p", (void*)0x12345678U, (void*)0x7EDCBA98U); } else { - REQUIRE(!strcmp(buffer, "0000000012345678-000000007EDCBA98")); + TEST_SPRINTF(Equals("0000000012345678-000000007EDCBA98"), "%p-%p", (void*)0x12345678U, (void*)0x7EDCBA98U); } if (sizeof(uintptr_t) == sizeof(uint64_t)) { - test::sprintf(buffer, "%p", (void*)(uintptr_t)0xFFFFFFFFU); - REQUIRE(!strcmp(buffer, "00000000FFFFFFFF")); + TEST_SPRINTF(Equals("00000000FFFFFFFF"), "%p", (void*)(uintptr_t)0xFFFFFFFFU); } else { - test::sprintf(buffer, "%p", (void*)(uintptr_t)0xFFFFFFFFU); - REQUIRE(!strcmp(buffer, "FFFFFFFF")); + TEST_SPRINTF(Equals("FFFFFFFF"), "%p", (void*)(uintptr_t)0xFFFFFFFFU); } } TEST_CASE("unknown flag", "[]" ) { - char buffer[100]; + TEST_DEF; - test::sprintf(buffer, "%kmarco", 42, 37); - REQUIRE(!strcmp(buffer, "kmarco")); + TEST_SPRINTF(Equals("kmarco"), "%kmarco", 42, 37); } TEST_CASE("string length", "[]" ) { - char buffer[100]; - - test::sprintf(buffer, "%.4s", "This is a test"); - REQUIRE(!strcmp(buffer, "This")); - - test::sprintf(buffer, "%.4s", "test"); - REQUIRE(!strcmp(buffer, "test")); - - test::sprintf(buffer, "%.7s", "123"); - REQUIRE(!strcmp(buffer, "123")); - - test::sprintf(buffer, "%.7s", ""); - REQUIRE(!strcmp(buffer, "")); - - test::sprintf(buffer, "%.4s%.2s", "123456", "abcdef"); - REQUIRE(!strcmp(buffer, "1234ab")); - - test::sprintf(buffer, "%.4.2s", "123456"); - REQUIRE(!strcmp(buffer, ".2s")); - - test::sprintf(buffer, "%.*s", 3, "123456"); - REQUIRE(!strcmp(buffer, "123")); + CMP_DEF; + + CMP_SPRINTF("%.4s", "This is a test"); + CMP_SPRINTF("%.4s", "test"); + CMP_SPRINTF("%.7s", "123"); + CMP_SPRINTF("%.7s", ""); + CMP_SPRINTF("%.4s%.2s", "123456", "abcdef"); + TEST_SPRINTF(Equals(".2s"), "%.4.2s", "123456"); + CMP_SPRINTF("%.*s", 3, "123456"); } TEST_CASE("buffer length", "[]" ) { - char buffer[100]; - int ret; + TEST_DEF; - ret = test::snprintf(nullptr, 10, "%s", "Test"); + ret = /*test::*/snprintf(nullptr, 10, "%s", "Test"); REQUIRE(ret == 4); - ret = test::snprintf(nullptr, 0, "%s", "Test"); + ret = /*test::*/snprintf(nullptr, 0, "%s", "Test"); REQUIRE(ret == 4); buffer[0] = (char)0xA5; - ret = test::snprintf(buffer, 0, "%s", "Test"); + ret = /*test::*/snprintf(buffer, 0, "%s", "Test"); REQUIRE(buffer[0] == (char)0xA5); REQUIRE(ret == 4); buffer[0] = (char)0xCC; - test::snprintf(buffer, 1, "%s", "Test"); + /*test::*/snprintf(buffer, 1, "%s", "Test"); REQUIRE(buffer[0] == '\0'); - test::snprintf(buffer, 2, "%s", "Hello"); - REQUIRE(!strcmp(buffer, "H")); + /*test::*/snprintf(buffer, 2, "%s", "Hello"); + REQUIRE_THAT(buffer, Equals("H")); } TEST_CASE("ret value", "[]" ) { - char buffer[100] ; - int ret; + CMP_DEF; + + CMP_SNPRINTF(6, "0%s", "1234"); + + CMP_SNPRINTF(6, "0%s", "12345"); + CMP_SNPRINTF(6, "0%s", "1234567"); // '567' are truncated + + CMP_SNPRINTF(10, ("hello, world")); // ret is longer + + CMP_SNPRINTF(3, "%d", 10000); +} + + +TEST_CASE("misc", "[]" ) { + CMP_DEF; + + CMP_SPRINTF("%u%u%ctest%d %s", 5, 3000, 'a', -20, "bit"); + CMP_SPRINTF("%.*f", 2, 0.33333333); + CMP_SPRINTF("%.*d", -1, 1); + CMP_SPRINTF("%.3s", "foobar"); + CMP_SPRINTF("% .0d", 0); + CMP_SPRINTF("%10.5d", 4); + CMP_SPRINTF("%*sx", -3, "hi"); +#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL + CMP_SPRINTF("%.*g", 2, 0.33333333); + CMP_SPRINTF("%.*e", 2, 0.33333333); +#endif +} - ret = test::snprintf(buffer, 6, "0%s", "1234"); - REQUIRE(!strcmp(buffer, "01234")); +TEST_CASE("libc-sprintf", "[]") { + CMP_DEF; + /* Ein String ohne alles */ + TEST("Hallo heimur", 12, "Hallo heimur"); + + /* Einfache Konvertierungen */ + TEST("Hallo heimur", 12, "%s", "Hallo heimur"); + TEST("1024", 4, "%d", 1024); + TEST("-1024", 5, "%d", -1024); + TEST("1024", 4, "%i", 1024); + TEST("-1024", 5, "%i", -1024); + TEST("1024", 4, "%u", 1024u); + TEST("4294966272", 10, "%u", -1024u); + TEST("777", 3, "%o", 0777u); + TEST("37777777001", 11, "%o", -0777u); + TEST("1234abcd", 8, "%x", 0x1234abcdu); + TEST("edcb5433", 8, "%x", -0x1234abcdu); + TEST("1234ABCD", 8, "%X", 0x1234abcdu); + TEST("EDCB5433", 8, "%X", -0x1234abcdu); + TEST("x", 1, "%c", 'x'); + TEST("%", 1, "%%"); + + /* Mit %c kann man auch Nullbytes ausgeben */ + TEST("\0", 1, "%c", '\0'); + + /* Vorzeichen erzwingen (Flag +) */ + TEST("Hallo heimur", 12, "%+s", "Hallo heimur"); + TEST("+1024", 5, "%+d", 1024); + TEST("-1024", 5, "%+d", -1024); + TEST("+1024", 5, "%+i", 1024); + TEST("-1024", 5, "%+i", -1024); + TEST("1024", 4, "%+u", 1024u); + TEST("4294966272", 10, "%+u", -1024u); + TEST("777", 3, "%+o", 0777u); + TEST("37777777001", 11, "%+o", -0777u); + TEST("1234abcd", 8, "%+x", 0x1234abcdu); + TEST("edcb5433", 8, "%+x", -0x1234abcdu); + TEST("1234ABCD", 8, "%+X", 0x1234abcdu); + TEST("EDCB5433", 8, "%+X", -0x1234abcdu); + TEST("x", 1, "%+c", 'x'); + + /* Vorzeichenplatzhalter erzwingen (Flag ) */ + TEST("Hallo heimur", 12, "% s", "Hallo heimur"); + TEST(" 1024", 5, "% d", 1024); + TEST("-1024", 5, "% d", -1024); + TEST(" 1024", 5, "% i", 1024); + TEST("-1024", 5, "% i", -1024); + TEST("1024", 4, "% u", 1024u); + TEST("4294966272", 10, "% u", -1024u); + TEST("777", 3, "% o", 0777u); + TEST("37777777001", 11, "% o", -0777u); + TEST("1234abcd", 8, "% x", 0x1234abcdu); + TEST("edcb5433", 8, "% x", -0x1234abcdu); + TEST("1234ABCD", 8, "% X", 0x1234abcdu); + TEST("EDCB5433", 8, "% X", -0x1234abcdu); + TEST("x", 1, "% c", 'x'); + + /* Flag + hat Vorrang über */ + TEST("Hallo heimur", 12, "%+ s", "Hallo heimur"); + TEST("+1024", 5, "%+ d", 1024); + TEST("-1024", 5, "%+ d", -1024); + TEST("+1024", 5, "%+ i", 1024); + TEST("-1024", 5, "%+ i", -1024); + TEST("1024", 4, "%+ u", 1024u); + TEST("4294966272", 10, "%+ u", -1024u); + TEST("777", 3, "%+ o", 0777u); + TEST("37777777001", 11, "%+ o", -0777u); + TEST("1234abcd", 8, "%+ x", 0x1234abcdu); + TEST("edcb5433", 8, "%+ x", -0x1234abcdu); + TEST("1234ABCD", 8, "%+ X", 0x1234abcdu); + TEST("EDCB5433", 8, "%+ X", -0x1234abcdu); + TEST("x", 1, "%+ c", 'x'); + + /* Alternative Form */ + TEST("0777", 4, "%#o", 0777u); + TEST("037777777001", 12, "%#o", -0777u); + TEST("0x1234abcd", 10, "%#x", 0x1234abcdu); + TEST("0xedcb5433", 10, "%#x", -0x1234abcdu); + TEST("0X1234ABCD", 10, "%#X", 0x1234abcdu); + TEST("0XEDCB5433", 10, "%#X", -0x1234abcdu); + TEST("0", 1, "%#o", 0u); + TEST("0", 1, "%#x", 0u); + TEST("0", 1, "%#X", 0u); + + /* Feldbreite: Kleiner als Ausgabe */ + TEST("Hallo heimur", 12, "%1s", "Hallo heimur"); + TEST("1024", 4, "%1d", 1024); + TEST("-1024", 5, "%1d", -1024); + TEST("1024", 4, "%1i", 1024); + TEST("-1024", 5, "%1i", -1024); + TEST("1024", 4, "%1u", 1024u); + TEST("4294966272", 10, "%1u", -1024u); + TEST("777", 3, "%1o", 0777u); + TEST("37777777001", 11, "%1o", -0777u); + TEST("1234abcd", 8, "%1x", 0x1234abcdu); + TEST("edcb5433", 8, "%1x", -0x1234abcdu); + TEST("1234ABCD", 8, "%1X", 0x1234abcdu); + TEST("EDCB5433", 8, "%1X", -0x1234abcdu); + TEST("x", 1, "%1c", 'x'); + + /* Feldbreite: Größer als Ausgabe */ + TEST(" Hallo", 20, "%20s", "Hallo"); + TEST(" 1024", 20, "%20d", 1024); + TEST(" -1024", 20, "%20d", -1024); + TEST(" 1024", 20, "%20i", 1024); + TEST(" -1024", 20, "%20i", -1024); + TEST(" 1024", 20, "%20u", 1024u); + TEST(" 4294966272", 20, "%20u", -1024u); + TEST(" 777", 20, "%20o", 0777u); + TEST(" 37777777001", 20, "%20o", -0777u); + TEST(" 1234abcd", 20, "%20x", 0x1234abcdu); + TEST(" edcb5433", 20, "%20x", -0x1234abcdu); + TEST(" 1234ABCD", 20, "%20X", 0x1234abcdu); + TEST(" EDCB5433", 20, "%20X", -0x1234abcdu); + TEST(" x", 20, "%20c", 'x'); + + /* Feldbreite: Linksbündig */ + TEST("Hallo ", 20, "%-20s", "Hallo"); + TEST("1024 ", 20, "%-20d", 1024); + TEST("-1024 ", 20, "%-20d", -1024); + TEST("1024 ", 20, "%-20i", 1024); + TEST("-1024 ", 20, "%-20i", -1024); + TEST("1024 ", 20, "%-20u", 1024u); + TEST("4294966272 ", 20, "%-20u", -1024u); + TEST("777 ", 20, "%-20o", 0777u); + TEST("37777777001 ", 20, "%-20o", -0777u); + TEST("1234abcd ", 20, "%-20x", 0x1234abcdu); + TEST("edcb5433 ", 20, "%-20x", -0x1234abcdu); + TEST("1234ABCD ", 20, "%-20X", 0x1234abcdu); + TEST("EDCB5433 ", 20, "%-20X", -0x1234abcdu); + TEST("x ", 20, "%-20c", 'x'); + + /* Feldbreite: Padding mit 0 */ + TEST("00000000000000001024", 20, "%020d", 1024); + TEST("-0000000000000001024", 20, "%020d", -1024); + TEST("00000000000000001024", 20, "%020i", 1024); + TEST("-0000000000000001024", 20, "%020i", -1024); + TEST("00000000000000001024", 20, "%020u", 1024u); + TEST("00000000004294966272", 20, "%020u", -1024u); + TEST("00000000000000000777", 20, "%020o", 0777u); + TEST("00000000037777777001", 20, "%020o", -0777u); + TEST("0000000000001234abcd", 20, "%020x", 0x1234abcdu); + TEST("000000000000edcb5433", 20, "%020x", -0x1234abcdu); + TEST("0000000000001234ABCD", 20, "%020X", 0x1234abcdu); + TEST("000000000000EDCB5433", 20, "%020X", -0x1234abcdu); + + /* Feldbreite: Padding und alternative Form */ + TEST(" 0777", 20, "%#20o", 0777u); + TEST(" 037777777001", 20, "%#20o", -0777u); + TEST(" 0x1234abcd", 20, "%#20x", 0x1234abcdu); + TEST(" 0xedcb5433", 20, "%#20x", -0x1234abcdu); + TEST(" 0X1234ABCD", 20, "%#20X", 0x1234abcdu); + TEST(" 0XEDCB5433", 20, "%#20X", -0x1234abcdu); + + TEST("00000000000000000777", 20, "%#020o", 0777u); + TEST("00000000037777777001", 20, "%#020o", -0777u); + TEST("0x00000000001234abcd", 20, "%#020x", 0x1234abcdu); + TEST("0x0000000000edcb5433", 20, "%#020x", -0x1234abcdu); + TEST("0X00000000001234ABCD", 20, "%#020X", 0x1234abcdu); + TEST("0X0000000000EDCB5433", 20, "%#020X", -0x1234abcdu); + + /* Feldbreite: - hat Vorrang vor 0 */ + TEST("Hallo ", 20, "%0-20s", "Hallo"); + TEST("1024 ", 20, "%0-20d", 1024); + TEST("-1024 ", 20, "%0-20d", -1024); + TEST("1024 ", 20, "%0-20i", 1024); + TEST("-1024 ", 20, "%0-20i", -1024); + TEST("1024 ", 20, "%0-20u", 1024u); + TEST("4294966272 ", 20, "%0-20u", -1024u); + TEST("777 ", 20, "%-020o", 0777u); + TEST("37777777001 ", 20, "%-020o", -0777u); + TEST("1234abcd ", 20, "%-020x", 0x1234abcdu); + TEST("edcb5433 ", 20, "%-020x", -0x1234abcdu); + TEST("1234ABCD ", 20, "%-020X", 0x1234abcdu); + TEST("EDCB5433 ", 20, "%-020X", -0x1234abcdu); + TEST("x ", 20, "%-020c", 'x'); + + /* Feldbreite: Aus Parameter */ + TEST(" Hallo", 20, "%*s", 20, "Hallo"); + TEST(" 1024", 20, "%*d", 20, 1024); + TEST(" -1024", 20, "%*d", 20, -1024); + TEST(" 1024", 20, "%*i", 20, 1024); + TEST(" -1024", 20, "%*i", 20, -1024); + TEST(" 1024", 20, "%*u", 20, 1024u); + TEST(" 4294966272", 20, "%*u", 20, -1024u); + TEST(" 777", 20, "%*o", 20, 0777u); + TEST(" 37777777001", 20, "%*o", 20, -0777u); + TEST(" 1234abcd", 20, "%*x", 20, 0x1234abcdu); + TEST(" edcb5433", 20, "%*x", 20, -0x1234abcdu); + TEST(" 1234ABCD", 20, "%*X", 20, 0x1234abcdu); + TEST(" EDCB5433", 20, "%*X", 20, -0x1234abcdu); + TEST(" x", 20, "%*c", 20, 'x'); + + /* Präzision / Mindestanzahl von Ziffern */ + TEST("Hallo heimur", 12, "%.20s", "Hallo heimur"); + TEST("00000000000000001024", 20, "%.20d", 1024); + TEST("-00000000000000001024", 21, "%.20d", -1024); + TEST("00000000000000001024", 20, "%.20i", 1024); + TEST("-00000000000000001024", 21, "%.20i", -1024); + TEST("00000000000000001024", 20, "%.20u", 1024u); + TEST("00000000004294966272", 20, "%.20u", -1024u); + TEST("00000000000000000777", 20, "%.20o", 0777u); + TEST("00000000037777777001", 20, "%.20o", -0777u); + TEST("0000000000001234abcd", 20, "%.20x", 0x1234abcdu); + TEST("000000000000edcb5433", 20, "%.20x", -0x1234abcdu); + TEST("0000000000001234ABCD", 20, "%.20X", 0x1234abcdu); + TEST("000000000000EDCB5433", 20, "%.20X", -0x1234abcdu); + + /* Feldbreite und Präzision */ + TEST(" Hallo", 20, "%20.5s", "Hallo heimur"); + TEST(" 01024", 20, "%20.5d", 1024); + TEST(" -01024", 20, "%20.5d", -1024); + TEST(" 01024", 20, "%20.5i", 1024); + TEST(" -01024", 20, "%20.5i", -1024); + TEST(" 01024", 20, "%20.5u", 1024u); + TEST(" 4294966272", 20, "%20.5u", -1024u); + TEST(" 00777", 20, "%20.5o", 0777u); + TEST(" 37777777001", 20, "%20.5o", -0777u); + TEST(" 1234abcd", 20, "%20.5x", 0x1234abcdu); + TEST(" 00edcb5433", 20, "%20.10x", -0x1234abcdu); + TEST(" 1234ABCD", 20, "%20.5X", 0x1234abcdu); + TEST(" 00EDCB5433", 20, "%20.10X", -0x1234abcdu); + + /* Präzision: 0 wird ignoriert */ + TEST(" Hallo", 20, "%020.5s", "Hallo heimur"); + TEST(" 01024", 20, "%020.5d", 1024); + TEST(" -01024", 20, "%020.5d", -1024); + TEST(" 01024", 20, "%020.5i", 1024); + TEST(" -01024", 20, "%020.5i", -1024); + TEST(" 01024", 20, "%020.5u", 1024u); + TEST(" 4294966272", 20, "%020.5u", -1024u); + TEST(" 00777", 20, "%020.5o", 0777u); + TEST(" 37777777001", 20, "%020.5o", -0777u); + TEST(" 1234abcd", 20, "%020.5x", 0x1234abcdu); + TEST(" 00edcb5433", 20, "%020.10x", -0x1234abcdu); + TEST(" 1234ABCD", 20, "%020.5X", 0x1234abcdu); + TEST(" 00EDCB5433", 20, "%020.10X", -0x1234abcdu); + + /* Präzision 0 */ + TEST("", 0, "%.0s", "Hallo heimur"); + TEST(" ", 20, "%20.0s", "Hallo heimur"); + TEST("", 0, "%.s", "Hallo heimur"); + TEST(" ", 20, "%20.s", "Hallo heimur"); + TEST(" 1024", 20, "%20.0d", 1024); + TEST(" -1024", 20, "%20.d", -1024); + TEST(" ", 20, "%20.d", 0); + TEST(" 1024", 20, "%20.0i", 1024); + TEST(" -1024", 20, "%20.i", -1024); + TEST(" ", 20, "%20.i", 0); + TEST(" 1024", 20, "%20.u", 1024u); + TEST(" 4294966272", 20, "%20.0u", -1024u); + TEST(" ", 20, "%20.u", 0u); + TEST(" 777", 20, "%20.o", 0777u); + TEST(" 37777777001", 20, "%20.0o", -0777u); + TEST(" ", 20, "%20.o", 0u); + TEST(" 1234abcd", 20, "%20.x", 0x1234abcdu); + TEST(" edcb5433", 20, "%20.0x", -0x1234abcdu); + TEST(" ", 20, "%20.x", 0u); + TEST(" 1234ABCD", 20, "%20.X", 0x1234abcdu); + TEST(" EDCB5433", 20, "%20.0X", -0x1234abcdu); + TEST(" ", 20, "%20.X", 0u); + + /* Negative Präzision wird ignoriert */ + /* XXX glibc tut nicht, was ich erwartet habe, vorerst deaktiviert... */ +#if 0 + TEST("Hallo heimur", 12, "%.-42s", "Hallo heimur"); + TEST("1024", 4, "%.-42d", 1024); + TEST("-1024", 5, "%.-42d", -1024); + TEST("1024", 4, "%.-42i", 1024); + TEST("-1024", 5, "%.-42i", -1024); + TEST("1024", 4, "%.-42u", 1024u); + TEST("4294966272", 10, "%.-42u", -1024u); + TEST("777", 3, "%.-42o", 0777u); + TEST("37777777001", 11, "%.-42o", -0777u); + TEST("1234abcd", 8, "%.-42x", 0x1234abcdu); + TEST("edcb5433", 8, "%.-42x", -0x1234abcdu); + TEST("1234ABCD", 8, "%.-42X", 0x1234abcdu); + TEST("EDCB5433", 8, "%.-42X", -0x1234abcdu); +#endif + + /* + * Präzision und Feldbreite aus Parameter. + * + hat Vorrang vor , - hat Vorrang vor 0 (das eh ignoriert wird, + * weil eine Präzision angegeben ist); + */ + TEST("Hallo ", 20, "% -0+*.*s", 20, 5, "Hallo heimur"); + CMP_SPRINTF("%.*d", -6, 0); +#if 1 + TEST("+01024 ", 20, "% -0+*.*d", 20, 5, 1024); + TEST("-01024 ", 20, "% -0+*.*d", 20, 5, -1024); + TEST("+01024 ", 20, "% -0+*.*i", 20, 5, 1024); + TEST("-01024 ", 20, "% 0-+*.*i", 20, 5, -1024); + TEST("01024 ", 20, "% 0-+*.*u", 20, 5, 1024u); + TEST("4294966272 ", 20, "% 0-+*.*u", 20, 5, -1024u); + TEST("00777 ", 20, "%+ -0*.*o", 20, 5, 0777u); + TEST("37777777001 ", 20, "%+ -0*.*o", 20, 5, -0777u); + TEST("1234abcd ", 20, "%+ -0*.*x", 20, 5, 0x1234abcdu); + TEST("00edcb5433 ", 20, "%+ -0*.*x", 20, 10, -0x1234abcdu); + TEST("1234ABCD ", 20, "% -+0*.*X", 20, 5, 0x1234abcdu); + TEST("00EDCB5433 ", 20, "% -+0*.*X", 20, 10, -0x1234abcdu); +#endif +} + +#define TEST_REDUNDANT_FLAGS 1 +#define TEST_NON_STANDARD 1 +#define TEST_IMPL_DEFINED 1 + +#define TEST_SNPRINTF(par, exp) CMP_SPRINTF par +#define TEST_SNPRINTF_N(par, len) CMP_SPRINTF par + +#define cur_snprintf snprintf + +#define REQUIRE_STR_EQ(s1,s2) REQUIRE_THAT(s1, Equals(s2)) + +TEST_CASE("libsanity printf 1", "[]") +{ + CMP_DEF; + + TEST_SNPRINTF(("%d", -1000), "-1000"); + CMP_SNPRINTF(3U, "%d", -1000); + + TEST_SNPRINTF(("%d", -1), "-1"); + + CMP_SNPRINTF(sizeof(buffer), "%d %d %s", 3, -1000, "test"); + + TEST_SNPRINTF(("% d", 42), " 42"); + TEST_SNPRINTF(("% d", -42), "-42"); + TEST_SNPRINTF(("% 5d", 42), " 42"); + TEST_SNPRINTF(("% 5d", -42), " -42"); + TEST_SNPRINTF(("% 15d", 42), " 42"); + TEST_SNPRINTF(("% 15d", -42), " -42"); + TEST_SNPRINTF(("% 15d", -42), " -42"); + TEST_SNPRINTF(("% 15.3f", -42.987), " -42.987"); + TEST_SNPRINTF(("% 15.3f", 42.987), " 42.987"); +#if TEST_REDUNDANT_FLAGS + TEST_SNPRINTF(("% s", "Hello testing"), "Hello testing"); +#endif + + TEST_SNPRINTF(("% d", 1024), " 1024"); + TEST_SNPRINTF(("% d", -1024), "-1024"); + TEST_SNPRINTF(("% i", 1024), " 1024"); + TEST_SNPRINTF(("% i", -1024), "-1024"); +#if TEST_REDUNDANT_FLAGS + TEST_SNPRINTF(("% u", 1024), "1024"); + TEST_SNPRINTF(("% u", 4294966272U), "4294966272"); + TEST_SNPRINTF(("% o", 511), "777"); + TEST_SNPRINTF(("% o", 4294966785U), "37777777001"); + TEST_SNPRINTF(("% x", 305441741), "1234abcd"); + TEST_SNPRINTF(("% x", 3989525555U), "edcb5433"); + TEST_SNPRINTF(("% X", 305441741), "1234ABCD"); + TEST_SNPRINTF(("% X", 3989525555U), "EDCB5433"); + TEST_SNPRINTF(("% c", 'x'), "x"); +#endif + + TEST_SNPRINTF(("%+d", 42), "+42"); + TEST_SNPRINTF(("%+d", -42), "-42"); + TEST_SNPRINTF(("%+5d", 42), " +42"); + TEST_SNPRINTF(("%+5d", -42), " -42"); + TEST_SNPRINTF(("%+15d", 42), " +42"); + TEST_SNPRINTF(("%+15d", -42), " -42"); +#if TEST_REDUNDANT_FLAGS + TEST_SNPRINTF(("%+s", "Hello testing"), "Hello testing"); +#endif + + TEST_SNPRINTF(("%+d", 1024), "+1024"); + TEST_SNPRINTF(("%+d", -1024), "-1024"); + TEST_SNPRINTF(("%+i", 1024), "+1024"); + TEST_SNPRINTF(("%+i", -1024), "-1024"); +#if TEST_REDUNDANT_FLAGS + TEST_SNPRINTF(("%+u", 1024), "1024"); + TEST_SNPRINTF(("%+u", 4294966272U), "4294966272"); + TEST_SNPRINTF(("%+o", 511), "777"); + TEST_SNPRINTF(("%+o", 4294966785U), "37777777001"); + TEST_SNPRINTF(("%+x", 305441741), "1234abcd"); + TEST_SNPRINTF(("%+x", 3989525555U), "edcb5433"); + TEST_SNPRINTF(("%+X", 305441741), "1234ABCD"); + TEST_SNPRINTF(("%+X", 3989525555U), "EDCB5433"); + TEST_SNPRINTF(("%+c", 'x'), "x"); +#endif + + TEST_SNPRINTF(("%0d", 42), "42"); + TEST_SNPRINTF(("%0ld", 42L), "42"); + TEST_SNPRINTF(("%0d", -42), "-42"); + TEST_SNPRINTF(("%05d", 42), "00042"); + TEST_SNPRINTF(("%05d", -42), "-0042"); + TEST_SNPRINTF(("%015d", 42), "000000000000042"); + TEST_SNPRINTF(("%015d", -42), "-00000000000042"); + TEST_SNPRINTF(("%015.2f", 42.1234), "000000000042.12"); + TEST_SNPRINTF(("%015.3f", 42.9876), "00000000042.988"); + TEST_SNPRINTF(("%015.5f", -42.9876), "-00000042.98760"); + TEST_SNPRINTF(("%-d", 42), "42"); + TEST_SNPRINTF(("%-d", -42), "-42"); + TEST_SNPRINTF(("%-5d", 42), "42 "); + TEST_SNPRINTF(("%-5d", -42), "-42 "); + TEST_SNPRINTF(("%-15d", 42), "42 "); + TEST_SNPRINTF(("%-15d", -42), "-42 "); +#if TEST_REDUNDANT_FLAGS + TEST_SNPRINTF(("%-0d", 42), "42"); + TEST_SNPRINTF(("%-0d", -42), "-42"); + TEST_SNPRINTF(("%-05d", 42), "42 "); + TEST_SNPRINTF(("%-05d", -42), "-42 "); + TEST_SNPRINTF(("%-015d", 42), "42 "); + TEST_SNPRINTF(("%-015d", -42), "-42 "); + TEST_SNPRINTF(("%0-d", 42), "42"); + TEST_SNPRINTF(("%0-d", -42), "-42"); + TEST_SNPRINTF(("%0-5d", 42), "42 "); + TEST_SNPRINTF(("%0-5d", -42), "-42 "); + TEST_SNPRINTF(("%0-15d", 42), "42 "); + TEST_SNPRINTF(("%0-15d", -42), "-42 "); +#endif +} + +TEST_CASE("libsanity printf 2", "[]") +{ + CMP_DEF; + + CMP_SNPRINTF(sizeof(buffer), "Hello testing"); + + TEST_SNPRINTF(("%s", "Hello testing"), "Hello testing"); + TEST_SNPRINTF(("%d", 1024), "1024"); + TEST_SNPRINTF(("%d", -1024), "-1024"); + TEST_SNPRINTF(("%i", 1024), "1024"); + TEST_SNPRINTF(("%i", -1024), "-1024"); + TEST_SNPRINTF(("%u", 1024), "1024"); + TEST_SNPRINTF(("%u", 4294966272U), "4294966272"); + TEST_SNPRINTF(("%o", 511), "777"); + TEST_SNPRINTF(("%o", 4294966785U), "37777777001"); + TEST_SNPRINTF(("%x", 305441741), "1234abcd"); + TEST_SNPRINTF(("%x", 3989525555U), "edcb5433"); + TEST_SNPRINTF(("%X", 305441741), "1234ABCD"); + TEST_SNPRINTF(("%X", 3989525555U), "EDCB5433"); + TEST_SNPRINTF(("%%"), "%"); + + TEST_SNPRINTF(("%1s", "Hello testing"), "Hello testing"); + TEST_SNPRINTF(("%1d", 1024), "1024"); + TEST_SNPRINTF(("%1d", -1024), "-1024"); + TEST_SNPRINTF(("%1i", 1024), "1024"); + TEST_SNPRINTF(("%1i", -1024), "-1024"); + TEST_SNPRINTF(("%1u", 1024), "1024"); + TEST_SNPRINTF(("%1u", 4294966272U), "4294966272"); + TEST_SNPRINTF(("%1o", 511), "777"); + TEST_SNPRINTF(("%1o", 4294966785U), "37777777001"); + TEST_SNPRINTF(("%1x", 305441741), "1234abcd"); + TEST_SNPRINTF(("%1x", 3989525555U), "edcb5433"); + TEST_SNPRINTF(("%1X", 305441741), "1234ABCD"); + TEST_SNPRINTF(("%1X", 3989525555U), "EDCB5433"); + TEST_SNPRINTF(("%1c", 'x'), "x"); + TEST_SNPRINTF(("%20s", "Hello"), " Hello"); + TEST_SNPRINTF(("%20d", 1024), " 1024"); + TEST_SNPRINTF(("%20d", -1024), " -1024"); + TEST_SNPRINTF(("%20i", 1024), " 1024"); + TEST_SNPRINTF(("%20i", -1024), " -1024"); + TEST_SNPRINTF(("%20u", 1024), " 1024"); + TEST_SNPRINTF(("%20u", 4294966272U), " 4294966272"); + TEST_SNPRINTF(("%20o", 511), " 777"); + TEST_SNPRINTF(("%20o", 4294966785U), " 37777777001"); + TEST_SNPRINTF(("%20x", 305441741), " 1234abcd"); + TEST_SNPRINTF(("%20x", 3989525555U), " edcb5433"); + TEST_SNPRINTF(("%20X", 305441741), " 1234ABCD"); + TEST_SNPRINTF(("%20X", 3989525555U), " EDCB5433"); + TEST_SNPRINTF(("%20c", 'x'), " x"); + TEST_SNPRINTF(("%*s", 20, "Hello"), " Hello"); + TEST_SNPRINTF(("%*d", 20, 1024), " 1024"); + TEST_SNPRINTF(("%*d", 20, -1024), " -1024"); + TEST_SNPRINTF(("%*i", 20, 1024), " 1024"); + TEST_SNPRINTF(("%*i", 20, -1024), " -1024"); + TEST_SNPRINTF(("%*u", 20, 1024), " 1024"); + TEST_SNPRINTF(("%*u", 20, 4294966272U), " 4294966272"); + TEST_SNPRINTF(("%*o", 20, 511), " 777"); + TEST_SNPRINTF(("%*o", 20, 4294966785U), " 37777777001"); + TEST_SNPRINTF(("%*x", 20, 305441741), " 1234abcd"); + TEST_SNPRINTF(("%*x", 20, 3989525555U), " edcb5433"); + TEST_SNPRINTF(("%*X", 20, 305441741), " 1234ABCD"); + TEST_SNPRINTF(("%*X", 20, 3989525555U), " EDCB5433"); + TEST_SNPRINTF(("%*c", 20, 'x'), " x"); + TEST_SNPRINTF(("%-20s", "Hello"), "Hello "); + TEST_SNPRINTF(("%-20d", 1024), "1024 "); + TEST_SNPRINTF(("%-20d", -1024), "-1024 "); + TEST_SNPRINTF(("%-20i", 1024), "1024 "); + TEST_SNPRINTF(("%-20i", -1024), "-1024 "); + TEST_SNPRINTF(("%-20u", 1024), "1024 "); + TEST_SNPRINTF(("%-20.4f", 1024.1234), "1024.1234 "); + TEST_SNPRINTF(("%-20u", 4294966272U), "4294966272 "); + TEST_SNPRINTF(("%-20o", 511), "777 "); + TEST_SNPRINTF(("%-20o", 4294966785U), "37777777001 "); + TEST_SNPRINTF(("%-20x", 305441741), "1234abcd "); + TEST_SNPRINTF(("%-20x", 3989525555U), "edcb5433 "); + TEST_SNPRINTF(("%-20X", 305441741), "1234ABCD "); + TEST_SNPRINTF(("%-20X", 3989525555U), "EDCB5433 "); + TEST_SNPRINTF(("%-20c", 'x'), "x "); + TEST_SNPRINTF(("|%5d| |%-2d| |%5d|", 9, 9, 9), "| 9| |9 | | 9|"); + TEST_SNPRINTF(("|%5d| |%-2d| |%5d|", 10, 10, 10), "| 10| |10| | 10|"); + TEST_SNPRINTF(("|%5d| |%-12d| |%5d|", 9, 9, 9), + "| 9| |9 | | 9|"); + TEST_SNPRINTF(("|%5d| |%-12d| |%5d|", 10, 10, 10), + "| 10| |10 | | 10|"); +#if TEST_REDUNDANT_FLAGS + TEST_SNPRINTF(("%0-20s", "Hello"), "Hello "); + TEST_SNPRINTF(("%0-20d", 1024), "1024 "); + TEST_SNPRINTF(("%0-20d", -1024), "-1024 "); + TEST_SNPRINTF(("%0-20i", 1024), "1024 "); + TEST_SNPRINTF(("%0-20i", -1024), "-1024 "); + TEST_SNPRINTF(("%0-20u", 1024), "1024 "); + TEST_SNPRINTF(("%0-20u", 4294966272U), "4294966272 "); + TEST_SNPRINTF(("%0-20o", 511), "777 "); + TEST_SNPRINTF(("%0-20o", 4294966785U), "37777777001 "); + TEST_SNPRINTF(("%0-20x", 305441741), "1234abcd "); + TEST_SNPRINTF(("%0-20x", 3989525555U), "edcb5433 "); + TEST_SNPRINTF(("%0-20X", 305441741), "1234ABCD "); + TEST_SNPRINTF(("%0-20X", 3989525555U), "EDCB5433 "); + TEST_SNPRINTF(("%0-20c", 'x'), "x "); +#endif +} + +TEST_CASE("libsanity printf 3", "[]") +{ + CMP_DEF; + + + TEST_SNPRINTF(("%020d", 1024), "00000000000000001024"); + TEST_SNPRINTF(("%020d", -1024), "-0000000000000001024"); + TEST_SNPRINTF(("%020i", 1024), "00000000000000001024"); + TEST_SNPRINTF(("%020i", -1024), "-0000000000000001024"); + TEST_SNPRINTF(("%020u", 1024), "00000000000000001024"); + TEST_SNPRINTF(("%020u", 4294966272U), "00000000004294966272"); + TEST_SNPRINTF(("%020o", 511), "00000000000000000777"); + TEST_SNPRINTF(("%020o", 4294966785U), "00000000037777777001"); + TEST_SNPRINTF(("%020x", 305441741), "0000000000001234abcd"); + TEST_SNPRINTF(("%020x", 3989525555U), "000000000000edcb5433"); + TEST_SNPRINTF(("%020X", 305441741), "0000000000001234ABCD"); + TEST_SNPRINTF(("%020X", 3989525555U), "000000000000EDCB5433"); + TEST_SNPRINTF(("%.20d", 1024), "00000000000000001024"); + TEST_SNPRINTF(("%.20d", -1024), "-00000000000000001024"); + TEST_SNPRINTF(("%.20i", 1024), "00000000000000001024"); + TEST_SNPRINTF(("%.20i", -1024), "-00000000000000001024"); + TEST_SNPRINTF(("%.20u", 1024), "00000000000000001024"); + TEST_SNPRINTF(("%.20u", 4294966272U), "00000000004294966272"); + TEST_SNPRINTF(("%.20o", 511), "00000000000000000777"); + TEST_SNPRINTF(("%.20o", 4294966785U), "00000000037777777001"); + TEST_SNPRINTF(("%.20x", 305441741), "0000000000001234abcd"); + TEST_SNPRINTF(("%.20x", 3989525555U), "000000000000edcb5433"); + TEST_SNPRINTF(("%.20X", 305441741), "0000000000001234ABCD"); + TEST_SNPRINTF(("%.20X", 3989525555U), "000000000000EDCB5433"); +#if TEST_REDUNDANT_FLAGS + TEST_SNPRINTF(("%#020d", 1024), "00000000000000001024"); + TEST_SNPRINTF(("%#020d", -1024), "-0000000000000001024"); + TEST_SNPRINTF(("%#020i", 1024), "00000000000000001024"); + TEST_SNPRINTF(("%#020i", -1024), "-0000000000000001024"); + TEST_SNPRINTF(("%#020u", 1024), "00000000000000001024"); + TEST_SNPRINTF(("%#020u", 4294966272U), "00000000004294966272"); +#endif + + TEST_SNPRINTF(("%#020o", 511), "00000000000000000777"); + TEST_SNPRINTF(("%#020o", 4294966785U), "00000000037777777001"); + TEST_SNPRINTF(("%#020x", 305441741), "0x00000000001234abcd"); + TEST_SNPRINTF(("%#020x", 3989525555U), "0x0000000000edcb5433"); + TEST_SNPRINTF(("%#020X", 305441741), "0X00000000001234ABCD"); + TEST_SNPRINTF(("%#020X", 3989525555U), "0X0000000000EDCB5433"); +#if TEST_REDUNDANT_FLAGS + TEST_SNPRINTF(("%#20d", 1024), " 1024"); + TEST_SNPRINTF(("%#20d", -1024), " -1024"); + TEST_SNPRINTF(("%#20i", 1024), " 1024"); + TEST_SNPRINTF(("%#20i", -1024), " -1024"); + TEST_SNPRINTF(("%#20u", 1024), " 1024"); + TEST_SNPRINTF(("%#20u", 4294966272U), " 4294966272"); +#endif + + TEST_SNPRINTF(("%#20o", 511), " 0777"); + TEST_SNPRINTF(("%#20o", 4294966785U), " 037777777001"); + TEST_SNPRINTF(("%#20x", 305441741), " 0x1234abcd"); + TEST_SNPRINTF(("%#20x", 3989525555U), " 0xedcb5433"); + TEST_SNPRINTF(("%#20X", 305441741), " 0X1234ABCD"); + TEST_SNPRINTF(("%#20X", 3989525555U), " 0XEDCB5433"); + TEST_SNPRINTF(("%20.5d", 1024), " 01024"); + TEST_SNPRINTF(("%20.5d", -1024), " -01024"); + TEST_SNPRINTF(("%20.5i", 1024), " 01024"); + TEST_SNPRINTF(("%20.5i", -1024), " -01024"); + TEST_SNPRINTF(("%20.5u", 1024), " 01024"); + TEST_SNPRINTF(("%20.5u", 4294966272U), " 4294966272"); + TEST_SNPRINTF(("%20.5o", 511), " 00777"); + TEST_SNPRINTF(("%20.5o", 4294966785U), " 37777777001"); + TEST_SNPRINTF(("%20.5x", 305441741), " 1234abcd"); + TEST_SNPRINTF(("%20.10x", 3989525555U), " 00edcb5433"); + TEST_SNPRINTF(("%20.5X", 305441741), " 1234ABCD"); + TEST_SNPRINTF(("%20.10X", 3989525555U), " 00EDCB5433"); + TEST_SNPRINTF(("%.0s", "Hello testing"), ""); + TEST_SNPRINTF(("%20.0s", "Hello testing"), " "); + TEST_SNPRINTF(("%.s", "Hello testing"), ""); + TEST_SNPRINTF(("%20.s", "Hello testing"), " "); + TEST_SNPRINTF(("%20.0d", 1024), " 1024"); + TEST_SNPRINTF(("%20.0d", -1024), " -1024"); + TEST_SNPRINTF(("%20.d", 0), " "); + TEST_SNPRINTF(("%20.0i", 1024), " 1024"); + TEST_SNPRINTF(("%20.i", -1024), " -1024"); + TEST_SNPRINTF(("%20.i", 0), " "); + TEST_SNPRINTF(("%20.u", 1024), " 1024"); + TEST_SNPRINTF(("%20.0u", 4294966272U), " 4294966272"); + TEST_SNPRINTF(("%20.u", 0U), " "); + TEST_SNPRINTF(("%20.o", 511), " 777"); + TEST_SNPRINTF(("%20.0o", 4294966785U), " 37777777001"); + TEST_SNPRINTF(("%20.o", 0U), " "); + TEST_SNPRINTF(("%20.x", 305441741), " 1234abcd"); + TEST_SNPRINTF(("%50.x", 305441741), + " 1234abcd"); + TEST_SNPRINTF(("%50.x%10.u", 305441741, 12345), + " 1234abcd 12345"); + TEST_SNPRINTF(("%20.0x", 3989525555U), " edcb5433"); + TEST_SNPRINTF(("%20.x", 0U), " "); + TEST_SNPRINTF(("%20.X", 305441741), " 1234ABCD"); + TEST_SNPRINTF(("%20.0X", 3989525555U), " EDCB5433"); + TEST_SNPRINTF(("%20.X", 0U), " "); + TEST_SNPRINTF(("%.4f", 3.1415354), "3.1415"); + TEST_SNPRINTF(("%.3f", 30343.1415354), "30343.142"); + TEST_SNPRINTF(("%.0f", 34.1415354), "34"); + TEST_SNPRINTF(("%.2f", 42.8952), "42.90"); + TEST_SNPRINTF(("%.9f", 42.8952), "42.895200000"); + TEST_SNPRINTF(("%.10f", 42.895223), "42.8952230000"); +// TODO limited number of digits +// TEST_SNPRINTF(("%.12f", 42.89522312345678), "42.895223123457"); +// TEST_SNPRINTF(("%.12f", 42.89522387654321), "42.895223876543"); + TEST_SNPRINTF(("%6.2f", 42.8952), " 42.90"); + TEST_SNPRINTF(("%+6.2f", 42.8952), "+42.90"); + TEST_SNPRINTF(("%+5.1f", 42.9252), "+42.9"); + TEST_SNPRINTF(("%f", 42.5), "42.500000"); + TEST_SNPRINTF(("%.1f", 42.5), "42.5"); + TEST_SNPRINTF(("%f", 42167.0), "42167.000000"); + TEST_SNPRINTF(("%.9f", -12345.987654321), "-12345.987654321"); + TEST_SNPRINTF(("%.1f", 3.999), "4.0"); + TEST_SNPRINTF(("%.0f", 3.5), "4"); + TEST_SNPRINTF(("%.0f", 3.49), "3"); + TEST_SNPRINTF(("%.1f", 3.49), "3.5"); +// TODO - automatic switch to %g +// TEST_SNPRINTF(("%.1f", 1E20), "100000000000000000000.0"); + TEST_SNPRINTF(("a%-5.1f", 0.5), "a0.5 "); + TEST_SNPRINTF(("%i", 0), "0"); + TEST_SNPRINTF(("%i", 1234), "1234"); + TEST_SNPRINTF(("%i", 32767), "32767"); + TEST_SNPRINTF(("%i", -32767), "-32767"); + TEST_SNPRINTF(("%li", 30L), "30"); + TEST_SNPRINTF(("%li", -2147483647L), "-2147483647"); + TEST_SNPRINTF(("%li", 2147483647L), "2147483647"); + TEST_SNPRINTF(("%lli", 30LL), "30"); + TEST_SNPRINTF(("%lli", -9223372036854775807LL), "-9223372036854775807"); + TEST_SNPRINTF(("%lli", 9223372036854775807LL), "9223372036854775807"); + TEST_SNPRINTF(("%lu", 100000L), "100000"); + TEST_SNPRINTF(("%lu", 0xFFFFFFFFL), "4294967295"); + TEST_SNPRINTF(("%llu", 281474976710656LLU), "281474976710656"); + TEST_SNPRINTF(("%llu", 18446744073709551615LLU), "18446744073709551615"); + TEST_SNPRINTF(("%zu", (size_t)2147483647UL), "2147483647"); + TEST_SNPRINTF(("%zd", (ptrdiff_t)2147483647UL), "2147483647"); + TEST_SNPRINTF(("%tu", (size_t)2147483647UL), "2147483647"); + TEST_SNPRINTF(("%td", (ptrdiff_t)2147483647UL), "2147483647"); +#if TEST_NON_STANDARD + // Unportable extension in original printf implementation. + TEST_SNPRINTF(("%b", 60000), "1110101001100000"); + TEST_SNPRINTF(("%lb", 12345678L), "101111000110000101001110"); + TEST_SNPRINTF(("%#b", 60000), "0b1110101001100000"); +#endif +} + +TEST_CASE("libsanity printf 4", "[]") +{ + CMP_DEF; + + + TEST_SNPRINTF(("%o", 60000), "165140"); + TEST_SNPRINTF(("%lo", 12345678L), "57060516"); + TEST_SNPRINTF(("%lx", 0x12345678L), "12345678"); + TEST_SNPRINTF(("%llx", 0x1234567891234567LLU), "1234567891234567"); + TEST_SNPRINTF(("%lx", 0xabcdefabL), "abcdefab"); + TEST_SNPRINTF(("%lX", 0xabcdefabL), "ABCDEFAB"); + TEST_SNPRINTF(("%c", 'v'), "v"); + TEST_SNPRINTF(("%cv", 'w'), "wv"); + TEST_SNPRINTF(("%s", "A Test"), "A Test"); + TEST_SNPRINTF(("%hhu", (unsigned char)0xFFFFU), "255"); + TEST_SNPRINTF(("%hu", (unsigned short)0x123456U), "13398"); + TEST_SNPRINTF(("%s%hhi %hu", "Test", 10000, 0xFFFFFFFF), "Test16 65535"); + TEST_SNPRINTF(("%tx", &buffer[10] - &buffer[0]), "a"); + TEST_SNPRINTF(("%ji", (intmax_t)-2147483647L), "-2147483647"); + TEST_SNPRINTF(("%ju", (uintmax_t)2147483647UL), "2147483647"); + + TEST_SNPRINTF(("%.*d", -1, 1), "1"); + TEST_SNPRINTF(("%.*d", -1, 0), "0"); + + TEST_SNPRINTF(("%hhd", -1), "-1"); + +#if TEST_IMPL_DEFINED + CMP_SNPRINTF(sizeof(buffer), "%p", (void *)(uintptr_t)0x1234U); + + CMP_SNPRINTF(sizeof(buffer), "%p", (void *)(uintptr_t)0x12345678U); + + CMP_SNPRINTF(sizeof(buffer), "%p-%p", (void *)0x12345678U, + (void *)(uintptr_t)0x7EDCBA98U); + + CMP_SNPRINTF(sizeof(buffer), "%p", + (void *)(uintptr_t)0xFFFFFFFFU); +#endif + + buffer[0] = (char)0xA5; + ret = cur_snprintf(buffer, 0, "%s", "Test"); + REQUIRE(buffer[0] == (char)0xA5); + REQUIRE(ret == 4); + + buffer[0] = (char)0xCC; + cur_snprintf(buffer, 1, "%s", "Test"); + REQUIRE(buffer[0] == '\0'); + + cur_snprintf(buffer, 2, "%s", "Hello"); + REQUIRE_STR_EQ(buffer, "H"); + + ret = cur_snprintf(buffer, 6, "0%s", "1234"); + REQUIRE_STR_EQ(buffer, "01234"); REQUIRE(ret == 5); - ret = test::snprintf(buffer, 6, "0%s", "12345"); - REQUIRE(!strcmp(buffer, "01234")); + ret = cur_snprintf(buffer, 6, "0%s", "12345"); + REQUIRE_STR_EQ(buffer, "01234"); REQUIRE(ret == 6); // '5' is truncated - ret = test::snprintf(buffer, 6, "0%s", "1234567"); - REQUIRE(!strcmp(buffer, "01234")); + ret = cur_snprintf(buffer, 6, "0%s", "1234567"); + REQUIRE_STR_EQ(buffer, "01234"); REQUIRE(ret == 8); // '567' are truncated - ret = test::snprintf(buffer, 10, "hello, world"); + ret = cur_snprintf(buffer, 10, "hello, world"); REQUIRE(ret == 12); - ret = test::snprintf(buffer, 3, "%d", 10000); + ret = cur_snprintf(buffer, 3, "%d", 10000); REQUIRE(ret == 5); REQUIRE(strlen(buffer) == 2U); REQUIRE(buffer[0] == '1'); REQUIRE(buffer[1] == '0'); REQUIRE(buffer[2] == '\0'); -} + TEST_SNPRINTF(("%u%u%ctest%d %s", 5, 3000, 'a', -20, "bit"), + "53000atest-20 bit"); + TEST_SNPRINTF(("%.*f", 2, 0.33333333), "0.33"); + TEST_SNPRINTF(("%.3s", "foobar"), "foo"); + TEST_SNPRINTF(("%10.5d", 4), " 00004"); + TEST_SNPRINTF(("%*sx", -3, "hi"), "hi x"); + +#if INT_MAX == 2147483647 + TEST_SNPRINTF(("%d", INT_MAX), "2147483647"); + TEST_SNPRINTF(("%d", INT_MIN), "-2147483648"); + TEST_SNPRINTF(("%u", UINT_MAX), "4294967295"); +#endif +#if LONG_MAX == 2147483647L + TEST_SNPRINTF(("%ld", LONG_MAX), "2147483647"); + TEST_SNPRINTF(("%ld", LONG_MIN), "-2147483648"); + TEST_SNPRINTF(("%lu", ULONG_MAX), "4294967295"); +#endif +#if LONG_MAX == 9223372036854775807L + TEST_SNPRINTF(("%ld", LONG_MAX), "9223372036854775807"); + TEST_SNPRINTF(("%ld", LONG_MIN), "-9223372036854775808"); + TEST_SNPRINTF(("%lu", ULONG_MAX), "18446744073709551615"); +#endif + TEST_SNPRINTF(("%" PRIi32, INT32_MAX), "2147483647"); + TEST_SNPRINTF(("%" PRIi32, INT32_MIN), "-2147483648"); + TEST_SNPRINTF(("%" PRIi64, INT64_MAX), "9223372036854775807"); + TEST_SNPRINTF(("%" PRIi64, INT64_MIN), "-9223372036854775808"); + TEST_SNPRINTF(("%" PRIu64, UINT64_MAX), "18446744073709551615"); + +#if TEST_IMPL_DEFINED + // libinsanity/Microsoft extensions for explicitly sized integer types + TEST_SNPRINTF(("%I64d", INT64_MIN), "-9223372036854775808"); + TEST_SNPRINTF(("%I32d", INT32_MIN), "-2147483648"); + TEST_SNPRINTF(("%Id", (ptrdiff_t)-123), "-123"); + TEST_SNPRINTF(("%Iu", (size_t)123), "123"); + // libinsanity only extensions + TEST_SNPRINTF(("%I8u", 10000), "16"); + TEST_SNPRINTF(("%I8d", 255), "-1"); + TEST_SNPRINTF(("%I16u", 100000), "34464"); + TEST_SNPRINTF(("%I16d", 65535), "-1"); + TEST_SNPRINTF_N(("%I34d", 123), -1); +#endif -TEST_CASE("misc", "[]" ) { - char buffer[100]; + TEST_SNPRINTF(("%x", 0), "0"); + TEST_SNPRINTF(("%#x", 0), "0"); + TEST_SNPRINTF(("%#04x", 0), "0000"); + TEST_SNPRINTF(("%#08x", 0x614e), "0x00614e"); + TEST_SNPRINTF(("%#.3x", 0x614e), "0x614e"); + TEST_SNPRINTF(("%#.4x", 0x614e), "0x614e"); + TEST_SNPRINTF(("%#.5x", 0x614e), "0x0614e"); + TEST_SNPRINTF(("%#.6x", 0x614e), "0x00614e"); + TEST_SNPRINTF(("%#.7x", 0x614e), "0x000614e"); + + TEST_SNPRINTF(("%o", 00), "0"); + TEST_SNPRINTF(("%#o", 00), "0"); + TEST_SNPRINTF(("%#04o", 0), "0000"); + TEST_SNPRINTF(("%#08o", 06143), "00006143"); + TEST_SNPRINTF(("%#.3o", 06143), "06143"); + TEST_SNPRINTF(("%#.4o", 06143), "06143"); + TEST_SNPRINTF(("%#.5o", 06143), "06143"); // TODO! + TEST_SNPRINTF(("%#.6o", 06143), "006143"); + TEST_SNPRINTF(("%#.7o", 06143), "0006143"); +} - test::sprintf(buffer, "%u%u%ctest%d %s", 5, 3000, 'a', -20, "bit"); - REQUIRE(!strcmp(buffer, "53000atest-20 bit")); +TEST_CASE("libsanity printf 5", "[]") +{ + CMP_DEF; + + // libc-testsuite tests + + /* width, precision, alignment */ + TEST_SNPRINTF(("%04d", 12), "0012"); + TEST_SNPRINTF(("%.3d", 12), "012"); + TEST_SNPRINTF(("%3d", 12), " 12"); + TEST_SNPRINTF(("%-3d", 12), "12 "); + TEST_SNPRINTF(("%+3d", 12), "+12"); + TEST_SNPRINTF(("%+-5d", 12), "+12 "); +#if TEST_REDUNDANT_FLAGS + TEST_SNPRINTF(("%+- 5d", 12), "+12 "); +#endif + TEST_SNPRINTF(("%- 5d", 12), " 12 "); + TEST_SNPRINTF(("% d", 12), " 12"); +#if TEST_REDUNDANT_FLAGS + TEST_SNPRINTF(("%0-5d", 12), "12 "); + TEST_SNPRINTF(("%-05d", 12), "12 "); +#endif - test::sprintf(buffer, "%.*f", 2, 0.33333333); - REQUIRE(!strcmp(buffer, "0.33")); +} + +TEST_CASE("libsanity printf 6", "[]") +{ + CMP_DEF; - test::sprintf(buffer, "%.*d", -1, 1); - REQUIRE(!strcmp(buffer, "1")); - test::sprintf(buffer, "%.3s", "foobar"); - REQUIRE(!strcmp(buffer, "foo")); + /* ...explicit precision of 0 shall be no characters. */ + TEST_SNPRINTF(("%.0d", 0), ""); + TEST_SNPRINTF(("%.0o", 0), ""); +#if TEST_REDUNDANT_FLAGS + TEST_SNPRINTF(("%#.0d", 0), ""); +#endif + // Note: the original libc-testsuite specifies "" as expected. + TEST_SNPRINTF(("%#.0o", 0), "0"); + TEST_SNPRINTF(("%#.0x", 0), ""); + TEST_SNPRINTF(("%#3.0x", 0), " "); + + /* ...but it still has to honor width and flags. */ + TEST_SNPRINTF(("%2.0u", 0), " "); +#if TEST_REDUNDANT_FLAGS + TEST_SNPRINTF(("%02.0u", 0), " "); + TEST_SNPRINTF(("%02.0d", 0), " "); +#endif + TEST_SNPRINTF(("%2.0d", 0), " "); + TEST_SNPRINTF(("% .0d", 0), " "); + TEST_SNPRINTF(("%+.0d", 0), "+"); + + /* hex: test alt form and case */ + TEST_SNPRINTF(("%x", 63), "3f"); + TEST_SNPRINTF(("%#x", 63), "0x3f"); + TEST_SNPRINTF(("%X", 63), "3F"); + + /* octal: test alt form */ + TEST_SNPRINTF(("%o", 15), "17"); + TEST_SNPRINTF(("%#o", 15), "017"); + + /* basic form, handling of exponent/precision for 0 */ + TEST_SNPRINTF(("%e", 0.0), "0.000000e+00"); + TEST_SNPRINTF(("%f", 0.0), "0.000000"); + TEST_SNPRINTF(("%g", 0.0), "0"); + TEST_SNPRINTF(("%#g", 0.0), "0.00000"); + + /* rounding */ + TEST_SNPRINTF(("%f", 1.1), "1.100000"); + TEST_SNPRINTF(("%f", 1.2), "1.200000"); + TEST_SNPRINTF(("%f", 1.3), "1.300000"); + TEST_SNPRINTF(("%f", 1.4), "1.400000"); + TEST_SNPRINTF(("%f", 1.5), "1.500000"); + // Note: the original libc-testsuite test specifies "1.0612" as expected. + TEST_SNPRINTF(("%.4f", 1.06125), "1.0613"); + TEST_SNPRINTF(("%.2f", 1.375), "1.38"); + TEST_SNPRINTF(("%.1f", 1.375), "1.4"); + TEST_SNPRINTF(("%.15f", 1.1), "1.100000000000000"); + TEST_SNPRINTF(("%.16f", 1.1), "1.1000000000000001"); + TEST_SNPRINTF(("%.17f", 1.1), "1.10000000000000009"); + TEST_SNPRINTF(("%.2e", 1500001.0), "1.50e+06"); + TEST_SNPRINTF(("%.2e", 1505000.0), "1.50e+06"); + TEST_SNPRINTF(("%.2e", 1505000.00000095367431640625), "1.51e+06"); + TEST_SNPRINTF(("%.2e", 1505001.0), "1.51e+06"); + TEST_SNPRINTF(("%.2e", 1506000.0), "1.51e+06"); + + /* correctness in DBL_DIG places */ + TEST_SNPRINTF(("%.15g", 1.23456789012345), "1.23456789012345"); + + /* correct choice of notation for %g */ + TEST_SNPRINTF(("%g", 0.0001), "0.0001"); + TEST_SNPRINTF(("%g", 0.00001), "1e-05"); + TEST_SNPRINTF(("%g", 123456.0), "123456"); + TEST_SNPRINTF(("%g", 1234567.0), "1.23457e+06"); + TEST_SNPRINTF(("%.7g", 1234567.0), "1234567"); + TEST_SNPRINTF(("%.7g", 12345678.0), "1.234568e+07"); + TEST_SNPRINTF(("%.8g", 0.1), "0.1"); + TEST_SNPRINTF(("%.9g", 0.1), "0.1"); + TEST_SNPRINTF(("%.10g", 0.1), "0.1"); + TEST_SNPRINTF(("%.11g", 0.1), "0.1"); + + /* pi in double precision, printed to a few extra places */ + TEST_SNPRINTF(("%.15f", M_PI), "3.141592653589793"); + TEST_SNPRINTF(("%.18f", M_PI), "3.141592653589793116"); + + /* exact conversion of large integers */ + TEST_SNPRINTF(("%.0f", 340282366920938463463374607431768211456.0), + "340282366920938463463374607431768211456"); + + TEST_SNPRINTF_N(("%d", 123456), 6); + TEST_SNPRINTF_N(("%.4s", "hello"), 4); + TEST_SNPRINTF_N(("%.0s", "goodbye"), 0); + + { + char b[] = "xxxxxxxx"; + const char *s = "%d"; + int res = cur_snprintf(b, 4, s, 123456); + REQUIRE(res == 6); + REQUIRE_STR_EQ(b, "123"); + REQUIRE(b[5] == 'x'); // buffer overrun + } - test::sprintf(buffer, "% .0d", 0); - REQUIRE(!strcmp(buffer, " ")); + { + char b[2000]; + /* Perform ascii arithmetic to test printing tiny doubles */ + int res = cur_snprintf(b, sizeof(b), "%.1022f", 0x1p0-1021); + REQUIRE(res == 1024); + b[1] = '0'; + int i, k, j; + for (i = 0; i < 1021; i++) { + for (k = 0, j = 1023; j > 0; j--) { + if (b[j] < '5') + b[j] += b[j] - '0' + k, k = 0; + else + b[j] += b[j] - '0' - 10 + k, k = 1; + } + } + REQUIRE(b[1] == '1'); + for (j = 2; b[j] == '0'; j++); + REQUIRE(j == 1024); + } - test::sprintf(buffer, "%10.5d", 4); - REQUIRE(!strcmp(buffer, " 00004")); + // Not all implementations handle this correctly (such as glibc). It also + // can be slow. +#if TEST_IMPL_DEFINED + TEST_SNPRINTF_N(("%.*u", 2147483647, 0), 2147483647); + TEST_SNPRINTF_N(("%.*u ", 2147483647, 0), -1); +#endif - test::sprintf(buffer, "%*sx", -3, "hi"); - REQUIRE(!strcmp(buffer, "hi x")); + TEST_SNPRINTF_N(("%.4a", 1.0), 11); -#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL - test::sprintf(buffer, "%.*g", 2, 0.33333333); - REQUIRE(!strcmp(buffer, "0.33")); +#if TEST_IMPL_DEFINED +// int r = cur_snprintf(buffer, sizeof(buffer), "a%wb", &(int){0}); +// assert(r < 0); +// REQUIRE_STR_EQ(buffer, "a%wb"); +#endif - test::sprintf(buffer, "%.*e", 2, 0.33333333); - REQUIRE(!strcmp(buffer, "3.33e-01")); +#if TEST_IMPL_DEFINED && 0 + test_rprintf(cur_snprintf, buffer, sizeof(buffer), "%s_%s", "%s:%.3s", + "hello", "world"); + REQUIRE_STR_EQ(buffer, "hello_worldhello:wor"); #endif } +