CrystalSpace

Public API Reference

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

formatter.h

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

Generated for Crystal Space by doxygen 1.3.9.1