diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0a0b876 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ + +/.idea +/.vs +/cmake-build-release-mingw diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..cdbdeef --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,42 @@ +cmake_minimum_required(VERSION 3.10) +project(TinyPrintfTests) + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -O3 -g") + +# set(RUNNER "valgrind") + +add_executable(test_printf test/printf.c tinyprintf.c) +add_executable(test_sprintf test/sprintf.c tinyprintf.c) + +add_library(tinyprintf_minimal OBJECT tinyprintf.c) +target_compile_definitions(tinyprintf_minimal PRIVATE + TINYPRINTF_DEFINE_TFP_PRINTF=0 + TINYPRINTF_DEFINE_TFP_SPRINTF=0 + TINYPRINTF_OVERRIDE_LIBC=0 +) + +add_library(tinyprintf_only_tfp_printf OBJECT tinyprintf.c) +target_compile_definitions(tinyprintf_only_tfp_printf PRIVATE + TINYPRINTF_DEFINE_TFP_PRINTF=1 + TINYPRINTF_DEFINE_TFP_SPRINTF=0 + TINYPRINTF_OVERRIDE_LIBC=0 +) + +add_library(tinyprintf_only_tfp_sprintf OBJECT tinyprintf.c) +target_compile_definitions(tinyprintf_only_tfp_sprintf PRIVATE + TINYPRINTF_DEFINE_TFP_PRINTF=0 + TINYPRINTF_DEFINE_TFP_SPRINTF=1 + TINYPRINTF_OVERRIDE_LIBC=0 +) + +target_link_libraries(test_printf PRIVATE tinyprintf_minimal) +target_link_libraries(test_sprintf PRIVATE tinyprintf_minimal) + +add_custom_target(runtests + COMMAND ${RUNNER} $ || exit $$? + COMMAND ${RUNNER} $ || exit $$? +) + +add_custom_target(cleanup + COMMAND ${CMAKE_COMMAND} -E remove -f test/printf test/sprintf +) diff --git a/Makefile b/Makefile deleted file mode 100644 index 43adc13..0000000 --- a/Makefile +++ /dev/null @@ -1,39 +0,0 @@ - -CPPFLAGS+=-I. -CFLAGS+=-Wall -W -Wextra -O3 -g -# RUNNER:=valgrind - -.PHONY: all test tests compiletests runtests clean - -all: tests - -test tests: compiletests runtests - -runtests: test/printf test/sprintf - set -x ; for prg in $^ ; do $(RUNNER) $$prg || exit $$? ; done - -compiletests: - $(COMPILE.c) \ - -DTINYPRINTF_DEFINE_TFP_PRINTF=0 \ - -DTINYPRINTF_DEFINE_TFP_SPRINTF=0 \ - -DTINYPRINTF_OVERRIDE_LIBC=0 \ - -o tinyprintf_minimal.o tinyprintf.c - $(COMPILE.c) \ - -DTINYPRINTF_DEFINE_TFP_PRINTF=1 \ - -DTINYPRINTF_DEFINE_TFP_SPRINTF=0 \ - -DTINYPRINTF_OVERRIDE_LIBC=0 \ - -o tinyprintf_only_tfp_printf.o tinyprintf.c - $(COMPILE.c) \ - -DTINYPRINTF_DEFINE_TFP_PRINTF=0 \ - -DTINYPRINTF_DEFINE_TFP_SPRINTF=1 \ - -DTINYPRINTF_OVERRIDE_LIBC=0 \ - -o tinyprintf_only_tfp_sprintf.o tinyprintf.c - -test/printf: test/printf.o tinyprintf.o - $(LINK.c) -o $@ $^ - -test/sprintf: test/sprintf.o tinyprintf.o - $(LINK.c) -o $@ $^ - -clean: - $(RM) *.o test/*.o *~ test/*~ test/printf test/sprintf diff --git a/README b/README index 64f27e5..e022172 100644 --- a/README +++ b/README @@ -3,4 +3,4 @@ Copyright (C) 2004,2012 Kustaa Nyholm Licenses: LGPL 2.1 or later (see LICENSE.LGPL-2.1) or BSD-new (see LICENSE.BSD-new) -"You are free to use the LGPL license [...] or the BSD license" +"You are free to use the LGPL license [...] or the BSD license" \ No newline at end of file diff --git a/tinyprintf.c b/tinyprintf.c index bb22700..094bd3c 100644 --- a/tinyprintf.c +++ b/tinyprintf.c @@ -21,6 +21,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "tinyprintf.h" +#include // For getenv /* * Configuration @@ -35,6 +36,11 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA /* Enable %z (size_t) support */ #define PRINTF_SIZE_T_SUPPORT +// Default to comma if no environment variable is set +#ifndef TINYPRINTF_GROUP_SEPARATOR +#define TINYPRINTF_GROUP_SEPARATOR ',' +#endif + /* * Configuration adjustments */ @@ -97,6 +103,11 @@ static void _TFP_GCC_NO_INLINE_ ulli2a( if (n || dgt > 0 || d == 0) { *bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10); ++n; + + // Add comma if appropriate (only for base 10) + if (p->base == 10 && n > 0 && (n % 3) == 0) { + *bf++ = TINYPRINTF_GROUP_SEPARATOR; + } } } *bf = 0; @@ -127,6 +138,11 @@ static void uli2a(unsigned long int num, struct param *p) if (n || dgt > 0 || d == 0) { *bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10); ++n; + + // Add comma if appropriate (only for base 10) + if (p->base == 10 && n > 0 && (n % 3) == 0) { + *bf++ = TINYPRINTF_GROUP_SEPARATOR; + } } } *bf = 0; @@ -142,34 +158,68 @@ static void li2a(long num, struct param *p) } #endif -static void ui2a(unsigned int num, struct param *p) -{ +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) + +static void ui2a(unsigned int num, struct param *p) { + char *bf = p->bf; int n = 0; unsigned int d = 1; - char *bf = p->bf; - while (num / d >= p->base) + while (likely(num / d >= p->base)) d *= p->base; - while (d != 0) { + while (likely(d != 0)) { int dgt = num / d; num %= d; d /= p->base; - if (n || dgt > 0 || d == 0) { + if (likely(n || dgt > 0 || d == 0)) { *bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10); ++n; + + // Add comma if appropriate (only for base 10) + if (p->base == 10 && n > 0 && (n % 3) == 0) { + *bf++ = TINYPRINTF_GROUP_SEPARATOR; + } } } *bf = 0; } -static void i2a(int num, struct param *p) -{ - if (num < 0) { +// static void ui2a(unsigned int num, struct param *p) +// { +// int n = 0; +// unsigned int d = 1; +// char *bf = p->bf; +// while (num / d >= p->base) +// d *= p->base; +// while (d != 0) { +// int dgt = num / d; +// num %= d; +// d /= p->base; +// if (n || dgt > 0 || d == 0) { +// *bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10); +// ++n; +// } +// } +// *bf = 0; +// } + +static inline void i2a(int num, struct param *p) { + if (unlikely(num < 0)) { num = -num; p->sign = '-'; } ui2a(num, p); } +// static void i2a(int num, struct param *p) +// { +// if (num < 0) { +// num = -num; +// p->sign = '-'; +// } +// ui2a(num, p); +// } + static int a2d(char ch) { if (ch >= '0' && ch <= '9') @@ -182,7 +232,7 @@ static int a2d(char ch) return -1; } -static char a2u(char ch, const char **src, int base, unsigned int *nump) +static char a2u(char ch, const char **src, const int base, unsigned int *nump) { const char *p = *src; unsigned int num = 0; @@ -198,33 +248,24 @@ static char a2u(char ch, const char **src, int base, unsigned int *nump) return ch; } -static void putchw(void *putp, putcf putf, struct param *p) -{ +static inline void putchw(void *putp, putcf putf, struct param *p) { char ch; int n = p->width; char *bf = p->bf; - /* Number of filling characters */ - while (*bf++ && n > 0) + while (*bf++ && likely(n > 0)) n--; if (p->sign) n--; - if (p->alt && p->base == 16) - n -= 2; - else if (p->alt && p->base == 8) - n--; - /* Fill with space to align to the right, before alternate or sign */ if (!p->lz && !p->align_left) { - while (n-- > 0) + while (likely(n-- > 0)) putf(putp, ' '); } - /* print sign */ if (p->sign) putf(putp, p->sign); - /* Alternate */ if (p->alt && p->base == 16) { putf(putp, '0'); putf(putp, (p->uc ? 'X' : 'x')); @@ -232,194 +273,327 @@ static void putchw(void *putp, putcf putf, struct param *p) putf(putp, '0'); } - /* Fill with zeros, after alternate or sign */ if (p->lz) { - while (n-- > 0) + while (likely(n-- > 0)) putf(putp, '0'); } - /* Put actual buffer */ bf = p->bf; while ((ch = *bf++)) putf(putp, ch); - /* Fill with space to align to the left, after string */ - if (!p->lz && p->align_left) { - while (n-- > 0) + if (p->align_left) { + while (likely(n-- > 0)) putf(putp, ' '); } } -void tfp_format(void *putp, putcf putf, const char *fmt, va_list va) -{ +// static void putchw(void *putp, putcf putf, struct param *p) +// { +// char ch; +// int n = p->width; +// char *bf = p->bf; +// +// /* Number of filling characters */ +// while (*bf++ && n > 0) +// n--; +// if (p->sign) +// n--; +// if (p->alt && p->base == 16) +// n -= 2; +// else if (p->alt && p->base == 8) +// n--; +// +// /* Fill with space to align to the right, before alternate or sign */ +// if (!p->lz && !p->align_left) { +// while (n-- > 0) +// putf(putp, ' '); +// } +// +// /* print sign */ +// if (p->sign) +// putf(putp, p->sign); +// +// /* Alternate */ +// if (p->alt && p->base == 16) { +// putf(putp, '0'); +// putf(putp, (p->uc ? 'X' : 'x')); +// } else if (p->alt && p->base == 8) { +// putf(putp, '0'); +// } +// +// /* Fill with zeros, after alternate or sign */ +// if (p->lz) { +// while (n-- > 0) +// putf(putp, '0'); +// } +// +// /* Put actual buffer */ +// bf = p->bf; +// while ((ch = *bf++)) +// putf(putp, ch); +// +// /* Fill with space to align to the left, after string */ +// if (!p->lz && p->align_left) { +// while (n-- > 0) +// putf(putp, ' '); +// } +// } + +void tfp_format(void *putp, putcf putf, const char *fmt, va_list va) { struct param p; #ifdef PRINTF_LONG_SUPPORT - char bf[23]; /* long = 64b on some architectures */ + char bf[23]; // Для long = 64b #else - char bf[12]; /* int = 32b on some architectures */ + char bf[12]; // Для int = 32b #endif - char ch; p.bf = bf; + char ch; while ((ch = *(fmt++))) { - if (ch != '%') { + if (likely(ch != '%')) { putf(putp, ch); } else { -#ifdef PRINTF_LONG_SUPPORT - char lng = 0; /* 1 for long, 2 for long long */ -#endif - /* Init parameter struct */ p.lz = 0; p.alt = 0; p.width = 0; p.align_left = 0; p.sign = 0; - /* Flags */ while ((ch = *(fmt++))) { switch (ch) { - case '-': - p.align_left = 1; - continue; - case '0': - p.lz = 1; - continue; - case '#': - p.alt = 1; - continue; - default: - break; + case '-': p.align_left = 1; continue; + case '0': p.lz = 1; continue; + case '#': p.alt = 1; continue; + case '\'': // Treat ' as a flag, but don't add commas for now + break; + default: break; } break; } - /* Width */ if (ch >= '0' && ch <= '9') { ch = a2u(ch, &fmt, 10, &(p.width)); } - /* We accept 'x.y' format but don't support it completely: - * we ignore the 'y' digit => this ignores 0-fill - * size and makes it == width (ie. 'x') */ if (ch == '.') { - p.lz = 1; /* zero-padding */ - /* ignore actual 0-fill size: */ - do { - ch = *(fmt++); - } while ((ch >= '0') && (ch <= '9')); + p.lz = 1; + do { + ch = *(fmt++); + } while ((ch >= '0') && (ch <= '9')); } -#ifdef PRINTF_SIZE_T_SUPPORT -# ifdef PRINTF_LONG_SUPPORT - if (ch == 'z') { - ch = *(fmt++); - if (sizeof(size_t) == sizeof(unsigned long int)) - lng = 1; -# ifdef PRINTF_LONG_LONG_SUPPORT - else if (sizeof(size_t) == sizeof(unsigned long long int)) - lng = 2; -# endif - } else -# endif -#endif - -#ifdef PRINTF_LONG_SUPPORT - if (ch == 'l') { - ch = *(fmt++); - lng = 1; -#ifdef PRINTF_LONG_LONG_SUPPORT - if (ch == 'l') { - ch = *(fmt++); - lng = 2; - } -#endif - } -#endif switch (ch) { - case 0: - goto abort; - case 'u': - p.base = 10; -#ifdef PRINTF_LONG_SUPPORT -#ifdef PRINTF_LONG_LONG_SUPPORT - if (2 == lng) - ulli2a(va_arg(va, unsigned long long int), &p); - else -#endif - if (1 == lng) - uli2a(va_arg(va, unsigned long int), &p); - else -#endif + case 'u': + p.base = 10; ui2a(va_arg(va, unsigned int), &p); - putchw(putp, putf, &p); - break; - case 'd': - case 'i': - p.base = 10; -#ifdef PRINTF_LONG_SUPPORT -#ifdef PRINTF_LONG_LONG_SUPPORT - if (2 == lng) - lli2a(va_arg(va, long long int), &p); - else -#endif - if (1 == lng) - li2a(va_arg(va, long int), &p); - else -#endif + putchw(putp, putf, &p); + break; + case 'd': + case 'i': + p.base = 10; i2a(va_arg(va, int), &p); - putchw(putp, putf, &p); - break; -#ifdef SIZEOF_POINTER - case 'p': - p.alt = 1; -# if defined(SIZEOF_INT) && SIZEOF_POINTER <= SIZEOF_INT - lng = 0; -# elif defined(SIZEOF_LONG) && SIZEOF_POINTER <= SIZEOF_LONG - lng = 1; -# elif defined(SIZEOF_LONG_LONG) && SIZEOF_POINTER <= SIZEOF_LONG_LONG - lng = 2; -# endif -#endif - case 'x': - case 'X': - p.base = 16; - p.uc = (ch == 'X')?1:0; -#ifdef PRINTF_LONG_SUPPORT -#ifdef PRINTF_LONG_LONG_SUPPORT - if (2 == lng) - ulli2a(va_arg(va, unsigned long long int), &p); - else -#endif - if (1 == lng) - uli2a(va_arg(va, unsigned long int), &p); - else -#endif + putchw(putp, putf, &p); + break; + case 'x': + case 'X': + p.base = 16; + p.uc = (ch == 'X'); ui2a(va_arg(va, unsigned int), &p); - putchw(putp, putf, &p); - break; - case 'o': - p.base = 8; - ui2a(va_arg(va, unsigned int), &p); - putchw(putp, putf, &p); - break; - case 'c': - putf(putp, (char)(va_arg(va, int))); - break; - case 's': - p.bf = va_arg(va, char *); - putchw(putp, putf, &p); - p.bf = bf; - break; - case '%': - putf(putp, ch); - default: - break; + putchw(putp, putf, &p); + break; + case 'o': + p.base = 8; + ui2a(va_arg(va, unsigned int), &p); + putchw(putp, putf, &p); + break; + case 'c': + putf(putp, (char)(va_arg(va, int))); + break; + case 's': + p.bf = va_arg(va, char *); + putchw(putp, putf, &p); + p.bf = bf; + break; + case '%': + putf(putp, ch); + default: + break; } } } - abort:; } +// void tfp_format(void *putp, putcf putf, const char *fmt, va_list va) +// { +// struct param p; +// #ifdef PRINTF_LONG_SUPPORT +// char bf[23]; /* long = 64b on some architectures */ +// #else +// char bf[12]; /* int = 32b on some architectures */ +// #endif +// char ch; +// p.bf = bf; +// +// while ((ch = *(fmt++))) { +// if (ch != '%') { +// putf(putp, ch); +// } else { +// #ifdef PRINTF_LONG_SUPPORT +// char lng = 0; /* 1 for long, 2 for long long */ +// #endif +// /* Init parameter struct */ +// p.lz = 0; +// p.alt = 0; +// p.width = 0; +// p.align_left = 0; +// p.sign = 0; +// +// /* Flags */ +// while ((ch = *(fmt++))) { +// switch (ch) { +// case '-': +// p.align_left = 1; +// continue; +// case '0': +// p.lz = 1; +// continue; +// case '#': +// p.alt = 1; +// continue; +// default: +// break; +// } +// break; +// } +// +// /* Width */ +// if (ch >= '0' && ch <= '9') { +// ch = a2u(ch, &fmt, 10, &(p.width)); +// } +// +// /* We accept 'x.y' format but don't support it completely: +// * we ignore the 'y' digit => this ignores 0-fill +// * size and makes it == width (ie. 'x') */ +// if (ch == '.') { +// p.lz = 1; /* zero-padding */ +// /* ignore actual 0-fill size: */ +// do { +// ch = *(fmt++); +// } while ((ch >= '0') && (ch <= '9')); +// } +// +// #ifdef PRINTF_SIZE_T_SUPPORT +// # ifdef PRINTF_LONG_SUPPORT +// if (ch == 'z') { +// ch = *(fmt++); +// if (sizeof(size_t) == sizeof(unsigned long int)) +// lng = 1; +// # ifdef PRINTF_LONG_LONG_SUPPORT +// else if (sizeof(size_t) == sizeof(unsigned long long int)) +// lng = 2; +// # endif +// } else +// # endif +// #endif +// +// #ifdef PRINTF_LONG_SUPPORT +// if (ch == 'l') { +// ch = *(fmt++); +// lng = 1; +// #ifdef PRINTF_LONG_LONG_SUPPORT +// if (ch == 'l') { +// ch = *(fmt++); +// lng = 2; +// } +// #endif +// } +// #endif +// switch (ch) { +// case 0: +// goto abort; +// case 'u': +// p.base = 10; +// #ifdef PRINTF_LONG_SUPPORT +// #ifdef PRINTF_LONG_LONG_SUPPORT +// if (2 == lng) +// ulli2a(va_arg(va, unsigned long long int), &p); +// else +// #endif +// if (1 == lng) +// uli2a(va_arg(va, unsigned long int), &p); +// else +// #endif +// ui2a(va_arg(va, unsigned int), &p); +// putchw(putp, putf, &p); +// break; +// case 'd': +// case 'i': +// p.base = 10; +// #ifdef PRINTF_LONG_SUPPORT +// #ifdef PRINTF_LONG_LONG_SUPPORT +// if (2 == lng) +// lli2a(va_arg(va, long long int), &p); +// else +// #endif +// if (1 == lng) +// li2a(va_arg(va, long int), &p); +// else +// #endif +// i2a(va_arg(va, int), &p); +// putchw(putp, putf, &p); +// break; +// #ifdef SIZEOF_POINTER +// case 'p': +// p.alt = 1; +// # if defined(SIZEOF_INT) && SIZEOF_POINTER <= SIZEOF_INT +// lng = 0; +// # elif defined(SIZEOF_LONG) && SIZEOF_POINTER <= SIZEOF_LONG +// lng = 1; +// # elif defined(SIZEOF_LONG_LONG) && SIZEOF_POINTER <= SIZEOF_LONG_LONG +// lng = 2; +// # endif +// #endif +// case 'x': +// case 'X': +// p.base = 16; +// p.uc = (ch == 'X')?1:0; +// #ifdef PRINTF_LONG_SUPPORT +// #ifdef PRINTF_LONG_LONG_SUPPORT +// if (2 == lng) +// ulli2a(va_arg(va, unsigned long long int), &p); +// else +// #endif +// if (1 == lng) +// uli2a(va_arg(va, unsigned long int), &p); +// else +// #endif +// ui2a(va_arg(va, unsigned int), &p); +// putchw(putp, putf, &p); +// break; +// case 'o': +// p.base = 8; +// ui2a(va_arg(va, unsigned int), &p); +// putchw(putp, putf, &p); +// break; +// case 'c': +// putf(putp, (char)(va_arg(va, int))); +// break; +// case 's': +// p.bf = va_arg(va, char *); +// putchw(putp, putf, &p); +// p.bf = bf; +// break; +// case '%': +// putf(putp, ch); +// default: +// break; +// } +// } +// } +// abort:; +// } + #if TINYPRINTF_DEFINE_TFP_PRINTF static putcf stdout_putf; static void *stdout_putp; @@ -518,4 +692,4 @@ int tfp_sprintf(char *str, const char *format, ...) va_end(ap); return retval; } -#endif +#endif \ No newline at end of file