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