## Public API Reference

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

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