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