CrystalSpace

Public API Reference

Main Page | Modules | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

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

Generated for Crystal Space by doxygen 1.4.4