]> git.cworth.org Git - vogl/blob - src/extlib/loki/include/loki/SafeFormat.h
Initial vogl checkin
[vogl] / src / extlib / loki / include / loki / SafeFormat.h
1 ////////////////////////////////////////////////////////////////////////////////
2 // Copyright (c) 2005 by Andrei Alexandrescu
3 // Copyright (c) 2006 Peter Kümmel
4 // Permission to use, copy, modify, distribute, and sell this software for any
5 //     purpose is hereby granted without fee, provided that the above copyright
6 //     notice appear in all copies and that both that copyright notice and this
7 //     permission notice appear in supporting documentation.
8 // The author makes no representations about the suitability of this software
9 //     for any purpose. It is provided "as is" without express or implied
10 //     warranty.
11 ////////////////////////////////////////////////////////////////////////////////
12 #ifndef LOKI_SAFEFORMAT_INC_
13 #define LOKI_SAFEFORMAT_INC_
14
15 // $Id: SafeFormat.h 911 2008-12-15 20:55:24Z syntheticpp $
16
17
18 ////////////////////////////////////////////////////////////////////////////////
19 // This file contains definitions for SafePrintf. SafeScanf coming soon (the
20 //   design is similar).
21 // See Alexandrescu, Andrei: Type-safe Formatting, C/C++ Users Journal, Aug 2005
22 ////////////////////////////////////////////////////////////////////////////////
23
24 #include <cstdio>
25 #include <climits>
26 #include <string>
27 #include <cstring>
28 #include <stdexcept>
29 #include <utility>
30 #include <cassert>
31 #include <locale>
32 #include <iostream>
33
34 #include <loki/LokiExport.h>
35
36
37 // long is 32 bit on 64-bit Windows!
38 // intptr_t used to get 64 bit on Win64
39 #if defined(_WIN32) || defined(_WIN64)
40 #  define LOKI_SAFEFORMAT_SIGNED_LONG intptr_t
41 #  define LOKI_SAFEFORMAT_UNSIGNED_LONG uintptr_t
42 #else
43 #  define LOKI_SAFEFORMAT_SIGNED_LONG signed long
44 #  define LOKI_SAFEFORMAT_UNSIGNED_LONG unsigned long
45 #endif
46
47 // Windows headers could have min/max defined
48 #ifdef max
49 #  undef max
50 #endif
51 #ifdef min
52 #  undef min
53 #endif
54
55 namespace Loki
56 {
57
58 // Crude writing method: writes straight to the file, unbuffered
59 // Must be combined with a buffer to work properly (and efficiently)
60 LOKI_EXPORT
61 void write(std::FILE *f, const char *from, const char *to);
62
63 // Write to an ostream
64 LOKI_EXPORT
65 void write(std::ostream &f, const char *from, const char *to);
66
67 // Write to a string
68 LOKI_EXPORT
69 void write(std::string &s, const char *from, const char *to);
70
71 // Write to a fixed-size buffer
72 template <class Char>
73 void write(std::pair<Char *, std::size_t>& s, const Char *from, const Char *to)
74 {
75         assert(from <= to);
76         if(from + s.second < to)
77                 throw std::overflow_error("");
78         // s.first: position one past the final copied element
79         s.first = std::copy(from, to, s.first);
80         // remaining buffer size
81         s.second -= to - from;
82 }
83
84 ////////////////////////////////////////////////////////////////////////////////
85 // PrintfState class template
86 // Holds the formatting state, and implements operator() to format stuff
87 // Todo: make sure errors are handled properly
88 ////////////////////////////////////////////////////////////////////////////////
89
90 template <class Device, class Char>
91 struct PrintfState
92 {
93         PrintfState(Device dev, const Char *format)
94                 : device_(dev)
95                 , format_(format)
96                 , width_(0)
97                 , prec_(0)
98                 , flags_(0)
99                 , result_(0)
100         {
101                 Advance();
102         }
103
104         ~PrintfState()
105         {
106         }
107
108 #define LOKI_PRINTF_STATE_FORWARD(type) \
109             PrintfState& operator()(type par) {\
110                 return (*this)(static_cast< LOKI_SAFEFORMAT_UNSIGNED_LONG >(par)); \
111             }
112
113         LOKI_PRINTF_STATE_FORWARD(bool)
114         LOKI_PRINTF_STATE_FORWARD(char)
115         LOKI_PRINTF_STATE_FORWARD(signed char)
116         LOKI_PRINTF_STATE_FORWARD(unsigned char)
117         LOKI_PRINTF_STATE_FORWARD(signed short)
118         LOKI_PRINTF_STATE_FORWARD(unsigned short)
119         LOKI_PRINTF_STATE_FORWARD(signed int)
120         LOKI_PRINTF_STATE_FORWARD(signed long)
121 #if (defined(_WIN32) || defined(_WIN64))
122         LOKI_PRINTF_STATE_FORWARD(unsigned long)
123 #else
124         // on Windows already defined by uintptr_t
125         LOKI_PRINTF_STATE_FORWARD(unsigned int)
126 #endif
127
128         // Print (or gobble in case of the "*" specifier) an int
129         PrintfState &operator()(LOKI_SAFEFORMAT_UNSIGNED_LONG i)
130         {
131                 if (result_ == -1) return *this; // don't even bother
132                 // % [flags] [width] [.prec] [modifier] type_char
133                 // Fetch the flags
134                 ReadFlags();
135                 if (*format_ == '*')
136                 {
137                         // read the width and get out
138                         SetWidth(static_cast<size_t>(i));
139                         ++format_;
140                         return *this;
141                 }
142                 ReadWidth();
143                 // precision
144                 if (*format_ == '.')
145                 {
146                         // deal with precision
147                         if (format_[1] == '*')
148                         {
149                                 // read the precision and get out
150                                 SetPrec(static_cast<size_t>(i));
151                                 format_ += 2;
152                                 return *this;
153                         }
154                         ReadPrecision();
155                 }
156                 ReadModifiers();
157                 // input size modifier
158                 if (ForceShort())
159                 {
160                         // short int
161                         const Char c = *format_;
162                         if (c == 'x' || c == 'X' || c == 'u' || c == 'o')
163                         {
164                                 i = static_cast<LOKI_SAFEFORMAT_UNSIGNED_LONG>(static_cast<unsigned short>(i));
165                         }
166                 }
167                 FormatWithCurrentFlags(i);
168                 return *this;
169         }
170
171         PrintfState &operator()(void *n)
172         {
173                 if (result_ == -1) return *this; // don't even bother
174                 PrintUsing_snprintf(n,"p");
175                 return *this;
176         }
177
178         PrintfState &operator()(double n)
179         {
180                 if (result_ == -1) return *this; // don't even bother
181                 PrintUsing_snprintf(n,"eEfgG");
182                 return *this;
183         }
184
185         PrintfState &operator()(long double n)
186         {
187                 if (result_ == -1) return *this; // don't even bother
188                 PrintUsing_snprintf(n,"eEfgG");
189                 return *this;
190         }
191
192         // Store the number of characters printed so far
193         PrintfState &operator()(int *pi)
194         {
195                 return StoreCountHelper(pi);
196         }
197
198         // Store the number of characters printed so far
199         PrintfState &operator()(short *pi)
200         {
201                 return StoreCountHelper(pi);
202         }
203
204         // Store the number of characters printed so far
205         PrintfState &operator()(long *pi)
206         {
207                 return StoreCountHelper(pi);
208         }
209
210         PrintfState &operator()(const std::string &stdstr)
211         {
212                 return operator()(stdstr.c_str());
213         }
214
215         PrintfState &operator()(const char *const s)
216         {
217                 if (result_ == -1) return *this;
218                 ReadLeaders();
219                 const char fmt = *format_;
220                 if (fmt == 'p')
221                 {
222                         FormatWithCurrentFlags(reinterpret_cast<LOKI_SAFEFORMAT_UNSIGNED_LONG>(s));
223                         return *this;
224                 }
225                 if (fmt != 's')
226                 {
227                         result_ = -1;
228                         return *this;
229                 }
230                 const size_t len = std::min(std::strlen(s), prec_);
231                 if (width_ > len)
232                 {
233                         if (LeftJustify())
234                         {
235                                 Write(s, s + len);
236                                 Fill(' ', width_ - len);
237                         }
238                         else
239                         {
240                                 Fill(' ', width_ - len);
241                                 Write(s, s + len);
242                         }
243                 }
244                 else
245                 {
246                         Write(s, s + len);
247                 }
248                 Next();
249                 return *this;
250         }
251
252         PrintfState &operator()(const void *const p)
253         {
254                 return (*this)(reinterpret_cast<LOKI_SAFEFORMAT_UNSIGNED_LONG>(p));
255         }
256
257         // read the result
258         operator int() const
259         {
260                 return static_cast<int>(result_);
261         }
262
263 private:
264         PrintfState &operator=(const PrintfState &);
265         template <typename T>
266         PrintfState &StoreCountHelper(T *const pi)
267         {
268                 if (result_ == -1) return *this; // don't even bother
269                 ReadLeaders();
270                 const char fmt = *format_;
271                 if (fmt == 'p')   // pointer
272                 {
273                         FormatWithCurrentFlags(reinterpret_cast<LOKI_SAFEFORMAT_UNSIGNED_LONG>(pi));
274                         return *this;
275                 }
276                 if (fmt != 'n')
277                 {
278                         result_ = -1;
279                         return *this;
280                 }
281                 assert(pi != 0);
282                 *pi = result_;
283                 Next();
284                 return *this;
285         }
286
287         void FormatWithCurrentFlags(const LOKI_SAFEFORMAT_UNSIGNED_LONG i)
288         {
289                 // look at the format character
290                 Char formatChar = *format_;
291                 bool isSigned = formatChar == 'd' || formatChar == 'i';
292                 if (formatChar == 'p')
293                 {
294                         formatChar = 'x'; // pointers go to hex
295                         SetAlternateForm(); // printed with '0x' in front
296                         isSigned = true; // that's what gcc does
297                 }
298                 if (!strchr("cdiuoxX", formatChar))
299                 {
300                         result_ = -1;
301                         return;
302                 }
303                 Char buf[
304                     sizeof(LOKI_SAFEFORMAT_UNSIGNED_LONG) * 3 // digits
305                     + 1 // sign or ' '
306                     + 2 // 0x or 0X
307                     + 1]; // terminating zero
308                 const Char *const bufEnd = buf + (sizeof(buf) / sizeof(Char));
309                 Char *bufLast = buf + (sizeof(buf) / sizeof(Char) - 1);
310                 Char signChar = 0;
311                 unsigned int base = 10;
312
313                 if (formatChar == 'c')
314                 {
315                         // Format only one character
316                         // The 'fill with zeros' flag is ignored
317                         ResetFillZeros();
318                         *bufLast = static_cast<char>(i);
319                 }
320                 else
321                 {
322                         // TODO: inefficient code, refactor
323                         const bool negative = isSigned && static_cast<LOKI_SAFEFORMAT_SIGNED_LONG>(i) < 0;
324                         if (formatChar == 'o') base = 8;
325                         else if (formatChar == 'x' || formatChar == 'X') base = 16;
326                         bufLast = isSigned
327                                   ? RenderWithoutSign(static_cast<LOKI_SAFEFORMAT_SIGNED_LONG>(i), bufLast, base,
328                                                       formatChar == 'X')
329                                   : RenderWithoutSign(i, bufLast, base,
330                                                       formatChar == 'X');
331                         // Add the sign
332                         if (isSigned)
333                         {
334                                 negative ? signChar = '-'
335                                                       : ShowSignAlways() ? signChar = '+'
336                                                               : Blank() ? signChar = ' '
337                                                                       : 0;
338                         }
339                 }
340                 // precision
341                 size_t
342                 countDigits = bufEnd - bufLast,
343                 countZeros = prec_ != size_t(-1) && countDigits < prec_ &&
344                              formatChar != 'c'
345                              ? prec_ - countDigits
346                              : 0,
347                              countBase = base != 10 && AlternateForm() && i != 0
348                                          ? (base == 16 ? 2 : countZeros > 0 ? 0 : 1)
349                                          : 0,
350                                          countSign = (signChar != 0),
351                                          totalPrintable = countDigits + countZeros + countBase + countSign;
352                 size_t countPadLeft = 0, countPadRight = 0;
353                 if (width_ > totalPrintable)
354                 {
355                         if (LeftJustify())
356                         {
357                                 countPadRight = width_ - totalPrintable;
358                                 countPadLeft = 0;
359                         }
360                         else
361                         {
362                                 countPadLeft = width_ - totalPrintable;
363                                 countPadRight = 0;
364                         }
365                 }
366                 if (FillZeros() && prec_ == size_t(-1))
367                 {
368                         // pad with zeros and no precision - transfer padding to precision
369                         countZeros = countPadLeft;
370                         countPadLeft = 0;
371                 }
372                 // ok, all computed, ready to print to device
373                 Fill(' ', countPadLeft);
374                 if (signChar != 0) Write(&signChar, &signChar + 1);
375                 if (countBase > 0) Fill('0', 1);
376                 if (countBase == 2) Fill(formatChar, 1);
377                 Fill('0', countZeros);
378                 Write(bufLast, bufEnd);
379                 Fill(' ', countPadRight);
380                 // done, advance
381                 Next();
382         }
383
384         void Write(const Char *b, const Char *e)
385         {
386                 if (result_ < 0) return;
387                 const LOKI_SAFEFORMAT_SIGNED_LONG x = e - b;
388                 write(device_, b, e);
389                 result_ += x;
390         }
391
392         template <class Value>
393         void PrintUsing_snprintf(Value n, const char *check_fmt_char)
394         {
395                 const Char *const fmt = format_ - 1;
396                 assert(*fmt == '%');
397                 // enforce format string validity
398                 ReadLeaders();
399                 // enforce format spec
400                 if (!strchr(check_fmt_char, *format_))
401                 {
402                         result_ = -1;
403                         return;
404                 }
405                 // format char validated, copy it to a temp and use legacy sprintf
406                 ++format_;
407                 Char fmtBuf[128], resultBuf[1024];
408                 if (format_  >= fmt + sizeof(fmtBuf) / sizeof(Char))
409                 {
410                         result_ = -1;
411                         return;
412                 }
413                 memcpy(fmtBuf, fmt, (format_ - fmt) * sizeof(Char));
414                 fmtBuf[format_ - fmt] = 0;
415
416                 const int stored =
417 #ifdef _MSC_VER
418 #if _MSC_VER < 1400
419                     _snprintf
420 #else
421                     _snprintf_s
422 #endif
423 #else
424                     snprintf
425 #endif
426                     (resultBuf, sizeof(resultBuf) / sizeof(Char), fmtBuf, n);
427
428                 if (stored < 0)
429                 {
430                         result_ = -1;
431                         return;
432                 }
433                 Write(resultBuf, resultBuf + strlen(resultBuf));
434                 Advance(); // output stuff to the next format directive
435         }
436
437         void Fill(const Char c, size_t n)
438         {
439                 for (; n > 0; --n)
440                 {
441                         Write(&c, &c + 1);
442                 }
443         }
444
445         Char *RenderWithoutSign(LOKI_SAFEFORMAT_UNSIGNED_LONG n, char *bufLast,
446                                 unsigned int base, bool uppercase)
447         {
448                 const Char hex1st = uppercase ? 'A' : 'a';
449                 for (;;)
450                 {
451                         const LOKI_SAFEFORMAT_UNSIGNED_LONG next = n / base;
452                         Char c = static_cast<Char>(n - next * base);
453                         c = static_cast<Char>(c + (c <= 9 ? '0' : static_cast<Char>(hex1st - 10)));
454                         *bufLast = c;
455                         n = next;
456                         if (n == 0) break;
457                         --bufLast;
458                 }
459                 return bufLast;
460         }
461
462         char *RenderWithoutSign(LOKI_SAFEFORMAT_SIGNED_LONG n, char *bufLast, unsigned int base,
463                                 bool uppercase)
464         {
465                 if (n != LONG_MIN)
466                 {
467                         return RenderWithoutSign(static_cast<LOKI_SAFEFORMAT_UNSIGNED_LONG>(n < 0 ? -n : n),
468                                                  bufLast, base, uppercase);
469                 }
470                 // annoying corner case
471                 char *save = bufLast;
472                 ++n;
473                 bufLast = RenderWithoutSign(static_cast<LOKI_SAFEFORMAT_UNSIGNED_LONG>(n),
474                                             bufLast, base, uppercase);
475                 --(*save);
476                 return bufLast;
477         }
478
479         void Next()
480         {
481                 ++format_;
482                 Advance();
483         }
484
485         void Advance()
486         {
487                 ResetAll();
488                 const Char *begin = format_;
489                 for (;;)
490                 {
491                         if (*format_ == '%')
492                         {
493                                 if (format_[1] != '%')   // It's a format specifier
494                                 {
495                                         Write(begin, format_);
496                                         ++format_;
497                                         break;
498                                 }
499                                 // It's a "%%"
500                                 Write(begin, ++format_);
501                                 begin = ++format_;
502                                 continue;
503                         }
504                         if (*format_ == 0)
505                         {
506                                 Write(begin, format_);
507                                 break;
508                         }
509                         ++format_;
510                 }
511         }
512
513         void ReadFlags()
514         {
515                 for (;; ++format_)
516                 {
517                         switch (*format_)
518                         {
519                         case '-':
520                                 SetLeftJustify();
521                                 break;
522                         case '+':
523                                 SetShowSignAlways();
524                                 break;
525                         case ' ':
526                                 SetBlank();
527                                 break;
528                         case '#':
529                                 SetAlternateForm();
530                                 break;
531                         case '0':
532                                 SetFillZeros();
533                                 break;
534                         default:
535                                 return;
536                         }
537                 }
538         }
539
540         void ParseDecimalSizeT(size_t &dest)
541         {
542                 if (!std::isdigit(*format_, std::locale())) return;
543                 size_t r = 0;
544                 do
545                 {
546                         // TODO: inefficient - rewrite
547                         r *= 10;
548                         r += *format_ - '0';
549                         ++format_;
550                 }
551                 while (std::isdigit(*format_, std::locale()));
552                 dest = r;
553         }
554
555         void ReadWidth()
556         {
557                 ParseDecimalSizeT(width_);
558         }
559
560         void ReadPrecision()
561         {
562                 assert(*format_ == '.');
563                 ++format_;
564                 ParseDecimalSizeT(prec_);
565         }
566
567         void ReadModifiers()
568         {
569                 switch (*format_)
570                 {
571                 case 'h':
572                         SetForceShort();
573                         ++format_;
574                         break;
575                 case 'l':
576                         ++format_;
577                         break;
578                         // more (C99 and platform-specific modifiers) to come
579                 }
580         }
581
582         void ReadLeaders()
583         {
584                 ReadFlags();
585                 ReadWidth();
586                 if (*format_ == '.') ReadPrecision();
587                 ReadModifiers();
588         }
589
590         enum
591         {
592                 leftJustify = 1,
593                 showSignAlways = 2,
594                 blank = 4,
595                 alternateForm = 8,
596                 fillZeros = 16,
597                 forceShort = 32
598         };
599
600         bool LeftJustify() const
601         {
602                 return (flags_ & leftJustify) != 0;
603         }
604         bool ShowSignAlways() const
605         {
606                 return (flags_ & showSignAlways) != 0;
607         }
608         void SetWidth(size_t w)
609         {
610                 width_  = w;
611         }
612         void SetLeftJustify()
613         {
614                 flags_  |= leftJustify;
615         }
616         void SetShowSignAlways()
617         {
618                 flags_ |= showSignAlways;
619         }
620         bool Blank() const
621         {
622                 return (flags_ & blank) != 0;
623         }
624         bool AlternateForm() const
625         {
626                 return (flags_ & alternateForm) != 0;
627         }
628         bool FillZeros() const
629         {
630                 return (flags_ & fillZeros) != 0;
631         }
632         bool ForceShort() const
633         {
634                 return (flags_ & forceShort) != 0;
635         }
636
637         void SetPrec(size_t p)
638         {
639                 prec_ = p;
640         }
641         void SetBlank()
642         {
643                 flags_ |= blank;
644         }
645         void SetAlternateForm()
646         {
647                 flags_ |=  alternateForm;
648         }
649         void SetFillZeros()
650         {
651                 flags_ |= fillZeros;
652         }
653         void ResetFillZeros()
654         {
655                 flags_ &= ~fillZeros;
656         }
657         void SetForceShort()
658         {
659                 flags_ |= forceShort;
660         }
661
662         void ResetAll()
663         {
664                 assert(result_ != EOF);
665                 width_ = 0;
666                 prec_ = size_t(-1);
667                 flags_ = 0;
668         }
669
670         // state
671         Device device_;
672         const Char *format_;
673         size_t width_;
674         size_t prec_;
675         unsigned int flags_;
676         LOKI_SAFEFORMAT_SIGNED_LONG result_;
677 };
678
679 LOKI_EXPORT
680 PrintfState<std::FILE *, char> Printf(const char *format);
681
682 LOKI_EXPORT
683 PrintfState<std::FILE *, char> Printf(const std::string &format);
684
685 LOKI_EXPORT
686 PrintfState<std::FILE *, char> FPrintf(std::FILE *f, const char *format);
687
688 LOKI_EXPORT
689 PrintfState<std::FILE *, char> FPrintf(std::FILE *f, const std::string &format);
690
691 LOKI_EXPORT
692 PrintfState<std::ostream &, char> FPrintf(std::ostream &f, const char *format);
693
694 LOKI_EXPORT
695 PrintfState<std::ostream &, char> FPrintf(std::ostream &f, const std::string &format);
696
697 LOKI_EXPORT
698 PrintfState<std::string &, char> SPrintf(std::string &s, const char *format);
699
700 LOKI_EXPORT
701 PrintfState<std::string &, char> SPrintf(std::string &s, const std::string &format);
702
703 template <class T, class Char>
704 PrintfState<T &, Char> XPrintf(T &device, const Char *format)
705 {
706         return PrintfState<T &, Char>(device, format);
707 }
708
709 template <class T>
710 PrintfState<T &, char> XPrintf(T &device, const std::string &format)
711 {
712         return PrintfState<T &, char>(device, format.c_str());
713 }
714
715 template <class Char, std::size_t N>
716 PrintfState<std::pair<Char *, std::size_t>, Char>
717 BufPrintf(Char (&buf)[N], const Char *format)
718 {
719         std::pair<Char *, std::size_t> temp(buf, N);
720         return PrintfState<std::pair<Char *, std::size_t>, Char>(temp, format);
721 }
722
723 }// namespace Loki
724
725
726 #endif // end file guardian
727