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