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