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" 
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_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 
192void 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 
202void tSystem::tRegister(const char* machineName, tSystem::tChannel channelsToSee
203
204 if (!machineName
205 return
206 
207 tRegister(tHash::tHashStringFast32(machineName), channelsToSee); 
208
209 
210 
211void tSystem::tSetChannels(tChannel channelsToSee
212
213 OutputChannels = channelsToSee
214
215 
216 
217void tSystem::tSetStdoutRedirectCallback(RedirectCallback cb
218
219 StdoutRedirectCallback = cb
220
221 
222 
223void tSystem::tSetSupplementaryDebuggerOutput(bool enable
224
225 SupplementaryDebuggerOutput = enable
226
227 
228 
229int 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 
238int 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 
300void tSystem::tSetDefaultPrecision(int precision
301
302 DefaultPrecision = precision
303
304 
305 
306int tSystem::tGetDefaultPrecision() 
307
308 return DefaultPrecision
309
310 
311 
312void 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 
331void 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 
366void 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  
396void 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". 
406tSystem::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. 
430const int tSystem::NumHandlers = sizeof(tSystem::HandlerInfos) / sizeof(*tSystem::HandlerInfos); 
431int 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 
462int 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 
476int 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 
490int 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 
501int 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 
511tString& 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 
524tString& 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 
534int 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 
557int 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 
567int 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 
577int 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 
588int 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 
598int 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 
612int 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 
622int 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 
639void tFlush(tFileHandle handle
640
641 fflush(handle); 
642
643 
644 
645tSystem::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 
673bool 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 
696void 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 
890void 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 
905void 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 
917void 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 
1040void 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 
1170void 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 
1197void 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 
1221void 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 
1245void 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 
1269void 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 
1293void 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 
1317void 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 
1341void 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 
1361int 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 
1388bool 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 
1417void 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 
1550tSystem::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 
1671void 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 
1701void 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 
1867void 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 
1894void 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 
1906void 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 
1945void 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 
2016void 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 
2023void 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