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

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