Rewrite int printing to be non-recursive. Limit ints to 32 bits
[fw/pdclib] / functions / _PDCLIB / print.c
index 5027c5fa8e5aaada9972292fa590a1c40b359a95..9751e05ff9a7d0621789bcfb68642e5e66a68757 100644 (file)
@@ -43,7 +43,7 @@
    n - pointer to maximum number of characters to be delivered in this call
    s - the buffer into which the character shall be delivered
 */
-#define DELIVER( x ) \
+#define PUT( x ) \
 do { \
     int character = x; \
     if ( status->i < status->n ) { \
@@ -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";
@@ -104,7 +107,7 @@ static void intformat( intmax_t value, struct _PDCLIB_status_t * status )
         {
             for ( size_t i = 0; i < status->width - characters; ++i )
             {
-                DELIVER( ' ' );
+                PUT( ' ' );
                 ++(status->current);
             }
         }
@@ -113,7 +116,7 @@ static void intformat( intmax_t value, struct _PDCLIB_status_t * status )
     preidx = 0;
     while ( preface[ preidx ] != '\0' )
     {
-        DELIVER( preface[ preidx++ ] );
+        PUT( preface[ preidx++ ] );
         ++(status->current);
     }
     if ( ( ! ( status->flags & E_minus ) ) && ( status->flags & E_zero ) )
@@ -123,14 +126,14 @@ static void intformat( intmax_t value, struct _PDCLIB_status_t * status )
         */
         while ( status->current < status->width )
         {
-            DELIVER( '0' );
+            PUT( '0' );
             ++(status->current);
         }
     }
     /* Do the precision padding if necessary. */
     for ( size_t i = 0; i < prec_pads; ++i )
     {
-        DELIVER( '0' );
+        PUT( '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...(). */
-        DELIVER( _PDCLIB_digits[ digit ] );
-    }
-    else
-    {
-        /* Uppercase letters. Array only used here, only 0-F. */
-        DELIVER( _PDCLIB_Xdigits[ digit ] );
-    }
-    }
+
+    intformat( last_value, status );
+
+    while ( (c = *d++) )
+       PUT(c);
 }
 
 
@@ -192,7 +197,7 @@ const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status
     if ( *(++spec) == '%' )
     {
         /* %% -> print single '%' */
-        DELIVER( *spec );
+        PUT( *spec );
         return ++spec;
     }
     /* Initializing status structure */
@@ -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':
@@ -381,7 +386,7 @@ const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status
             break;
         case 'c':
             /* TODO: Flags, wide chars. */
-            DELIVER( va_arg( status->arg, int ) );
+            PUT( va_arg( status->arg, int ) );
             return ++spec;
         case 's':
             /* TODO: Flags, wide chars. */
@@ -389,7 +394,7 @@ const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status
                 char * s = va_arg( status->arg, char * );
                 while ( *s != '\0' )
                 {
-                    DELIVER( *(s++) );
+                    PUT( *(s++) );
                 }
                 return ++spec;
             }
@@ -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 )
@@ -456,11 +461,11 @@ const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status
             }
             if ( status->flags & E_lower )
             {
-                DELIVER( _PDCLIB_digits[ digit ] );
+                PUT( _PDCLIB_digits[ digit ] );
             }
             else
             {
-                DELIVER( _PDCLIB_Xdigits[ digit ] );
+                PUT( _PDCLIB_Xdigits[ digit ] );
             }
         }
         else
@@ -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;
             }
         }
@@ -494,7 +499,7 @@ const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status
         {
             while ( status->current < status->width )
             {
-                DELIVER( ' ' );
+                PUT( ' ' );
                 ++(status->current);
             }
         }