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 | |