Rewrite int printing to be non-recursive. Limit ints to 32 bits
authorKeith Packard <keithp@keithp.com>
Fri, 16 Nov 2012 15:38:40 +0000 (07:38 -0800)
committerKeith Packard <keithp@keithp.com>
Fri, 16 Nov 2012 15:38:40 +0000 (07:38 -0800)
This saves a ton of CPU time and stack space

Signed-off-by: Keith Packard <keithp@keithp.com>
functions/_PDCLIB/print.c

index 72db66bed2590a275e9bb346a1bc5a06ff2c55e5..9751e05ff9a7d0621789bcfb68642e5e66a68757 100644 (file)
@@ -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;
             }
         }