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

Generated for Crystal Space 2.0 by doxygen 1.7.6.1