From 198ebe9cf612cd9a3e7df3c1223c2497e60c0750 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Mon, 10 Dec 2012 17:16:08 -0800 Subject: [PATCH] Add a Buffer that stores initial data on stack. --- format.cc | 46 +++++++++++---------- format.h | 119 +++++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 125 insertions(+), 40 deletions(-) diff --git a/format.cc b/format.cc index 7eef716c..bbdf7007 100644 --- a/format.cc +++ b/format.cc @@ -113,7 +113,7 @@ void fmt::Formatter::FormatInt(T value, unsigned flags, int width, char type) { ++size; } while ((n /= 10) != 0); width = std::max(width, size); - buffer_.resize(buffer_.size() + width, fill); + buffer_.resize(buffer_.size() + width); p = &buffer_.back(); n = abs_value; do { @@ -129,7 +129,7 @@ void fmt::Formatter::FormatInt(T value, unsigned flags, int width, char type) { ++size; } while ((n >>= 4) != 0); width = std::max(width, size); - buffer_.resize(buffer_.size() + width, fill); + buffer_.resize(buffer_.size() + width); p = &buffer_.back(); n = abs_value; const char *digits = type == 'x' ? "0123456789abcdef" : "0123456789ABCDEF"; @@ -148,7 +148,7 @@ void fmt::Formatter::FormatInt(T value, unsigned flags, int width, char type) { ++size; } while ((n >>= 3) != 0); width = std::max(width, size); - buffer_.resize(buffer_.size() + width, fill); + buffer_.resize(buffer_.size() + width); p = &buffer_.back(); n = abs_value; do { @@ -162,10 +162,11 @@ void fmt::Formatter::FormatInt(T value, unsigned flags, int width, char type) { } if (sign) { if ((flags & ZERO_FLAG) != 0) - buffer_[start] = sign; + buffer_[start++] = sign; else - *p = sign; + *p-- = sign; } + std::fill(&buffer_[start], p + 1, fill); } template @@ -205,9 +206,8 @@ void fmt::Formatter::FormatDouble( // Format using snprintf. size_t offset = buffer_.size(); - buffer_.resize(buffer_.capacity()); for (;;) { - size_t size = buffer_.size() - offset; + size_t size = buffer_.capacity() - offset; int n = 0; if (width <= 0) { n = precision < 0 ? @@ -218,29 +218,28 @@ void fmt::Formatter::FormatDouble( snprintf(&buffer_[offset], size, format, width, value) : snprintf(&buffer_[offset], size, format, width, precision, value); } - if (n >= 0 && offset + n < buffer_.size()) { + if (n >= 0 && offset + n < buffer_.capacity()) { buffer_.resize(offset + n); return; } - buffer_.resize(n >= 0 ? offset + n + 1 : 2 * buffer_.size()); + buffer_.reserve(n >= 0 ? offset + n + 1 : 2 * buffer_.capacity()); } } void fmt::Formatter::Format() { - buffer_.reserve(500); const char *start = format_; const char *s = start; while (*s) { char c = *s++; if (c != '{' && c != '}') continue; if (*s == c) { - buffer_.insert(buffer_.end(), start, s); + buffer_.append(start, s); start = ++s; continue; } if (c == '}') throw FormatError("unmatched '}' in format"); - buffer_.insert(buffer_.end(), start, s - 1); + buffer_.append(start, s - 1); // Parse argument index. if (*s < '0' || *s > '9') @@ -248,7 +247,7 @@ void fmt::Formatter::Format() { unsigned arg_index = ParseUInt(s); if (arg_index >= args_.size()) ReportError(s, "argument index is out of range in format"); - Arg &arg = args_[arg_index]; + const Arg &arg = args_[arg_index]; unsigned flags = 0; int width = 0; @@ -328,14 +327,17 @@ void fmt::Formatter::Format() { case LONG_DOUBLE: FormatDouble(arg.long_double_value, flags, width, precision, type); break; - case CHAR: + case CHAR: { if (type && type != 'c') ReportUnknownType(type, "char"); - buffer_.reserve(std::max(width, 1)); - buffer_.push_back(arg.int_value); + std::size_t offset = buffer_.size(); + buffer_.resize(offset + std::max(width, 1)); + char *out = &buffer_[offset]; + *out++ = arg.int_value; if (width > 1) - buffer_.resize(buffer_.size() + width - 1, ' '); + std::fill_n(out, width - 1, ' '); break; + } case STRING: { if (type && type != 's') ReportUnknownType(type, "string"); @@ -343,10 +345,12 @@ void fmt::Formatter::Format() { size_t size = arg.size; if (size == 0 && *str) size = std::strlen(str); - buffer_.reserve(buffer_.size() + std::max(width, size)); - buffer_.insert(buffer_.end(), str, str + size); + size_t offset = buffer_.size(); + buffer_.resize(offset + std::max(width, size)); + char *out = &buffer_[offset]; + std::copy(str, str + size, out); if (width > size) - buffer_.resize(buffer_.size() + width - size, ' '); + std::fill_n(out + size, width - size, ' '); break; } case POINTER: @@ -365,7 +369,7 @@ void fmt::Formatter::Format() { break; } } - buffer_.insert(buffer_.end(), start, s + 1); + buffer_.append(start, s + 1); } fmt::ArgFormatter::~ArgFormatter() { diff --git a/format.h b/format.h index e6aebf46..f1573215 100644 --- a/format.h +++ b/format.h @@ -25,11 +25,89 @@ class ArgFormatter; template class ArgFormatterWithCallback; +// A buffer with the first SIZE elements stored in the object itself. +template +class Buffer { + private: + std::size_t size_; + std::size_t capacity_; + T *ptr_; + T data_[SIZE]; + + void Grow(std::size_t size) { + capacity_ = std::max(size, capacity_ + capacity_ / 2); + T *p = new T[capacity_]; + std::copy(ptr_, ptr_ + size_, p); + if (ptr_ != data_) + delete [] ptr_; + ptr_ = p; + } + + // Do not implement! + Buffer(const Buffer &); + void operator=(const Buffer &); + + public: + Buffer() : size_(0), capacity_(SIZE), ptr_(data_) {} + ~Buffer() { + if (ptr_ != data_) delete [] ptr_; + } + + std::size_t size() const { return size_; } + std::size_t capacity() const { return capacity_; } + + void resize(std::size_t size) { + if (size > capacity_) + Grow(size); + size_ = size; + } + + void reserve(std::size_t capacity) { + if (capacity < capacity_) + Grow(capacity - capacity_); + } + + void push_back(const T &value) { + if (size_ == capacity_) + Grow(size_ + 1); + ptr_[size_++] = value; + } + + void append(const T *begin, const T *end) { + std::ptrdiff_t size = end - begin; + if (size_ + size > capacity_) + Grow(size); + std::copy(begin, end, ptr_ + size_); + size_ += size; + } + + T &operator[](std::size_t index) { return ptr_[index]; } + const T &operator[](std::size_t index) const { return ptr_[index]; } + + const T &back() const { return ptr_[size_ - 1]; } + T &back() { return ptr_[size_ - 1]; } + + void clear() { size_ = 0; } + + void Take(Buffer &other) { + if (ptr_ != data_) + delete [] ptr_; + size_ = other.size_; + if (other.ptr_ != data_) { + ptr_ = other.ptr_; + other.ptr_ = 0; + } else { + ptr_ = data_; + std::copy(other.ptr_, other.ptr_ + size_, data_); + } + } +}; + // A sprintf-like formatter that automatically allocates enough storage to // fit all the output. class Formatter { private: - std::vector buffer_; // Output buffer. + Buffer buffer_; // Output buffer. enum Type { // Numeric types should go first. @@ -40,7 +118,8 @@ class Formatter { typedef void (Formatter::*FormatFunc)(const void *arg, int width); - // An argument. + public: + // A format argument. struct Arg { Type type; union { @@ -50,11 +129,9 @@ class Formatter { long long_value; unsigned long ulong_value; long double long_double_value; + const void *pointer_value; struct { - union { - const char *string_value; - const void *pointer_value; - }; + const char *string_value; std::size_t size; }; struct { @@ -63,6 +140,7 @@ class Formatter { }; }; + Arg() {} 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) {} @@ -78,15 +156,13 @@ class Formatter { : type(CUSTOM), custom_value(value), format(f) {} }; - std::vector args_; // Format arguments. + Buffer args_; // Format arguments. const char *format_; // Format string. friend class ArgFormatter; void Add(const Arg &arg) { - if (args_.empty()) - args_.reserve(10); args_.push_back(arg); } @@ -105,6 +181,11 @@ class Formatter { void Format(); + void Take(Formatter &f) { + buffer_.Take(f.buffer_); + args_.Take(f.args_); + } + public: Formatter() : format_(0) {} @@ -113,14 +194,10 @@ class Formatter { template ArgFormatterWithCallback FormatWithCallback(const char *format); - const char *c_str() const { return &buffer_[0]; } - const char *data() const { return &buffer_[0]; } std::size_t size() const { return buffer_.size(); } - void Swap(Formatter &f) { - buffer_.swap(f.buffer_); - args_.swap(f.args_); - } + const char *data() const { return &buffer_[0]; } + const char *c_str() const { return &buffer_[0]; } }; class ArgFormatter { @@ -255,14 +332,17 @@ void Formatter::FormatCustomArg(const void *arg, int width) { std::ostringstream os; os << value; std::string str(os.str()); - buffer_.reserve(buffer_.size() + std::max(width, str.size())); - buffer_.insert(buffer_.end(), str.begin(), str.end()); + std::size_t offset = buffer_.size(); + buffer_.resize(offset + std::max(width, str.size())); + char *out = &buffer_[offset]; + std::copy(str.begin(), str.end(), out); if (width > str.size()) - buffer_.resize(buffer_.size() + width - str.size(), ' '); + std::fill_n(out + str.size(), width - str.size(), ' '); } inline ArgFormatter Formatter::operator()(const char *format) { format_ = format; + args_.clear(); return ArgFormatter(*this); } @@ -270,6 +350,7 @@ template ArgFormatterWithCallback Formatter::FormatWithCallback(const char *format) { format_ = format; + args_.clear(); return ArgFormatterWithCallback(*this); } @@ -286,7 +367,7 @@ class FullFormat : public ArgFormatter { } FullFormat(const FullFormat& other) : ArgFormatter(other) { - formatter_.Swap(other.formatter_); + formatter_.Take(other.formatter_); } ~FullFormat() {