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