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 if (currentFormat.conversion == convInt) 00689 param.vInt = (short)(va_arg (args, int)); 00690 else 00691 param.vInt = (unsigned short)(va_arg (args, int)); 00692 break; 00693 case typeChar: 00694 if (currentFormat.conversion == convInt) 00695 param.vInt = (char)(va_arg (args, int)); 00696 else 00697 param.vInt = (unsigned char)(va_arg (args, int)); 00698 break; 00699 default: 00700 param.vInt = va_arg (args, int); 00701 break; 00702 } 00703 } 00704 break; 00705 case convErrno: 00706 param.vInt = errno; 00707 break; 00708 case convChar: 00709 if (currentFormat.type == typeLong) 00710 { 00711 param.vInt = (wint_t)(va_arg (args, int)); 00712 } 00713 else 00714 { 00715 param.vInt = (unsigned char)(va_arg (args, int)); 00716 } 00717 break; 00718 case convFloatFix: 00719 case convFloatExp: 00720 case convFloatGeneral: 00721 case convFloatHex: 00722 if (currentFormat.type == typeLongLong) 00723 { 00724 param.vLongDbl = va_arg (args, long double); 00725 } 00726 else 00727 { 00728 param.vDbl = va_arg (args, double); 00729 } 00730 break; 00731 case convStr: 00732 case convPtr: 00733 case convGetNum: 00734 param.vPtr = va_arg (args, void*); 00735 break; 00736 case convNone: 00737 break; 00738 } 00739 } 00740 } 00741 00742 void Init (va_list args) 00743 { 00744 ParseSpec (); 00745 FetchArgs (args); 00746 } 00747 00749 template<class T> 00750 void OutputString (Twriter& writer, const FormatSpec& currentFormat, 00751 const T* stringPtr) 00752 { 00753 if (stringPtr == 0) 00754 { 00755 OutputString (writer, currentFormat, (utf8_char*)"(null)"); 00756 return; 00757 } 00758 00759 size_t len = 0; 00760 { 00761 const T* ptr = stringPtr; 00762 while (*ptr++ != 0) len++; 00763 } 00764 if (currentFormat.precision > -1) 00765 len = MIN(len, (size_t)currentFormat.precision); 00766 00767 // How many utf32_chars were written 00768 size_t writtenLen; 00769 /* Check if we can circumvent using the scratch array: 00770 we actually only need it when the string is right-justified 00771 (and a width is given). */ 00772 bool fastTrack = currentFormat.leftJustify 00773 || (currentFormat.width == 0); 00774 00775 if (fastTrack) 00776 { 00777 writtenLen = 0; 00778 while (len > 0) 00779 { 00780 utf32_char ch; 00781 int n = csUnicodeTransform::Decode (stringPtr, len, ch); 00782 writer.Put (ch); 00783 stringPtr += n; 00784 len -= (size_t)n; 00785 writtenLen++; 00786 } 00787 } 00788 else 00789 { 00790 size_t scratchOffs = scratch.GetSize (); 00791 while (len > 0) 00792 { 00793 utf32_char ch; 00794 int n = csUnicodeTransform::Decode (stringPtr, len, ch); 00795 scratch.Push (ch); 00796 stringPtr += n; 00797 len -= (size_t)n; 00798 } 00799 writtenLen = scratch.GetSize () - scratchOffs; 00800 if (!currentFormat.leftJustify 00801 && ((size_t)currentFormat.width > writtenLen)) 00802 { 00803 size_t d = (size_t)currentFormat.width - writtenLen; 00804 while (d-- > 0) writer.Put (' '); 00805 } 00806 scratch.WriteTo (writer, scratchOffs); 00807 scratch.Truncate (scratchOffs); 00808 } 00809 if (currentFormat.leftJustify 00810 && ((size_t)currentFormat.width > writtenLen)) 00811 { 00812 size_t d = (size_t)currentFormat.width - writtenLen; 00813 while (d-- > 0) writer.Put (' '); 00814 } 00815 } 00816 00818 void DoPadding (const FormatSpec& currentFormat, const size_t scratchOffs, 00819 const size_t insert0offs) 00820 { 00821 if (currentFormat.leftJustify) 00822 { 00823 while ((size_t)currentFormat.width > (scratch.GetSize () - scratchOffs)) 00824 { 00825 scratch.Push (' '); 00826 } 00827 } 00828 else 00829 { 00830 if (currentFormat.padZero) 00831 { 00832 while ((size_t)currentFormat.width > (scratch.GetSize () - scratchOffs)) 00833 { 00834 scratch.Insert (insert0offs, '0'); 00835 } 00836 } 00837 else 00838 { 00839 while ((size_t)currentFormat.width > (scratch.GetSize () - scratchOffs)) 00840 { 00841 scratch.Insert (scratchOffs, ' '); 00842 } 00843 } 00844 } 00845 } 00846 00848 template<class T> 00849 void OutputInt (Twriter& writer, const FormatSpec& currentFormat, T value) 00850 { 00851 const size_t scratchOffs = scratch.GetSize (); 00852 size_t insertOffs = scratchOffs; 00853 00854 if (value < 0) 00855 { 00856 scratch.Push ('-'); 00857 insertOffs++; 00858 value = -value; 00859 } 00860 else if (currentFormat.plusSign) 00861 { 00862 scratch.Push ('+'); 00863 insertOffs++; 00864 } 00865 else if (currentFormat.spacePrefix) 00866 { 00867 scratch.Push (' '); 00868 insertOffs++; 00869 } 00870 00871 int width = 0; 00872 int numDigits = currentFormat.precision; 00873 if (!((value == 0) && (numDigits == 0))) 00874 { 00875 do 00876 { 00877 int d = (int)(value % 10); 00878 scratch.Insert (insertOffs, d + '0'); 00879 width++; 00880 value = value / 10; 00881 } 00882 while ((value != 0) || (width < numDigits)); 00883 } 00884 DoPadding (currentFormat, scratchOffs, insertOffs); 00885 scratch.WriteTo (writer, scratchOffs); 00886 scratch.Truncate (scratchOffs); 00887 } 00888 00890 template<class T> 00891 void OutputUint (Twriter& writer, const FormatSpec& currentFormat, 00892 T value, uint radix = 10, const char* prefix = 0) 00893 { 00894 const utf32_char letterFirst = currentFormat.uppercase ? 'A' : 'a'; 00895 const size_t scratchOffs = scratch.GetSize (); 00896 size_t insertOffs = scratchOffs; 00897 00898 if (prefix != 0) 00899 { 00900 while (*prefix != 0) 00901 { 00902 utf32_char ch = (value != 0) ? *prefix : ' '; 00903 scratch.Push (ch); 00904 insertOffs++; 00905 prefix++; 00906 } 00907 } 00908 00909 int width = 0; 00910 int numDigits = currentFormat.precision; 00911 if (!((value == 0) && (numDigits == 0))) 00912 { 00913 do 00914 { 00915 uint d = (uint)(value % radix); 00916 utf32_char ch; 00917 if (d <= 9) 00918 ch = d + '0'; 00919 else 00920 ch = d - 10 + letterFirst; 00921 scratch.Insert (insertOffs, ch); 00922 width++; 00923 value = value / radix; 00924 } 00925 while ((value != 0) || (width < numDigits)); 00926 } 00927 DoPadding (currentFormat, scratchOffs, insertOffs); 00928 scratch.WriteTo (writer, scratchOffs); 00929 scratch.Truncate (scratchOffs); 00930 } 00931 00933 template<class T> 00934 void OutputFloat (Twriter& writer, const FormatSpec& currentFormat, 00935 const T& value, const char* type) 00936 { 00937 char flags[5] = ""; 00938 if (currentFormat.plusSign) 00939 strcat (flags, "+"); 00940 if (currentFormat.spacePrefix) 00941 strcat (flags, " "); 00942 if (currentFormat.basePrefix) 00943 strcat (flags, "#"); 00944 if (currentFormat.padZero) 00945 strcat (flags, "0"); 00946 /* (sizeof(x)*25)/10+1 is an approximation of the number of characters 00947 * needed to display x in decimal system. (x can be at most 256^sizeof(x). 00948 * You need log10(256^sizeof(x)) characters, becoming 00949 * sizeof(x)*log10(256). 25/10 is an (over-)approximation of log10(256). 00950 * Add 1 for sign.) */ 00951 CS_ALLOC_STACK_ARRAY(char, precStr, 00952 (sizeof(currentFormat.precision) * 25) / 10 + 2); 00953 if (currentFormat.precision >= 0) 00954 sprintf (precStr, ".%d", currentFormat.precision); 00955 else 00956 precStr[0] = 0; 00957 CS_ALLOC_STACK_ARRAY(char, formatStr, 1 + strlen (flags) 00958 + (sizeof(currentFormat.width) * 25) / 10 + 1 + strlen (precStr) + 2); 00959 sprintf (formatStr, "%%%s%d%s%s", flags, currentFormat.width, precStr, 00960 type); 00961 // Make sure *any* number thrown at us fits 00962 char formattedStr[LDBL_MAX_10_EXP+3]; 00963 sprintf (formattedStr, formatStr, value); 00964 00965 char* p = formattedStr; 00966 while (*p != 0) 00967 writer.Put (*p++); 00968 } 00969 00973 template<class T, class Tbase> 00974 struct IEEEFloatMantissa 00975 { 00976 Tbase mantissa[sizeof(T)/sizeof(Tbase)]; 00977 00978 Tbase& operator[] (int index) 00979 { return mantissa[index]; } 00980 bool Eq0 () 00981 { 00982 for (uint n = 0; n < sizeof(T)/sizeof(Tbase); n++) 00983 { 00984 if (mantissa[n] != 0) return false; 00985 } 00986 return true; 00987 } 00988 const Tbase operator& (Tbase other) const 00989 { return mantissa[0] & other; } 00990 IEEEFloatMantissa& operator<<= (int shift) 00991 { 00992 const int ovShift = sizeof(Tbase) * 8 - shift; 00993 Tbase overflow = 0; 00994 for (uint n = 0; n < sizeof(T)/sizeof(Tbase); n++) 00995 { 00996 Tbase newOverflow = mantissa[n] >> ovShift; 00997 mantissa[n] = (mantissa[n] << shift) | overflow; 00998 overflow = newOverflow; 00999 } 01000 return *this; 01001 } 01002 Tbase& Leftmost () 01003 { return mantissa[sizeof(T)/sizeof(Tbase)-1]; } 01004 }; 01005 01007 template<class T, class Tbase> 01008 struct IEEEFloatSplitter 01009 { 01010 bool sign; 01011 Tbase exp; 01012 01013 typename csPrintfFormatter<Twriter,Treader>:: 01014 template IEEEFloatMantissa<T, Tbase> mantissa; 01015 01016 IEEEFloatSplitter (const T& val, const int mantissaBits, 01017 const int expBits) 01018 { 01019 const int baseBits = sizeof(Tbase) * 8; 01020 const int signBit = mantissaBits + expBits; 01021 01022 union 01023 { 01024 T v; 01025 Tbase vB[sizeof(T)/sizeof(Tbase)]; 01026 } toBase; 01027 toBase.v = val; 01028 #ifdef CS_LITTLE_ENDIAN 01029 const int hi = (sizeof (T) / sizeof (Tbase)) - 1; 01030 const int lo = 0; 01031 const int d = 1; 01032 #else 01033 const int hi = 0; 01034 const int lo = (sizeof (T) / sizeof (Tbase)) - 1; 01035 const int d = -1; 01036 #endif 01037 sign = ((toBase.vB[lo + (signBit / baseBits) * d] 01038 & (1 << (signBit % baseBits))) != 0); 01039 exp = (toBase.vB[hi] >> (mantissaBits % (baseBits))) 01040 & ((1 << expBits) - 1); 01041 for (int n = lo, p = 0; n != hi + d; n += d, p++) 01042 { 01043 const int bit = p * baseBits; 01044 const Tbase mask = ((bit + baseBits) <= mantissaBits) ? ~0 01045 : ((1 << (mantissaBits % baseBits)) - 1); 01046 mantissa[p] = toBase.vB[n] & mask; 01047 } 01048 } 01049 }; 01051 template <class T> 01052 void OutputFloatHex (Twriter& writer, const FormatSpec& currentFormat, 01053 const T& value, const int vMantissaBits, const int expBits, const int bias) 01054 { 01055 #ifdef CS_IEEE_DOUBLE_FORMAT 01056 const utf32_char letterFirst = currentFormat.uppercase ? 'A' : 'a'; 01057 01058 #ifdef CS_PROCESSOR_X86 01059 // @@@ x86 long double uses explicit mantissa MSB 01060 const bool hiddenBit = !(vMantissaBits >= 63); 01061 #else 01062 const bool hiddenBit = false; 01063 #endif 01064 const int mantissaBits = vMantissaBits - (hiddenBit ? 1 : 0); 01065 IEEEFloatSplitter<T, uint> vSplit (value, mantissaBits, expBits); 01066 const uint expMax = (1 << (sizeof(T) * 8 - mantissaBits - 1)) - 1; 01067 01068 if ((vSplit.exp == expMax) && vSplit.mantissa.Eq0()) 01069 { 01070 char infStr[5]; 01071 if (vSplit.sign) 01072 { 01073 strcpy (infStr, "-"); 01074 } 01075 else 01076 { 01077 if (currentFormat.plusSign) 01078 strcpy (infStr, "+"); 01079 else if (currentFormat.spacePrefix) 01080 strcpy (infStr, " "); 01081 else 01082 strcpy (infStr, ""); 01083 } 01084 strcat (infStr, currentFormat.uppercase ? "INF" : "inf"); 01085 OutputString (writer, currentFormat, 01086 (utf8_char*)infStr); 01087 return; 01088 } 01089 else if ((vSplit.exp == expMax) && !vSplit.mantissa.Eq0()) 01090 { 01091 char nanStr[5]; 01092 if (vSplit.sign) 01093 { 01094 strcpy (nanStr, "-"); 01095 } 01096 else 01097 { 01098 if (currentFormat.plusSign) 01099 strcpy (nanStr, "+"); 01100 else if (currentFormat.spacePrefix) 01101 strcpy (nanStr, " "); 01102 else 01103 strcpy (nanStr, ""); 01104 } 01105 strcat (nanStr, currentFormat.uppercase ? "NAN" : "nan"); 01106 OutputString (writer, currentFormat, 01107 (utf8_char*)nanStr); 01108 return; 01109 } 01110 01111 const size_t scratchOffs = scratch.GetSize (); 01112 if (vSplit.sign) 01113 { 01114 scratch.Push ('-'); 01115 } 01116 scratch.Push ('0'); 01117 scratch.Push (currentFormat.uppercase ? 'X' : 'x'); 01118 if (hiddenBit) 01119 { 01120 if (vSplit.exp == 0) 01121 scratch.Push ('0'); 01122 else 01123 scratch.Push ('1'); 01124 } 01125 else 01126 { 01127 const int bitNum = mantissaBits - 1; 01128 const int baseBits = sizeof (uint) * 8; 01129 const int bitIndex = bitNum / baseBits; 01130 scratch.Push ('0' + ((vSplit.mantissa[bitIndex] 01131 >> (bitNum % baseBits)) & 1)); 01132 vSplit.mantissa <<= 1; 01133 } 01134 if ((currentFormat.precision > 0) || (!vSplit.mantissa.Eq0())) 01135 { 01136 scratch.Push ('.'); 01137 01138 IEEEFloatMantissa<T, uint> m (vSplit.mantissa); 01139 m <<= sizeof(T)*8 - mantissaBits; 01140 int w = 0; 01141 do 01142 { 01143 uint d = m.Leftmost() >> ((sizeof(uint)*8)-4); 01144 utf32_char ch; 01145 if (d <= 9) 01146 ch = d + '0'; 01147 else 01148 ch = d - 10 + letterFirst; 01149 scratch.Push (ch); 01150 m <<= 4; 01151 w++; 01152 } 01153 while ((w < currentFormat.precision) 01154 || ((currentFormat.precision <= 0) && !m.Eq0())); 01155 } 01156 scratch.Push (currentFormat.uppercase ? 'P' : 'p'); 01157 int e; 01158 if ((vSplit.exp == 0) && vSplit.mantissa.Eq0()) 01159 e = 0; 01160 else 01161 e = (int)vSplit.exp + bias; 01162 if (e < 0) 01163 { 01164 scratch.Push ('-'); 01165 e = -e; 01166 } 01167 else 01168 scratch.Push ('+'); 01169 const size_t insertOffs = scratch.GetSize ();; 01170 do 01171 { 01172 uint d = e % 10; 01173 scratch.Insert (insertOffs, d + '0'); 01174 e = e / 10; 01175 } 01176 while (e != 0); 01177 01178 DoPadding (currentFormat, scratchOffs, 01179 vSplit.sign ? scratchOffs + 1 : scratchOffs); 01180 scratch.WriteTo (writer, scratchOffs); 01181 scratch.Truncate (scratchOffs); 01182 #else 01183 #if defined(CS_COMPILER_GCC) 01184 #warning Do not know how to hex-format floats 01185 #elif defined(CS_COMPILER_MSVC) 01186 #pragma message("Do not know how to hex-format floats") 01187 #endif 01188 #endif 01189 } 01190 public: 01192 csPrintfFormatter (Treader* reader, va_list args) : reader (*reader) 01193 { 01194 Init (args); 01195 } 01197 csPrintfFormatter (Treader* reader, ...) : reader (*reader) 01198 { 01199 va_list ap; 01200 va_start(ap, reader); 01201 Init (ap); 01202 va_end(ap); 01203 } 01205 void Format (Twriter& writer) 01206 { 01207 reader.Reset(); 01208 size_t i = 0; 01209 utf32_char ch; 01210 while (i < formatSpecs.GetSize ()) 01211 { 01212 const FormatSpec& currentFormat = formatSpecs[i]; 01213 size_t n; 01214 for (n = 0; n < currentFormat.copyRun; n++) 01215 { 01216 if (!reader.GetNext (ch)) break; 01217 writer.Put (ch); 01218 } 01219 01220 switch (currentFormat.conversion) 01221 { 01222 case convStr: 01223 { 01224 if (currentFormat.type == typeLong) 01225 OutputString (writer, currentFormat, 01226 (wchar_t*)(params[currentFormat.paramIdx].vPtr)); 01227 else 01228 OutputString (writer, currentFormat, 01229 (utf8_char*)(params[currentFormat.paramIdx].vPtr)); 01230 } 01231 break; 01232 case convChar: 01233 { 01234 writer.Put (params[currentFormat.paramIdx].vInt); 01235 } 01236 break; 01237 case convInt: 01238 { 01239 const FmtParam& param = params[currentFormat.paramIdx]; 01240 switch (currentFormat.type) 01241 { 01242 case typeIntmax: 01243 { 01244 intmax_t v = param.vIMT; 01245 OutputInt (writer, currentFormat, v); 01246 } 01247 break; 01248 case typeLong: 01249 { 01250 long v = param.vLong; 01251 OutputInt (writer, currentFormat, v); 01252 } 01253 break; 01254 case typeLongLong: 01255 { 01256 longlong v = param.vLL; 01257 OutputInt (writer, currentFormat, v); 01258 } 01259 break; 01260 case typePtrDiffT: 01261 { 01262 ptrdiff_t v = param.vPDT; 01263 OutputInt (writer, currentFormat, v); 01264 } 01265 break; 01266 case typeSizeT: 01267 { 01268 size_t v = param.vSzT; 01269 OutputUint (writer, currentFormat, v); 01270 } 01271 break; 01272 default: 01273 { 01274 int v = param.vInt; 01275 OutputInt (writer, currentFormat, v); 01276 } 01277 break; 01278 } 01279 } 01280 break; 01281 case convHex: 01282 case convUint: 01283 case convOctal: 01284 { 01285 uint uiradix; 01286 const char* prefix; 01287 if (currentFormat.conversion == convHex) 01288 { 01289 uiradix = 16; 01290 prefix = currentFormat.basePrefix 01291 ? (currentFormat.uppercase ? "0X" : "0x") : 0; 01292 } 01293 else if (currentFormat.conversion == convOctal) 01294 { 01295 uiradix = 8; 01296 prefix = currentFormat.basePrefix ? "0" : 0; 01297 } 01298 else 01299 { 01300 uiradix = 10; 01301 prefix = 0; 01302 } 01303 const FmtParam& param = params[currentFormat.paramIdx]; 01304 switch (currentFormat.type) 01305 { 01306 case typeIntmax: 01307 { 01308 intmax_t v = param.vIMT; 01309 OutputUint (writer, currentFormat, v, uiradix, prefix); 01310 } 01311 break; 01312 case typeLong: 01313 { 01314 unsigned long v = param.vLong; 01315 OutputUint (writer, currentFormat, v, uiradix, prefix); 01316 } 01317 break; 01318 case typeLongLong: 01319 { 01320 ulonglong v = param.vLL; 01321 OutputUint (writer, currentFormat, v, uiradix, prefix); 01322 } 01323 break; 01324 case typePtrDiffT: 01325 { 01326 ptrdiff_t v = param.vPDT; 01327 OutputUint (writer, currentFormat, v, uiradix, prefix); 01328 } 01329 break; 01330 case typeSizeT: 01331 { 01332 size_t v = param.vSzT; 01333 OutputUint (writer, currentFormat, v, uiradix, prefix); 01334 } 01335 break; 01336 default: 01337 { 01338 uint v = param.vInt; 01339 OutputUint (writer, currentFormat, v, uiradix, prefix); 01340 } 01341 break; 01342 } 01343 } 01344 break; 01345 case convGetNum: 01346 *((int*)(params[currentFormat.paramIdx].vPtr)) 01347 = (int)writer.GetTotal(); 01348 break; 01349 case convErrno: 01350 OutputString (writer, currentFormat, 01351 (utf8_char*)strerror (params[currentFormat.paramIdx].vInt)); 01352 break; 01353 case convPtr: 01354 { 01355 FormatSpec fakeFormat; 01356 fakeFormat.leftJustify = currentFormat.leftJustify; 01357 fakeFormat.precision = sizeof (uintptr_t) * 2; 01358 if (params[currentFormat.paramIdx].vPtr == 0) 01359 { 01360 OutputString (writer, fakeFormat, (utf8_char*)"(nil)"); 01361 } 01362 else 01363 { 01364 OutputUint (writer, fakeFormat, 01365 (uintptr_t)params[currentFormat.paramIdx].vPtr, 16, "0x"); 01366 } 01367 } 01368 break; 01369 case convFloatFix: 01370 { 01371 if (currentFormat.type == typeLongLong) 01372 { 01373 #ifdef CS_FORMATTER_NO_LONG_DOUBLE_FORMAT 01374 OutputFloat (writer, currentFormat, 01375 (double)params[currentFormat.paramIdx].vLongDbl, "f"); 01376 #else 01377 OutputFloat (writer, currentFormat, 01378 params[currentFormat.paramIdx].vLongDbl, "Lf"); 01379 #endif 01380 } 01381 else 01382 OutputFloat (writer, currentFormat, 01383 params[currentFormat.paramIdx].vDbl, "f"); 01384 } 01385 break; 01386 case convFloatExp: 01387 { 01388 if (currentFormat.type == typeLongLong) 01389 { 01390 #ifdef CS_FORMATTER_NO_LONG_DOUBLE_FORMAT 01391 OutputFloat (writer, currentFormat, 01392 (double)params[currentFormat.paramIdx].vLongDbl, 01393 currentFormat.uppercase ? "E" : "e"); 01394 #else 01395 OutputFloat (writer, currentFormat, 01396 params[currentFormat.paramIdx].vLongDbl, 01397 currentFormat.uppercase ? "LE" : "Le"); 01398 #endif 01399 } 01400 else 01401 OutputFloat (writer, currentFormat, 01402 params[currentFormat.paramIdx].vDbl, 01403 currentFormat.uppercase ? "E" : "e"); 01404 } 01405 break; 01406 case convFloatGeneral: 01407 { 01408 if (currentFormat.type == typeLongLong) 01409 { 01410 #ifdef CS_FORMATTER_NO_LONG_DOUBLE_FORMAT 01411 OutputFloat (writer, currentFormat, 01412 (double)params[currentFormat.paramIdx].vLongDbl, 01413 currentFormat.uppercase ? "G" : "g"); 01414 #else 01415 OutputFloat (writer, currentFormat, 01416 params[currentFormat.paramIdx].vLongDbl, 01417 currentFormat.uppercase ? "LG" : "Lg"); 01418 #endif 01419 } 01420 else 01421 OutputFloat (writer, currentFormat, 01422 params[currentFormat.paramIdx].vDbl, 01423 currentFormat.uppercase ? "G" : "g"); 01424 } 01425 break; 01426 case convFloatHex: 01427 { 01428 if (currentFormat.type == typeLongLong) 01429 OutputFloatHex (writer, currentFormat, 01430 params[currentFormat.paramIdx].vLongDbl, LDBL_MANT_DIG, 01431 csLog2 (LDBL_MAX_EXP) + 1, -(LDBL_MAX_EXP - 1)); 01432 else 01433 OutputFloatHex (writer, currentFormat, 01434 params[currentFormat.paramIdx].vDbl, DBL_MANT_DIG, 01435 csLog2 (DBL_MAX_EXP) + 1, -(DBL_MAX_EXP - 1)); 01436 } 01437 break; 01438 default: 01439 break; 01440 } 01441 01442 for (n = 0; n < currentFormat.fmtSkip; n++) 01443 { 01444 if (!reader.GetNext (ch)) break; 01445 } 01446 i++; 01447 } 01448 while (reader.GetNext (ch)) 01449 writer.Put (ch); 01450 writer.Put (0); 01451 } 01452 }; 01453 01456 #endif // __CS_CSUTIL_FORMATTER_H__
Generated for Crystal Space 1.2.1 by doxygen 1.5.3