CrystalSpace

Public API Reference

csutil/formatter.h

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

Generated for Crystal Space by doxygen 1.4.6