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