summaryrefslogtreecommitdiff
path: root/shared/loki/SafeFormat.h
diff options
context:
space:
mode:
Diffstat (limited to 'shared/loki/SafeFormat.h')
-rw-r--r--shared/loki/SafeFormat.h590
1 files changed, 590 insertions, 0 deletions
diff --git a/shared/loki/SafeFormat.h b/shared/loki/SafeFormat.h
new file mode 100644
index 00000000..9d948581
--- /dev/null
+++ b/shared/loki/SafeFormat.h
@@ -0,0 +1,590 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2005 by Andrei Alexandrescu
+// Copyright (c) 2006 Peter Kümmel
+// Permission to use, copy, modify, distribute, and sell this software for any
+// purpose is hereby granted without fee, provided that the above copyright
+// notice appear in all copies and that both that copyright notice and this
+// permission notice appear in supporting documentation.
+// The author makes no representations about the suitability of this software
+// for any purpose. It is provided "as is" without express or implied
+// warranty.
+////////////////////////////////////////////////////////////////////////////////
+#ifndef LOKI_SAFEFORMAT_INC_
+#define LOKI_SAFEFORMAT_INC_
+
+// $Id: SafeFormat.h 911 2008-12-15 20:55:24Z syntheticpp $
+
+
+////////////////////////////////////////////////////////////////////////////////
+// This file contains definitions for SafePrintf. SafeScanf coming soon (the
+// design is similar).
+// See Alexandrescu, Andrei: Type-safe Formatting, C/C++ Users Journal, Aug 2005
+////////////////////////////////////////////////////////////////////////////////
+
+#include <cstdio>
+#include <climits>
+#include <string>
+#include <cstring>
+#include <stdexcept>
+#include <utility>
+#include <cassert>
+#include <locale>
+#include <iostream>
+
+#include <loki/LokiExport.h>
+
+
+// long is 32 bit on 64-bit Windows!
+// intptr_t used to get 64 bit on Win64
+#if defined(_WIN32) || defined(_WIN64)
+# define LOKI_SAFEFORMAT_SIGNED_LONG intptr_t
+# define LOKI_SAFEFORMAT_UNSIGNED_LONG uintptr_t
+#else
+# define LOKI_SAFEFORMAT_SIGNED_LONG signed long
+# define LOKI_SAFEFORMAT_UNSIGNED_LONG unsigned long
+#endif
+
+// Windows headers could have min/max defined
+#ifdef max
+# undef max
+#endif
+#ifdef min
+# undef min
+#endif
+
+namespace Loki
+{
+
+ // Crude writing method: writes straight to the file, unbuffered
+ // Must be combined with a buffer to work properly (and efficiently)
+ LOKI_EXPORT
+ void write(std::FILE* f, const char* from, const char* to);
+
+ // Write to an ostream
+ LOKI_EXPORT
+ void write(std::ostream& f, const char* from, const char* to);
+
+ // Write to a string
+ LOKI_EXPORT
+ void write(std::string& s, const char* from, const char* to);
+
+ // Write to a fixed-size buffer
+ template <class Char>
+ void write(std::pair<Char*, std::size_t>& s, const Char* from, const Char* to) {
+ assert(from <= to);
+ if(from + s.second < to)
+ throw std::overflow_error("");
+ // s.first: position one past the final copied element
+ s.first = std::copy(from, to, s.first);
+ // remaining buffer size
+ s.second -= to - from;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // PrintfState class template
+ // Holds the formatting state, and implements operator() to format stuff
+ // Todo: make sure errors are handled properly
+ ////////////////////////////////////////////////////////////////////////////////
+
+ template <class Device, class Char>
+ struct PrintfState {
+ PrintfState(Device dev, const Char * format)
+ : device_(dev)
+ , format_(format)
+ , width_(0)
+ , prec_(0)
+ , flags_(0)
+ , result_(0) {
+ Advance();
+ }
+
+ ~PrintfState() {
+ }
+
+ #define LOKI_PRINTF_STATE_FORWARD(type) \
+ PrintfState& operator()(type par) {\
+ return (*this)(static_cast< LOKI_SAFEFORMAT_UNSIGNED_LONG >(par)); \
+ }
+
+ LOKI_PRINTF_STATE_FORWARD(bool)
+ LOKI_PRINTF_STATE_FORWARD(char)
+ LOKI_PRINTF_STATE_FORWARD(signed char)
+ LOKI_PRINTF_STATE_FORWARD(unsigned char)
+ LOKI_PRINTF_STATE_FORWARD(signed short)
+ LOKI_PRINTF_STATE_FORWARD(unsigned short)
+ LOKI_PRINTF_STATE_FORWARD(signed int)
+ LOKI_PRINTF_STATE_FORWARD(signed long)
+#if (defined(_WIN32) || defined(_WIN64))
+ LOKI_PRINTF_STATE_FORWARD(unsigned long)
+#else
+ // on Windows already defined by uintptr_t
+ LOKI_PRINTF_STATE_FORWARD(unsigned int)
+#endif
+
+ // Print (or gobble in case of the "*" specifier) an int
+ PrintfState& operator()(LOKI_SAFEFORMAT_UNSIGNED_LONG i) {
+ if (result_ == -1) return *this; // don't even bother
+ // % [flags] [width] [.prec] [modifier] type_char
+ // Fetch the flags
+ ReadFlags();
+ if (*format_ == '*') {
+ // read the width and get out
+ SetWidth(static_cast<size_t>(i));
+ ++format_;
+ return *this;
+ }
+ ReadWidth();
+ // precision
+ if (*format_ == '.') {
+ // deal with precision
+ if (format_[1] == '*') {
+ // read the precision and get out
+ SetPrec(static_cast<size_t>(i));
+ format_ += 2;
+ return *this;
+ }
+ ReadPrecision();
+ }
+ ReadModifiers();
+ // input size modifier
+ if (ForceShort()) {
+ // short int
+ const Char c = *format_;
+ if (c == 'x' || c == 'X' || c == 'u' || c == 'o') {
+ i = static_cast<LOKI_SAFEFORMAT_UNSIGNED_LONG>(static_cast<unsigned short>(i));
+ }
+ }
+ FormatWithCurrentFlags(i);
+ return *this;
+ }
+
+ PrintfState& operator()(void* n) {
+ if (result_ == -1) return *this; // don't even bother
+ PrintUsing_snprintf(n,"p");
+ return *this;
+ }
+
+ PrintfState& operator()(double n) {
+ if (result_ == -1) return *this; // don't even bother
+ PrintUsing_snprintf(n,"eEfgG");
+ return *this;
+ }
+
+ PrintfState& operator()(long double n) {
+ if (result_ == -1) return *this; // don't even bother
+ PrintUsing_snprintf(n,"eEfgG");
+ return *this;
+ }
+
+ // Store the number of characters printed so far
+ PrintfState& operator()(int * pi) {
+ return StoreCountHelper(pi);
+ }
+
+ // Store the number of characters printed so far
+ PrintfState& operator()(short * pi) {
+ return StoreCountHelper(pi);
+ }
+
+ // Store the number of characters printed so far
+ PrintfState& operator()(long * pi) {
+ return StoreCountHelper(pi);
+ }
+
+ PrintfState& operator()(const std::string& stdstr) {
+ return operator()(stdstr.c_str());
+ }
+
+ PrintfState& operator()(const char *const s) {
+ if (result_ == -1) return *this;
+ ReadLeaders();
+ const char fmt = *format_;
+ if (fmt == 'p') {
+ FormatWithCurrentFlags(reinterpret_cast<LOKI_SAFEFORMAT_UNSIGNED_LONG>(s));
+ return *this;
+ }
+ if (fmt != 's') {
+ result_ = -1;
+ return *this;
+ }
+ const size_t len = std::min(std::strlen(s), prec_);
+ if (width_ > len) {
+ if (LeftJustify()) {
+ Write(s, s + len);
+ Fill(' ', width_ - len);
+ } else {
+ Fill(' ', width_ - len);
+ Write(s, s + len);
+ }
+ } else {
+ Write(s, s + len);
+ }
+ Next();
+ return *this;
+ }
+
+ PrintfState& operator()(const void *const p) {
+ return (*this)(reinterpret_cast<LOKI_SAFEFORMAT_UNSIGNED_LONG>(p));
+ }
+
+ // read the result
+ operator int() const {
+ return static_cast<int>(result_);
+ }
+
+ private:
+ PrintfState& operator=(const PrintfState&);
+ template <typename T>
+ PrintfState& StoreCountHelper(T *const pi) {
+ if (result_ == -1) return *this; // don't even bother
+ ReadLeaders();
+ const char fmt = *format_;
+ if (fmt == 'p') { // pointer
+ FormatWithCurrentFlags(reinterpret_cast<LOKI_SAFEFORMAT_UNSIGNED_LONG>(pi));
+ return *this;
+ }
+ if (fmt != 'n') {
+ result_ = -1;
+ return *this;
+ }
+ assert(pi != 0);
+ *pi = result_;
+ Next();
+ return *this;
+ }
+
+ void FormatWithCurrentFlags(const LOKI_SAFEFORMAT_UNSIGNED_LONG i) {
+ // look at the format character
+ Char formatChar = *format_;
+ bool isSigned = formatChar == 'd' || formatChar == 'i';
+ if (formatChar == 'p') {
+ formatChar = 'x'; // pointers go to hex
+ SetAlternateForm(); // printed with '0x' in front
+ isSigned = true; // that's what gcc does
+ }
+ if (!strchr("cdiuoxX", formatChar)) {
+ result_ = -1;
+ return;
+ }
+ Char buf[
+ sizeof(LOKI_SAFEFORMAT_UNSIGNED_LONG) * 3 // digits
+ + 1 // sign or ' '
+ + 2 // 0x or 0X
+ + 1]; // terminating zero
+ const Char *const bufEnd = buf + (sizeof(buf) / sizeof(Char));
+ Char * bufLast = buf + (sizeof(buf) / sizeof(Char) - 1);
+ Char signChar = 0;
+ unsigned int base = 10;
+
+ if (formatChar == 'c') {
+ // Format only one character
+ // The 'fill with zeros' flag is ignored
+ ResetFillZeros();
+ *bufLast = static_cast<char>(i);
+ } else {
+ // TODO: inefficient code, refactor
+ const bool negative = isSigned && static_cast<LOKI_SAFEFORMAT_SIGNED_LONG>(i) < 0;
+ if (formatChar == 'o') base = 8;
+ else if (formatChar == 'x' || formatChar == 'X') base = 16;
+ bufLast = isSigned
+ ? RenderWithoutSign(static_cast<LOKI_SAFEFORMAT_SIGNED_LONG>(i), bufLast, base,
+ formatChar == 'X')
+ : RenderWithoutSign(i, bufLast, base,
+ formatChar == 'X');
+ // Add the sign
+ if (isSigned) {
+ negative ? signChar = '-'
+ : ShowSignAlways() ? signChar = '+'
+ : Blank() ? signChar = ' '
+ : 0;
+ }
+ }
+ // precision
+ size_t
+ countDigits = bufEnd - bufLast,
+ countZeros = prec_ != size_t(-1) && countDigits < prec_ &&
+ formatChar != 'c'
+ ? prec_ - countDigits
+ : 0,
+ countBase = base != 10 && AlternateForm() && i != 0
+ ? (base == 16 ? 2 : countZeros > 0 ? 0 : 1)
+ : 0,
+ countSign = (signChar != 0),
+ totalPrintable = countDigits + countZeros + countBase + countSign;
+ size_t countPadLeft = 0, countPadRight = 0;
+ if (width_ > totalPrintable) {
+ if (LeftJustify()) {
+ countPadRight = width_ - totalPrintable;
+ countPadLeft = 0;
+ } else {
+ countPadLeft = width_ - totalPrintable;
+ countPadRight = 0;
+ }
+ }
+ if (FillZeros() && prec_ == size_t(-1)) {
+ // pad with zeros and no precision - transfer padding to precision
+ countZeros = countPadLeft;
+ countPadLeft = 0;
+ }
+ // ok, all computed, ready to print to device
+ Fill(' ', countPadLeft);
+ if (signChar != 0) Write(&signChar, &signChar + 1);
+ if (countBase > 0) Fill('0', 1);
+ if (countBase == 2) Fill(formatChar, 1);
+ Fill('0', countZeros);
+ Write(bufLast, bufEnd);
+ Fill(' ', countPadRight);
+ // done, advance
+ Next();
+ }
+
+ void Write(const Char* b, const Char* e) {
+ if (result_ < 0) return;
+ const LOKI_SAFEFORMAT_SIGNED_LONG x = e - b;
+ write(device_, b, e);
+ result_ += x;
+ }
+
+ template <class Value>
+ void PrintUsing_snprintf(Value n, const char* check_fmt_char) {
+ const Char *const fmt = format_ - 1;
+ assert(*fmt == '%');
+ // enforce format string validity
+ ReadLeaders();
+ // enforce format spec
+ if (!strchr(check_fmt_char, *format_)) {
+ result_ = -1;
+ return;
+ }
+ // format char validated, copy it to a temp and use legacy sprintf
+ ++format_;
+ Char fmtBuf[128], resultBuf[1024];
+ if (format_ >= fmt + sizeof(fmtBuf) / sizeof(Char)) {
+ result_ = -1;
+ return;
+ }
+ memcpy(fmtBuf, fmt, (format_ - fmt) * sizeof(Char));
+ fmtBuf[format_ - fmt] = 0;
+
+ const int stored =
+#ifdef _MSC_VER
+#if _MSC_VER < 1400
+ _snprintf
+#else
+ _snprintf_s
+#endif
+#else
+ snprintf
+#endif
+ (resultBuf, sizeof(resultBuf) / sizeof(Char), fmtBuf, n);
+
+ if (stored < 0) {
+ result_ = -1;
+ return;
+ }
+ Write(resultBuf, resultBuf + strlen(resultBuf));
+ Advance(); // output stuff to the next format directive
+ }
+
+ void Fill(const Char c, size_t n) {
+ for (; n > 0; --n) {
+ Write(&c, &c + 1);
+ }
+ }
+
+ Char* RenderWithoutSign(LOKI_SAFEFORMAT_UNSIGNED_LONG n, char* bufLast,
+ unsigned int base, bool uppercase) {
+ const Char hex1st = uppercase ? 'A' : 'a';
+ for (;;) {
+ const LOKI_SAFEFORMAT_UNSIGNED_LONG next = n / base;
+ Char c = static_cast<Char>(n - next * base);
+ c = static_cast<Char>(c + (c <= 9 ? '0' : static_cast<Char>(hex1st - 10)));
+ *bufLast = c;
+ n = next;
+ if (n == 0) break;
+ --bufLast;
+ }
+ return bufLast;
+ }
+
+ char* RenderWithoutSign(LOKI_SAFEFORMAT_SIGNED_LONG n, char* bufLast, unsigned int base,
+ bool uppercase) {
+ if (n != LONG_MIN) {
+ return RenderWithoutSign(static_cast<LOKI_SAFEFORMAT_UNSIGNED_LONG>(n < 0 ? -n : n),
+ bufLast, base, uppercase);
+ }
+ // annoying corner case
+ char* save = bufLast;
+ ++n;
+ bufLast = RenderWithoutSign(static_cast<LOKI_SAFEFORMAT_UNSIGNED_LONG>(n),
+ bufLast, base, uppercase);
+ --(*save);
+ return bufLast;
+ }
+
+ void Next() {
+ ++format_;
+ Advance();
+ }
+
+ void Advance() {
+ ResetAll();
+ const Char* begin = format_;
+ for (;;) {
+ if (*format_ == '%') {
+ if (format_[1] != '%') { // It's a format specifier
+ Write(begin, format_);
+ ++format_;
+ break;
+ }
+ // It's a "%%"
+ Write(begin, ++format_);
+ begin = ++format_;
+ continue;
+ }
+ if (*format_ == 0) {
+ Write(begin, format_);
+ break;
+ }
+ ++format_;
+ }
+ }
+
+ void ReadFlags() {
+ for (;; ++format_) {
+ switch (*format_) {
+ case '-': SetLeftJustify(); break;
+ case '+': SetShowSignAlways(); break;
+ case ' ': SetBlank(); break;
+ case '#': SetAlternateForm(); break;
+ case '0': SetFillZeros(); break;
+ default: return;
+ }
+ }
+ }
+
+ void ParseDecimalSizeT(size_t& dest) {
+ if (!std::isdigit(*format_, std::locale())) return;
+ size_t r = 0;
+ do {
+ // TODO: inefficient - rewrite
+ r *= 10;
+ r += *format_ - '0';
+ ++format_;
+ } while (std::isdigit(*format_, std::locale()));
+ dest = r;
+ }
+
+ void ReadWidth() {
+ ParseDecimalSizeT(width_);
+ }
+
+ void ReadPrecision() {
+ assert(*format_ == '.');
+ ++format_;
+ ParseDecimalSizeT(prec_);
+ }
+
+ void ReadModifiers() {
+ switch (*format_) {
+ case 'h': SetForceShort(); ++format_; break;
+ case 'l': ++format_; break;
+ // more (C99 and platform-specific modifiers) to come
+ }
+ }
+
+ void ReadLeaders() {
+ ReadFlags();
+ ReadWidth();
+ if (*format_ == '.') ReadPrecision();
+ ReadModifiers();
+ }
+
+ enum {
+ leftJustify = 1,
+ showSignAlways = 2,
+ blank = 4,
+ alternateForm = 8,
+ fillZeros = 16,
+ forceShort = 32
+ };
+
+ bool LeftJustify() const { return (flags_ & leftJustify) != 0; }
+ bool ShowSignAlways() const { return (flags_ & showSignAlways) != 0; }
+ void SetWidth(size_t w) { width_ = w; }
+ void SetLeftJustify() { flags_ |= leftJustify; }
+ void SetShowSignAlways() { flags_ |= showSignAlways; }
+ bool Blank() const { return (flags_ & blank) != 0; }
+ bool AlternateForm() const { return (flags_ & alternateForm) != 0; }
+ bool FillZeros() const { return (flags_ & fillZeros) != 0; }
+ bool ForceShort() const { return (flags_ & forceShort) != 0; }
+
+ void SetPrec(size_t p) { prec_ = p; }
+ void SetBlank() { flags_ |= blank; }
+ void SetAlternateForm() { flags_ |= alternateForm; }
+ void SetFillZeros() { flags_ |= fillZeros; }
+ void ResetFillZeros() { flags_ &= ~fillZeros; }
+ void SetForceShort() { flags_ |= forceShort; }
+
+ void ResetAll() {
+ assert(result_ != EOF);
+ width_ = 0;
+ prec_ = size_t(-1);
+ flags_ = 0;
+ }
+
+ // state
+ Device device_;
+ const Char* format_;
+ size_t width_;
+ size_t prec_;
+ unsigned int flags_;
+ LOKI_SAFEFORMAT_SIGNED_LONG result_;
+ };
+
+ LOKI_EXPORT
+ PrintfState<std::FILE*, char> Printf(const char* format);
+
+ LOKI_EXPORT
+ PrintfState<std::FILE*, char> Printf(const std::string& format);
+
+ LOKI_EXPORT
+ PrintfState<std::FILE*, char> FPrintf(std::FILE* f, const char* format);
+
+ LOKI_EXPORT
+ PrintfState<std::FILE*, char> FPrintf(std::FILE* f, const std::string& format);
+
+ LOKI_EXPORT
+ PrintfState<std::ostream&, char> FPrintf(std::ostream& f, const char* format);
+
+ LOKI_EXPORT
+ PrintfState<std::ostream&, char> FPrintf(std::ostream& f, const std::string& format);
+
+ LOKI_EXPORT
+ PrintfState<std::string&, char> SPrintf(std::string& s, const char* format);
+
+ LOKI_EXPORT
+ PrintfState<std::string&, char> SPrintf(std::string& s, const std::string& format);
+
+ template <class T, class Char>
+ PrintfState<T&, Char> XPrintf(T& device, const Char* format) {
+ return PrintfState<T&, Char>(device, format);
+ }
+
+ template <class T>
+ PrintfState<T&, char> XPrintf(T& device, const std::string& format) {
+ return PrintfState<T&, char>(device, format.c_str());
+ }
+
+ template <class Char, std::size_t N>
+ PrintfState<std::pair<Char*, std::size_t>, Char>
+ BufPrintf(Char (&buf)[N], const Char* format) {
+ std::pair<Char*, std::size_t> temp(buf, N);
+ return PrintfState<std::pair<Char*, std::size_t>, Char>(temp, format);
+ }
+
+}// namespace Loki
+
+
+#endif // end file guardian
+
bgstack15