| 1 | // tPrint.cpp  |
| 2 | //  |
| 3 | // Formatted print functions that improve upon the standard printf family of functions. The functions found here  |
| 4 | // support custom type handlers for things like vectors, matrices, and quaternions. They have more robust support for  |
| 5 | // different type sizes and can print integral types in a variety of bases. Redirection via a callback as well as  |
| 6 | // visibility channels are also supported.  |
| 7 | //  |
| 8 | // Copyright (c) 2004-2006, 2015, 2017, 2019, 2020 Tristan Grimmer.  |
| 9 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby  |
| 10 | // granted, provided that the above copyright notice and this permission notice appear in all copies.  |
| 11 | //  |
| 12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL  |
| 13 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,  |
| 14 | // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN  |
| 15 | // AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR  |
| 16 | // PERFORMANCE OF THIS SOFTWARE.  |
| 17 |   |
| 18 | #ifdef PLATFORM_WINDOWS  |
| 19 | #include <windows.h>  |
| 20 | #endif  |
| 21 | #include <Foundation/tStandard.h>  |
| 22 | #include <Foundation/tArray.h>  |
| 23 | #include <Foundation/tHash.h>  |
| 24 | #include <Math/tLinearAlgebra.h>  |
| 25 | #include "System/tMachine.h"  |
| 26 | #include "System/tTime.h"  |
| 27 | #include "System/tFile.h"  |
| 28 | #include "System/tPrint.h"  |
| 29 | using namespace tMath;  |
| 30 |   |
| 31 |   |
| 32 | namespace tSystem  |
| 33 | {  |
| 34 | // Global settings for all print functionality.  |
| 35 | static int DefaultPrecision = 4;  |
| 36 |   |
| 37 | // This class receives the final properly formatted characters. As it receives them it counts how many were  |
| 38 | // received. If you construct with either an external character buffer or external string, it populates them.  |
| 39 | class Receiver  |
| 40 | {  |
| 41 | public:  |
| 42 | // This constructor creates a receiver that only counts characters received.  |
| 43 | Receiver() : Buffer(nullptr), ReceiveLimit(-1), String(nullptr), NumReceived(0) { }  |
| 44 |   |
| 45 | // Populates buffer as chars are received. Buffer is owned externally and its lifespan must outlast Receiver.  |
| 46 | Receiver(tArray<char>* buffer) : Buffer(buffer), ReceiveLimit(-1), String(nullptr), NumReceived(0) { }  |
| 47 |   |
| 48 | // Populates string as chars are received. Buffer is owned externally and its lifespan must outlast Receiver.  |
| 49 | // The caller must ensure enough room in string for all the receives that will be called.  |
| 50 | Receiver(char* string) : Buffer(nullptr), ReceiveLimit(-1), String(string), NumReceived(0) { }  |
| 51 |   |
| 52 | // Populates string as chars are received. Buffer is owned externally and its lifespan must outlast Receiver.  |
| 53 | // The caller must ensure enough room in string for all the receives that will be called. After receiveLimit  |
| 54 | // characters are received, string will no longer be written to.  |
| 55 | Receiver(char* string, int receiveLimit) : Buffer(nullptr), ReceiveLimit(receiveLimit), String(string), NumReceived(0) { }  |
| 56 |   |
| 57 | void Receive(char chr);  |
| 58 | void Receive(const char* str); // Assumes null termination.  |
| 59 | void Receive(const char* str, int numChars); // No null termination necessary.  |
| 60 | void Receive(const tArray<char>&);  |
| 61 | int GetNumReceived() const { return NumReceived; }  |
| 62 |   |
| 63 | private:  |
| 64 | // We could have used a tString here but it wouldn't have been very efficient since appending a single character  |
| 65 | // would cause a memcpy.  |
| 66 | tArray<char>* Buffer;  |
| 67 |   |
| 68 | // This string is not owned by this class. It is supplied by the caller of one of the string-printf style  |
| 69 | // functions. A receive limit of -1 means no limit.  |
| 70 | int ReceiveLimit;  |
| 71 | char* String;  |
| 72 | int NumReceived;  |
| 73 | };  |
| 74 |   |
| 75 | // This is the workhorse. It processes the format string and deposits the resulting formatted text in the receiver.  |
| 76 | void Process(Receiver&, const char* format, va_list);  |
| 77 |   |
| 78 | // Channel system. This is lazy initialized (using the name hash as the state) without any need for shutdown.  |
| 79 | uint32 ComputerNameHash = 0;  |
| 80 | tChannel OutputChannels = tChannel_Systems;  |
| 81 | bool SupplementaryDebuggerOutput = false;  |
| 82 | RedirectCallback* StdoutRedirectCallback = nullptr;  |
| 83 |   |
| 84 | // A format specification consists of the information stored in the expression:  |
| 85 | // %[flags] [width] [.precision] [:typesize][|typesize]type  |
| 86 | // except for the type character.  |
| 87 | enum Flag  |
| 88 | {  |
| 89 | Flag_ForcePosOrNegSign = 1 << 0,  |
| 90 | Flag_SpaceForPosSign = 1 << 1,  |
| 91 | Flag_LeadingZeros = 1 << 2,  |
| 92 | Flag_LeftJustify = 1 << 3,  |
| 93 | Flag_DecorativeFormatting = 1 << 4,  |
| 94 | Flag_DecorativeFormattingAlt = 1 << 5,  |
| 95 | Flag_BasePrefix = 1 << 6  |
| 96 | };  |
| 97 |   |
| 98 | struct FormatSpec  |
| 99 | {  |
| 100 | FormatSpec() : Flags(0), Width(0), Precision(-1), TypeSizeBytes(0) { }  |
| 101 | FormatSpec(const FormatSpec& src) : Flags(src.Flags), Width(src.Width), Precision(src.Precision), TypeSizeBytes(src.TypeSizeBytes) { }  |
| 102 | FormatSpec& operator=(const FormatSpec& src) { Flags = src.Flags; Width = src.Width; Precision = src.Precision; TypeSizeBytes = src.TypeSizeBytes; return *this; }  |
| 103 |   |
| 104 | uint32 Flags;  |
| 105 | int Width;  |
| 106 | int Precision;  |
| 107 | int TypeSizeBytes; // Defaults to 0, not set.  |
| 108 | };  |
| 109 |   |
| 110 | // Type handler stuff below.  |
| 111 | typedef void (*HandlerFn)(Receiver& out, const FormatSpec&, void* data);  |
| 112 |   |
| 113 | // The BaseType indicates what a passed-in type is made of in terms of built-in types. This allows  |
| 114 | // us to call va_arg with precisely the right type instead of just one with the correct size. The latter  |
| 115 | // works fine with MSVC but not Clang. No idea why.  |
| 116 | enum class BaseType  |
| 117 | {  |
| 118 | None,  |
| 119 | Int,  |
| 120 | Flt,  |
| 121 | Dbl  |
| 122 | };  |
| 123 |   |
| 124 | struct HandlerInfo  |
| 125 | {  |
| 126 | char SpecChar; // The specifier character. eg. 'f', 'd', 'X', etc.  |
| 127 | BaseType TypeBase;  |
| 128 | int DefaultByteSize;  |
| 129 | HandlerFn Handler;  |
| 130 | };  |
| 131 | extern const int NumHandlers;  |
| 132 | extern HandlerInfo HandlerInfos[];  |
| 133 | extern int HandlerJumpTable[256];  |
| 134 | HandlerInfo* FindHandler(char format);  |
| 135 | bool IsValidFormatSpecifierCharacter(char);  |
| 136 |   |
| 137 | // Does the heavy-lifting of converting (built-in) integer types to strings. This function can handle both 32 and  |
| 138 | // 64 bit integers (signed and unsigned). To print Tacent integral types or bit-fields of 128, 256, or 512 bits  |
| 139 | // please see the function HandlerHelper_IntegerTacent.  |
| 140 | void HandlerHelper_IntegerNative  |
| 141 | (  |
| 142 | tArray<char>&, const FormatSpec&, void* data, bool treatAsUnsigned,  |
| 143 | int bitSize, bool upperCase, int base, bool forcePrefixLowerCase = false  |
| 144 | );  |
| 145 |   |
| 146 | void HandlerHelper_IntegerTacent  |
| 147 | (  |
| 148 | tArray<char>&, const FormatSpec&, void* data, bool treatAsUnsigned,  |
| 149 | int bitSize, bool upperCase, int base, bool forcePrefixLowerCase = false  |
| 150 | );  |
| 151 |   |
| 152 | enum class PrologHelperFloat  |
| 153 | {  |
| 154 | None,  |
| 155 | NeedsPlus,  |
| 156 | NeedsNeg,  |
| 157 | NeedsSpace,  |
| 158 | NoZeros,  |
| 159 | };  |
| 160 | PrologHelperFloat HandlerHelper_FloatNormal  |
| 161 | (  |
| 162 | tArray<char>&, const FormatSpec&, double value, bool treatPrecisionAsSigDigits = false  |
| 163 | );  |
| 164 | bool HandlerHelper_HandleSpecialFloatTypes(tArray<char>&, double value);  |
| 165 | int HandlerHelper_FloatComputeExponent(double value);  |
| 166 | void HandlerHelper_Vector(Receiver&, const FormatSpec&, const float* components, int numComponents);  |
| 167 | void HandlerHelper_JustificationProlog(Receiver&, int itemLength, const FormatSpec&);  |
| 168 | void HandlerHelper_JustificationEpilog(Receiver&, int itemLength, const FormatSpec&);  |
| 169 |   |
| 170 | // Here are all the handler functions. One per type.  |
| 171 | void Handler_b(Receiver& out, const FormatSpec&, void* data);  |
| 172 | void Handler_o(Receiver& out, const FormatSpec&, void* data);  |
| 173 | void Handler_d(Receiver& out, const FormatSpec&, void* data);  |
| 174 | void Handler_i(Receiver& out, const FormatSpec&, void* data);  |
| 175 | void Handler_u(Receiver& out, const FormatSpec&, void* data);  |
| 176 | void Handler_x(Receiver& out, const FormatSpec&, void* data);  |
| 177 | void Handler_X(Receiver& out, const FormatSpec&, void* data);  |
| 178 | void Handler_p(Receiver& out, const FormatSpec&, void* data);  |
| 179 |   |
| 180 | void Handler_e(Receiver& out, const FormatSpec&, void* data);  |
| 181 | void Handler_f(Receiver& out, const FormatSpec&, void* data);  |
| 182 | void Handler_g(Receiver& out, const FormatSpec&, void* data);  |
| 183 | void Handler_v(Receiver& out, const FormatSpec&, void* data);  |
| 184 | void Handler_q(Receiver& out, const FormatSpec&, void* data);  |
| 185 |   |
| 186 | void Handler_m(Receiver& out, const FormatSpec&, void* data);  |
| 187 | void Handler_c(Receiver& out, const FormatSpec&, void* data);  |
| 188 | void Handler_s(Receiver& out, const FormatSpec&, void* data);  |
| 189 | }  |
| 190 |   |
| 191 |   |
| 192 | void tSystem::tRegister(uint32 machineNameHash, tSystem::tChannel channelsToSee)  |
| 193 | {  |
| 194 | if (!ComputerNameHash)  |
| 195 | ComputerNameHash = tHash::tHashStringFast32( tSystem::tGetCompName() );  |
| 196 |   |
| 197 | if (machineNameHash == ComputerNameHash)  |
| 198 | tSetChannels(channelsToSee);  |
| 199 | }  |
| 200 |   |
| 201 |   |
| 202 | void tSystem::tRegister(const char* machineName, tSystem::tChannel channelsToSee)  |
| 203 | {  |
| 204 | if (!machineName)  |
| 205 | return;  |
| 206 |   |
| 207 | tRegister(tHash::tHashStringFast32(machineName), channelsToSee);  |
| 208 | }  |
| 209 |   |
| 210 |   |
| 211 | void tSystem::tSetChannels(tChannel channelsToSee)  |
| 212 | {  |
| 213 | OutputChannels = channelsToSee;  |
| 214 | }  |
| 215 |   |
| 216 |   |
| 217 | void tSystem::tSetStdoutRedirectCallback(RedirectCallback cb)  |
| 218 | {  |
| 219 | StdoutRedirectCallback = cb;  |
| 220 | }  |
| 221 |   |
| 222 |   |
| 223 | void tSystem::tSetSupplementaryDebuggerOutput(bool enable)  |
| 224 | {  |
| 225 | SupplementaryDebuggerOutput = enable;  |
| 226 | }  |
| 227 |   |
| 228 |   |
| 229 | int tSystem::tPrint(const char* text, tSystem::tChannel channels)  |
| 230 | {  |
| 231 | if (!(channels & OutputChannels))  |
| 232 | return 0;  |
| 233 |   |
| 234 | return tPrint(text, tFileHandle(0));  |
| 235 | }  |
| 236 |   |
| 237 |   |
| 238 | int tSystem::tPrint(const char* text, tFileHandle fileHandle)  |
| 239 | {  |
| 240 | int numPrinted = 0;  |
| 241 | if (!text || (*text == '\0'))  |
| 242 | return numPrinted;  |
| 243 |   |
| 244 | // Print supplementary output unfiltered.  |
| 245 | #ifdef PLATFORM_WINDOWS  |
| 246 | if (!fileHandle && SupplementaryDebuggerOutput && IsDebuggerPresent())  |
| 247 | OutputDebugStringA(text);  |
| 248 | #endif  |
| 249 |   |
| 250 | // If we have an OutputCallback and the output destination is stdout we redirect to the output callback and we're done.  |
| 251 | if (!fileHandle && StdoutRedirectCallback)  |
| 252 | {  |
| 253 | int numChars = tStd::tStrlen(text);  |
| 254 | StdoutRedirectCallback(text, numChars);  |
| 255 | return numChars;  |
| 256 | }  |
| 257 |   |
| 258 | #ifdef PLATFORM_WINDOWS  |
| 259 | // Skip some specific undesirable characters.  |
| 260 | const char* startValid = text;  |
| 261 | while (*startValid)  |
| 262 | {  |
| 263 | const char* endValid = startValid;  |
| 264 |   |
| 265 | while ((*endValid) && (*endValid != '\r'))  |
| 266 | endValid++;  |
| 267 |   |
| 268 | if ((endValid - startValid) > 0)  |
| 269 | {  |
| 270 | if (fileHandle)  |
| 271 | tSystem::tWriteFile(fileHandle, startValid, int(endValid - startValid));  |
| 272 | else  |
| 273 | tSystem::tWriteFile(stdout, startValid, int(endValid - startValid));  |
| 274 | }  |
| 275 |   |
| 276 | if (*endValid != '\r')  |
| 277 | startValid = endValid;  |
| 278 | else  |
| 279 | startValid = endValid + 1;  |
| 280 | }  |
| 281 |   |
| 282 | tFlush(stdout);  |
| 283 | numPrinted = int(startValid - text);  |
| 284 |   |
| 285 | #else  |
| 286 | int len = tStd::tStrlen(text);  |
| 287 | if (fileHandle)  |
| 288 | tSystem::tWriteFile(fileHandle, text, len);  |
| 289 | else  |
| 290 | tSystem::tWriteFile(stdout, text, len);  |
| 291 |   |
| 292 | fflush(stdout);  |
| 293 | numPrinted = len;  |
| 294 | #endif  |
| 295 |   |
| 296 | return numPrinted;  |
| 297 | }  |
| 298 |   |
| 299 |   |
| 300 | void tSystem::tSetDefaultPrecision(int precision)  |
| 301 | {  |
| 302 | DefaultPrecision = precision;  |
| 303 | }  |
| 304 |   |
| 305 |   |
| 306 | int tSystem::tGetDefaultPrecision()  |
| 307 | {  |
| 308 | return DefaultPrecision;  |
| 309 | }  |
| 310 |   |
| 311 |   |
| 312 | void tSystem::Receiver::Receive(char c)  |
| 313 | {  |
| 314 | // Are we full?  |
| 315 | if (String && (ReceiveLimit != -1) && (NumReceived >= ReceiveLimit))  |
| 316 | return;  |
| 317 |   |
| 318 | if (Buffer)  |
| 319 | Buffer->Append(c);  |
| 320 |   |
| 321 | if (String)  |
| 322 | {  |
| 323 | *String = c;  |
| 324 | String++;  |
| 325 | }  |
| 326 |   |
| 327 | NumReceived++;  |
| 328 | }  |
| 329 |   |
| 330 |   |
| 331 | void tSystem::Receiver::Receive(const char* str)  |
| 332 | {  |
| 333 | if (!str)  |
| 334 | return;  |
| 335 |   |
| 336 | int len = tStd::tStrlen(str);  |
| 337 |   |
| 338 | // How much room is avail? May need to reduce len.  |
| 339 | if (String && (ReceiveLimit != -1))  |
| 340 | {  |
| 341 | // Are we full?  |
| 342 | if (NumReceived >= ReceiveLimit)  |
| 343 | return;  |
| 344 |   |
| 345 | int remaining = ReceiveLimit - NumReceived;  |
| 346 | if (len > remaining)  |
| 347 | len = remaining;  |
| 348 | }  |
| 349 |   |
| 350 | if (!len)  |
| 351 | return;  |
| 352 |   |
| 353 | if (Buffer)  |
| 354 | Buffer->Append(str, len);  |
| 355 |   |
| 356 | if (String)  |
| 357 | {  |
| 358 | tStd::tMemcpy(String, str, len);  |
| 359 | String += len;  |
| 360 | }  |
| 361 |   |
| 362 | NumReceived += len;  |
| 363 | }  |
| 364 |   |
| 365 |   |
| 366 | void tSystem::Receiver::Receive(const char* str, int numChars)  |
| 367 | {  |
| 368 | if (!numChars || !str)  |
| 369 | return;  |
| 370 |   |
| 371 | // How much room is avail? May need to reduce len.  |
| 372 | if (String && (ReceiveLimit != -1))  |
| 373 | {  |
| 374 | // Are we full?  |
| 375 | if (NumReceived >= ReceiveLimit)  |
| 376 | return;  |
| 377 |   |
| 378 | int remaining = ReceiveLimit - NumReceived;  |
| 379 | if (numChars > remaining)  |
| 380 | numChars = remaining;  |
| 381 | }  |
| 382 |   |
| 383 | if (Buffer)  |
| 384 | Buffer->Append(str, numChars);  |
| 385 |   |
| 386 | if (String)  |
| 387 | {  |
| 388 | tStd::tMemcpy(String, str, numChars);  |
| 389 | String += numChars;  |
| 390 | }  |
| 391 |   |
| 392 | NumReceived += numChars;  |
| 393 | }  |
| 394 |   |
| 395 |   |
| 396 | void tSystem::Receiver::Receive(const tArray<char>& buf)  |
| 397 | {  |
| 398 | int len = buf.GetNumAppendedElements();  |
| 399 | Receive(buf.GetElements(), len);  |
| 400 | }  |
| 401 |   |
| 402 |   |
| 403 | // Don't forget to update the jump table if you add a new handler to this table. Also note that the  |
| 404 | // default size may be overridden by the format spec. For example, %d can be used for tint256 with the  |
| 405 | // string "%:8X", "%!32X", or "%|256d".  |
| 406 | tSystem::HandlerInfo tSystem::HandlerInfos[] =  |
| 407 | {  |
| 408 | // Type Spec Base Type Default Size (bytes) Handler Function Fast Jump Index  |
| 409 | { 'b', tSystem::BaseType::Int, 4, tSystem::Handler_b }, // 0  |
| 410 | { 'o', tSystem::BaseType::Int, 4, tSystem::Handler_o }, // 1  |
| 411 | { 'd', tSystem::BaseType::Int, 4, tSystem::Handler_d }, // 2  |
| 412 | { 'i', tSystem::BaseType::Int, 4, tSystem::Handler_i }, // 3  |
| 413 | { 'u', tSystem::BaseType::Int, 4, tSystem::Handler_u }, // 4  |
| 414 | { 'x', tSystem::BaseType::Int, 4, tSystem::Handler_x }, // 5  |
| 415 | { 'X', tSystem::BaseType::Int, 4, tSystem::Handler_X }, // 6  |
| 416 | { 'p', tSystem::BaseType::Int, sizeof(void*), tSystem::Handler_p }, // 7  |
| 417 | { 'e', tSystem::BaseType::Dbl, 8, tSystem::Handler_e }, // 8  |
| 418 | { 'f', tSystem::BaseType::Dbl, 8, tSystem::Handler_f }, // 9  |
| 419 | { 'g', tSystem::BaseType::Dbl, 8, tSystem::Handler_g }, // 10  |
| 420 | { 'v', tSystem::BaseType::Flt, sizeof(tVec3), tSystem::Handler_v }, // 11  |
| 421 | { 'q', tSystem::BaseType::Flt, sizeof(tQuat), tSystem::Handler_q }, // 12  |
| 422 | { 'm', tSystem::BaseType::Flt, sizeof(tMat4), tSystem::Handler_m }, // 13  |
| 423 | { 'c', tSystem::BaseType::Int, 4, tSystem::Handler_c }, // 14  |
| 424 | { 's', tSystem::BaseType::Int, sizeof(char*), tSystem::Handler_s }, // 15  |
| 425 | };  |
| 426 |   |
| 427 | // Filling this in correctly will speed things up. However, not filling it in or filling it in incorrectly will still  |
| 428 | // work. Fill it in by looking at the type character in the handler info table. Find the letter entry in the jump table,  |
| 429 | // and populate it with the fast jump index.  |
| 430 | const int tSystem::NumHandlers = sizeof(tSystem::HandlerInfos) / sizeof(*tSystem::HandlerInfos);  |
| 431 | int tSystem::HandlerJumpTable[256] =  |
| 432 | {  |
| 433 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // [0, 15]  |
| 434 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // [16, 31]  |
| 435 |   |
| 436 | // %  |
| 437 | -1, -1, -1, -1, -1, 16, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // [32, 47]  |
| 438 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // [48, 63]  |
| 439 |   |
| 440 | // A B C D E F G H I J K L M N O  |
| 441 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // [64, 79]  |
| 442 |   |
| 443 | // Q R S T U V W X Y Z  |
| 444 | -1, -1, -1, -1, -1, -1, -1, -1, 6, -1, -1, -1, -1, -1, -1, -1, // [80, 95]  |
| 445 |   |
| 446 | // a b c d e f g h i j k l m n o  |
| 447 | -1, -1, 0, 14, 2, 8, 9, 10, -1, 3, -1, -1, -1, 13, -1, 1, // [96, 111]  |
| 448 |   |
| 449 | // q r s t u v w x y z  |
| 450 | 7, 12, -1, 15, -1, 4, 11, -1, 5, -1, -1, -1, -1, -1, -1, -1, // [112, 127]  |
| 451 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // [128, 143]  |
| 452 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // [144, 159]  |
| 453 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // [160, 175]  |
| 454 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // [176, 191]  |
| 455 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // [192, 207]  |
| 456 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // [208, 223]  |
| 457 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // [224, 239]  |
| 458 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // [240, 255]  |
| 459 | };  |
| 460 |   |
| 461 |   |
| 462 | int tvPrintf(const char* format, va_list argList)  |
| 463 | {  |
| 464 | if (!format)  |
| 465 | return 0;  |
| 466 |   |
| 467 | tArray<char> buffer;  |
| 468 | tSystem::Receiver receiver(&buffer);  |
| 469 |   |
| 470 | Process(receiver, format, argList);  |
| 471 | tSystem::tPrint(buffer.GetElements());  |
| 472 | return receiver.GetNumReceived() - 1;  |
| 473 | }  |
| 474 |   |
| 475 |   |
| 476 | int tvPrintf(tSystem::tChannel channels, const char* format, va_list argList)  |
| 477 | {  |
| 478 | if (!format)  |
| 479 | return 0;  |
| 480 |   |
| 481 | tArray<char> buffer;  |
| 482 | tSystem::Receiver receiver(&buffer);  |
| 483 |   |
| 484 | Process(receiver, format, argList);  |
| 485 | tSystem::tPrint(buffer.GetElements(), channels);  |
| 486 | return receiver.GetNumReceived() - 1;  |
| 487 | }  |
| 488 |   |
| 489 |   |
| 490 | int tvsPrintf(char* dest, const char* format, va_list argList)  |
| 491 | {  |
| 492 | if (!dest || !format)  |
| 493 | return 0;  |
| 494 |   |
| 495 | tSystem::Receiver receiver(dest);  |
| 496 | Process(receiver, format, argList);  |
| 497 | return receiver.GetNumReceived() - 1;  |
| 498 | }  |
| 499 |   |
| 500 |   |
| 501 | int tsPrintf(char* dest, const char* format, ...)  |
| 502 | {  |
| 503 | va_list argList;  |
| 504 | va_start(argList, format);  |
| 505 | int count = tvsPrintf(dest, format, argList);  |
| 506 | va_end(argList);  |
| 507 | return count;  |
| 508 | }  |
| 509 |   |
| 510 |   |
| 511 | tString& tvsPrintf(tString& dest, const char* format, va_list argList)  |
| 512 | {  |
| 513 | va_list argList2;  |
| 514 | va_copy(argList2, argList);  |
| 515 |   |
| 516 | int reqChars = tvcPrintf(format, argList);  |
| 517 | dest.Reserve(reqChars);  |
| 518 |   |
| 519 | tvsPrintf(dest.Text(), format, argList2);  |
| 520 | return dest;  |
| 521 | }  |
| 522 |   |
| 523 |   |
| 524 | tString& tsPrintf(tString& dest, const char* format, ...)  |
| 525 | {  |
| 526 | va_list argList;  |
| 527 | va_start(argList, format);  |
| 528 | tvsPrintf(dest, format, argList);  |
| 529 | va_end(argList);  |
| 530 | return dest;  |
| 531 | }  |
| 532 |   |
| 533 |   |
| 534 | int tvsPrintf(char* dest, int destSize, const char* format, va_list argList)  |
| 535 | {  |
| 536 | if (!dest || !format || (destSize <= 0))  |
| 537 | return 0;  |
| 538 |   |
| 539 | if (destSize == 1)  |
| 540 | {  |
| 541 | dest[0] = '\0';  |
| 542 | return 0;  |
| 543 | }  |
| 544 |   |
| 545 | tSystem::Receiver receiver(dest, destSize);  |
| 546 | Process(receiver, format, argList);  |
| 547 |   |
| 548 | // Possibly write a missing terminating 0 if we filled up.  |
| 549 | int rec = receiver.GetNumReceived();  |
| 550 | int len = rec - 1;  |
| 551 | if (destSize == rec)  |
| 552 | dest[len] = '\0';  |
| 553 | return len;  |
| 554 | }  |
| 555 |   |
| 556 |   |
| 557 | int tsPrintf(char* dest, int destSize, const char* format, ...)  |
| 558 | {  |
| 559 | va_list argList;  |
| 560 | va_start(argList, format);  |
| 561 | int count = tvsPrintf(dest, destSize, format, argList);  |
| 562 | va_end(argList);  |
| 563 | return count;  |
| 564 | }  |
| 565 |   |
| 566 |   |
| 567 | int tcPrintf(const char* format, ...)  |
| 568 | {  |
| 569 | va_list argList;  |
| 570 | va_start(argList, format);  |
| 571 | int count = tvcPrintf(format, argList);  |
| 572 | va_end(argList);  |
| 573 | return count;  |
| 574 | }  |
| 575 |   |
| 576 |   |
| 577 | int tvcPrintf(const char* format, va_list argList)  |
| 578 | {  |
| 579 | if (!format)  |
| 580 | return 0;  |
| 581 |   |
| 582 | tSystem::Receiver receiver;  |
| 583 | Process(receiver, format, argList);  |
| 584 | return receiver.GetNumReceived() - 1;  |
| 585 | }  |
| 586 |   |
| 587 |   |
| 588 | int tfPrintf(tFileHandle dest, const char* format, ...)  |
| 589 | {  |
| 590 | va_list argList;  |
| 591 | va_start(argList, format);  |
| 592 | int count = tvfPrintf(dest, format, argList);  |
| 593 | va_end(argList);  |
| 594 | return count;  |
| 595 | }  |
| 596 |   |
| 597 |   |
| 598 | int tvfPrintf(tFileHandle dest, const char* format, va_list argList)  |
| 599 | {  |
| 600 | if (!format || !dest)  |
| 601 | return 0;  |
| 602 |   |
| 603 | tArray<char> buffer;  |
| 604 | tSystem::Receiver receiver(&buffer);  |
| 605 |   |
| 606 | Process(receiver, format, argList);  |
| 607 | tSystem::tPrint(buffer.GetElements(), dest);  |
| 608 | return receiver.GetNumReceived() - 1;  |
| 609 | }  |
| 610 |   |
| 611 |   |
| 612 | int ttfPrintf(tFileHandle dest, const char* format, ...)  |
| 613 | {  |
| 614 | va_list argList;  |
| 615 | va_start(argList, format);  |
| 616 | int count = ttvfPrintf(dest, format, argList);  |
| 617 | va_end(argList);  |
| 618 | return count;  |
| 619 | }  |
| 620 |   |
| 621 |   |
| 622 | int ttvfPrintf(tFileHandle dest, const char* format, va_list argList)  |
| 623 | {  |
| 624 | if (!format || !dest)  |
| 625 | return 0;  |
| 626 |   |
| 627 | tString stamp = tSystem::tConvertTimeToString(tSystem::tGetTimeLocal(), tSystem::tTimeFormat::Short) + " " ;  |
| 628 | int count = tSystem::tPrint(stamp.Pod(), dest);  |
| 629 |   |
| 630 | tArray<char> buffer;  |
| 631 | tSystem::Receiver receiver(&buffer);  |
| 632 |   |
| 633 | Process(receiver, format, argList);  |
| 634 | tSystem::tPrint(buffer.GetElements(), dest);  |
| 635 | return count + receiver.GetNumReceived() - 1;  |
| 636 | }  |
| 637 |   |
| 638 |   |
| 639 | void tFlush(tFileHandle handle)  |
| 640 | {  |
| 641 | fflush(handle);  |
| 642 | }  |
| 643 |   |
| 644 |   |
| 645 | tSystem::HandlerInfo* tSystem::FindHandler(char type)  |
| 646 | {  |
| 647 | if (type == '\0')  |
| 648 | return nullptr;  |
| 649 |   |
| 650 | // First we try to use the jump table.  |
| 651 | int index = HandlerJumpTable[int(type)];  |
| 652 | if ((index != -1) && (index < NumHandlers) && (index >= 0))  |
| 653 | {  |
| 654 | HandlerInfo* h = &HandlerInfos[index];  |
| 655 | tAssert(h);  |
| 656 | if (h->SpecChar == type)  |
| 657 | return h;  |
| 658 | }  |
| 659 |   |
| 660 | // No go? Do a full search.  |
| 661 | for (int i = 0; i < NumHandlers; i++)  |
| 662 | {  |
| 663 | HandlerInfo* h = &HandlerInfos[i];  |
| 664 | tAssert(h);  |
| 665 | if (h->SpecChar == type)  |
| 666 | return h;  |
| 667 | }  |
| 668 |   |
| 669 | return nullptr;  |
| 670 | }  |
| 671 |   |
| 672 |   |
| 673 | bool tSystem::IsValidFormatSpecifierCharacter(char c)  |
| 674 | {  |
| 675 | // Tests for valid character after a %. First we check optional flag characters.  |
| 676 | if ((c == '-') || (c == '+') || (c == ' ') || (c == '0') || (c == '#') || (c == '_') || (c == '\''))  |
| 677 | return true;  |
| 678 |   |
| 679 | // Next test for width and precision.  |
| 680 | if (tStd::tIsdigit(c) || (c == '.') || (c == '*'))  |
| 681 | return true;  |
| 682 |   |
| 683 | // Next check for typesize. We've already checked for the digit part.  |
| 684 | if ((c == ':') || (c == '!') || (c == '|'))  |
| 685 | return true;  |
| 686 |   |
| 687 | // Finally check for type.  |
| 688 | for (int i = 0; i < NumHandlers; i++)  |
| 689 | if (c == HandlerInfos[i].SpecChar)  |
| 690 | return true;  |
| 691 |   |
| 692 | return false;  |
| 693 | }  |
| 694 |   |
| 695 |   |
| 696 | void tSystem::Process(Receiver& receiver, const char* format, va_list argList)  |
| 697 | {  |
| 698 | while (format[0] != '\0')  |
| 699 | {  |
| 700 | if (format[0] != '%')  |
| 701 | {  |
| 702 | // Nothing special. Just receive the character.  |
| 703 | receiver.Receive(format[0]);  |
| 704 | format++;  |
| 705 | }  |
| 706 | else if (!IsValidFormatSpecifierCharacter(format[1]))  |
| 707 | {  |
| 708 | // Invalid character after the % so receive that character. This allows stuff like %% (percent symbol) to work.  |
| 709 | receiver.Receive(format[1]);  |
| 710 | format += 2;  |
| 711 | }  |
| 712 | else  |
| 713 | {  |
| 714 | // Time to process a format specification. Again, it looks like:  |
| 715 | // %[flags][width][.precision][:typesize][!typesize][|typesize]type  |
| 716 | format++;  |
| 717 | FormatSpec spec;  |
| 718 |   |
| 719 | while ((format[0] == '-') || (format[0] == '+') || (format[0] == ' ') || (format[0] == '0') || (format[0] == '_') || (format[0] == '\'') || (format[0] == '#'))  |
| 720 | {  |
| 721 | switch (format[0])  |
| 722 | {  |
| 723 | case '-': spec.Flags |= Flag_LeftJustify; break;  |
| 724 | case '+': spec.Flags |= Flag_ForcePosOrNegSign; break;  |
| 725 | case ' ': spec.Flags |= Flag_SpaceForPosSign; break;  |
| 726 | case '0': spec.Flags |= Flag_LeadingZeros; break;  |
| 727 | case '_': spec.Flags |= Flag_DecorativeFormatting; break;  |
| 728 | case '\'': spec.Flags |= Flag_DecorativeFormattingAlt; break;  |
| 729 | case '#': spec.Flags |= Flag_BasePrefix; break;  |
| 730 | }  |
| 731 | format++;  |
| 732 | }  |
| 733 |   |
| 734 | // From docs: If 0 (leading zeroes) and - (left justify) appear, leading-zeroes is ignored.  |
| 735 | if ((spec.Flags & Flag_LeadingZeros) && (spec.Flags & Flag_LeftJustify))  |
| 736 | spec.Flags &= ~Flag_LeadingZeros;  |
| 737 |   |
| 738 | // Read optional width specification. The '*' means get the value from tha argument list.  |
| 739 | if (format[0] != '*')  |
| 740 | {  |
| 741 | while (tStd::tIsdigit(format[0]))  |
| 742 | {  |
| 743 | spec.Width = spec.Width * 10 + ( format[0] - '0' ) ;  |
| 744 | format++;  |
| 745 | }  |
| 746 | }  |
| 747 | else  |
| 748 | {  |
| 749 | spec.Width = va_arg(argList, int);  |
| 750 | format++;  |
| 751 | }  |
| 752 |   |
| 753 | // Read optional precision specification. The '*' means get the value from the argument list.  |
| 754 | if (format[0] == '.')  |
| 755 | {  |
| 756 | spec.Precision = 0;  |
| 757 | format++;  |
| 758 |   |
| 759 | if (format[0] != '*')  |
| 760 | {  |
| 761 | while (tStd::tIsdigit(format[0]))  |
| 762 | {  |
| 763 | spec.Precision = spec.Precision * 10 + ( format[0] - '0' ) ;  |
| 764 | format++;  |
| 765 | }  |
| 766 | }  |
| 767 | else  |
| 768 | {  |
| 769 | spec.Precision = va_arg(argList, int);  |
| 770 | format++;  |
| 771 | }  |
| 772 | }  |
| 773 |   |
| 774 | // Read optional type size specification. Tacent-specific and cleaner than posix or ansi.  |
| 775 | if ((format[0] == ':') || (format[0] == '!') || (format[0] == '|'))  |
| 776 | {  |
| 777 | char typeUnit = format[0];  |
| 778 | spec.TypeSizeBytes = 0;  |
| 779 | format++;  |
| 780 | while (tStd::tIsdigit(format[0]))  |
| 781 | {  |
| 782 | spec.TypeSizeBytes = spec.TypeSizeBytes * 10 + ( format[0] - '0' ) ;  |
| 783 | format++;  |
| 784 | }  |
| 785 |   |
| 786 | switch (typeUnit)  |
| 787 | {  |
| 788 | case ':': spec.TypeSizeBytes *= 4; break;  |
| 789 | case '|': spec.TypeSizeBytes /= 8; break;  |
| 790 | }  |
| 791 | }  |
| 792 |   |
| 793 | // Format now points to the type character.  |
| 794 | HandlerInfo* handler = FindHandler(*format);  |
| 795 | tAssert(handler);  |
| 796 | if (!spec.TypeSizeBytes)  |
| 797 | spec.TypeSizeBytes = handler->DefaultByteSize;  |
| 798 |   |
| 799 | // Note the type promotions caused by the variadic calling convention,  |
| 800 | // float -> double. char, short, int -> int.  |
| 801 | //  |
| 802 | // GNU:  |
| 803 | // Normal (int, float, enum, etc) types are placed in registers... so you MUST use va_arg to access.  |
| 804 | // Structs and classes must be POD types. The address gets placed in a register.  |
| 805 | // You can access the pointer by casting the va_list. Not portable though.  |
| 806 | //  |
| 807 | // WIN/MSVC:  |
| 808 | // Everything goes on the stack. Casting of the va_list always to gets a pointer to the object.  |
| 809 | //  |
| 810 | // I think for now we'll do the less efficient, but more portable, va_arg method in all cases.  |
| 811 | // It isn't quite as fast cuz it always creates a byte for byte copy. The variables below are holders  |
| 812 | // of the va_arg retrieved data. The holders below must be POD types, specifically no constructor or  |
| 813 | // destructor because on windows we want to ensure that after va_arg does it's byte-wise copy, that  |
| 814 | // the copy (that was not properly constructed) is not destructed.  |
| 815 | struct Val4I { uint32 a; } val4i; // 32 bit integers like int32.  |
| 816 | struct Val4F { float a; } val4f;  |
| 817 | struct Val8I { uint64 a; } val8i; // 64 bit integers like uint64.  |
| 818 | struct Val8F { float a[2]; } val8f; // 64 bit. 2 floats. Like tVec2.  |
| 819 | struct Val8D { double a; } val8d; // 64 bit double.  |
| 820 | struct Val12I { float a[3]; } val12i;  |
| 821 | struct Val12F { float a[3]; } val12f; // 96 bit. 3 floats. Like tVec3.  |
| 822 | struct Val16I { uint32 a[4]; } val16i; // 128 bit integral types. Like tuint128.  |
| 823 | struct Val16F { float a[4]; } val16f; // 128 bit float types. Like tVec4 or tMat2.  |
| 824 | struct Val32I { uint32 a[8]; } val32i; // 256 bit types (like tuint256).  |
| 825 | struct Val32F { float a[8]; } val32f;  |
| 826 | struct Val64I { uint32 a[16]; } val64i; // 512 bit types (like tuint512, and tbit512).  |
| 827 | struct Val64F { float a[16]; } val64f; // 512 bit types (like tMatrix4).  |
| 828 |   |
| 829 | void* pval = nullptr;  |
| 830 | BaseType bt = handler->TypeBase;  |
| 831 | switch (spec.TypeSizeBytes)  |
| 832 | {  |
| 833 | case 0: pval = nullptr; break;  |
| 834 | case 4:  |
| 835 | switch (bt)  |
| 836 | {  |
| 837 | case BaseType::Int: val4i = va_arg(argList, Val4I); pval = &val4i; break;  |
| 838 | case BaseType::Flt: val4f = va_arg(argList, Val4F); pval = &val4f; break;  |
| 839 | } break;  |
| 840 | case 8:  |
| 841 | switch (bt)  |
| 842 | {  |
| 843 | case BaseType::Int: val8i = va_arg(argList, Val8I); pval = &val8i; break;  |
| 844 | case BaseType::Flt: val8f = va_arg(argList, Val8F); pval = &val8f; break;  |
| 845 | case BaseType::Dbl: val8d = va_arg(argList, Val8D); pval = &val8d; break;  |
| 846 | } break;  |
| 847 | case 12:  |
| 848 | switch (bt)  |
| 849 | {  |
| 850 | case BaseType::Int: val12i = va_arg(argList, Val12I); pval = &val12i; break;  |
| 851 | case BaseType::Flt: val12f = va_arg(argList, Val12F); pval = &val12f; break;  |
| 852 | } break;  |
| 853 | case 16:  |
| 854 | switch (bt)  |
| 855 | {  |
| 856 | case BaseType::Int: val16i = va_arg(argList, Val16I); pval = &val16i; break;  |
| 857 | case BaseType::Flt: val16f = va_arg(argList, Val16F); pval = &val16f; break;  |
| 858 | } break;  |
| 859 | case 32:  |
| 860 | switch (bt)  |
| 861 | {  |
| 862 | case BaseType::Int: val32i = va_arg(argList, Val32I); pval = &val32i; break;  |
| 863 | case BaseType::Flt: val32f = va_arg(argList, Val32F); pval = &val32f; break;  |
| 864 | } break;  |
| 865 | case 64:  |
| 866 | switch (bt)  |
| 867 | {  |
| 868 | case BaseType::Int: val64i = va_arg(argList, Val64I); pval = &val64i; break;  |
| 869 | case BaseType::Flt: val64f = va_arg(argList, Val64F); pval = &val64f; break;  |
| 870 | } break;  |
| 871 | }  |
| 872 | tAssertMsg(pval, "Cannot deal with this size print vararg." );  |
| 873 |   |
| 874 | // Here's where the work is done... call the handler.  |
| 875 | (handler->Handler)(receiver, spec, pval);  |
| 876 |   |
| 877 | // We've now processed the whole format specification.  |
| 878 | format++;  |
| 879 | }  |
| 880 | }  |
| 881 |   |
| 882 | // Write the terminating 0.  |
| 883 | receiver.Receive('\0');  |
| 884 | }  |
| 885 |   |
| 886 |   |
| 887 | // Below are all the handlers and their helper functions.  |
| 888 |   |
| 889 |   |
| 890 | void tSystem::HandlerHelper_JustificationProlog(Receiver& receiver, int itemLength, const FormatSpec& spec)  |
| 891 | {  |
| 892 | // Prolog only outputs characters if we are right justifying.  |
| 893 | if (spec.Flags & Flag_LeftJustify)  |
| 894 | return;  |
| 895 |   |
| 896 | // Right justify.  |
| 897 | for (int s = 0; s < (spec.Width - itemLength); s++)  |
| 898 | if (spec.Flags & Flag_LeadingZeros)  |
| 899 | receiver.Receive('0');  |
| 900 | else  |
| 901 | receiver.Receive(' ');  |
| 902 | }  |
| 903 |   |
| 904 |   |
| 905 | void tSystem::HandlerHelper_JustificationEpilog(Receiver& receiver, int itemLength, const FormatSpec& spec)  |
| 906 | {  |
| 907 | // Epilog only outputs characters if we are left justifying.  |
| 908 | if (!(spec.Flags & Flag_LeftJustify))  |
| 909 | return;  |
| 910 |   |
| 911 | // Left justify.  |
| 912 | for (int s = 0; s < (spec.Width - itemLength); s++)  |
| 913 | receiver.Receive(' ');  |
| 914 | }  |
| 915 |   |
| 916 |   |
| 917 | void tSystem::HandlerHelper_IntegerNative  |
| 918 | (  |
| 919 | tArray<char>& convBuf, const FormatSpec& spec, void* data, bool treatAsUnsigned,  |
| 920 | int bitSize, bool upperCase, int base, bool forcePrefixLowerCase  |
| 921 | )  |
| 922 | {  |
| 923 | tAssert((bitSize == 32) || (bitSize == 64));  |
| 924 | uint64 rawValue = (bitSize == 32) ? (*((uint32*)data)) : (*((uint64*)data));  |
| 925 | bool negative = (rawValue >> (bitSize-1)) ? true : false;  |
| 926 | int remWidth = spec.Width;  |
| 927 |   |
| 928 | if (base == 10)  |
| 929 | {  |
| 930 | if (!treatAsUnsigned && negative)  |
| 931 | {  |
| 932 | // Negative values need a - in front. Then we can print the rest as if it were positive.  |
| 933 | rawValue = -( int64(rawValue) );  |
| 934 | convBuf.Append('-');  |
| 935 | remWidth--;  |
| 936 | }  |
| 937 | else if (spec.Flags & Flag_ForcePosOrNegSign)  |
| 938 | {  |
| 939 | convBuf.Append('+');  |
| 940 | remWidth--;  |
| 941 | }  |
| 942 | else if (spec.Flags & Flag_SpaceForPosSign)  |
| 943 | {  |
| 944 | convBuf.Append(' ');  |
| 945 | remWidth--;  |
| 946 | }  |
| 947 | }  |
| 948 |   |
| 949 | if (bitSize == 32)  |
| 950 | rawValue &= 0x00000000FFFFFFFF;  |
| 951 |   |
| 952 | // According to the standard, the # should only cause the prefix to be appended if the value  |
| 953 | // is non-zero. Also, we support a %p pointer type, where we DO want the prefix even for a  |
| 954 | // null pointer... that what forcePrefix is for.  |
| 955 | if (((spec.Flags & Flag_BasePrefix) && rawValue) || forcePrefixLowerCase)  |
| 956 | {  |
| 957 | switch (base)  |
| 958 | {  |
| 959 | case 8:  |
| 960 | convBuf.Append('0');  |
| 961 | remWidth--;  |
| 962 | break;  |
| 963 |   |
| 964 | case 16:  |
| 965 | convBuf.Append((!upperCase || forcePrefixLowerCase) ? "0x" : "0X" , 2);  |
| 966 | remWidth -= 2;  |
| 967 | break;  |
| 968 | }  |
| 969 | }  |
| 970 |   |
| 971 | char baseBiggerThanTenOffsetToLetters = 'a' - '9' - 1;  |
| 972 | if (upperCase)  |
| 973 | baseBiggerThanTenOffsetToLetters = 'A' - '9' - 1;  |
| 974 |   |
| 975 | // According to MS printf docs if 0 is specified with an integer format (i, u, x, X, o, d) and a precision  |
| 976 | // specification is also present (for example, %04.d), the 0 is ignored. Note that default 'precision' for  |
| 977 | // integral types is 1.  |
| 978 | uint32 flags = spec.Flags;  |
| 979 | int precision = spec.Precision;  |
| 980 | if (precision == -1)  |
| 981 | precision = 1;  |
| 982 | else  |
| 983 | flags &= ~Flag_LeadingZeros;  |
| 984 |   |
| 985 | // It needs to be this big to handle 64 bit in binary.  |
| 986 | char buf[128];  |
| 987 | buf[127] = '\0';  |
| 988 | char* curr = &buf[126];  |
| 989 |   |
| 990 | while ((precision-- > 0) || rawValue)  |
| 991 | {  |
| 992 | char digit = char((rawValue % base) + '0');  |
| 993 | rawValue /= base;  |
| 994 | if (digit > '9')  |
| 995 | digit += baseBiggerThanTenOffsetToLetters;  |
| 996 | *curr = digit;  |
| 997 | curr--;  |
| 998 | }  |
| 999 |   |
| 1000 | curr++;  |
| 1001 | if (flags & Flag_LeadingZeros)  |
| 1002 | {  |
| 1003 | int numZeroes = remWidth - tStd::tStrlen(curr);  |
| 1004 | for (int z = 0; z < numZeroes; z++)  |
| 1005 | {  |
| 1006 | curr--;  |
| 1007 | *curr = '0';  |
| 1008 | }  |
| 1009 | }  |
| 1010 |   |
| 1011 | if (flags & Flag_DecorativeFormatting)  |
| 1012 | {  |
| 1013 | int len = tStd::tStrlen(curr);  |
| 1014 | int mod = 4 - (len % 4);  |
| 1015 | for (int i = 0; i < len; i++)  |
| 1016 | {  |
| 1017 | convBuf.Append(curr[i]);  |
| 1018 | if (!(++mod % 4) && (i != (len-1)))  |
| 1019 | convBuf.Append('_');  |
| 1020 | }  |
| 1021 | }  |
| 1022 | else if (flags & Flag_DecorativeFormattingAlt)  |
| 1023 | {  |
| 1024 | int len = tStd::tStrlen(curr);  |
| 1025 | int mod = 3 - (len % 3);  |
| 1026 | for (int i = 0; i < len; i++)  |
| 1027 | {  |
| 1028 | convBuf.Append(curr[i]);  |
| 1029 | if (!(++mod % 3) && (i != (len-1)))  |
| 1030 | convBuf.Append(',');  |
| 1031 | }  |
| 1032 | }  |
| 1033 | else  |
| 1034 | {  |
| 1035 | convBuf.Append(curr, tStd::tStrlen(curr));  |
| 1036 | }  |
| 1037 | }  |
| 1038 |   |
| 1039 |   |
| 1040 | void tSystem::HandlerHelper_IntegerTacent  |
| 1041 | (  |
| 1042 | tArray<char>& convBuf, const FormatSpec& spec, void* data, bool treatAsUnsigned,  |
| 1043 | int bitSize, bool upperCase, int base, bool forcePrefixLowerCase  |
| 1044 | )  |
| 1045 | {  |
| 1046 | tAssert((bitSize == 128) || (bitSize == 256) || (bitSize == 512));  |
| 1047 | tuint512 rawValue;  |
| 1048 | if (bitSize == 128)  |
| 1049 | rawValue = *((tuint128*)data);  |
| 1050 | else if (bitSize == 256)  |
| 1051 | rawValue = *((tuint256*)data);  |
| 1052 | else  |
| 1053 | rawValue = *((tuint512*)data);  |
| 1054 |   |
| 1055 | bool negative = (rawValue >> (bitSize-1)) ? true : false;  |
| 1056 | int remWidth = spec.Width;  |
| 1057 |   |
| 1058 | if (base == 10)  |
| 1059 | {  |
| 1060 | if (!treatAsUnsigned && negative)  |
| 1061 | {  |
| 1062 | // Negative values need a - in front. Then we can print the rest as if it were positive.  |
| 1063 | rawValue = -( tint512(rawValue) );  |
| 1064 | convBuf.Append('-');  |
| 1065 | remWidth--;  |
| 1066 | }  |
| 1067 | else if (spec.Flags & Flag_ForcePosOrNegSign)  |
| 1068 | {  |
| 1069 | convBuf.Append('+');  |
| 1070 | remWidth--;  |
| 1071 | }  |
| 1072 | else if (spec.Flags & Flag_SpaceForPosSign)  |
| 1073 | {  |
| 1074 | convBuf.Append(' ');  |
| 1075 | remWidth--;  |
| 1076 | }  |
| 1077 | }  |
| 1078 |   |
| 1079 | if (bitSize == 128)  |
| 1080 | rawValue &= tuint512("0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" );  |
| 1081 | if (bitSize == 256)  |
| 1082 | rawValue &= tuint512("0x0000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" );  |
| 1083 |   |
| 1084 | // According to the standard, the # should only cause the prefix to be appended if the value  |
| 1085 | // is non-zero. Also, we support a %p pointer type, where we DO want the prefix even for a  |
| 1086 | // null pointer... that is what forcePrefix is for.  |
| 1087 | if (((spec.Flags & Flag_BasePrefix) && rawValue) || forcePrefixLowerCase)  |
| 1088 | {  |
| 1089 | switch (base)  |
| 1090 | {  |
| 1091 | case 8:  |
| 1092 | convBuf.Append('0');  |
| 1093 | remWidth--;  |
| 1094 | break;  |
| 1095 |   |
| 1096 | case 16:  |
| 1097 | convBuf.Append((!upperCase || forcePrefixLowerCase) ? "0x" : "0X" , 2);  |
| 1098 | remWidth -= 2;  |
| 1099 | break;  |
| 1100 | }  |
| 1101 | }  |
| 1102 |   |
| 1103 | char baseBiggerThanTenOffsetToLetters = 'a' - '9' - 1;  |
| 1104 | if (upperCase)  |
| 1105 | baseBiggerThanTenOffsetToLetters = 'A' - '9' - 1;  |
| 1106 |   |
| 1107 | uint32 flags = spec.Flags;  |
| 1108 | int precision = spec.Precision;  |
| 1109 | if (precision == -1)  |
| 1110 | precision = 1;  |
| 1111 | else  |
| 1112 | flags &= ~Flag_LeadingZeros;  |
| 1113 |   |
| 1114 | // It needs to be big enough to handle a 512 bit integer as a string in binary.  |
| 1115 | char buf[1024];  |
| 1116 | buf[1023] = '\0';  |
| 1117 | char* curr = &buf[1022];  |
| 1118 |   |
| 1119 | while ((precision-- > 0) || rawValue)  |
| 1120 | {  |
| 1121 | int modVal = rawValue % base;  |
| 1122 | char digit = char(modVal + '0');  |
| 1123 | rawValue /= base;  |
| 1124 | if (digit > '9')  |
| 1125 | digit += baseBiggerThanTenOffsetToLetters;  |
| 1126 | *curr = digit;  |
| 1127 | curr--;  |
| 1128 | }  |
| 1129 |   |
| 1130 | curr++;  |
| 1131 | if (flags & Flag_LeadingZeros)  |
| 1132 | {  |
| 1133 | int numZeroes = remWidth - tStd::tStrlen(curr);  |
| 1134 | for (int z = 0; z < numZeroes; z++)  |
| 1135 | {  |
| 1136 | curr--;  |
| 1137 | *curr = '0';  |
| 1138 | }  |
| 1139 | }  |
| 1140 |   |
| 1141 | if (flags & Flag_DecorativeFormatting)  |
| 1142 | {  |
| 1143 | int len = tStd::tStrlen(curr);  |
| 1144 | int mod = 8 - (len % 8);  |
| 1145 | for (int i = 0; i < len; i++)  |
| 1146 | {  |
| 1147 | convBuf.Append(curr[i]);  |
| 1148 | if ((!(++mod % 8)) && (i != (len-1)))  |
| 1149 | convBuf.Append('_');  |
| 1150 | }  |
| 1151 | }  |
| 1152 | else if (flags & Flag_DecorativeFormattingAlt)  |
| 1153 | {  |
| 1154 | int len = tStd::tStrlen(curr);  |
| 1155 | int mod = 3 - (len % 3);  |
| 1156 | for (int i = 0; i < len; i++)  |
| 1157 | {  |
| 1158 | convBuf.Append(curr[i]);  |
| 1159 | if ((!(++mod % 3)) && (i != (len-1)))  |
| 1160 | convBuf.Append(',');  |
| 1161 | }  |
| 1162 | }  |
| 1163 | else  |
| 1164 | {  |
| 1165 | convBuf.Append(curr, tStd::tStrlen(curr));  |
| 1166 | }  |
| 1167 | }  |
| 1168 |   |
| 1169 |   |
| 1170 | void tSystem::Handler_b(Receiver& receiver, const FormatSpec& spec, void* data)  |
| 1171 | {  |
| 1172 | bool treatAsUnsigned = true;  |
| 1173 | int bitSize = spec.TypeSizeBytes*8;  |
| 1174 | bool upperCase = false;  |
| 1175 | int base = 2;  |
| 1176 |   |
| 1177 | bool nativeInt = ((bitSize == 32) || (bitSize == 64));  |
| 1178 | bool tacentInt = ((bitSize == 128) || (bitSize == 256) || (bitSize == 512));  |
| 1179 | tAssert(nativeInt || tacentInt);  |
| 1180 |   |
| 1181 | // Tacent integers are quite a bit bigger and will require more buffer space. Enough for 512 binary digits.  |
| 1182 | // Native: max 64 (*2) = 128. Tacent max 512 (*2) = 1024. Or, if the native needed X bytes (for 64bit number)  |
| 1183 | // then the tacent type will need 8 times that (4*X) cuz it's 512 bits.  |
| 1184 | int bufSize = nativeInt ? 128 : 1024;  |
| 1185 | tArray<char> convInt(bufSize, 0);  |
| 1186 | if (nativeInt)  |
| 1187 | HandlerHelper_IntegerNative(convInt, spec, data, treatAsUnsigned, bitSize, upperCase, base);  |
| 1188 | else  |
| 1189 | HandlerHelper_IntegerTacent(convInt, spec, data, treatAsUnsigned, bitSize, upperCase, base);  |
| 1190 |   |
| 1191 | HandlerHelper_JustificationProlog(receiver, convInt.GetNumAppendedElements(), spec);  |
| 1192 | receiver.Receive(convInt);  |
| 1193 | HandlerHelper_JustificationEpilog(receiver, convInt.GetNumAppendedElements(), spec);  |
| 1194 | }  |
| 1195 |   |
| 1196 |   |
| 1197 | void tSystem::Handler_o(Receiver& receiver, const FormatSpec& spec, void* data)  |
| 1198 | {  |
| 1199 | bool treatAsUnsigned = true;  |
| 1200 | int bitSize = spec.TypeSizeBytes*8;  |
| 1201 | bool upperCase = false;  |
| 1202 | int base = 8;  |
| 1203 |   |
| 1204 | bool nativeInt = ((bitSize == 32) || (bitSize == 64));  |
| 1205 | bool tacentInt = ((bitSize == 128) || (bitSize == 256) || (bitSize == 512));  |
| 1206 | tAssert(nativeInt || tacentInt);  |
| 1207 |   |
| 1208 | int bufSize = nativeInt ? 64 : 512;  |
| 1209 | tArray<char> convInt(bufSize, 0);  |
| 1210 | if (nativeInt)  |
| 1211 | HandlerHelper_IntegerNative(convInt, spec, data, treatAsUnsigned, bitSize, upperCase, base);  |
| 1212 | else  |
| 1213 | HandlerHelper_IntegerTacent(convInt, spec, data, treatAsUnsigned, bitSize, upperCase, base);  |
| 1214 |   |
| 1215 | HandlerHelper_JustificationProlog(receiver, convInt.GetNumAppendedElements(), spec);  |
| 1216 | receiver.Receive(convInt);  |
| 1217 | HandlerHelper_JustificationEpilog(receiver, convInt.GetNumAppendedElements(), spec);  |
| 1218 | }  |
| 1219 |   |
| 1220 |   |
| 1221 | void tSystem::Handler_d(Receiver& receiver, const FormatSpec& spec, void* data)  |
| 1222 | {  |
| 1223 | bool treatAsUnsigned = false;  |
| 1224 | int bitSize = spec.TypeSizeBytes*8;  |
| 1225 | bool upperCase = false;  |
| 1226 | int base = 10;  |
| 1227 |   |
| 1228 | bool nativeInt = ((bitSize == 32) || (bitSize == 64));  |
| 1229 | bool tacentInt = ((bitSize == 128) || (bitSize == 256) || (bitSize == 512));  |
| 1230 | tAssert(nativeInt || tacentInt);  |
| 1231 |   |
| 1232 | int bufSize = nativeInt ? 64 : 512;  |
| 1233 | tArray<char> convInt(bufSize, 0);  |
| 1234 | if (nativeInt)  |
| 1235 | HandlerHelper_IntegerNative(convInt, spec, data, treatAsUnsigned, bitSize, upperCase, base);  |
| 1236 | else  |
| 1237 | HandlerHelper_IntegerTacent(convInt, spec, data, treatAsUnsigned, bitSize, upperCase, base);  |
| 1238 |   |
| 1239 | HandlerHelper_JustificationProlog(receiver, convInt.GetNumAppendedElements(), spec);  |
| 1240 | receiver.Receive(convInt);  |
| 1241 | HandlerHelper_JustificationEpilog(receiver, convInt.GetNumAppendedElements(), spec);  |
| 1242 | }  |
| 1243 |   |
| 1244 |   |
| 1245 | void tSystem::Handler_i(Receiver& receiver, const FormatSpec& spec, void* data)  |
| 1246 | {  |
| 1247 | bool treatAsUnsigned = false;  |
| 1248 | int bitSize = spec.TypeSizeBytes*8;  |
| 1249 | bool upperCase = false;  |
| 1250 | int base = 10;  |
| 1251 |   |
| 1252 | bool nativeInt = ((bitSize == 32) || (bitSize == 64));  |
| 1253 | bool tacentInt = ((bitSize == 128) || (bitSize == 256) || (bitSize == 512));  |
| 1254 | tAssert(nativeInt || tacentInt);  |
| 1255 |   |
| 1256 | int bufSize = nativeInt ? 64 : 512;  |
| 1257 | tArray<char> convInt(bufSize, 0);  |
| 1258 | if (nativeInt)  |
| 1259 | HandlerHelper_IntegerNative(convInt, spec, data, treatAsUnsigned, bitSize, upperCase, base);  |
| 1260 | else  |
| 1261 | HandlerHelper_IntegerTacent(convInt, spec, data, treatAsUnsigned, bitSize, upperCase, base);  |
| 1262 |   |
| 1263 | HandlerHelper_JustificationProlog(receiver, convInt.GetNumAppendedElements(), spec);  |
| 1264 | receiver.Receive(convInt);  |
| 1265 | HandlerHelper_JustificationEpilog(receiver, convInt.GetNumAppendedElements(), spec);  |
| 1266 | }  |
| 1267 |   |
| 1268 |   |
| 1269 | void tSystem::Handler_u(Receiver& receiver, const FormatSpec& spec, void* data)  |
| 1270 | {  |
| 1271 | bool treatAsUnsigned = true;  |
| 1272 | int bitSize = spec.TypeSizeBytes*8;  |
| 1273 | bool upperCase = false;  |
| 1274 | int base = 10;  |
| 1275 |   |
| 1276 | bool nativeInt = ((bitSize == 32) || (bitSize == 64));  |
| 1277 | bool tacentInt = ((bitSize == 128) || (bitSize == 256) || (bitSize == 512));  |
| 1278 | tAssert(nativeInt || tacentInt);  |
| 1279 |   |
| 1280 | int bufSize = nativeInt ? 64 : 512;  |
| 1281 | tArray<char> convInt(bufSize, 0);  |
| 1282 | if (nativeInt)  |
| 1283 | HandlerHelper_IntegerNative(convInt, spec, data, treatAsUnsigned, bitSize, upperCase, base);  |
| 1284 | else  |
| 1285 | HandlerHelper_IntegerTacent(convInt, spec, data, treatAsUnsigned, bitSize, upperCase, base);  |
| 1286 |   |
| 1287 | HandlerHelper_JustificationProlog(receiver, convInt.GetNumAppendedElements(), spec);  |
| 1288 | receiver.Receive(convInt);  |
| 1289 | HandlerHelper_JustificationEpilog(receiver, convInt.GetNumAppendedElements(), spec);  |
| 1290 | }  |
| 1291 |   |
| 1292 |   |
| 1293 | void tSystem::Handler_x(Receiver& receiver, const FormatSpec& spec, void* data)  |
| 1294 | {  |
| 1295 | bool treatAsUnsigned = true;  |
| 1296 | int bitSize = spec.TypeSizeBytes*8;  |
| 1297 | bool upperCase = false;  |
| 1298 | int base = 16;  |
| 1299 |   |
| 1300 | bool nativeInt = ((bitSize == 32) || (bitSize == 64));  |
| 1301 | bool tacentInt = ((bitSize == 128) || (bitSize == 256) || (bitSize == 512));  |
| 1302 | tAssert(nativeInt || tacentInt);  |
| 1303 |   |
| 1304 | int bufSize = nativeInt ? 64 : 512;  |
| 1305 | tArray<char> convInt(bufSize, 0);  |
| 1306 | if (nativeInt)  |
| 1307 | HandlerHelper_IntegerNative(convInt, spec, data, treatAsUnsigned, bitSize, upperCase, base);  |
| 1308 | else  |
| 1309 | HandlerHelper_IntegerTacent(convInt, spec, data, treatAsUnsigned, bitSize, upperCase, base);  |
| 1310 |   |
| 1311 | HandlerHelper_JustificationProlog(receiver, convInt.GetNumAppendedElements(), spec);  |
| 1312 | receiver.Receive(convInt);  |
| 1313 | HandlerHelper_JustificationEpilog(receiver, convInt.GetNumAppendedElements(), spec);  |
| 1314 | }  |
| 1315 |   |
| 1316 |   |
| 1317 | void tSystem::Handler_X(Receiver& receiver, const FormatSpec& spec, void* data)  |
| 1318 | {  |
| 1319 | bool treatAsUnsigned = true;  |
| 1320 | int bitSize = spec.TypeSizeBytes*8;  |
| 1321 | bool upperCase = true;  |
| 1322 | int base = 16;  |
| 1323 |   |
| 1324 | bool nativeInt = ((bitSize == 32) || (bitSize == 64));  |
| 1325 | bool tacentInt = ((bitSize == 128) || (bitSize == 256) || (bitSize == 512));  |
| 1326 | tAssert(nativeInt || tacentInt);  |
| 1327 |   |
| 1328 | int bufSize = nativeInt ? 64 : 512;  |
| 1329 | tArray<char> convInt(bufSize, 0);  |
| 1330 | if (nativeInt)  |
| 1331 | HandlerHelper_IntegerNative(convInt, spec, data, treatAsUnsigned, bitSize, upperCase, base);  |
| 1332 | else  |
| 1333 | HandlerHelper_IntegerTacent(convInt, spec, data, treatAsUnsigned, bitSize, upperCase, base);  |
| 1334 |   |
| 1335 | HandlerHelper_JustificationProlog(receiver, convInt.GetNumAppendedElements(), spec);  |
| 1336 | receiver.Receive(convInt);  |
| 1337 | HandlerHelper_JustificationEpilog(receiver, convInt.GetNumAppendedElements(), spec);  |
| 1338 | }  |
| 1339 |   |
| 1340 |   |
| 1341 | void tSystem::Handler_p(Receiver& receiver, const FormatSpec& spec, void* data)  |
| 1342 | {  |
| 1343 | FormatSpec pspec = spec;  |
| 1344 | pspec.Flags |= Flag_LeadingZeros;  |
| 1345 | if (!spec.Width)  |
| 1346 | pspec.Width = 2 + 2*spec.TypeSizeBytes;  |
| 1347 | bool treatAsUnsigned = true;  |
| 1348 | int bitSize = spec.TypeSizeBytes*8;  |
| 1349 | bool upperCase = true;  |
| 1350 | int base = 16;  |
| 1351 | bool forcePrefixLowerCase = true;  |
| 1352 | tArray<char> convInt(64, 0);  |
| 1353 | HandlerHelper_IntegerNative(convInt, pspec, data, treatAsUnsigned, bitSize, upperCase, base, forcePrefixLowerCase);  |
| 1354 |   |
| 1355 | HandlerHelper_JustificationProlog(receiver, convInt.GetNumAppendedElements(), pspec);  |
| 1356 | receiver.Receive(convInt);  |
| 1357 | HandlerHelper_JustificationEpilog(receiver, convInt.GetNumAppendedElements(), pspec);  |
| 1358 | }  |
| 1359 |   |
| 1360 |   |
| 1361 | int tSystem::HandlerHelper_FloatComputeExponent(double value)  |
| 1362 | {  |
| 1363 | int exponent = 0;  |
| 1364 | value = (value < 0.0) ? -value : value;  |
| 1365 | if (value >= 10.0)  |
| 1366 | {  |
| 1367 | while (value >= 10.0)  |
| 1368 | {  |
| 1369 | value /= 10.0;  |
| 1370 | exponent++;  |
| 1371 | }  |
| 1372 | }  |
| 1373 | else if (value < 1.0)  |
| 1374 | {  |
| 1375 | int digit = int(value);  |
| 1376 | while (value && !digit)  |
| 1377 | {  |
| 1378 | value *= 10.0;  |
| 1379 | exponent--;  |
| 1380 | digit = int(value);  |
| 1381 | }  |
| 1382 | }  |
| 1383 |   |
| 1384 | return exponent;  |
| 1385 | }  |
| 1386 |   |
| 1387 |   |
| 1388 | bool tSystem::HandlerHelper_HandleSpecialFloatTypes(tArray<char>& convBuf, double value)  |
| 1389 | {  |
| 1390 | tStd::tFloatType ft = tStd::tGetFloatType(value);  |
| 1391 | switch (ft)  |
| 1392 | {  |
| 1393 | case tStd::tFloatType::PQNAN: convBuf.Append("nan" , 3); return true;  |
| 1394 | case tStd::tFloatType::NQNAN: convBuf.Append("-nan" , 4); return true;  |
| 1395 |   |
| 1396 | #if defined(PLATFORM_WINDOWS)  |
| 1397 | case tStd::tFloatType::PSNAN: convBuf.Append("nan(snan)" , 9); return true;  |
| 1398 | case tStd::tFloatType::NSNAN: convBuf.Append("-nan(snan)" , 10); return true;  |
| 1399 | case tStd::tFloatType::IQNAN: convBuf.Append("-nan(ind)" , 9); return true;  |
| 1400 | #elif defined(PLATFORM_LINUX)  |
| 1401 | case tStd::tFloatType::PSNAN: convBuf.Append("nan" , 3); return true;  |
| 1402 | case tStd::tFloatType::NSNAN: convBuf.Append("-nan" , 4); return true;  |
| 1403 | case tStd::tFloatType::IQNAN: convBuf.Append("-nan" , 4); return true;  |
| 1404 | #endif  |
| 1405 |   |
| 1406 | case tStd::tFloatType::PINF: convBuf.Append("inf" , 3); return true;  |
| 1407 | case tStd::tFloatType::NINF: convBuf.Append("-inf" , 4); return true;  |
| 1408 | default:  |
| 1409 | case tStd::tFloatType::NORM:  |
| 1410 | break;  |
| 1411 | }  |
| 1412 |   |
| 1413 | return false;  |
| 1414 | }  |
| 1415 |   |
| 1416 |   |
| 1417 | void tSystem::Handler_e(Receiver& receiver, const FormatSpec& spec, void* data)  |
| 1418 | {  |
| 1419 | // Variable argument specifies data should be treated data as double. i.e. %f is 64 bits.  |
| 1420 | double v = *((double*)data);  |
| 1421 |   |
| 1422 | // Check for early exit infinities and NANs.  |
| 1423 | tArray<char> convBuf(64, 32);  |
| 1424 | if (HandlerHelper_HandleSpecialFloatTypes(convBuf, v))  |
| 1425 | {  |
| 1426 | receiver.Receive(convBuf);  |
| 1427 | return;  |
| 1428 | }  |
| 1429 |   |
| 1430 | // @todo Fix this like the Handler_f was fixed so it can handle appending directly into appending to the dynamically growing convBuf.  |
| 1431 | char result[64];  |
| 1432 | const int maxLeadingZeroes = 16;  |
| 1433 | char* curr = result + maxLeadingZeroes;  |
| 1434 | bool negative = false;  |
| 1435 |   |
| 1436 | if (v < 0.0f)  |
| 1437 | {  |
| 1438 | v = -v;  |
| 1439 | negative = true;  |
| 1440 | }  |
| 1441 |   |
| 1442 | double val = double(v);  |
| 1443 | int exponent = HandlerHelper_FloatComputeExponent(val);  |
| 1444 |   |
| 1445 | // Convert val so it is a single non-zero digit before the decimal point.  |
| 1446 | double power10 = 1.0;  |
| 1447 | int absExp = (exponent < 0) ? -exponent : exponent;  |
| 1448 | for (int e = 0; e < absExp; e++)  |
| 1449 | power10 *= 10.0;  |
| 1450 |   |
| 1451 | if (exponent != 0)  |
| 1452 | val = (exponent < 0) ? (val * power10) : (val / power10);  |
| 1453 |   |
| 1454 | // Sometimes errors can cause 9.999999 -> 10.0.  |
| 1455 | while (val >= 10.0)  |
| 1456 | {  |
| 1457 | val /= 10.0;  |
| 1458 | exponent++;  |
| 1459 | }  |
| 1460 |   |
| 1461 | // Default floating point printf precision. ANSI is 6, ours is 4.  |
| 1462 | int precision = spec.Precision;  |
| 1463 | if (precision == -1)  |
| 1464 | precision = DefaultPrecision;  |
| 1465 |   |
| 1466 | power10 = 1.0;  |
| 1467 | for (int e = 0; e < precision; e++)  |
| 1468 | power10 *= 10.0;  |
| 1469 | double precisionRound = 0.5 / power10;  |
| 1470 | val += precisionRound;  |
| 1471 |   |
| 1472 | bool firstDigit = true;  |
| 1473 | while (precision)  |
| 1474 | {  |
| 1475 | int digit = int(val);  |
| 1476 | val -= digit;  |
| 1477 | val *= 10.0;  |
| 1478 | *curr++ = '0' + digit;  |
| 1479 | if (firstDigit)  |
| 1480 | *curr++ = '.';  |
| 1481 | else  |
| 1482 | precision--;  |
| 1483 |   |
| 1484 | firstDigit = false;  |
| 1485 | }  |
| 1486 |   |
| 1487 | *curr++ = 'e'; // Need to pass in an uppercase boolean.  |
| 1488 | if (exponent >= 0)  |
| 1489 | {  |
| 1490 | *curr++ = '+';  |
| 1491 | }  |
| 1492 | else  |
| 1493 | {  |
| 1494 | *curr++ = '-';  |
| 1495 | exponent = -exponent;  |
| 1496 | }  |
| 1497 |   |
| 1498 | // @todo Make width here controllable by opt display flag.  |
| 1499 | const int expWidthMax = 3;  |
| 1500 |   |
| 1501 | // First we need to write the exponent characters into a temp buffer backwards. This is so we have to whole thing  |
| 1502 | // before we don't process leading zeroes.  |
| 1503 | int expBuf[expWidthMax] = { 0, 0, 0 };  |
| 1504 | for (int n = expWidthMax-1; n >= 0; n--)  |
| 1505 | {  |
| 1506 | int digit = exponent % 10;  |
| 1507 | exponent /= 10;  |
| 1508 | expBuf[n] = digit;  |
| 1509 | }  |
| 1510 |   |
| 1511 | // We always include the last two least-significant digits of the base 10 exponent, even if they are both zeroes.  |
| 1512 | // We only include the first digit if it is non-zero. This can only happen with doubles, not floats which max at 38.  |
| 1513 | if (expBuf[0] != 0)  |
| 1514 | *curr++ = '0' + expBuf[0];  |
| 1515 | *curr++ = '0' + expBuf[1];  |
| 1516 | *curr++ = '0' + expBuf[2];  |
| 1517 | *curr++ = '\0';  |
| 1518 |   |
| 1519 | // If there are no leading zeroes any possible plus or negative sign must go beside the first valid character of the  |
| 1520 | // converted string. However, if there ARE leading zeroes, we still need to place the plus or negative based on the  |
| 1521 | // width.  |
| 1522 | curr = result + maxLeadingZeroes;  |
| 1523 | if (!(spec.Flags & Flag_LeadingZeros))  |
| 1524 | {  |
| 1525 | if (negative)  |
| 1526 | *--curr = '-';  |
| 1527 | else if (spec.Flags & Flag_ForcePosOrNegSign)  |
| 1528 | *--curr = '+';  |
| 1529 | else if (!negative && (spec.Flags & Flag_SpaceForPosSign))  |
| 1530 | *--curr = ' ';  |
| 1531 | }  |
| 1532 | else  |
| 1533 | {  |
| 1534 | int numZeroes = spec.Width - tStd::tStrlen(curr);  |
| 1535 | if (numZeroes > maxLeadingZeroes)  |
| 1536 | numZeroes = maxLeadingZeroes;  |
| 1537 | while (numZeroes-- > 0)  |
| 1538 | *--curr = '0';  |
| 1539 |   |
| 1540 | if (negative)  |
| 1541 | *curr = '-';  |
| 1542 | else if (spec.Flags & Flag_ForcePosOrNegSign)  |
| 1543 | *curr = '+';  |
| 1544 | }  |
| 1545 |   |
| 1546 | receiver.Receive(curr, tStd::tStrlen(curr));  |
| 1547 | }  |
| 1548 |   |
| 1549 |   |
| 1550 | tSystem::PrologHelperFloat tSystem::HandlerHelper_FloatNormal(tArray<char>& convBuf, const FormatSpec& spec, double value, bool treatPrecisionAsSigDigits)  |
| 1551 | {  |
| 1552 | tArray<char> buf(64, 32);  |
| 1553 | buf.Append('0');  |
| 1554 |   |
| 1555 | // Default floating point printf precision. ANSI is 6, ours is 4.  |
| 1556 | int precision = spec.Precision;  |
| 1557 | if (precision == -1)  |
| 1558 | precision = DefaultPrecision;  |
| 1559 |   |
| 1560 | bool wasNeg = (value < 0.0f) ? true : false;  |
| 1561 | if (value < 0.0f)  |
| 1562 | value = -value;  |
| 1563 |   |
| 1564 | // We always need to use a minus sign if val was negative.  |
| 1565 | PrologHelperFloat ret = PrologHelperFloat::None;  |
| 1566 | if (wasNeg)  |
| 1567 | ret = PrologHelperFloat::NeedsNeg;  |
| 1568 | else if (spec.Flags & Flag_ForcePosOrNegSign)  |
| 1569 | ret = PrologHelperFloat::NeedsPlus;  |
| 1570 | else if (spec.Flags & Flag_SpaceForPosSign)  |
| 1571 | ret = PrologHelperFloat::NeedsSpace;  |
| 1572 |   |
| 1573 | double dec = 1.0;  |
| 1574 | while (dec < value)  |
| 1575 | dec *= 10.0;  |
| 1576 |   |
| 1577 | if (dec > value)  |
| 1578 | dec /= 10.0;  |
| 1579 |   |
| 1580 | // Is there a mantissa?  |
| 1581 | bool hasMantissa = false;  |
| 1582 | while (dec >= 1.0)  |
| 1583 | {  |
| 1584 | char digit = char(value / dec);  |
| 1585 | value -= digit * dec;  |
| 1586 | buf.Append(digit + '0');  |
| 1587 | if (treatPrecisionAsSigDigits && (precision > 0))  |
| 1588 | precision--;  |
| 1589 | dec /= 10.0;  |
| 1590 | hasMantissa = true;  |
| 1591 | }  |
| 1592 |   |
| 1593 | // No mantissa means use a 0 instead.  |
| 1594 | if (!hasMantissa)  |
| 1595 | buf.Append('0');  |
| 1596 |   |
| 1597 | if (precision > 0)  |
| 1598 | buf.Append('.');  |
| 1599 |   |
| 1600 | // We're now after the decimal point... how far we go depends on precision.  |
| 1601 | while (precision--)  |
| 1602 | {  |
| 1603 | value *= 10.0;  |
| 1604 | char digit = char(value);  |
| 1605 |   |
| 1606 | value -= digit;  |
| 1607 | dec += digit;  |
| 1608 | buf.Append(digit + '0');  |
| 1609 | }  |
| 1610 |   |
| 1611 | bool useIdxZeroForResult = false;  |
| 1612 | if ((value * 10.0) >= 5.0)  |
| 1613 | {  |
| 1614 | // Round. We need to start at the end and work BACKWARDS to the left.  |
| 1615 | // We gave already reserved a character at the beginning of the buffer for a possible carry.  |
| 1616 | char* end = buf.GetElements() + buf.GetNumAppendedElements() - 1;  |
| 1617 | while (1)  |
| 1618 | {  |
| 1619 | if (*end == '9')  |
| 1620 | {  |
| 1621 | *end = '0';  |
| 1622 | }  |
| 1623 | else if (*end == '.')  |
| 1624 | {  |
| 1625 | end--;  |
| 1626 | continue;  |
| 1627 | }  |
| 1628 | else  |
| 1629 | {  |
| 1630 | break;  |
| 1631 | }  |
| 1632 |   |
| 1633 | end--;  |
| 1634 | }  |
| 1635 |   |
| 1636 | // Write to the buffer.  |
| 1637 | (*end)++ ;  |
| 1638 |   |
| 1639 | // The first character of buf was reserved just for this.  |
| 1640 | if (end == &buf[0])  |
| 1641 | useIdxZeroForResult = true;  |
| 1642 | }  |
| 1643 |   |
| 1644 | buf.Append('\0');  |
| 1645 | char* result = &buf[1];  |
| 1646 | if (useIdxZeroForResult)  |
| 1647 | result = &buf[0];  |
| 1648 |   |
| 1649 | // This is tricky. If there are no leading zeroes any possible plus or negative sign must go beside  |
| 1650 | // the first valid character of the converted string. However, if there ARE leading zeroes, we still  |
| 1651 | // need to place the plus or negative based on the width, which is done outside this helper.  |
| 1652 | if (!(spec.Flags & Flag_LeadingZeros))  |
| 1653 | {  |
| 1654 | if (ret == PrologHelperFloat::NeedsNeg)  |
| 1655 | {  |
| 1656 | convBuf.Append('-');  |
| 1657 | ret = PrologHelperFloat::None;  |
| 1658 | }  |
| 1659 | else if (ret == PrologHelperFloat::NeedsPlus)  |
| 1660 | {  |
| 1661 | convBuf.Append('+');  |
| 1662 | ret = PrologHelperFloat::None;  |
| 1663 | }  |
| 1664 | }  |
| 1665 |   |
| 1666 | convBuf.Append(result, tStd::tStrlen(result));  |
| 1667 | return ret;  |
| 1668 | }  |
| 1669 |   |
| 1670 |   |
| 1671 | void tSystem::Handler_f(Receiver& receiver, const FormatSpec& spec, void* data)  |
| 1672 | {  |
| 1673 | // Variable arg rules say you must treat the data as double. It converts automatically. That's why %f is always 64 bits.  |
| 1674 | double value = *((double*)data);  |
| 1675 | tArray<char> convFloat(64, 32);  |
| 1676 |   |
| 1677 | // Check for early exit infinities and NANs.  |
| 1678 | PrologHelperFloat res = PrologHelperFloat::None;  |
| 1679 | if (HandlerHelper_HandleSpecialFloatTypes(convFloat, value))  |
| 1680 | res = PrologHelperFloat::NoZeros;  |
| 1681 | else  |
| 1682 | res = HandlerHelper_FloatNormal(convFloat, spec, value);  |
| 1683 |   |
| 1684 | FormatSpec modSpec(spec);  |
| 1685 | int effectiveLength = convFloat.GetNumAppendedElements();  |
| 1686 | switch (res)  |
| 1687 | {  |
| 1688 | case PrologHelperFloat::NeedsNeg: receiver.Receive('-'); effectiveLength++; break;  |
| 1689 | case PrologHelperFloat::NeedsPlus: receiver.Receive('+'); effectiveLength++; break;  |
| 1690 | case PrologHelperFloat::NeedsSpace: receiver.Receive(' '); effectiveLength++; break;  |
| 1691 | case PrologHelperFloat::NoZeros: modSpec.Flags &= ~Flag_LeadingZeros; break;  |
| 1692 | case PrologHelperFloat::None: break;  |
| 1693 | }  |
| 1694 |   |
| 1695 | HandlerHelper_JustificationProlog(receiver, effectiveLength, modSpec);  |
| 1696 | receiver.Receive(convFloat);  |
| 1697 | HandlerHelper_JustificationEpilog(receiver, effectiveLength, modSpec);  |
| 1698 | }  |
| 1699 |   |
| 1700 |   |
| 1701 | void tSystem::Handler_g(Receiver& receiver, const FormatSpec& spec, void* data)  |
| 1702 | {  |
| 1703 | // Variable argument specifies data should be treated data as double. i.e. %f is 64 bits.  |
| 1704 | double v = *((double*)data);  |
| 1705 | tArray<char> convBuf(64, 32);  |
| 1706 |   |
| 1707 | // Default floating point printf precision. ANSI is 6, ours is 4.  |
| 1708 | // For %g, the precision is treated as significant digits, not number of digits after the decimal point.  |
| 1709 | int precision = spec.Precision;  |
| 1710 | if (precision == -1)  |
| 1711 | precision = DefaultPrecision;  |
| 1712 |   |
| 1713 | double noExpFormatThreshold = tPow(10.0, double(precision));  |
| 1714 | if (v < noExpFormatThreshold)  |
| 1715 | {  |
| 1716 | // Check for early exit infinities and NANs.  |
| 1717 | PrologHelperFloat res = PrologHelperFloat::None;  |
| 1718 | if (HandlerHelper_HandleSpecialFloatTypes(convBuf, v))  |
| 1719 | res = PrologHelperFloat::NoZeros;  |
| 1720 | else  |
| 1721 | res = HandlerHelper_FloatNormal(convBuf, spec, v, true);  |
| 1722 |   |
| 1723 | FormatSpec modSpec(spec);  |
| 1724 | int effectiveLength = convBuf.GetNumAppendedElements();  |
| 1725 | switch (res)  |
| 1726 | {  |
| 1727 | case PrologHelperFloat::NeedsNeg: receiver.Receive('-'); effectiveLength++; break;  |
| 1728 | case PrologHelperFloat::NeedsPlus: receiver.Receive('+'); effectiveLength++; break;  |
| 1729 | case PrologHelperFloat::NeedsSpace: receiver.Receive(' '); effectiveLength++; break;  |
| 1730 | case PrologHelperFloat::NoZeros: modSpec.Flags &= ~Flag_LeadingZeros; break;  |
| 1731 | case PrologHelperFloat::None: break;  |
| 1732 | }  |
| 1733 |   |
| 1734 | HandlerHelper_JustificationProlog(receiver, effectiveLength, modSpec);  |
| 1735 | receiver.Receive(convBuf);  |
| 1736 | HandlerHelper_JustificationEpilog(receiver, effectiveLength, modSpec);  |
| 1737 | return;  |
| 1738 | }  |
| 1739 |   |
| 1740 | // Check for early exit infinities and NANs.  |
| 1741 | if (HandlerHelper_HandleSpecialFloatTypes(convBuf, v))  |
| 1742 | {  |
| 1743 | receiver.Receive(convBuf);  |
| 1744 | return;  |
| 1745 | }  |
| 1746 |   |
| 1747 | // @todo Fix this like the Handler_f was fixed so it can handle appending directly into appending to the dynamically growing convBuf.  |
| 1748 | char result[64];  |
| 1749 | const int maxLeadingZeroes = 16;  |
| 1750 | char* curr = result + maxLeadingZeroes;  |
| 1751 | bool negative = false;  |
| 1752 |   |
| 1753 | if (v < 0.0f)  |
| 1754 | {  |
| 1755 | v = -v;  |
| 1756 | negative = true;  |
| 1757 | }  |
| 1758 |   |
| 1759 | double val = double(v);  |
| 1760 | int exponent = HandlerHelper_FloatComputeExponent(val);  |
| 1761 |   |
| 1762 | // Convert val so it is a single non-zero digit before the decimal point.  |
| 1763 | double power10 = 1.0;  |
| 1764 | int absExp = (exponent < 0) ? -exponent : exponent;  |
| 1765 | for (int e = 0; e < absExp; e++)  |
| 1766 | power10 *= 10.0;  |
| 1767 |   |
| 1768 | if (exponent != 0)  |
| 1769 | val = (exponent < 0) ? (val * power10) : (val / power10);  |
| 1770 |   |
| 1771 | // Sometimes errors can cause 9.999999 -> 10.0.  |
| 1772 | while (val >= 10.0)  |
| 1773 | {  |
| 1774 | val /= 10.0;  |
| 1775 | exponent++;  |
| 1776 | }  |
| 1777 |   |
| 1778 | power10 = 1.0;  |
| 1779 | for (int e = 0; e < precision; e++)  |
| 1780 | power10 *= 10.0;  |
| 1781 | double precisionRound = 0.5 / power10;  |
| 1782 | val += precisionRound;  |
| 1783 |   |
| 1784 | bool firstDigit = true;  |
| 1785 | while (precision)  |
| 1786 | {  |
| 1787 | int digit = int(val);  |
| 1788 | val -= digit;  |
| 1789 | val *= 10.0;  |
| 1790 | precision--;  |
| 1791 | // Round the last digit up if necessary. There's a subtle error here: if the digit is  |
| 1792 | // 9 we just truncate, whereas we really need another rounding loop to carry the round upwards  |
| 1793 | // through the 9s.  |
| 1794 | if ((precision == 0) && (int(val) >= 5) && (digit < 9))  |
| 1795 | digit++;  |
| 1796 | *curr++ = '0' + digit;  |
| 1797 |   |
| 1798 | if (firstDigit)  |
| 1799 | *curr++ = '.';  |
| 1800 |   |
| 1801 | firstDigit = false;  |
| 1802 | }  |
| 1803 |   |
| 1804 | *curr++ = 'e'; // Need to pass in an uppercase boolean.  |
| 1805 | if (exponent >= 0)  |
| 1806 | {  |
| 1807 | *curr++ = '+';  |
| 1808 | }  |
| 1809 | else  |
| 1810 | {  |
| 1811 | *curr++ = '-';  |
| 1812 | exponent = -exponent;  |
| 1813 | }  |
| 1814 |   |
| 1815 | // @todo Make width here controllable by opt display flag.  |
| 1816 | const int expWidthMax = 3;  |
| 1817 |   |
| 1818 | // First we need to write the exponent characters into a temp buffer backwards. This is so we have to whole thing  |
| 1819 | // before we don't process leading zeroes.  |
| 1820 | int expBuf[expWidthMax] = { 0, 0, 0 };  |
| 1821 | for (int n = expWidthMax-1; n >= 0; n--)  |
| 1822 | {  |
| 1823 | int digit = exponent % 10;  |
| 1824 | exponent /= 10;  |
| 1825 | expBuf[n] = digit;  |
| 1826 | }  |
| 1827 |   |
| 1828 | // We always include the last two least-significant digits of the base 10 exponent, even if they are both zeroes.  |
| 1829 | // We only include the first digit if it is non-zero. This can only happen with doubles, not floats which max at 38.  |
| 1830 | if (expBuf[0] != 0)  |
| 1831 | *curr++ = '0' + expBuf[0];  |
| 1832 | *curr++ = '0' + expBuf[1];  |
| 1833 | *curr++ = '0' + expBuf[2];  |
| 1834 | *curr++ = '\0';  |
| 1835 |   |
| 1836 | // If there are no leading zeroes any possible plus or negative sign must go beside the first valid character of the  |
| 1837 | // converted string. However, if there ARE leading zeroes, we still need to place the plus or negative based on the  |
| 1838 | // width.  |
| 1839 | curr = result + maxLeadingZeroes;  |
| 1840 | if (!(spec.Flags & Flag_LeadingZeros))  |
| 1841 | {  |
| 1842 | if (negative)  |
| 1843 | *--curr = '-';  |
| 1844 | else if (spec.Flags & Flag_ForcePosOrNegSign)  |
| 1845 | *--curr = '+';  |
| 1846 | else if (!negative && (spec.Flags & Flag_SpaceForPosSign))  |
| 1847 | *--curr = ' ';  |
| 1848 | }  |
| 1849 | else  |
| 1850 | {  |
| 1851 | int numZeroes = spec.Width - tStd::tStrlen(curr);  |
| 1852 | if (numZeroes > maxLeadingZeroes)  |
| 1853 | numZeroes = maxLeadingZeroes;  |
| 1854 | while (numZeroes-- > 0)  |
| 1855 | *--curr = '0';  |
| 1856 |   |
| 1857 | if (negative)  |
| 1858 | *curr = '-';  |
| 1859 | else if (spec.Flags & Flag_ForcePosOrNegSign)  |
| 1860 | *curr = '+';  |
| 1861 | }  |
| 1862 |   |
| 1863 | receiver.Receive(curr, tStd::tStrlen(curr));  |
| 1864 | }  |
| 1865 |   |
| 1866 |   |
| 1867 | void tSystem::HandlerHelper_Vector(Receiver& receiver, const FormatSpec& spec, const float* components, int numComponents)  |
| 1868 | {  |
| 1869 | if (spec.Flags & Flag_DecorativeFormatting)  |
| 1870 | {  |
| 1871 | for (int c = 0; c < numComponents; c++)  |
| 1872 | {  |
| 1873 | double comp = double(components[c]);  |
| 1874 | Handler_f(receiver, spec, &comp);  |
| 1875 | if (c < (numComponents-1))  |
| 1876 | receiver.Receive(' ');  |
| 1877 | }  |
| 1878 | }  |
| 1879 | else  |
| 1880 | {  |
| 1881 | receiver.Receive('(');  |
| 1882 | for (int c = 0; c < numComponents; c++)  |
| 1883 | {  |
| 1884 | double comp = double(components[c]);  |
| 1885 | Handler_f(receiver, spec, &comp);  |
| 1886 | if (c < (numComponents-1))  |
| 1887 | receiver.Receive(", " , 2);  |
| 1888 | }  |
| 1889 | receiver.Receive(')');  |
| 1890 | }  |
| 1891 | }  |
| 1892 |   |
| 1893 |   |
| 1894 | void tSystem::Handler_v(Receiver& receiver, const FormatSpec& spec, void* data)  |
| 1895 | {  |
| 1896 | int numComponents = spec.TypeSizeBytes >> 2;  |
| 1897 | tAssert((numComponents >= 2) && (numComponents <= 4));  |
| 1898 |   |
| 1899 | tVec4* vec = (tVec4*)data;  |
| 1900 | float* components = &vec->x;  |
| 1901 |   |
| 1902 | HandlerHelper_Vector(receiver, spec, components, numComponents);  |
| 1903 | }  |
| 1904 |   |
| 1905 |   |
| 1906 | void tSystem::Handler_q(Receiver& receiver, const FormatSpec& spec, void* data)  |
| 1907 | {  |
| 1908 | tQuat* quat = (tQuat*)data;  |
| 1909 |   |
| 1910 | if (spec.Flags & Flag_DecorativeFormatting)  |
| 1911 | {  |
| 1912 | receiver.Receive('(');  |
| 1913 | double w = double(quat->w);  |
| 1914 | Handler_f(receiver, spec, &w);  |
| 1915 | receiver.Receive(", (" , 3);  |
| 1916 |   |
| 1917 | double x = double(quat->x);  |
| 1918 | Handler_f(receiver, spec, &x);  |
| 1919 | receiver.Receive(", " , 2);  |
| 1920 |   |
| 1921 | double y = double(quat->y);  |
| 1922 | Handler_f(receiver, spec, &y);  |
| 1923 | receiver.Receive(", " , 2);  |
| 1924 |   |
| 1925 | double z = double(quat->z);  |
| 1926 | Handler_f(receiver, spec, &z);  |
| 1927 | receiver.Receive("))" , 2);  |
| 1928 | }  |
| 1929 | else  |
| 1930 | {  |
| 1931 | float* components = &quat->x;  |
| 1932 | receiver.Receive('(');  |
| 1933 | for (int c = 0; c < 4; c++)  |
| 1934 | {  |
| 1935 | double comp = double(components[c]);  |
| 1936 | Handler_f(receiver, spec, &comp);  |
| 1937 | if (c < 3)  |
| 1938 | receiver.Receive(", " , 2);  |
| 1939 | }  |
| 1940 | receiver.Receive(')');  |
| 1941 | }  |
| 1942 | }  |
| 1943 |   |
| 1944 |   |
| 1945 | void tSystem::Handler_m(Receiver& receiver, const FormatSpec& spec, void* data)  |
| 1946 | {  |
| 1947 | bool is4x4 = (spec.TypeSizeBytes == sizeof(tMat4)) ? true : false;  |
| 1948 | bool is2x2 = (spec.TypeSizeBytes == sizeof(tMat2)) ? true : false;  |
| 1949 | tAssert(is4x4 || is2x2);  |
| 1950 |   |
| 1951 | if (is4x4)  |
| 1952 | {  |
| 1953 | tMat4* mat = (tMat4*)data;  |
| 1954 |   |
| 1955 | if (spec.Flags & Flag_DecorativeFormatting)  |
| 1956 | {  |
| 1957 | FormatSpec vecSpec(spec);  |
| 1958 | if (!spec.Width)  |
| 1959 | vecSpec.Width = 9;  |
| 1960 | if (spec.Precision == -1)  |
| 1961 | vecSpec.Precision = 4;  |
| 1962 |   |
| 1963 | tVec4 row1 = { mat->C1.x, mat->C2.x, mat->C3.x, mat->C4.x };  |
| 1964 | tVec4 row2 = { mat->C1.y, mat->C2.y, mat->C3.y, mat->C4.y };  |
| 1965 | tVec4 row3 = { mat->C1.z, mat->C2.z, mat->C3.z, mat->C4.z };  |
| 1966 | tVec4 row4 = { mat->C1.w, mat->C2.w, mat->C3.w, mat->C4.w };  |
| 1967 |   |
| 1968 | receiver.Receive("[ " , 2); HandlerHelper_Vector(receiver, vecSpec, &row1.x, 4); receiver.Receive('\n');  |
| 1969 | receiver.Receive(" " , 2); HandlerHelper_Vector(receiver, vecSpec, &row2.x, 4); receiver.Receive('\n');  |
| 1970 | receiver.Receive(" " , 2); HandlerHelper_Vector(receiver, vecSpec, &row3.x, 4); receiver.Receive('\n');  |
| 1971 | receiver.Receive(" " , 2); HandlerHelper_Vector(receiver, vecSpec, &row4.x, 4); receiver.Receive(" ]\n" , 3);  |
| 1972 | }  |
| 1973 | else  |
| 1974 | {  |
| 1975 | receiver.Receive('(');  |
| 1976 | HandlerHelper_Vector(receiver, spec, &mat->C1.x, 4);  |
| 1977 | receiver.Receive(", " , 2);  |
| 1978 | HandlerHelper_Vector(receiver, spec, &mat->C2.x, 4);  |
| 1979 | receiver.Receive(", " , 2);  |
| 1980 | HandlerHelper_Vector(receiver, spec, &mat->C3.x, 4);  |
| 1981 | receiver.Receive(", " , 2);  |
| 1982 | HandlerHelper_Vector(receiver, spec, &mat->C4.x, 4);  |
| 1983 | receiver.Receive(')');  |
| 1984 | }  |
| 1985 | }  |
| 1986 | else  |
| 1987 | {  |
| 1988 | tMat2* mat = (tMat2*)data;  |
| 1989 |   |
| 1990 | if (spec.Flags & Flag_DecorativeFormatting)  |
| 1991 | {  |
| 1992 | FormatSpec vecSpec(spec);  |
| 1993 | if (!spec.Width)  |
| 1994 | vecSpec.Width = 9;  |
| 1995 | if (spec.Precision == -1)  |
| 1996 | vecSpec.Precision = 4;  |
| 1997 |   |
| 1998 | tVec2 row1 = { mat->C1.x, mat->C2.x };  |
| 1999 | tVec2 row2 = { mat->C1.y, mat->C2.y };  |
| 2000 |   |
| 2001 | receiver.Receive("[ " , 2); HandlerHelper_Vector(receiver, vecSpec, &row1.x, 2); receiver.Receive('\n');  |
| 2002 | receiver.Receive(" " , 2); HandlerHelper_Vector(receiver, vecSpec, &row2.x, 2); receiver.Receive(" ]\n" , 3);  |
| 2003 | }  |
| 2004 | else  |
| 2005 | {  |
| 2006 | receiver.Receive('(');  |
| 2007 | HandlerHelper_Vector(receiver, spec, &mat->C1.x, 2);  |
| 2008 | receiver.Receive(", " , 2);  |
| 2009 | HandlerHelper_Vector(receiver, spec, &mat->C2.x, 2);  |
| 2010 | receiver.Receive(')');  |
| 2011 | }  |
| 2012 | }  |
| 2013 | }  |
| 2014 |   |
| 2015 |   |
| 2016 | void tSystem::Handler_c(Receiver& receiver, const FormatSpec& spec, void* data)  |
| 2017 | {  |
| 2018 | const char chr = *((const char*)data);  |
| 2019 | receiver.Receive(chr);  |
| 2020 | }  |
| 2021 |   |
| 2022 |   |
| 2023 | void tSystem::Handler_s(Receiver& receiver, const FormatSpec& spec, void* data)  |
| 2024 | {  |
| 2025 | const char* str = *((const char**)data);  |
| 2026 |   |
| 2027 | int numToAppend = tStd::tStrlen(str);  |
| 2028 | if ((spec.Precision != -1) && (numToAppend > spec.Precision))  |
| 2029 | numToAppend = spec.Precision;  |
| 2030 |   |
| 2031 | HandlerHelper_JustificationProlog(receiver, numToAppend, spec);  |
| 2032 | receiver.Receive(str, numToAppend);  |
| 2033 | HandlerHelper_JustificationEpilog(receiver, numToAppend, spec);  |
| 2034 | }  |
| 2035 | |