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                 param.vInt = (short)(va_arg (args, int));
00689                 break;
00690               case typeChar:
00691                 param.vInt = (char)(va_arg (args, int));
00692                 break;
00693               default:
00694                 param.vInt = va_arg (args, int);
00695                 break;
00696             }
00697           }
00698           break;
00699         case convErrno:
00700           param.vInt = errno;
00701           break;
00702         case convChar:
00703           if (currentFormat.type == typeLong)
00704           {
00705             param.vInt = (wint_t)(va_arg (args, int));
00706           }
00707           else
00708           {
00709             param.vInt = (unsigned char)(va_arg (args, int));
00710           }
00711           break;
00712         case convFloatFix:
00713         case convFloatExp:
00714         case convFloatGeneral:
00715         case convFloatHex:
00716           if (currentFormat.type == typeLongLong)
00717           {
00718             param.vLongDbl = va_arg (args, long double);
00719           }
00720           else
00721           {
00722             param.vDbl = va_arg (args, double);
00723           }
00724           break;
00725         case convStr:
00726         case convPtr:
00727         case convGetNum:
00728           param.vPtr = va_arg (args, void*);
00729           break;
00730         case convNone:
00731           break;
00732       }
00733     }
00734   }
00735 
00736   void Init (va_list args)
00737   {
00738     ParseSpec ();
00739     FetchArgs (args);
00740   }
00741 
00743   template<class T>
00744   void OutputString (Twriter& writer, const FormatSpec& currentFormat,
00745     const T* stringPtr)
00746   {
00747     if (stringPtr == 0)
00748     {
00749       OutputString (writer, currentFormat, (utf8_char*)"(null)");
00750       return;
00751     }
00752 
00753     size_t len = 0;
00754     {
00755       const T* ptr = stringPtr;
00756       while (*ptr++ != 0) len++;
00757     }
00758     if (currentFormat.precision > -1)
00759       len = MIN(len, (size_t)currentFormat.precision);
00760 
00761     // How many utf32_chars were written
00762     size_t writtenLen;
00763     /* Check if we can circumvent using the scratch array:
00764        we actually only need it when the string is right-justified
00765        (and a width is given). */
00766     bool fastTrack = currentFormat.leftJustify
00767       || (currentFormat.width == 0);
00768 
00769     if (fastTrack)
00770     {
00771       writtenLen = 0;
00772       while (len > 0)
00773       {
00774         utf32_char ch;
00775         int n = csUnicodeTransform::Decode (stringPtr, len, ch);
00776         writer.Put (ch);
00777         stringPtr += n;
00778         len -= (size_t)n;
00779         writtenLen++;
00780       }
00781     }
00782     else
00783     {
00784       size_t scratchOffs = scratch.GetSize ();
00785       while (len > 0)
00786       {
00787         utf32_char ch;
00788         int n = csUnicodeTransform::Decode (stringPtr, len, ch);
00789         scratch.Push (ch);
00790         stringPtr += n;
00791         len -= (size_t)n;
00792       }
00793       writtenLen = scratch.GetSize () - scratchOffs;
00794       if (!currentFormat.leftJustify 
00795         && ((size_t)currentFormat.width > writtenLen))
00796       {
00797         size_t d = (size_t)currentFormat.width - writtenLen;
00798         while (d-- > 0) writer.Put (' ');
00799       }
00800       scratch.WriteTo (writer, scratchOffs);
00801       scratch.Truncate (scratchOffs);
00802     }
00803     if (currentFormat.leftJustify 
00804       && ((size_t)currentFormat.width > writtenLen))
00805     {
00806       size_t d = (size_t)currentFormat.width - writtenLen;
00807       while (d-- > 0) writer.Put (' ');
00808     }
00809   }
00810 
00812   void DoPadding (const FormatSpec& currentFormat, const size_t scratchOffs,
00813     const size_t insert0offs)
00814   {
00815     if (currentFormat.leftJustify)
00816     {
00817       while ((size_t)currentFormat.width > (scratch.GetSize () - scratchOffs))
00818       {
00819         scratch.Push (' ');
00820       }
00821     }
00822     else
00823     {
00824       if (currentFormat.padZero)
00825       {
00826         while ((size_t)currentFormat.width > (scratch.GetSize () - scratchOffs))
00827         {
00828           scratch.Insert (insert0offs, '0');
00829         }
00830       }
00831       else
00832       {
00833         while ((size_t)currentFormat.width > (scratch.GetSize () - scratchOffs))
00834         {
00835           scratch.Insert (scratchOffs, ' ');
00836         }
00837       }
00838     }
00839   }
00840 
00842   template<class T>
00843   void OutputInt (Twriter& writer, const FormatSpec& currentFormat, T value)
00844   {
00845     const size_t scratchOffs = scratch.GetSize ();
00846     size_t insertOffs = scratchOffs;
00847 
00848     if (value < 0)
00849     {
00850       scratch.Push ('-');
00851       insertOffs++;
00852       value = -value;
00853     }
00854     else if (currentFormat.plusSign)
00855     {
00856       scratch.Push ('+');
00857       insertOffs++;
00858     }
00859     else if (currentFormat.spacePrefix)
00860     {
00861       scratch.Push (' ');
00862       insertOffs++;
00863     }
00864 
00865     int width = 0;
00866     int numDigits = currentFormat.precision;
00867     if (!((value == 0) && (numDigits == 0)))
00868     {
00869       do
00870       {
00871         int d = (int)(value % 10);
00872         scratch.Insert (insertOffs, d + '0');
00873         width++;
00874         value = value / 10;
00875       }
00876       while ((value != 0) || (width < numDigits));
00877     }
00878     DoPadding (currentFormat, scratchOffs, insertOffs);
00879     scratch.WriteTo (writer, scratchOffs);
00880     scratch.Truncate (scratchOffs);
00881   }
00882 
00884   template<class T>
00885   void OutputUint (Twriter& writer, const FormatSpec& currentFormat,
00886     T value, uint radix = 10, const char* prefix = 0)
00887   {
00888     const utf32_char letterFirst = currentFormat.uppercase ? 'A' : 'a';
00889     const size_t scratchOffs = scratch.GetSize ();
00890     size_t insertOffs = scratchOffs;
00891 
00892     if (prefix != 0)
00893     {
00894       while (*prefix != 0)
00895       {
00896         utf32_char ch = (value != 0) ? *prefix : ' ';
00897         scratch.Push (ch);
00898         insertOffs++;
00899         prefix++;
00900       }
00901     }
00902 
00903     int width = 0;
00904     int numDigits = currentFormat.precision;
00905     if (!((value == 0) && (numDigits == 0)))
00906     {
00907       do
00908       {
00909         uint d = (uint)(value % radix);
00910         utf32_char ch;
00911         if (d <= 9)
00912           ch = d + '0';
00913         else
00914           ch = d - 10 + letterFirst;
00915         scratch.Insert (insertOffs, ch);
00916         width++;
00917         value = value / radix;
00918       }
00919       while ((value != 0) || (width < numDigits));
00920     }
00921     DoPadding (currentFormat, scratchOffs, insertOffs);
00922     scratch.WriteTo (writer, scratchOffs);
00923     scratch.Truncate (scratchOffs);
00924   }
00925 
00927   template<class T>
00928   void OutputFloat (Twriter& writer, const FormatSpec& currentFormat,
00929     const T& value, const char* type)
00930   {
00931     char flags[5] = "";
00932     if (currentFormat.plusSign)
00933       strcat (flags, "+");
00934     if (currentFormat.spacePrefix)
00935       strcat (flags, " ");
00936     if (currentFormat.basePrefix)
00937       strcat (flags, "#");
00938     if (currentFormat.padZero)
00939       strcat (flags, "0");
00940     /* (sizeof(x)*25)/10+1 is an approximation of the number of characters
00941      * needed to display x in decimal system. (x can be at most 256^sizeof(x).
00942      * You need log10(256^sizeof(x)) characters, becoming
00943      * sizeof(x)*log10(256). 25/10 is an (over-)approximation of log10(256).
00944      * Add 1 for sign.) */
00945     CS_ALLOC_STACK_ARRAY(char, precStr, 
00946       (sizeof(currentFormat.precision) * 25) / 10 + 2);
00947     if (currentFormat.precision >= 0)
00948       sprintf (precStr, ".%d", currentFormat.precision);
00949     else
00950       precStr[0] = 0;
00951     CS_ALLOC_STACK_ARRAY(char, formatStr, 1 + strlen (flags)
00952       + (sizeof(currentFormat.width) * 25) / 10 + 1 + strlen (precStr) + 2);
00953     sprintf (formatStr, "%%%s%d%s%s", flags, currentFormat.width, precStr,
00954       type);
00955     // Make sure *any* number thrown at us fits
00956     char formattedStr[LDBL_MAX_10_EXP+3]; 
00957     sprintf (formattedStr, formatStr, value);
00958 
00959     char* p = formattedStr;
00960     while (*p != 0)
00961       writer.Put (*p++);
00962   }
00963 
00967   template<class T, class Tbase>
00968   struct IEEEFloatMantissa
00969   {
00970     Tbase mantissa[sizeof(T)/sizeof(Tbase)];
00971 
00972     Tbase& operator[] (int index)
00973     { return mantissa[index]; }
00974     bool Eq0 ()
00975     {
00976       for (uint n = 0; n < sizeof(T)/sizeof(Tbase); n++)
00977       {
00978         if (mantissa[n] != 0) return false;
00979       }
00980       return true;
00981     }
00982     const Tbase operator& (Tbase other) const
00983     { return mantissa[0] & other; }
00984     IEEEFloatMantissa& operator<<= (int shift)
00985     { 
00986       const int ovShift = sizeof(Tbase) * 8 - shift;
00987       Tbase overflow = 0;
00988       for (uint n = 0; n < sizeof(T)/sizeof(Tbase); n++)
00989       {
00990         Tbase newOverflow = mantissa[n] >> ovShift;
00991         mantissa[n] = (mantissa[n] << shift) | overflow;
00992         overflow = newOverflow;
00993       }
00994       return *this;
00995     }
00996     Tbase& Leftmost ()
00997     { return mantissa[sizeof(T)/sizeof(Tbase)-1]; }
00998   };
00999 
01001   template<class T, class Tbase>
01002   struct IEEEFloatSplitter
01003   {
01004     bool sign;
01005     Tbase exp;
01006 
01007     typename csPrintfFormatter<Twriter,Treader>::
01008       template IEEEFloatMantissa<T, Tbase> mantissa;
01009 
01010     IEEEFloatSplitter (const T& val, const int mantissaBits,
01011       const int expBits) 
01012     {
01013       const int baseBits = sizeof(Tbase) * 8;
01014       const int signBit = mantissaBits + expBits;
01015 
01016       union
01017       {
01018         T v;
01019         Tbase vB[sizeof(T)/sizeof(Tbase)];
01020       } toBase;
01021       toBase.v = val;
01022   #ifdef CS_LITTLE_ENDIAN
01023       const int hi = (sizeof (T) / sizeof (Tbase)) - 1;
01024       const int lo = 0;
01025       const int d = 1;
01026   #else
01027       const int hi = 0;
01028       const int lo = (sizeof (T) / sizeof (Tbase)) - 1;
01029       const int d = -1;
01030   #endif
01031       sign = ((toBase.vB[lo + (signBit / baseBits) * d]
01032         & (1 << (signBit % baseBits))) != 0);
01033       exp = (toBase.vB[hi] >> (mantissaBits % (baseBits)))
01034         & ((1 << expBits) - 1);
01035       for (int n = lo, p = 0; n != hi + d; n += d, p++)
01036       {
01037         const int bit = p * baseBits;
01038         const Tbase mask = ((bit + baseBits) <= mantissaBits) ? ~0 
01039           : ((1 << (mantissaBits % baseBits)) - 1);
01040         mantissa[p] = toBase.vB[n] & mask;
01041       }
01042     }
01043   };
01045   template <class T>
01046   void OutputFloatHex (Twriter& writer, const FormatSpec& currentFormat,
01047     const T& value, const int vMantissaBits, const int expBits, const int bias)
01048   {
01049 #ifdef CS_IEEE_DOUBLE_FORMAT
01050     const utf32_char letterFirst = currentFormat.uppercase ? 'A' : 'a';
01051 
01052 #ifdef CS_PROCESSOR_X86
01053     // @@@ x86 long double uses explicit mantissa MSB
01054     const bool hiddenBit = !(vMantissaBits >= 63);
01055 #else
01056     const bool hiddenBit = false;
01057 #endif
01058     const int mantissaBits = vMantissaBits - (hiddenBit ? 1 : 0);
01059     IEEEFloatSplitter<T, uint> vSplit (value, mantissaBits, expBits);
01060     const uint expMax = (1 << (sizeof(T) * 8 - mantissaBits - 1)) - 1;
01061 
01062     if ((vSplit.exp == expMax) && vSplit.mantissa.Eq0())
01063     {
01064       char infStr[5];
01065       if (vSplit.sign)
01066       {
01067         strcpy (infStr, "-");
01068       }
01069       else
01070       {
01071         if (currentFormat.plusSign)
01072           strcpy (infStr, "+");
01073         else if (currentFormat.spacePrefix)
01074           strcpy (infStr, " ");
01075         else
01076           strcpy (infStr, "");
01077       }
01078       strcat (infStr, currentFormat.uppercase ? "INF" : "inf");
01079       OutputString (writer, currentFormat, 
01080         (utf8_char*)infStr);
01081       return;
01082     }
01083     else if ((vSplit.exp == expMax) && !vSplit.mantissa.Eq0())
01084     {
01085       char nanStr[5];
01086       if (vSplit.sign)
01087       {
01088         strcpy (nanStr, "-");
01089       }
01090       else
01091       {
01092         if (currentFormat.plusSign)
01093           strcpy (nanStr, "+");
01094         else if (currentFormat.spacePrefix)
01095           strcpy (nanStr, " ");
01096         else
01097           strcpy (nanStr, "");
01098       }
01099       strcat (nanStr, currentFormat.uppercase ? "NAN" : "nan");
01100       OutputString (writer, currentFormat, 
01101         (utf8_char*)nanStr);
01102       return;
01103     }
01104 
01105     const size_t scratchOffs = scratch.GetSize ();
01106     if (vSplit.sign)
01107     {
01108       scratch.Push ('-');
01109     }
01110     scratch.Push ('0');
01111     scratch.Push (currentFormat.uppercase ? 'X' : 'x');
01112     if (hiddenBit)
01113     {
01114       if (vSplit.exp == 0)
01115         scratch.Push ('0');
01116       else
01117         scratch.Push ('1');
01118     }
01119     else
01120     {
01121       const int bitNum = mantissaBits - 1;
01122       const int baseBits = sizeof (uint) * 8;
01123       const int bitIndex = bitNum / baseBits;
01124       scratch.Push ('0' + ((vSplit.mantissa[bitIndex] 
01125         >> (bitNum % baseBits)) & 1));
01126       vSplit.mantissa <<= 1;
01127     }
01128     if ((currentFormat.precision > 0) || (!vSplit.mantissa.Eq0()))
01129     {
01130       scratch.Push ('.');
01131       
01132       IEEEFloatMantissa<T, uint> m (vSplit.mantissa);
01133       m <<= sizeof(T)*8 - mantissaBits;
01134       int w = 0;
01135       do
01136       {
01137         uint d = m.Leftmost() >> ((sizeof(uint)*8)-4);
01138         utf32_char ch;
01139         if (d <= 9)
01140           ch = d + '0';
01141         else
01142           ch = d - 10 + letterFirst;
01143         scratch.Push (ch);
01144         m <<= 4;
01145         w++;
01146       }
01147       while ((w < currentFormat.precision) 
01148         || ((currentFormat.precision <= 0) && !m.Eq0()));
01149     }
01150     scratch.Push (currentFormat.uppercase ? 'P' : 'p');
01151     int e;
01152     if ((vSplit.exp == 0) && vSplit.mantissa.Eq0())
01153       e = 0;
01154     else
01155       e = (int)vSplit.exp + bias;
01156     if (e < 0)
01157     {
01158       scratch.Push ('-');
01159       e = -e;
01160     }
01161     else
01162       scratch.Push ('+');
01163     const size_t insertOffs = scratch.GetSize ();;
01164     do
01165     {
01166       uint d = e % 10;
01167       scratch.Insert (insertOffs, d + '0');
01168       e = e / 10;
01169     }
01170     while (e != 0);
01171 
01172     DoPadding (currentFormat, scratchOffs, 
01173       vSplit.sign ? scratchOffs + 1 : scratchOffs);
01174     scratch.WriteTo (writer, scratchOffs);
01175     scratch.Truncate (scratchOffs);
01176 #else
01177   #if defined(CS_COMPILER_GCC)
01178     #warning Do not know how to hex-format floats
01179   #elif defined(CS_COMPILER_MSVC)
01180     #pragma message("Do not know how to hex-format floats")
01181   #endif
01182 #endif
01183   }
01184 public:
01186   csPrintfFormatter (Treader* reader, va_list args) : reader (*reader)
01187   {
01188     Init (args);
01189   }
01191   csPrintfFormatter (Treader* reader, ...) : reader (*reader)
01192   {
01193     va_list ap;
01194     va_start(ap, reader);
01195     Init (ap);
01196     va_end(ap);
01197   }
01199   void Format (Twriter& writer)
01200   {
01201     reader.Reset();
01202     size_t i = 0;
01203     utf32_char ch;
01204     while (i < formatSpecs.GetSize ())
01205     {
01206       const FormatSpec& currentFormat = formatSpecs[i];
01207       size_t n;
01208       for (n = 0; n < currentFormat.copyRun; n++)
01209       {
01210         if (!reader.GetNext (ch)) break;
01211         writer.Put (ch);
01212       }
01213 
01214       switch (currentFormat.conversion)
01215       {
01216         case convStr:
01217           {
01218             if (currentFormat.type == typeLong)
01219               OutputString (writer, currentFormat, 
01220               (wchar_t*)(params[currentFormat.paramIdx].vPtr));
01221             else
01222               OutputString (writer, currentFormat, 
01223               (utf8_char*)(params[currentFormat.paramIdx].vPtr));
01224           }
01225           break;
01226         case convChar:
01227           {
01228             writer.Put (params[currentFormat.paramIdx].vInt);
01229           }
01230           break;
01231         case convInt:
01232           {
01233             const FmtParam& param = params[currentFormat.paramIdx];
01234             switch (currentFormat.type)
01235             {
01236               case typeIntmax:
01237                 {
01238                   intmax_t v = param.vIMT;
01239                   OutputInt (writer, currentFormat, v);
01240                 }
01241                 break;
01242               case typeLong:
01243                 {
01244                   long v = param.vLong;
01245                   OutputInt (writer, currentFormat, v);
01246                 }
01247                 break;
01248               case typeLongLong:
01249                 {
01250                   longlong v = param.vLL;
01251                   OutputInt (writer, currentFormat, v);
01252                 }
01253                 break;
01254               case typePtrDiffT:
01255                 {
01256                   ptrdiff_t v = param.vPDT;
01257                   OutputInt (writer, currentFormat, v);
01258                 }
01259                 break;
01260               case typeSizeT:
01261                 {
01262                   size_t v = param.vSzT;
01263                   OutputUint (writer, currentFormat, v);
01264                 }
01265                 break;
01266               default:
01267                 {
01268                   int v = param.vInt;
01269                   OutputInt (writer, currentFormat, v);
01270                 }
01271                 break;
01272             }
01273           }
01274           break;
01275         case convHex:
01276         case convUint:
01277         case convOctal:
01278           {
01279             uint uiradix;
01280             const char* prefix;
01281             if (currentFormat.conversion == convHex)
01282             {
01283               uiradix = 16;
01284               prefix = currentFormat.basePrefix 
01285                 ? (currentFormat.uppercase ? "0X" : "0x") : 0;
01286             }
01287             else if (currentFormat.conversion == convOctal)
01288             {
01289               uiradix = 8;
01290               prefix = currentFormat.basePrefix ? "0" : 0;
01291             }
01292             else
01293             {
01294               uiradix = 10;
01295               prefix = 0;
01296             }
01297             const FmtParam& param = params[currentFormat.paramIdx];
01298             switch (currentFormat.type)
01299             {
01300               case typeIntmax:
01301                 {
01302                   intmax_t v = param.vIMT;
01303                   OutputUint (writer, currentFormat, v, uiradix, prefix);
01304                 }
01305                 break;
01306               case typeLong:
01307                 {
01308                   unsigned long v = param.vLong;
01309                   OutputUint (writer, currentFormat, v, uiradix, prefix);
01310                 }
01311                 break;
01312               case typeLongLong:
01313                 {
01314                   ulonglong v = param.vLL;
01315                   OutputUint (writer, currentFormat, v, uiradix, prefix);
01316                 }
01317                 break;
01318               case typePtrDiffT:
01319                 {
01320                   ptrdiff_t v = param.vPDT;
01321                   OutputUint (writer, currentFormat, v, uiradix, prefix);
01322                 }
01323                 break;
01324               case typeSizeT:
01325                 {
01326                   size_t v = param.vSzT;
01327                   OutputUint (writer, currentFormat, v, uiradix, prefix);
01328                 }
01329                 break;
01330               default:
01331                 {
01332                   uint v = param.vInt;
01333                   OutputUint (writer, currentFormat, v, uiradix, prefix);
01334                 }
01335                 break;
01336             }
01337           }
01338           break;
01339         case convGetNum:
01340           *((int*)(params[currentFormat.paramIdx].vPtr))
01341                 = (int)writer.GetTotal();
01342           break;
01343         case convErrno:
01344           OutputString (writer, currentFormat, 
01345             (utf8_char*)strerror (params[currentFormat.paramIdx].vInt));
01346           break;
01347         case convPtr:
01348           {
01349             FormatSpec fakeFormat;
01350             fakeFormat.leftJustify = currentFormat.leftJustify;
01351             fakeFormat.precision = sizeof (uintptr_t) * 2;
01352             if (params[currentFormat.paramIdx].vPtr == 0)
01353             {
01354               OutputString (writer, fakeFormat, (utf8_char*)"(nil)");
01355             }
01356             else
01357             {
01358               OutputUint (writer, fakeFormat, 
01359                 (uintptr_t)params[currentFormat.paramIdx].vPtr, 16, "0x");
01360             }
01361           }
01362           break;
01363         case convFloatFix:
01364           {
01365             if (currentFormat.type == typeLongLong)
01366             {
01367 #ifdef CS_FORMATTER_NO_LONG_DOUBLE_FORMAT
01368               OutputFloat (writer, currentFormat, 
01369               (double)params[currentFormat.paramIdx].vLongDbl, "f");
01370 #else
01371               OutputFloat (writer, currentFormat, 
01372               params[currentFormat.paramIdx].vLongDbl, "Lf");
01373 #endif
01374             }
01375             else
01376               OutputFloat (writer, currentFormat, 
01377               params[currentFormat.paramIdx].vDbl, "f");
01378           }
01379           break;
01380         case convFloatExp:
01381           {
01382             if (currentFormat.type == typeLongLong)
01383             {
01384 #ifdef CS_FORMATTER_NO_LONG_DOUBLE_FORMAT
01385               OutputFloat (writer, currentFormat, 
01386               (double)params[currentFormat.paramIdx].vLongDbl, 
01387               currentFormat.uppercase ? "E" : "e");
01388 #else
01389               OutputFloat (writer, currentFormat, 
01390               params[currentFormat.paramIdx].vLongDbl, 
01391               currentFormat.uppercase ? "LE" : "Le");
01392 #endif
01393             }
01394             else
01395               OutputFloat (writer, currentFormat, 
01396               params[currentFormat.paramIdx].vDbl, 
01397               currentFormat.uppercase ? "E" : "e");
01398           }
01399           break;
01400         case convFloatGeneral:
01401           {
01402             if (currentFormat.type == typeLongLong)
01403             {
01404 #ifdef CS_FORMATTER_NO_LONG_DOUBLE_FORMAT
01405               OutputFloat (writer, currentFormat, 
01406               (double)params[currentFormat.paramIdx].vLongDbl, 
01407               currentFormat.uppercase ? "G" : "g");
01408 #else
01409               OutputFloat (writer, currentFormat, 
01410               params[currentFormat.paramIdx].vLongDbl, 
01411               currentFormat.uppercase ? "LG" : "Lg");
01412 #endif
01413             }
01414             else
01415               OutputFloat (writer, currentFormat, 
01416               params[currentFormat.paramIdx].vDbl, 
01417               currentFormat.uppercase ? "G" : "g");
01418           }
01419           break;
01420         case convFloatHex:
01421           {
01422             if (currentFormat.type == typeLongLong)
01423               OutputFloatHex (writer, currentFormat, 
01424               params[currentFormat.paramIdx].vLongDbl, LDBL_MANT_DIG, 
01425               csLog2 (LDBL_MAX_EXP) + 1, -(LDBL_MAX_EXP - 1));
01426             else
01427               OutputFloatHex (writer, currentFormat, 
01428               params[currentFormat.paramIdx].vDbl, DBL_MANT_DIG, 
01429               csLog2 (DBL_MAX_EXP) + 1, -(DBL_MAX_EXP - 1));
01430           }
01431           break;
01432         default:
01433           break;
01434       }
01435 
01436       for (n = 0; n < currentFormat.fmtSkip; n++)
01437       {
01438         if (!reader.GetNext (ch)) break;
01439       }
01440       i++;
01441     }
01442     while (reader.GetNext (ch))
01443       writer.Put (ch);
01444     writer.Put (0);
01445   }
01446 };
01447 
01450 #endif // __CS_CSUTIL_FORMATTER_H__

Generated for Crystal Space 1.2 by doxygen 1.4.7