From 2b197c4185ac95ec421fb85a472f13a4ff333587 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 16 Nov 2012 07:38:40 -0800 Subject: [PATCH] Rewrite int printing to be non-recursive. Limit ints to 32 bits This saves a ton of CPU time and stack space Signed-off-by: Keith Packard --- functions/_PDCLIB/print.c | 117 ++++++++++++++++++++------------------ 1 file changed, 61 insertions(+), 56 deletions(-) diff --git a/functions/_PDCLIB/print.c b/functions/_PDCLIB/print.c index 72db66b..9751e05 100644 --- a/functions/_PDCLIB/print.c +++ b/functions/_PDCLIB/print.c @@ -56,7 +56,10 @@ do { \ } while ( 0 ) -static void intformat( intmax_t value, struct _PDCLIB_status_t * status ) +typedef int32_t intfmt_t; +typedef uint32_t uintfmt_t; + +static void intformat( intfmt_t value, struct _PDCLIB_status_t * status ) { /* At worst, we need two prefix characters (hex prefix). */ char preface[3] = "\0"; @@ -144,45 +147,47 @@ static void intformat( intmax_t value, struct _PDCLIB_status_t * status ) output once the number of characters to be printed is known, which happens at the lowermost recursion level. */ -static void int2base( intmax_t value, struct _PDCLIB_status_t * status ) + +#define INT_MAX_WIDTH (sizeof(intfmt_t) * 8 + 2) / 3 + +static void int2base( intfmt_t value, struct _PDCLIB_status_t * status ) { - /* Registering the character being printed at the end of the function here - already so it will be taken into account when the deepestmost recursion - does the prefix / padding stuff. + char digits[INT_MAX_WIDTH + 1]; + char c, *d = &digits[sizeof(digits)]; + intfmt_t last_value = value; + + *--d = '\0'; + + do { + int digit = value % status->base; + + last_value = value; + value /= status->base; + + if (digit < 0) + digit = -digit; + + /* + Account for character in the output + */ + + ++(status->current); + if ( (status->flags & E_lower) ) + *--d = _PDCLIB_digits[digit]; + else + *--d = _PDCLIB_Xdigits[digit]; + } while (value != 0); + + /* We reached the last digit, and only now know how long the + number to be printed actually is. Now we have to do the sign, + prefix, width, and precision padding stuff before printing the + digits */ - ++(status->current); - if ( ( value / status->base ) != 0 ) - { - /* More digits to be done - recurse deeper */ - int2base( value / status->base, status ); - } - else - { - /* We reached the last digit, the deepest point of our recursion, and - only now know how long the number to be printed actually is. Now we - have to do the sign, prefix, width, and precision padding stuff - before printing the numbers while we resurface from the recursion. - */ - intformat( value, status ); - } - /* Recursion tail - print the current digit. */ - { - int digit = value % status->base; - if ( digit < 0 ) - { - digit *= -1; - } - if ( status->flags & E_lower ) - { - /* Lowercase letters. Same array used for strto...(). */ - PUT( _PDCLIB_digits[ digit ] ); - } - else - { - /* Uppercase letters. Array only used here, only 0-F. */ - PUT( _PDCLIB_Xdigits[ digit ] ); - } - } + + intformat( last_value, status ); + + while ( (c = *d++) ) + PUT(c); } @@ -325,7 +330,7 @@ const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status } break; case 'j': - /* j -> intmax_t, which might or might not be long long */ + /* j -> intfmt_t, which might or might not be long long */ status->flags |= E_intmax; break; case 'z': @@ -416,38 +421,38 @@ const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status /* TODO: Check for invalid flag combinations. */ if ( status->flags & E_unsigned ) { - uintmax_t value; + uintfmt_t value; switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_size ) ) { case E_char: - value = (uintmax_t)(unsigned char)va_arg( status->arg, int ); + value = (uintfmt_t)(unsigned char)va_arg( status->arg, int ); break; case E_short: - value = (uintmax_t)(unsigned short)va_arg( status->arg, int ); + value = (uintfmt_t)(unsigned short)va_arg( status->arg, int ); break; case 0: - value = (uintmax_t)va_arg( status->arg, unsigned int ); + value = (uintfmt_t)va_arg( status->arg, unsigned int ); break; case E_long: - value = (uintmax_t)va_arg( status->arg, unsigned long ); + value = (uintfmt_t)va_arg( status->arg, unsigned long ); break; case E_llong: - value = (uintmax_t)va_arg( status->arg, unsigned long long ); + value = (uintfmt_t)va_arg( status->arg, unsigned long long ); break; case E_size: - value = (uintmax_t)va_arg( status->arg, size_t ); + value = (uintfmt_t)va_arg( status->arg, size_t ); break; } ++(status->current); /* FIXME: The if clause means one-digit values do not get formatted */ - /* Was introduced originally to get value to "safe" levels re. uintmax_t. */ + /* Was introduced originally to get value to "safe" levels re. uintfmt_t. */ if ( ( value / status->base ) != 0 ) { - int2base( (intmax_t)(value / status->base), status ); + int2base( (intfmt_t)(value / status->base), status ); } else { - intformat( (intmax_t)value, status ); + intformat( (intfmt_t)value, status ); } int digit = value % status->base; if ( digit < 0 ) @@ -468,25 +473,25 @@ const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_intmax ) ) { case E_char: - int2base( (intmax_t)(char)va_arg( status->arg, int ), status ); + int2base( (intfmt_t)(char)va_arg( status->arg, int ), status ); break; case E_short: - int2base( (intmax_t)(short)va_arg( status->arg, int ), status ); + int2base( (intfmt_t)(short)va_arg( status->arg, int ), status ); break; case 0: - int2base( (intmax_t)va_arg( status->arg, int ), status ); + int2base( (intfmt_t)va_arg( status->arg, int ), status ); break; case E_long: - int2base( (intmax_t)va_arg( status->arg, long ), status ); + int2base( (intfmt_t)va_arg( status->arg, long ), status ); break; case E_llong: - int2base( (intmax_t)va_arg( status->arg, long long ), status ); + int2base( (intfmt_t)va_arg( status->arg, long long ), status ); break; case E_ptrdiff: - int2base( (intmax_t)va_arg( status->arg, ptrdiff_t ), status ); + int2base( (intfmt_t)va_arg( status->arg, ptrdiff_t ), status ); break; case E_intmax: - int2base( va_arg( status->arg, intmax_t ), status ); + int2base( va_arg( status->arg, intfmt_t ), status ); break; } } -- 2.30.2