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