csutil/formatter.h Source File (Crystal Space 2.0 Public API Reference)

| ## Public API Reference

- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Files

- File List
- File Members

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__