CrystalSpace

Public API Reference

csutil/formatter.h

Go to the documentation of this file.
00001 /*
00002     Copyright (C) 2005 by Frank Richter
00003 
00004     This library is free software; you can redistribute it and/or
00005     modify it under the terms of the GNU Library General Public
00006     License as published by the Free Software Foundation; either
00007     version 2 of the License, or (at your option) any later version.
00008 
00009     This library is distributed in the hope that it will be useful,
00010     but WITHOUT ANY WARRANTY; without even the implied warranty of
00011     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012     Library General Public License for more details.
00013 
00014     You should have received a copy of the GNU Library General Public
00015     License along with this library; if not, write to the Free
00016     Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00017 */
00018 
00019 #ifndef __CS_CSUTIL_FORMATTER_H__
00020 #define __CS_CSUTIL_FORMATTER_H__
00021 
00027 #include "cssysdef.h"
00028 #include "csgeom/math.h"
00029 #include "csutil/csuctransform.h"
00030 #include "csutil/dirtyaccessarray.h"
00031 #include "csutil/util.h"
00032 
00033 // MinGW uses MS CRT, but it can't grok long double.  VC doesn't have long
00034 // double and CRT printf() doesn't know %Lf, %Lg, or %Le.
00035 #if defined(__MINGW32__) || defined(CS_COMPILER_MSVC)
00036 #define CS_FORMATTER_NO_LONG_DOUBLE_FORMAT
00037 #endif
00038 // MinGWs <inttypes.h> uses the MS-specific 'I64' format specifier in its
00039 // PR?64 macros. Enable support for I64 so the PR?64 macros can be used.
00040 #if defined(__MINGW32__) || defined(CS_COMPILER_MSVC)
00041 #define CS_FORMATTER_PROVIDE_I64
00042 #endif
00043 
00071 template <class T>
00072 class csFmtDefaultReader
00073 {
00074   const T* str;
00075   const T* const startStr;
00076   size_t len;
00077   const size_t startLen;
00078 public:
00080   csFmtDefaultReader (const T* string, size_t length) : startStr (string), 
00081     startLen (length) { Reset(); }
00083   bool GetNext (utf32_char& ch) 
00084   {
00085     int n = csUnicodeTransform::Decode (str, len, ch);
00086     if (n == 0) return false;
00087     str += (size_t)n;
00088     len -= (size_t)n;
00089     return true;
00090   }
00092   void Reset() { str = startStr; len = startLen; }
00094   size_t GetPosition() const { return str - startStr; }
00095 };
00096 
00097 
00103 template <class T>
00104 class csFmtDefaultWriter
00105 {
00106   T* dest;
00107   size_t size;
00108   size_t total;
00109 public:
00111   csFmtDefaultWriter (T* dest, size_t size) : dest (dest), size (size), 
00112     total (0) {}
00114   void Put (utf32_char ch) 
00115   { 
00116     size_t n = (size_t)csUnicodeTransform::Encode (ch, dest, size);
00117     total += n;
00118     n = csMin (size, n);
00119     dest += n;
00120     size -= n;
00121   }
00126   size_t GetTotal() const { return total; }
00127 };
00128 
00134 template <class Twriter, class Treader>
00135 class csPrintfFormatter
00136 {
00137   class Scratch : public csDirtyAccessArray<utf32_char>
00138   {
00139   public:
00140     void WriteTo (Twriter& writer, size_t offset = 0, size_t len = (size_t)~0)
00141     {
00142       const size_t n = MIN (len, GetSize ());
00143       for (size_t i = offset; i < n; i++) writer.Put (Get (i));
00144     }
00145   };
00146   Scratch scratch;
00147 
00149   struct FmtParam
00150   {
00151     union
00152     {
00153       int vInt;
00154       void* vPtr;
00155       long vLong;
00156       longlong vLL;
00157       double vDbl;
00158       long double vLongDbl;
00159       size_t vSzT;
00160       ptrdiff_t vPDT;
00161       intmax_t vIMT;
00162     };
00163   };
00164   enum Conversion
00165   {
00166     convBogus = 0,
00167     convNone,
00168     convInt,
00169     convOctal,
00170     convUint,
00171     convHex,
00172     convFloatFix,
00173     convFloatExp,
00174     convFloatGeneral,
00175     convFloatHex,
00176     convChar,
00177     convStr,
00178     convPtr,
00179     convGetNum,
00180     convErrno
00181   };
00182   enum Type
00183   {
00184     typeNone = 0,
00185     typeLongLong = 3, // The reason for that: see I64 support
00186     typeChar,
00187     typeShort,
00188     typeIntmax,
00189     typeLong,
00190     typePtrDiffT,
00191     typeSizeT
00192   };
00194   struct FormatSpec
00195   {
00196     size_t copyRun;
00197     size_t fmtSkip;
00198 
00199     int paramIdx;
00200     bool leftJustify;
00201     bool plusSign;
00202     bool spacePrefix;
00203     bool basePrefix;
00204     bool padZero;
00205     int width;
00206     int precision;
00207     Conversion conversion;
00208     bool uppercase;
00209     Type type;
00210 
00211     FormatSpec() { Reset(); }
00212     void Reset () 
00213     { 
00214       memset (this, 0, sizeof (*this)); 
00215       precision = -1;
00216     }
00217   };
00218   csArray<FormatSpec> formatSpecs;
00219   csArray<FmtParam> params;
00220   Treader& reader;
00221 
00222   struct SpecParseState
00223   {
00224     utf32_char ch;
00225     FormatSpec currentFormat;
00226     size_t charRun;
00227     int paramIdx;
00228     size_t fmtBegin;
00229 
00230     SpecParseState() : paramIdx(0) {}
00231     void Reset()
00232     {
00233       charRun = 0;
00234       currentFormat.Reset();
00235     }
00236   };
00237 
00238   bool ParseFlag (SpecParseState& state)
00239   {
00240     switch (state.ch)
00241     {
00242       case '-':
00243         {
00244           state.currentFormat.leftJustify = true;
00245           return true;
00246         }
00247       case '+':
00248         {
00249           state.currentFormat.plusSign = true;
00250           return true;
00251         }
00252       case ' ':
00253         {
00254           state.currentFormat.spacePrefix = true;
00255           return true;
00256         }
00257       case '#':
00258         {
00259           state.currentFormat.basePrefix = true;
00260           return true;
00261         }
00262       case '0':
00263         {
00264           state.currentFormat.padZero = true;
00265           return true;
00266         }
00267       case '\'':
00268         {
00269           return true;
00270         }
00271     }
00272     return false;
00273   }
00274   
00275   bool ParseType (SpecParseState& state)
00276   {
00277     switch (state.ch)
00278     {
00279       case 'h':
00280         {
00281           if (state.currentFormat.type == typeNone)
00282             state.currentFormat.type = typeShort;
00283           else if (state.currentFormat.type == typeShort)
00284             state.currentFormat.type = typeChar;
00285           else
00286             return false;
00287           return true;
00288         }
00289       case 'j':
00290         {
00291           if (state.currentFormat.type == typeNone)
00292             state.currentFormat.type = typeIntmax;
00293           else
00294             return false;
00295           return true;
00296         }
00297       case 'l':
00298         {
00299           if (state.currentFormat.type == typeNone)
00300             state.currentFormat.type = typeLong;
00301           else if (state.currentFormat.type == typeLong)
00302             state.currentFormat.type = typeLongLong;
00303           else
00304             return false;
00305           return true;
00306         }
00307       case 'L':
00308       case 'q':
00309         {
00310           if (state.currentFormat.type == typeNone)
00311             state.currentFormat.type = typeLongLong;
00312           else
00313             return false;
00314           return true;
00315         }
00316       case 't':
00317         {
00318           if (state.currentFormat.type == typeNone)
00319             state.currentFormat.type = typePtrDiffT;
00320           else
00321             return false;
00322           return true;
00323         }
00324       case 'z':
00325         {
00326           if (state.currentFormat.type == typeNone)
00327             state.currentFormat.type = typeSizeT;
00328           else
00329             return false;
00330           return true;
00331         }
00332 #ifdef CS_FORMATTER_PROVIDE_I64
00333       case 'I':
00334       case '6':
00335       case '4':
00336         {
00337           static const utf32_char I64spec[3] = {'I', '6', '4'};
00338           const int I64specStartType = typeLongLong - 2;
00339           if (state.ch == I64spec[0])
00340             state.currentFormat.type = (Type)I64specStartType;
00341           else
00342           {
00343             state.currentFormat.type = (Type)(state.currentFormat.type + 1);
00344             if (state.ch != 
00345               I64spec[state.currentFormat.type - I64specStartType])
00346               return false;
00347           }
00348           return true;
00349         }
00350         break;
00351 #endif
00352     }
00353     return false;
00354   }
00355 
00356   bool ParseConversion (SpecParseState& state)
00357   {
00358 #ifdef CS_FORMATTER_PROVIDE_I64
00359     // Check to detect incomplete I64 specifiers
00360     const int I64specStartType = typeLongLong - 2;
00361     if ((state.currentFormat.type >= I64specStartType)
00362       && (state.currentFormat.type < typeLongLong))
00363       return false;
00364 #endif
00365     switch (state.ch)
00366     {
00367       case '%':
00368         {
00369           const size_t fmtLen = (reader.GetPosition() - 1) - state.fmtBegin;
00370           if (fmtLen == 1)
00371           {
00372             state.currentFormat.conversion = convNone;
00373             state.fmtBegin++;
00374             state.currentFormat.copyRun++;
00375             return true;
00376           }
00377           break;
00378         }
00379       case 'd':
00380       case 'i':
00381         {
00382           state.currentFormat.conversion = convInt;
00383           return true;
00384         }
00385       case 'o':
00386         {
00387           state.currentFormat.conversion = convOctal;
00388           return true;
00389         }
00390       case 'u':
00391         {
00392           state.currentFormat.conversion = convUint;
00393           return true;
00394         }
00395       case 'x':
00396       case 'X':
00397         {
00398           state.currentFormat.conversion = convHex;
00399           state.currentFormat.uppercase = (state.ch == 'X');
00400           return true;
00401         }
00402       case 'f':
00403         {
00404           state.currentFormat.conversion = convFloatFix;
00405           return true;
00406         }
00407       case 'e':
00408       case 'E':
00409         {
00410           state.currentFormat.conversion = convFloatExp;
00411           state.currentFormat.uppercase = (state.ch == 'E');
00412           return true;
00413         }
00414       case 'g':
00415       case 'G':
00416         {
00417           state.currentFormat.conversion = convFloatGeneral;
00418           state.currentFormat.uppercase = (state.ch == 'G');
00419           return true;
00420         }
00421       case 'a':
00422       case 'A':
00423         {
00424           state.currentFormat.conversion = convFloatHex;
00425           state.currentFormat.uppercase = (state.ch == 'A');
00426           return true;
00427         }
00428       case 'c':
00429         {
00430           state.currentFormat.conversion = convChar;
00431           return true;
00432         }
00433       case 'C':
00434         {
00435           state.currentFormat.conversion = convChar;
00436           state.currentFormat.type = typeLong;
00437           return true;
00438         }
00439       case 's':
00440         {
00441           state.currentFormat.conversion = convStr;
00442           return true;
00443         }
00444       case 'S':
00445         {
00446           state.currentFormat.conversion = convStr;
00447           state.currentFormat.type = typeLong;
00448           return true;
00449         }
00450       case 'p':
00451         {
00452           state.currentFormat.conversion = convPtr;
00453           return true;
00454         }
00455       case 'n':
00456         {
00457           state.currentFormat.conversion = convGetNum;
00458           return true;
00459         }
00460       case 'm':
00461         {
00462           state.currentFormat.conversion = convErrno;
00463           return true;
00464         }
00465     }
00466     return false;
00467   }
00468 
00469   void ParseSpec ()
00470   {
00471     enum {
00472       scanFormat,
00473       formatParamFlagsWidthPrecTypeConversion,
00474       formatFlagsWidthPrecTypeConversion,
00475       formatParamWidth,
00476       formatDotPrecTypeConversion,
00477       formatPrecTypeConversion,
00478       formatTypeConversion
00479     } parseState = scanFormat;
00480 
00481     // Collect positions of state specifiers from format string
00482     SpecParseState state;
00483     state.Reset();
00484     while (reader.GetNext (state.ch))
00485     {
00486       switch (parseState)
00487       {
00488         // Note: all falling through in this switch() is intentional.
00489         case scanFormat:
00490           {
00491             // Check for a % sign
00492             if (state.ch == '%')
00493             {
00494               parseState = formatParamFlagsWidthPrecTypeConversion;
00495               state.fmtBegin = reader.GetPosition() - 1;
00496               state.currentFormat.copyRun = state.charRun;
00497             }
00498             else
00499               state.charRun++;
00500           }
00501           break;
00502         case formatParamFlagsWidthPrecTypeConversion:
00503           // Check for start of width or param index
00504           if ((state.ch >= '1') && (state.ch <= '9'))
00505           {
00506             state.currentFormat.width = state.ch - '0';
00507             parseState = formatParamWidth;
00508             break;
00509           }
00510           // Check for '*' (fetch width from args)
00511           else if (state.ch == '*')
00512           {
00513             state.currentFormat.width = -2;
00514             parseState = formatDotPrecTypeConversion;
00515             break;
00516           }
00517           // Param delimiter
00518           else if (state.ch == '$')
00519           {
00520             // \todo fix for empty param
00521             parseState = formatFlagsWidthPrecTypeConversion;
00522             break;
00523           }
00524         case formatParamWidth:
00525           if (parseState == formatParamWidth) // != can occur due fallthrough
00526           {
00527             // Subsequent digits width or param index
00528             if ((state.ch >= '0') && (state.ch <= '9'))
00529             {
00530               state.currentFormat.width *= 10;
00531               state.currentFormat.width += state.ch - '0';
00532               break;
00533             }
00534             // Param delimiter
00535             else if (state.ch == '$')
00536             {
00537               state.paramIdx = state.currentFormat.width - 1;
00538               state.currentFormat.width = 0;
00539               parseState = formatFlagsWidthPrecTypeConversion;
00540               break;
00541             }
00542           }
00543         case formatFlagsWidthPrecTypeConversion:
00544           // Check for start of width
00545           if ((state.ch >= '1') && (state.ch <= '9'))
00546           {
00547             state.currentFormat.width *= 10;
00548             state.currentFormat.width += state.ch - '0';
00549             parseState = formatParamWidth;
00550             break;
00551           }
00552           // Check for '*' (fetch width from args)
00553           else if (state.ch == '*')
00554           {
00555             state.currentFormat.width = -2;
00556             parseState = formatDotPrecTypeConversion;
00557             break;
00558           }
00559           // Check for flags (0, -, ...)
00560           else if (ParseFlag (state))
00561           {
00562             parseState = formatFlagsWidthPrecTypeConversion;
00563             break;
00564           }
00565         case formatDotPrecTypeConversion:
00566           // Check for precision delimiter
00567           if (state.ch == '.')
00568           {
00569             parseState = formatPrecTypeConversion;
00570             state.currentFormat.precision = 0;
00571             break;
00572           }
00573         case formatPrecTypeConversion:
00574           // Precision digits
00575           if ((state.ch >= '0') && (state.ch <= '9'))
00576           {
00577             state.currentFormat.precision *= 10;
00578             state.currentFormat.precision += state.ch - '0';
00579             break;
00580           }
00581           // Check for '*' (fetch precision from args)
00582           else if (state.ch == '*')
00583           {
00584             state.currentFormat.precision = -2;
00585             parseState = formatTypeConversion;
00586             break;
00587           }
00588           // Check for param type modifier (l, h, ...)
00589         case formatTypeConversion:
00590           if (ParseType (state))
00591           {
00592             parseState = formatTypeConversion;
00593             break;
00594           }
00595           // Check actual conversion (s, d, ...)
00596           else if (ParseConversion (state))
00597           {
00598             state.currentFormat.fmtSkip =
00599               reader.GetPosition() - state.fmtBegin;
00600             if (state.currentFormat.conversion != convNone)
00601               state.currentFormat.paramIdx = state.paramIdx++;
00602             formatSpecs.Push (state.currentFormat);
00603 
00604             state.Reset();
00605           }
00606           else
00607           {
00608             state.charRun += reader.GetPosition() - state.fmtBegin;
00609             state.currentFormat.Reset();
00610           }
00611           parseState = scanFormat;
00612           break;
00613       }
00614     }
00615   }
00616 
00618   void FetchArgs (va_list args)
00619   {
00620     size_t i;
00621     // Determine order of params
00622     csArray<FormatSpec*> paramOrder;
00623     paramOrder.SetCapacity (formatSpecs.GetSize ());
00624     for (i = 0; i < formatSpecs.GetSize (); i++)
00625     {
00626       FormatSpec& currentFormat = formatSpecs[i];
00627       if (currentFormat.conversion == convNone) continue;
00628       if (paramOrder.GetSize () <= (size_t)currentFormat.paramIdx)
00629         paramOrder.SetSize (currentFormat.paramIdx + 1, 0);
00630       paramOrder[currentFormat.paramIdx] = &currentFormat;
00631     }
00632     // Fetch params from stack in order, store at correct place in params array
00633     for (i = 0; i < paramOrder.GetSize (); i++)
00634     {
00635       FmtParam& param = params.GetExtend (i);
00636       FormatSpec* fmtPtr = paramOrder[i];
00637       if (fmtPtr == 0) 
00638       {
00639         // Can just guess here...
00640         param.vInt = va_arg (args, int);
00641         continue;
00642       }
00643       FormatSpec& currentFormat = *fmtPtr;
00644 
00645       if (currentFormat.width == -2)
00646       {
00647         currentFormat.width = va_arg (args, int);
00648         if (currentFormat.width < 0)
00649         {
00650           currentFormat.width = -currentFormat.width;
00651           currentFormat.leftJustify = true;
00652         }
00653       }
00654       if (currentFormat.precision == -2)
00655       {
00656         int v = va_arg (args, int);
00657         if (v >= 0) 
00658           currentFormat.precision = v;
00659         else
00660           currentFormat.precision = -1;
00661       }
00662       switch (currentFormat.conversion)
00663       {
00664         case convInt:
00665         case convOctal:
00666         case convUint:
00667         case convHex:
00668         default:
00669           {
00670             switch (currentFormat.type)
00671             {
00672               case typeIntmax:
00673                 param.vIMT = va_arg (args, intmax_t);
00674                 break;
00675               case typeLong:
00676                 param.vLong = va_arg (args, long);
00677                 break;
00678               case typeLongLong:
00679                 param.vLL = va_arg (args, longlong);
00680                 break;
00681               case typePtrDiffT:
00682                 param.vPDT = va_arg (args, ptrdiff_t);
00683                 break;
00684               case typeSizeT:
00685                 param.vSzT = va_arg (args, size_t);
00686                 break;
00687               case typeShort:
00688                 if (currentFormat.conversion == convInt)
00689                   param.vInt = (short)(va_arg (args, int));
00690                 else
00691                   param.vInt = (unsigned short)(va_arg (args, int));
00692                 break;
00693               case typeChar:
00694                 if (currentFormat.conversion == convInt)
00695                   param.vInt = (char)(va_arg (args, int));
00696                 else
00697                   param.vInt = (unsigned char)(va_arg (args, int));
00698                 break;
00699               default:
00700                 param.vInt = va_arg (args, int);
00701                 break;
00702             }
00703           }
00704           break;
00705         case convErrno:
00706           param.vInt = errno;
00707           break;
00708         case convChar:
00709           if (currentFormat.type == typeLong)
00710           {
00711             param.vInt = (wint_t)(va_arg (args, int));
00712           }
00713           else
00714           {
00715             param.vInt = (unsigned char)(va_arg (args, int));
00716           }
00717           break;
00718         case convFloatFix:
00719         case convFloatExp:
00720         case convFloatGeneral:
00721         case convFloatHex:
00722           if (currentFormat.type == typeLongLong)
00723           {
00724             param.vLongDbl = va_arg (args, long double);
00725           }
00726           else
00727           {
00728             param.vDbl = va_arg (args, double);
00729           }
00730           break;
00731         case convStr:
00732         case convPtr:
00733         case convGetNum:
00734           param.vPtr = va_arg (args, void*);
00735           break;
00736         case convNone:
00737           break;
00738       }
00739     }
00740   }
00741 
00742   void Init (va_list args)
00743   {
00744     ParseSpec ();
00745     FetchArgs (args);
00746   }
00747 
00749   template<class T>
00750   void OutputString (Twriter& writer, const FormatSpec& currentFormat,
00751     const T* stringPtr)
00752   {
00753     if (stringPtr == 0)
00754     {
00755       OutputString (writer, currentFormat, (utf8_char*)"(null)");
00756       return;
00757     }
00758 
00759     size_t len = 0;
00760     {
00761       const T* ptr = stringPtr;
00762       while (*ptr++ != 0) len++;
00763     }
00764     if (currentFormat.precision > -1)
00765       len = MIN(len, (size_t)currentFormat.precision);
00766 
00767     // How many utf32_chars were written
00768     size_t writtenLen;
00769     /* Check if we can circumvent using the scratch array:
00770        we actually only need it when the string is right-justified
00771        (and a width is given). */
00772     bool fastTrack = currentFormat.leftJustify
00773       || (currentFormat.width == 0);
00774 
00775     if (fastTrack)
00776     {
00777       writtenLen = 0;
00778       while (len > 0)
00779       {
00780         utf32_char ch;
00781         int n = csUnicodeTransform::Decode (stringPtr, len, ch);
00782         writer.Put (ch);
00783         stringPtr += n;
00784         len -= (size_t)n;
00785         writtenLen++;
00786       }
00787     }
00788     else
00789     {
00790       size_t scratchOffs = scratch.GetSize ();
00791       while (len > 0)
00792       {
00793         utf32_char ch;
00794         int n = csUnicodeTransform::Decode (stringPtr, len, ch);
00795         scratch.Push (ch);
00796         stringPtr += n;
00797         len -= (size_t)n;
00798       }
00799       writtenLen = scratch.GetSize () - scratchOffs;
00800       if (!currentFormat.leftJustify 
00801         && ((size_t)currentFormat.width > writtenLen))
00802       {
00803         size_t d = (size_t)currentFormat.width - writtenLen;
00804         while (d-- > 0) writer.Put (' ');
00805       }
00806       scratch.WriteTo (writer, scratchOffs);
00807       scratch.Truncate (scratchOffs);
00808     }
00809     if (currentFormat.leftJustify 
00810       && ((size_t)currentFormat.width > writtenLen))
00811     {
00812       size_t d = (size_t)currentFormat.width - writtenLen;
00813       while (d-- > 0) writer.Put (' ');
00814     }
00815   }
00816 
00818   void DoPadding (const FormatSpec& currentFormat, const size_t scratchOffs,
00819     const size_t insert0offs)
00820   {
00821     if (currentFormat.leftJustify)
00822     {
00823       while ((size_t)currentFormat.width > (scratch.GetSize () - scratchOffs))
00824       {
00825         scratch.Push (' ');
00826       }
00827     }
00828     else
00829     {
00830       if (currentFormat.padZero)
00831       {
00832         while ((size_t)currentFormat.width > (scratch.GetSize () - scratchOffs))
00833         {
00834           scratch.Insert (insert0offs, '0');
00835         }
00836       }
00837       else
00838       {
00839         while ((size_t)currentFormat.width > (scratch.GetSize () - scratchOffs))
00840         {
00841           scratch.Insert (scratchOffs, ' ');
00842         }
00843       }
00844     }
00845   }
00846 
00848   template<class T>
00849   void OutputInt (Twriter& writer, const FormatSpec& currentFormat, T value)
00850   {
00851     const size_t scratchOffs = scratch.GetSize ();
00852     size_t insertOffs = scratchOffs;
00853 
00854     if (value < 0)
00855     {
00856       scratch.Push ('-');
00857       insertOffs++;
00858       value = -value;
00859     }
00860     else if (currentFormat.plusSign)
00861     {
00862       scratch.Push ('+');
00863       insertOffs++;
00864     }
00865     else if (currentFormat.spacePrefix)
00866     {
00867       scratch.Push (' ');
00868       insertOffs++;
00869     }
00870 
00871     int width = 0;
00872     int numDigits = currentFormat.precision;
00873     if (!((value == 0) && (numDigits == 0)))
00874     {
00875       do
00876       {
00877         int d = (int)(value % 10);
00878         scratch.Insert (insertOffs, d + '0');
00879         width++;
00880         value = value / 10;
00881       }
00882       while ((value != 0) || (width < numDigits));
00883     }
00884     DoPadding (currentFormat, scratchOffs, insertOffs);
00885     scratch.WriteTo (writer, scratchOffs);
00886     scratch.Truncate (scratchOffs);
00887   }
00888 
00890   template<class T>
00891   void OutputUint (Twriter& writer, const FormatSpec& currentFormat,
00892     T value, uint radix = 10, const char* prefix = 0)
00893   {
00894     const utf32_char letterFirst = currentFormat.uppercase ? 'A' : 'a';
00895     const size_t scratchOffs = scratch.GetSize ();
00896     size_t insertOffs = scratchOffs;
00897 
00898     if (prefix != 0)
00899     {
00900       while (*prefix != 0)
00901       {
00902         utf32_char ch = (value != 0) ? *prefix : ' ';
00903         scratch.Push (ch);
00904         insertOffs++;
00905         prefix++;
00906       }
00907     }
00908 
00909     int width = 0;
00910     int numDigits = currentFormat.precision;
00911     if (!((value == 0) && (numDigits == 0)))
00912     {
00913       do
00914       {
00915         uint d = (uint)(value % radix);
00916         utf32_char ch;
00917         if (d <= 9)
00918           ch = d + '0';
00919         else
00920           ch = d - 10 + letterFirst;
00921         scratch.Insert (insertOffs, ch);
00922         width++;
00923         value = value / radix;
00924       }
00925       while ((value != 0) || (width < numDigits));
00926     }
00927     DoPadding (currentFormat, scratchOffs, insertOffs);
00928     scratch.WriteTo (writer, scratchOffs);
00929     scratch.Truncate (scratchOffs);
00930   }
00931 
00933   template<class T>
00934   void OutputFloat (Twriter& writer, const FormatSpec& currentFormat,
00935     const T& value, const char* type)
00936   {
00937     char flags[5] = "";
00938     if (currentFormat.plusSign)
00939       strcat (flags, "+");
00940     if (currentFormat.spacePrefix)
00941       strcat (flags, " ");
00942     if (currentFormat.basePrefix)
00943       strcat (flags, "#");
00944     if (currentFormat.padZero)
00945       strcat (flags, "0");
00946     /* (sizeof(x)*25)/10+1 is an approximation of the number of characters
00947      * needed to display x in decimal system. (x can be at most 256^sizeof(x).
00948      * You need log10(256^sizeof(x)) characters, becoming
00949      * sizeof(x)*log10(256). 25/10 is an (over-)approximation of log10(256).
00950      * Add 1 for sign.) */
00951     CS_ALLOC_STACK_ARRAY(char, precStr, 
00952       (sizeof(currentFormat.precision) * 25) / 10 + 2);
00953     if (currentFormat.precision >= 0)
00954       sprintf (precStr, ".%d", currentFormat.precision);
00955     else
00956       precStr[0] = 0;
00957     CS_ALLOC_STACK_ARRAY(char, formatStr, 1 + strlen (flags)
00958       + (sizeof(currentFormat.width) * 25) / 10 + 1 + strlen (precStr) + 2);
00959     sprintf (formatStr, "%%%s%d%s%s", flags, currentFormat.width, precStr,
00960       type);
00961     // Make sure *any* number thrown at us fits
00962     char formattedStr[LDBL_MAX_10_EXP+3]; 
00963     sprintf (formattedStr, formatStr, value);
00964 
00965     char* p = formattedStr;
00966     while (*p != 0)
00967       writer.Put (*p++);
00968   }
00969 
00973   template<class T, class Tbase>
00974   struct IEEEFloatMantissa
00975   {
00976     Tbase mantissa[sizeof(T)/sizeof(Tbase)];
00977 
00978     Tbase& operator[] (int index)
00979     { return mantissa[index]; }
00980     bool Eq0 ()
00981     {
00982       for (uint n = 0; n < sizeof(T)/sizeof(Tbase); n++)
00983       {
00984         if (mantissa[n] != 0) return false;
00985       }
00986       return true;
00987     }
00988     const Tbase operator& (Tbase other) const
00989     { return mantissa[0] & other; }
00990     IEEEFloatMantissa& operator<<= (int shift)
00991     { 
00992       const int ovShift = sizeof(Tbase) * 8 - shift;
00993       Tbase overflow = 0;
00994       for (uint n = 0; n < sizeof(T)/sizeof(Tbase); n++)
00995       {
00996         Tbase newOverflow = mantissa[n] >> ovShift;
00997         mantissa[n] = (mantissa[n] << shift) | overflow;
00998         overflow = newOverflow;
00999       }
01000       return *this;
01001     }
01002     Tbase& Leftmost ()
01003     { return mantissa[sizeof(T)/sizeof(Tbase)-1]; }
01004   };
01005 
01007   template<class T, class Tbase>
01008   struct IEEEFloatSplitter
01009   {
01010     bool sign;
01011     Tbase exp;
01012 
01013     typename csPrintfFormatter<Twriter,Treader>::
01014       template IEEEFloatMantissa<T, Tbase> mantissa;
01015 
01016     IEEEFloatSplitter (const T& val, const int mantissaBits,
01017       const int expBits) 
01018     {
01019       const int baseBits = sizeof(Tbase) * 8;
01020       const int signBit = mantissaBits + expBits;
01021 
01022       union
01023       {
01024         T v;
01025         Tbase vB[sizeof(T)/sizeof(Tbase)];
01026       } toBase;
01027       toBase.v = val;
01028   #ifdef CS_LITTLE_ENDIAN
01029       const int hi = (sizeof (T) / sizeof (Tbase)) - 1;
01030       const int lo = 0;
01031       const int d = 1;
01032   #else
01033       const int hi = 0;
01034       const int lo = (sizeof (T) / sizeof (Tbase)) - 1;
01035       const int d = -1;
01036   #endif
01037       sign = ((toBase.vB[lo + (signBit / baseBits) * d]
01038         & (1 << (signBit % baseBits))) != 0);
01039       exp = (toBase.vB[hi] >> (mantissaBits % (baseBits)))
01040         & ((1 << expBits) - 1);
01041       for (int n = lo, p = 0; n != hi + d; n += d, p++)
01042       {
01043         const int bit = p * baseBits;
01044         const Tbase mask = ((bit + baseBits) <= mantissaBits) ? ~0 
01045           : ((1 << (mantissaBits % baseBits)) - 1);
01046         mantissa[p] = toBase.vB[n] & mask;
01047       }
01048     }
01049   };
01051   template <class T>
01052   void OutputFloatHex (Twriter& writer, const FormatSpec& currentFormat,
01053     const T& value, const int vMantissaBits, const int expBits, const int bias)
01054   {
01055 #ifdef CS_IEEE_DOUBLE_FORMAT
01056     const utf32_char letterFirst = currentFormat.uppercase ? 'A' : 'a';
01057 
01058 #ifdef CS_PROCESSOR_X86
01059     // @@@ x86 long double uses explicit mantissa MSB
01060     const bool hiddenBit = !(vMantissaBits >= 63);
01061 #else
01062     const bool hiddenBit = false;
01063 #endif
01064     const int mantissaBits = vMantissaBits - (hiddenBit ? 1 : 0);
01065     IEEEFloatSplitter<T, uint> vSplit (value, mantissaBits, expBits);
01066     const uint expMax = (1 << (sizeof(T) * 8 - mantissaBits - 1)) - 1;
01067 
01068     if ((vSplit.exp == expMax) && vSplit.mantissa.Eq0())
01069     {
01070       char infStr[5];
01071       if (vSplit.sign)
01072       {
01073         strcpy (infStr, "-");
01074       }
01075       else
01076       {
01077         if (currentFormat.plusSign)
01078           strcpy (infStr, "+");
01079         else if (currentFormat.spacePrefix)
01080           strcpy (infStr, " ");
01081         else
01082           strcpy (infStr, "");
01083       }
01084       strcat (infStr, currentFormat.uppercase ? "INF" : "inf");
01085       OutputString (writer, currentFormat, 
01086         (utf8_char*)infStr);
01087       return;
01088     }
01089     else if ((vSplit.exp == expMax) && !vSplit.mantissa.Eq0())
01090     {
01091       char nanStr[5];
01092       if (vSplit.sign)
01093       {
01094         strcpy (nanStr, "-");
01095       }
01096       else
01097       {
01098         if (currentFormat.plusSign)
01099           strcpy (nanStr, "+");
01100         else if (currentFormat.spacePrefix)
01101           strcpy (nanStr, " ");
01102         else
01103           strcpy (nanStr, "");
01104       }
01105       strcat (nanStr, currentFormat.uppercase ? "NAN" : "nan");
01106       OutputString (writer, currentFormat, 
01107         (utf8_char*)nanStr);
01108       return;
01109     }
01110 
01111     const size_t scratchOffs = scratch.GetSize ();
01112     if (vSplit.sign)
01113     {
01114       scratch.Push ('-');
01115     }
01116     scratch.Push ('0');
01117     scratch.Push (currentFormat.uppercase ? 'X' : 'x');
01118     if (hiddenBit)
01119     {
01120       if (vSplit.exp == 0)
01121         scratch.Push ('0');
01122       else
01123         scratch.Push ('1');
01124     }
01125     else
01126     {
01127       const int bitNum = mantissaBits - 1;
01128       const int baseBits = sizeof (uint) * 8;
01129       const int bitIndex = bitNum / baseBits;
01130       scratch.Push ('0' + ((vSplit.mantissa[bitIndex] 
01131         >> (bitNum % baseBits)) & 1));
01132       vSplit.mantissa <<= 1;
01133     }
01134     if ((currentFormat.precision > 0) || (!vSplit.mantissa.Eq0()))
01135     {
01136       scratch.Push ('.');
01137       
01138       IEEEFloatMantissa<T, uint> m (vSplit.mantissa);
01139       m <<= sizeof(T)*8 - mantissaBits;
01140       int w = 0;
01141       do
01142       {
01143         uint d = m.Leftmost() >> ((sizeof(uint)*8)-4);
01144         utf32_char ch;
01145         if (d <= 9)
01146           ch = d + '0';
01147         else
01148           ch = d - 10 + letterFirst;
01149         scratch.Push (ch);
01150         m <<= 4;
01151         w++;
01152       }
01153       while ((w < currentFormat.precision) 
01154         || ((currentFormat.precision <= 0) && !m.Eq0()));
01155     }
01156     scratch.Push (currentFormat.uppercase ? 'P' : 'p');
01157     int e;
01158     if ((vSplit.exp == 0) && vSplit.mantissa.Eq0())
01159       e = 0;
01160     else
01161       e = (int)vSplit.exp + bias;
01162     if (e < 0)
01163     {
01164       scratch.Push ('-');
01165       e = -e;
01166     }
01167     else
01168       scratch.Push ('+');
01169     const size_t insertOffs = scratch.GetSize ();;
01170     do
01171     {
01172       uint d = e % 10;
01173       scratch.Insert (insertOffs, d + '0');
01174       e = e / 10;
01175     }
01176     while (e != 0);
01177 
01178     DoPadding (currentFormat, scratchOffs, 
01179       vSplit.sign ? scratchOffs + 1 : scratchOffs);
01180     scratch.WriteTo (writer, scratchOffs);
01181     scratch.Truncate (scratchOffs);
01182 #else
01183   #if defined(CS_COMPILER_GCC)
01184     #warning Do not know how to hex-format floats
01185   #elif defined(CS_COMPILER_MSVC)
01186     #pragma message("Do not know how to hex-format floats")
01187   #endif
01188 #endif
01189   }
01190 public:
01192   csPrintfFormatter (Treader* reader, va_list args) : reader (*reader)
01193   {
01194     Init (args);
01195   }
01197   csPrintfFormatter (Treader* reader, ...) : reader (*reader)
01198   {
01199     va_list ap;
01200     va_start(ap, reader);
01201     Init (ap);
01202     va_end(ap);
01203   }
01205   void Format (Twriter& writer)
01206   {
01207     reader.Reset();
01208     size_t i = 0;
01209     utf32_char ch;
01210     while (i < formatSpecs.GetSize ())
01211     {
01212       const FormatSpec& currentFormat = formatSpecs[i];
01213       size_t n;
01214       for (n = 0; n < currentFormat.copyRun; n++)
01215       {
01216         if (!reader.GetNext (ch)) break;
01217         writer.Put (ch);
01218       }
01219 
01220       switch (currentFormat.conversion)
01221       {
01222         case convStr:
01223           {
01224             if (currentFormat.type == typeLong)
01225               OutputString (writer, currentFormat, 
01226               (wchar_t*)(params[currentFormat.paramIdx].vPtr));
01227             else
01228               OutputString (writer, currentFormat, 
01229               (utf8_char*)(params[currentFormat.paramIdx].vPtr));
01230           }
01231           break;
01232         case convChar:
01233           {
01234             writer.Put (params[currentFormat.paramIdx].vInt);
01235           }
01236           break;
01237         case convInt:
01238           {
01239             const FmtParam& param = params[currentFormat.paramIdx];
01240             switch (currentFormat.type)
01241             {
01242               case typeIntmax:
01243                 {
01244                   intmax_t v = param.vIMT;
01245                   OutputInt (writer, currentFormat, v);
01246                 }
01247                 break;
01248               case typeLong:
01249                 {
01250                   long v = param.vLong;
01251                   OutputInt (writer, currentFormat, v);
01252                 }
01253                 break;
01254               case typeLongLong:
01255                 {
01256                   longlong v = param.vLL;
01257                   OutputInt (writer, currentFormat, v);
01258                 }
01259                 break;
01260               case typePtrDiffT:
01261                 {
01262                   ptrdiff_t v = param.vPDT;
01263                   OutputInt (writer, currentFormat, v);
01264                 }
01265                 break;
01266               case typeSizeT:
01267                 {
01268                   size_t v = param.vSzT;
01269                   OutputUint (writer, currentFormat, v);
01270                 }
01271                 break;
01272               default:
01273                 {
01274                   int v = param.vInt;
01275                   OutputInt (writer, currentFormat, v);
01276                 }
01277                 break;
01278             }
01279           }
01280           break;
01281         case convHex:
01282         case convUint:
01283         case convOctal:
01284           {
01285             uint uiradix;
01286             const char* prefix;
01287             if (currentFormat.conversion == convHex)
01288             {
01289               uiradix = 16;
01290               prefix = currentFormat.basePrefix 
01291                 ? (currentFormat.uppercase ? "0X" : "0x") : 0;
01292             }
01293             else if (currentFormat.conversion == convOctal)
01294             {
01295               uiradix = 8;
01296               prefix = currentFormat.basePrefix ? "0" : 0;
01297             }
01298             else
01299             {
01300               uiradix = 10;
01301               prefix = 0;
01302             }
01303             const FmtParam& param = params[currentFormat.paramIdx];
01304             switch (currentFormat.type)
01305             {
01306               case typeIntmax:
01307                 {
01308                   intmax_t v = param.vIMT;
01309                   OutputUint (writer, currentFormat, v, uiradix, prefix);
01310                 }
01311                 break;
01312               case typeLong:
01313                 {
01314                   unsigned long v = param.vLong;
01315                   OutputUint (writer, currentFormat, v, uiradix, prefix);
01316                 }
01317                 break;
01318               case typeLongLong:
01319                 {
01320                   ulonglong v = param.vLL;
01321                   OutputUint (writer, currentFormat, v, uiradix, prefix);
01322                 }
01323                 break;
01324               case typePtrDiffT:
01325                 {
01326                   ptrdiff_t v = param.vPDT;
01327                   OutputUint (writer, currentFormat, v, uiradix, prefix);
01328                 }
01329                 break;
01330               case typeSizeT:
01331                 {
01332                   size_t v = param.vSzT;
01333                   OutputUint (writer, currentFormat, v, uiradix, prefix);
01334                 }
01335                 break;
01336               default:
01337                 {
01338                   uint v = param.vInt;
01339                   OutputUint (writer, currentFormat, v, uiradix, prefix);
01340                 }
01341                 break;
01342             }
01343           }
01344           break;
01345         case convGetNum:
01346           *((int*)(params[currentFormat.paramIdx].vPtr))
01347                 = (int)writer.GetTotal();
01348           break;
01349         case convErrno:
01350           OutputString (writer, currentFormat, 
01351             (utf8_char*)strerror (params[currentFormat.paramIdx].vInt));
01352           break;
01353         case convPtr:
01354           {
01355             FormatSpec fakeFormat;
01356             fakeFormat.leftJustify = currentFormat.leftJustify;
01357             fakeFormat.precision = sizeof (uintptr_t) * 2;
01358             if (params[currentFormat.paramIdx].vPtr == 0)
01359             {
01360               OutputString (writer, fakeFormat, (utf8_char*)"(nil)");
01361             }
01362             else
01363             {
01364               OutputUint (writer, fakeFormat, 
01365                 (uintptr_t)params[currentFormat.paramIdx].vPtr, 16, "0x");
01366             }
01367           }
01368           break;
01369         case convFloatFix:
01370           {
01371             if (currentFormat.type == typeLongLong)
01372             {
01373 #ifdef CS_FORMATTER_NO_LONG_DOUBLE_FORMAT
01374               OutputFloat (writer, currentFormat, 
01375               (double)params[currentFormat.paramIdx].vLongDbl, "f");
01376 #else
01377               OutputFloat (writer, currentFormat, 
01378               params[currentFormat.paramIdx].vLongDbl, "Lf");
01379 #endif
01380             }
01381             else
01382               OutputFloat (writer, currentFormat, 
01383               params[currentFormat.paramIdx].vDbl, "f");
01384           }
01385           break;
01386         case convFloatExp:
01387           {
01388             if (currentFormat.type == typeLongLong)
01389             {
01390 #ifdef CS_FORMATTER_NO_LONG_DOUBLE_FORMAT
01391               OutputFloat (writer, currentFormat, 
01392               (double)params[currentFormat.paramIdx].vLongDbl, 
01393               currentFormat.uppercase ? "E" : "e");
01394 #else
01395               OutputFloat (writer, currentFormat, 
01396               params[currentFormat.paramIdx].vLongDbl, 
01397               currentFormat.uppercase ? "LE" : "Le");
01398 #endif
01399             }
01400             else
01401               OutputFloat (writer, currentFormat, 
01402               params[currentFormat.paramIdx].vDbl, 
01403               currentFormat.uppercase ? "E" : "e");
01404           }
01405           break;
01406         case convFloatGeneral:
01407           {
01408             if (currentFormat.type == typeLongLong)
01409             {
01410 #ifdef CS_FORMATTER_NO_LONG_DOUBLE_FORMAT
01411               OutputFloat (writer, currentFormat, 
01412               (double)params[currentFormat.paramIdx].vLongDbl, 
01413               currentFormat.uppercase ? "G" : "g");
01414 #else
01415               OutputFloat (writer, currentFormat, 
01416               params[currentFormat.paramIdx].vLongDbl, 
01417               currentFormat.uppercase ? "LG" : "Lg");
01418 #endif
01419             }
01420             else
01421               OutputFloat (writer, currentFormat, 
01422               params[currentFormat.paramIdx].vDbl, 
01423               currentFormat.uppercase ? "G" : "g");
01424           }
01425           break;
01426         case convFloatHex:
01427           {
01428             if (currentFormat.type == typeLongLong)
01429               OutputFloatHex (writer, currentFormat, 
01430               params[currentFormat.paramIdx].vLongDbl, LDBL_MANT_DIG, 
01431               csLog2 (LDBL_MAX_EXP) + 1, -(LDBL_MAX_EXP - 1));
01432             else
01433               OutputFloatHex (writer, currentFormat, 
01434               params[currentFormat.paramIdx].vDbl, DBL_MANT_DIG, 
01435               csLog2 (DBL_MAX_EXP) + 1, -(DBL_MAX_EXP - 1));
01436           }
01437           break;
01438         default:
01439           break;
01440       }
01441 
01442       for (n = 0; n < currentFormat.fmtSkip; n++)
01443       {
01444         if (!reader.GetNext (ch)) break;
01445       }
01446       i++;
01447     }
01448     while (reader.GetNext (ch))
01449       writer.Put (ch);
01450     writer.Put (0);
01451   }
01452 };
01453 
01456 #endif // __CS_CSUTIL_FORMATTER_H__

Generated for Crystal Space 1.2.1 by doxygen 1.5.3