diff --git a/doc/api.rst b/doc/api.rst index 8b15a5d5..d3d5ca5e 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -48,6 +48,8 @@ participate in an overload resolution if the latter is not a string. .. doxygenfunction:: format(const S&, Args&&...) .. doxygenfunction:: vformat(const S&, basic_format_args>>) +.. doxygenfunction:: fmt::format_to(OutputIt, string_view, Args&&...) +.. doxygenfunction:: fmt::format_to_n(OutputIt, size_t, string_view, Args&&...) .. doxygenfunction:: fmt::formatted_size(string_view, Args&&...) .. _print: diff --git a/include/fmt/core.h b/include/fmt/core.h index e0112c95..70105b43 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -735,13 +735,33 @@ template class buffer { } }; -// A buffer that writes to an output iterator when flushed. -template -class iterator_buffer : public buffer { - private: - enum { buffer_size = 256 }; +struct buffer_traits { + explicit buffer_traits(size_t) {} + size_t count() const { return 0; } + size_t limit(size_t size) { return size; } +}; +class fixed_buffer_traits { + private: + size_t count_ = 0; + size_t limit_; + + public: + explicit fixed_buffer_traits(size_t limit) : limit_(limit) {} + size_t count() const { return count_; } + size_t limit(size_t size) { + size_t n = limit_ - count_; + count_ += size; + return size < n ? size : n; + } +}; + +// A buffer that writes to an output iterator when flushed. +template +class iterator_buffer : public Traits, public buffer { + private: OutputIt out_; + enum { buffer_size = 256 }; T data_[buffer_size]; protected: @@ -751,14 +771,17 @@ class iterator_buffer : public buffer { void flush(); public: - explicit iterator_buffer(OutputIt out) - : buffer(data_, 0, buffer_size), out_(out) {} + explicit iterator_buffer(OutputIt out, size_t n = 0) + : Traits(n), + buffer(data_, 0, n < size_t(buffer_size) ? n : size_t(buffer_size)), + out_(out) {} ~iterator_buffer() { flush(); } OutputIt out() { flush(); return out_; } + size_t count() const { return Traits::count() + this->size(); } }; template class iterator_buffer : public buffer { @@ -766,7 +789,7 @@ template class iterator_buffer : public buffer { void grow(size_t) final {} public: - explicit iterator_buffer(T* out) : buffer(out, 0, ~size_t()) {} + explicit iterator_buffer(T* out, size_t = 0) : buffer(out, 0, ~size_t()) {} T* out() { return &*this->end(); } }; @@ -781,7 +804,7 @@ class iterator_buffer, Container& container_; protected: - void grow(size_t capacity) FMT_OVERRIDE { + void grow(size_t capacity) final { container_.resize(capacity); this->set(&container_[0], capacity); } @@ -789,7 +812,7 @@ class iterator_buffer, public: explicit iterator_buffer(Container& c) : buffer(c.size()), container_(c) {} - explicit iterator_buffer(std::back_insert_iterator out) + explicit iterator_buffer(std::back_insert_iterator out, size_t = 0) : iterator_buffer(get_container(out)) {} std::back_insert_iterator out() { return std::back_inserter(container_); @@ -1972,6 +1995,41 @@ inline OutputIt format_to(OutputIt out, const S& format_str, Args&&... args) { return vformat_to(out, to_string_view(format_str), vargs); } +template struct format_to_n_result { + /** Iterator past the end of the output range. */ + OutputIt out; + /** Total (not truncated) output size. */ + size_t size; +}; + +template ::value)> +inline format_to_n_result vformat_to_n( + OutputIt out, size_t n, basic_string_view format_str, + basic_format_args>> args) { + detail::iterator_buffer buf(out, + n); + detail::vformat_to(buf, format_str, args); + return {buf.out(), buf.count()}; +} + +/** + \rst + Formats arguments, writes up to ``n`` characters of the result to the output + iterator ``out`` and returns the total output size and the iterator past the + end of the output range. + \endrst + */ +template ::value&& + detail::is_output_iterator::value)> +inline format_to_n_result format_to_n(OutputIt out, size_t n, + const S& format_str, + const Args&... args) { + const auto& vargs = fmt::make_args_checked(format_str, args...); + return vformat_to_n(out, n, to_string_view(format_str), vargs); +} + /** Returns the number of characters in the output of ``format(format_str, args...)``. diff --git a/include/fmt/format.h b/include/fmt/format.h index 60b6f623..336d2c67 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -566,9 +566,9 @@ void buffer::append(const U* begin, const U* end) { } while (begin != end); } -template -void iterator_buffer::flush() { - out_ = std::copy(data_, data_ + this->size(), out_); +template +void iterator_buffer::flush() { + out_ = std::copy_n(data_, this->limit(this->size()), out_); this->clear(); } } // namespace detail @@ -3530,13 +3530,6 @@ using format_context_t = basic_format_context; template using format_args_t = basic_format_args>; -template struct format_to_n_result { - /** Iterator past the end of the output range. */ - OutputIt out; - /** Total (not truncated) output size. */ - size_t size; -}; - template using format_to_n_context FMT_DEPRECATED_ALIAS = buffer_context; @@ -3550,33 +3543,6 @@ make_format_to_n_args(const Args&... args) { return format_arg_store, Args...>(args...); } -template ::value)> -inline format_to_n_result vformat_to_n( - OutputIt out, size_t n, basic_string_view format_str, - basic_format_args>> args) { - auto it = vformat_to(detail::truncating_iterator(out, n), - format_str, args); - return {it.base(), it.count()}; -} - -/** - \rst - Formats arguments, writes up to ``n`` characters of the result to the output - iterator ``out`` and returns the total output size and the iterator past the - end of the output range. - \endrst - */ -template ::value&& - detail::is_output_iterator::value)> -inline format_to_n_result format_to_n(OutputIt out, size_t n, - const S& format_str, - const Args&... args) { - const auto& vargs = fmt::make_args_checked(format_str, args...); - return vformat_to_n(out, n, to_string_view(format_str), vargs); -} - template ::value), int>> std::basic_string detail::vformat( basic_string_view format_str,