From b076df4f803af53e6316f981e0bd4189a0d4e40d Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Fri, 7 Dec 2012 08:31:09 -0800 Subject: [PATCH] Initial import --- README.md => README.rst | 0 format.cc | 184 ++++++++++++++++++++++++++++++ format.h | 247 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 431 insertions(+) rename README.md => README.rst (100%) create mode 100644 format.cc create mode 100644 format.h diff --git a/README.md b/README.rst similarity index 100% rename from README.md rename to README.rst diff --git a/format.cc b/format.cc new file mode 100644 index 00000000..dc0a7f72 --- /dev/null +++ b/format.cc @@ -0,0 +1,184 @@ +/* + Small, safe and fast printf-like formatting library for C++ + Author: Victor Zverovich + */ + +#include "format.h" + +#include +#include +#include + +using std::size_t; + +template +void fmt::Formatter::FormatArg( + const char *format, const T &arg, int width, int precision) { + size_t offset = buffer_.size(); + buffer_.resize(buffer_.capacity()); + for (;;) { + size_t size = buffer_.size() - offset; + int n = 0; + if (width < 0) { + n = precision < 0 ? + snprintf(&buffer_[offset], size, format, arg) : + snprintf(&buffer_[offset], size, format, precision, arg); + } else { + n = precision < 0 ? + snprintf(&buffer_[offset], size, format, width, arg) : + snprintf(&buffer_[offset], size, format, width, precision, arg); + } + if (n >= 0 && offset + n < buffer_.size()) { + buffer_.resize(offset + n); + return; + } + buffer_.resize(n >= 0 ? offset + n + 1 : 2 * buffer_.size()); + } +} + +void fmt::Formatter::Format() { + buffer_.reserve(500); + const char *start = format_; + const char *s = start; + for (; *s; ++s) { + if (*s != '{') continue; + buffer_.insert(buffer_.end(), start, s); + ++s; + + // Parse argument index. + unsigned arg_index = 0; + if ('0' <= *s && *s <= '9') { + do { + arg_index = arg_index * 10 + (*s++ - '0'); + } while ('0' <= *s && *s <= '9'); + } else { + throw FormatError("missing argument index in format string"); + } + // TODO: check if argument index is correct + + char arg_format[8]; // longest format: %+0*.*ld + char *arg_format_ptr = arg_format; + *arg_format_ptr++ = '%'; + + char type = 0; + int width = -1; + int precision = -1; + if (*s == ':') { + ++s; + if (*s == '+') + *arg_format_ptr++ = *s++; + if (*s == '0') + *arg_format_ptr++ = *s++; + + // Parse width. + if ('0' <= *s && *s <= '9') { + *arg_format_ptr++ = '*'; + width = 0; + do { + width = width * 10 + (*s++ - '0'); + } while ('0' <= *s && *s <= '9'); + } + + // Parse precision. + if (*s == '.') { + *arg_format_ptr++ = '.'; + *arg_format_ptr++ = '*'; + ++s; + precision = 0; + if ('0' <= *s && *s <= '9') { + do { + precision = precision * 10 + (*s++ - '0'); + } while ('0' <= *s && *s <= '9'); + } else { + // TODO: error + } + } + + // Parse type. + if (*s == 'f' || *s == 'g') + type = *s++; // TODO: check if the type matches + } + + if (*s++ != '}') + throw FormatError("single '{' encountered in format string"); + start = s; + + // Format argument. + Arg &arg = args_[arg_index]; + switch (arg.type) { + case CHAR: + if (width == -1 && precision == -1) { + buffer_.push_back(arg.int_value); + break; + } + *arg_format_ptr++ = 'c'; + *arg_format_ptr = '\0'; + FormatArg(arg_format, arg.int_value, width, precision); + break; + case INT: + *arg_format_ptr++ = 'd'; + *arg_format_ptr = '\0'; + FormatArg(arg_format, arg.int_value, width, precision); + break; + case UINT: + *arg_format_ptr++ = 'd'; + *arg_format_ptr = '\0'; + FormatArg(arg_format, arg.uint_value, width, precision); + break; + case LONG: + *arg_format_ptr++ = 'l'; + *arg_format_ptr++ = 'd'; + *arg_format_ptr = '\0'; + FormatArg(arg_format, arg.long_value, width, precision); + break; + case ULONG: + *arg_format_ptr++ = 'l'; + *arg_format_ptr++ = 'd'; + *arg_format_ptr = '\0'; + FormatArg(arg_format, arg.ulong_value, width, precision); + break; + case DOUBLE: + *arg_format_ptr++ = type ? type : 'g'; + *arg_format_ptr = '\0'; + FormatArg(arg_format, arg.double_value, width, precision); + break; + case LONG_DOUBLE: + *arg_format_ptr++ = 'l'; + *arg_format_ptr++ = 'g'; + *arg_format_ptr = '\0'; + FormatArg(arg_format, arg.long_double_value, width, precision); + break; + case STRING: + if (width == -1 && precision == -1) { + const char *str = arg.string_value; + buffer_.insert(buffer_.end(), str, str + std::strlen(str)); + break; + } + *arg_format_ptr++ = 's'; + *arg_format_ptr = '\0'; + FormatArg(arg_format, arg.string_value, width, precision); + break; + case WSTRING: + *arg_format_ptr++ = 'l'; + *arg_format_ptr++ = 's'; + *arg_format_ptr = '\0'; + FormatArg(arg_format, arg.wstring_value, width, precision); + break; + case POINTER: + *arg_format_ptr++ = 'p'; + *arg_format_ptr = '\0'; + FormatArg(arg_format, arg.pointer_value, width, precision); + break; + default: + assert(false); + break; + } + } + buffer_.insert(buffer_.end(), start, s + 1); +} + +fmt::ArgFormatter::~ArgFormatter() { + if (!formatter_) return; + FinishFormatting(); +} + diff --git a/format.h b/format.h new file mode 100644 index 00000000..2eb78cca --- /dev/null +++ b/format.h @@ -0,0 +1,247 @@ +/* + Small, safe and fast printf-like formatting library for C++ + Author: Victor Zverovich + */ + +#ifndef FORMAT_H_ +#define FORMAT_H_ + +#include +#include +#include +#include +#include + +namespace format { + +class FormatError : public std::runtime_error { + public: + FormatError(const std::string &message) : std::runtime_error(message) {} +}; + +class ArgFormatter; + +template +class ArgFormatterWithCallback; + +// A sprintf-like formatter that automatically allocates enough storage to +// fit all the output. +class Formatter { + private: + std::vector buffer_; + + enum Type { + CHAR, INT, UINT, LONG, ULONG, DOUBLE, LONG_DOUBLE, STRING, WSTRING, POINTER + }; + + struct Arg { + Type type; + union { + int int_value; + unsigned uint_value; + double double_value; + long long_value; + unsigned long ulong_value; + long double long_double_value; + const char *string_value; + const wchar_t *wstring_value; + const void *pointer_value; + }; + + explicit Arg(char value) : type(CHAR), int_value(value) {} + explicit Arg(int value) : type(INT), int_value(value) {} + explicit Arg(unsigned value) : type(UINT), uint_value(value) {} + explicit Arg(long value) : type(LONG), long_value(value) {} + explicit Arg(unsigned long value) : type(ULONG), ulong_value(value) {} + explicit Arg(double value) : type(DOUBLE), double_value(value) {} + explicit Arg(long double value) + : type(LONG_DOUBLE), long_double_value(value) {} + explicit Arg(const char *value) : type(STRING), string_value(value) {} + explicit Arg(const wchar_t *value) : type(WSTRING), wstring_value(value) {} + explicit Arg(const void *value) : type(POINTER), pointer_value(value) {} + }; + + std::vector args_; + + // Pointer to the part of the format string that hasn't been written yet. + const char *format_; + + friend class ArgFormatter; + + void Add(const Arg &arg) { + if (args_.empty()) + args_.reserve(10); + args_.push_back(arg); + } + + template + void FormatArg(const char *format, const T &arg, int width, int precision); + + void Format(); + + public: + Formatter() : format_(0) {} + + ArgFormatter operator()(const char *format); + + template + ArgFormatterWithCallback FormatWithCallback(const char *format); + + const char *c_str() const { return &buffer_[0]; } + std::size_t size() const { return buffer_.size() - 1; } +}; + +class ArgFormatter { + private: + friend class Formatter; + + protected: + mutable Formatter *formatter_; + + ArgFormatter(const ArgFormatter& other) : formatter_(other.formatter_) { + other.formatter_ = 0; + } + + ArgFormatter& operator=(const ArgFormatter& other) { + formatter_ = other.formatter_; + other.formatter_ = 0; + return *this; + } + + Formatter *FinishFormatting() const { + Formatter *f = formatter_; + if (f) { + formatter_ = 0; + f->Format(); + } + return f; + } + + public: + explicit ArgFormatter(Formatter &f) : formatter_(&f) {} + ~ArgFormatter(); + + friend const char *c_str(const ArgFormatter &af) { + return af.FinishFormatting()->c_str(); + } + + friend std::string str(const ArgFormatter &af) { + return af.FinishFormatting()->c_str(); + } + + ArgFormatter &operator<<(char value) { + formatter_->Add(Formatter::Arg(value)); + return *this; + } + + ArgFormatter &operator<<(int value) { + formatter_->Add(Formatter::Arg(value)); + return *this; + } + + ArgFormatter &operator<<(unsigned value) { + formatter_->Add(Formatter::Arg(value)); + return *this; + } + + ArgFormatter &operator<<(long value) { + formatter_->Add(Formatter::Arg(value)); + return *this; + } + + ArgFormatter &operator<<(unsigned long value) { + formatter_->Add(Formatter::Arg(value)); + return *this; + } + + ArgFormatter &operator<<(double value) { + formatter_->Add(Formatter::Arg(value)); + return *this; + } + + ArgFormatter &operator<<(long double value) { + formatter_->Add(Formatter::Arg(value)); + return *this; + } + + ArgFormatter &operator<<(const char *value) { + formatter_->Add(Formatter::Arg(value)); + return *this; + } + + ArgFormatter &operator<<(const void *value) { + formatter_->Add(Formatter::Arg(value)); + return *this; + } + + // This method is not implemented intentionally to disallow output of + // arbitrary pointers. If you want to output a pointer cast it to void*. + template + ArgFormatter &operator<<(const T *value); +}; + +template +class ArgFormatterWithCallback : public ArgFormatter { + public: + explicit ArgFormatterWithCallback(Formatter &f) : ArgFormatter(f) {} + + ~ArgFormatterWithCallback() { + if (!formatter_) return; + Callback callback; + callback(*formatter_); + } +}; + +inline ArgFormatter Formatter::operator()(const char *format) { + format_ = format; + return ArgFormatter(*this); +} + +template +ArgFormatterWithCallback +Formatter::FormatWithCallback(const char *format) { + format_ = format; + return ArgFormatterWithCallback(*this); +} + +class Format : public ArgFormatter { + private: + Formatter formatter_; + + // Do not implement. + Format(const Format&); + Format& operator=(const Format&); + + public: + explicit Format(const char *format) : ArgFormatter(formatter_) { + ArgFormatter::operator=(formatter_(format)); + } + + ~Format() { + FinishFormatting(); + } +}; + +class Print : public ArgFormatter { + private: + Formatter formatter_; + + // Do not implement. + Print(const Print&); + Print& operator=(const Print&); + + public: + explicit Print(const char *format) : ArgFormatter(formatter_) { + ArgFormatter::operator=(formatter_(format)); + } + + ~Print() { + FinishFormatting(); + std::fwrite(formatter_.c_str(), 1, formatter_.size(), stdout); + } +}; +} + +namespace fmt = format; + +#endif // FORMAT_H_