diff --git a/fmt/ostream.h b/fmt/ostream.h index 7e13a5a71ed3..f529e24146b1 100644 --- a/fmt/ostream.h +++ b/fmt/ostream.h @@ -24,32 +24,27 @@ class FormatBuf : public std::basic_streambuf { typedef typename std::basic_streambuf::traits_type traits_type; Buffer &buffer_; - Char *start_; public: - FormatBuf(Buffer &buffer) : buffer_(buffer), start_(&buffer[0]) { - this->setp(start_, start_ + buffer_.capacity()); - } + FormatBuf(Buffer &buffer) : buffer_(buffer) {} - FormatBuf(Buffer &buffer, Char *start) : buffer_(buffer) , start_(start) { - this->setp(start_, start_ + buffer_.capacity()); - } + protected: + // The put-area is actually always empty. This makes the implementation + // simpler and has the advantage that the streambuf and the buffer are always + // in sync and sputc never writes into uninitialized memory. The obvious + // disadvantage is that each call to sputc always results in a (virtual) call + // to overflow. There is no disadvantage here for sputn since this always + // results in a call to xsputn. int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE { - if (!traits_type::eq_int_type(ch, traits_type::eof())) { - size_t buf_size = size(); - buffer_.resize(buf_size); - buffer_.reserve(buf_size * 2); - - start_ = &buffer_[0]; - start_[buf_size] = traits_type::to_char_type(ch); - this->setp(start_+ buf_size + 1, start_ + buf_size * 2); - } + if (!traits_type::eq_int_type(ch, traits_type::eof())) + buffer_.push_back(ch); return ch; } - size_t size() const { - return to_unsigned(this->pptr() - start_); + std::streamsize xsputn(const Char *s, std::streamsize count) FMT_OVERRIDE { + buffer_.append(s, s + count); + return count; } }; @@ -99,7 +94,7 @@ void format_arg(BasicFormatter &f, std::basic_ostream output(&format_buf); output << value; - BasicStringRef str(&buffer[0], format_buf.size()); + BasicStringRef str(&buffer[0], buffer.size()); typedef internal::MakeArg< BasicFormatter > MakeArg; format_str = f.format(format_str, MakeArg(str)); } @@ -128,14 +123,10 @@ typename std::enable_if< operator<<(BasicWriter &writer, const T &value) { FMT_STATIC_ASSERT(internal::is_streamable::value, "T must be Streamable"); - auto &buffer = writer.buffer(); - Char *start = &buffer[0] + buffer.size(); - - internal::FormatBuf format_buf(buffer, start); + internal::FormatBuf format_buf(writer.buffer()); std::basic_ostream output(&format_buf); output << value; - buffer.resize(buffer.size() + format_buf.size()); return writer; } #endif diff --git a/test/ostream-test.cc b/test/ostream-test.cc index a7341065eb01..0e84f5275afd 100644 --- a/test/ostream-test.cc +++ b/test/ostream-test.cc @@ -191,3 +191,26 @@ TEST(OStreamTest, WriteToOStreamMaxSize) { } while (size != 0); fmt::internal::write(os, w); } + +#if __cplusplus >= 201103L +struct Xs { + const size_t size; + const std::string s; + Xs() : size(200), s(size, 'x') {} +}; + +inline std::ostream& operator<<(std::ostream& os, Xs const& xs) { + return os << xs.s; +} + +TEST(OStreamTest, FormatBuf1) { + Xs xs; + fmt::MemoryWriter w; + int n = fmt::internal::INLINE_BUFFER_SIZE / xs.size + 1; + for (int i = 0; i < n; ++i) + w << xs; + EXPECT_EQ(w.size(), size_t(n * xs.size)); + w << xs; + EXPECT_EQ(w.size(), size_t((n + 1) * xs.size)); +} +#endif