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 #include <locale.h> 00034 00035 // MinGW uses MS CRT, but it can't grok long double. VC doesn't have long 00036 // double and CRT printf() doesn't know %Lf, %Lg, or %Le. 00037 #if defined(__MINGW32__) || defined(CS_COMPILER_MSVC) 00038 #define CS_FORMATTER_NO_LONG_DOUBLE_FORMAT 00039 #endif 00040 // MinGWs <inttypes.h> uses the MS-specific 'I64' format specifier in its 00041 // PR?64 macros. Enable support for I64 so the PR?64 macros can be used. 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, // The reason for that: see I64 support 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 // Check to detect incomplete I64 specifiers 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 // Collect positions of state specifiers from format string 00486 SpecParseState state; 00487 state.Reset(); 00488 while (reader.GetNext (state.ch)) 00489 { 00490 switch (parseState) 00491 { 00492 // Note: all falling through in this switch() is intentional. 00493 case scanFormat: 00494 { 00495 // Check for a % sign 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 // Check for start of width or param index 00508 if ((state.ch >= '1') && (state.ch <= '9')) 00509 { 00510 state.currentFormat.width = state.ch - '0'; 00511 parseState = formatParamWidth; 00512 break; 00513 } 00514 // Check for '*' (fetch width from args) 00515 else if (state.ch == '*') 00516 { 00517 state.currentFormat.width = -2; 00518 parseState = formatDotPrecTypeConversion; 00519 break; 00520 } 00521 // Param delimiter 00522 else if (state.ch == '$') 00523 { 00524 // \todo fix for empty param 00525 parseState = formatFlagsWidthPrecTypeConversion; 00526 break; 00527 } 00528 case formatParamWidth: 00529 if (parseState == formatParamWidth) // != can occur due fallthrough 00530 { 00531 // Subsequent digits width or param index 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 // Param delimiter 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 // Check for start of width 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 // Check for '*' (fetch width from args) 00557 else if (state.ch == '*') 00558 { 00559 state.currentFormat.width = -2; 00560 parseState = formatDotPrecTypeConversion; 00561 break; 00562 } 00563 // Check for flags (0, -, ...) 00564 else if (ParseFlag (state)) 00565 { 00566 parseState = formatFlagsWidthPrecTypeConversion; 00567 break; 00568 } 00569 case formatDotPrecTypeConversion: 00570 // Check for precision delimiter 00571 if (state.ch == '.') 00572 { 00573 parseState = formatPrecTypeConversion; 00574 state.currentFormat.precision = 0; 00575 break; 00576 } 00577 case formatPrecTypeConversion: 00578 // Precision digits 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 // Check for '*' (fetch precision from args) 00586 else if (state.ch == '*') 00587 { 00588 state.currentFormat.precision = -2; 00589 parseState = formatTypeConversion; 00590 break; 00591 } 00592 // Check for param type modifier (l, h, ...) 00593 case formatTypeConversion: 00594 if (ParseType (state)) 00595 { 00596 parseState = formatTypeConversion; 00597 break; 00598 } 00599 // Check actual conversion (s, d, ...) 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 // Determine order of params 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 // Fetch params from stack in order, store at correct place in params array 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 // Can just guess here... 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 // How many utf32_chars were written 00772 size_t writtenLen; 00773 /* Check if we can circumvent using the scratch array: 00774 we actually only need it when the string is right-justified 00775 (and a width is given). */ 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 /* (sizeof(x)*25)/10+1 is an approximation of the number of characters 00959 * needed to display x in decimal system. (x can be at most 256^sizeof(x). 00960 * You need log10(256^sizeof(x)) characters, becoming 00961 * sizeof(x)*log10(256). 25/10 is an (over-)approximation of log10(256). 00962 * Add 1 for sign.) */ 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 // Make sure *any* number thrown at us fits 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 // Convert locale decimal to '.' 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 // @@@ x86 long double uses explicit mantissa MSB 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__
Generated for Crystal Space 2.0 by doxygen 1.6.1