diff --git a/libcxx/include/__format/buffer.h b/libcxx/include/__format/buffer.h --- a/libcxx/include/__format/buffer.h +++ b/libcxx/include/__format/buffer.h @@ -11,12 +11,14 @@ #define _LIBCPP___FORMAT_BUFFER_H #include <__algorithm/copy_n.h> +#include <__algorithm/min.h> #include <__algorithm/unwrap_iter.h> #include <__config> #include <__debug> #include <__format/formatter.h> // for __char_type TODO FMT Move the concept? #include <__iterator/back_insert_iterator.h> #include <__iterator/concepts.h> +#include <__iterator/incrementable_traits.h> #include <__iterator/iterator_traits.h> #include <__iterator/wrap_iter.h> #include <__utility/move.h> @@ -28,6 +30,9 @@ #pragma GCC system_header #endif +_LIBCPP_PUSH_MACROS +#include <__undef_macros> + _LIBCPP_BEGIN_NAMESPACE_STD #if _LIBCPP_STD_VER > 17 @@ -38,6 +43,13 @@ // to support compilers with partial C++20 support. #if !defined(_LIBCPP_HAS_NO_CONCEPTS) +// This is part of D113831 +template +struct _LIBCPP_TEMPLATE_VIS format_to_n_result { + _OutIt out; + iter_difference_t<_OutIt> size; +}; + namespace __format { /// A "buffer" that handles writing to the proper iterator. @@ -285,6 +297,102 @@ size_t __size_{0}; }; +/// The base of a buffer that counts and limits the number of insertions. +template +requires(output_iterator<_OutIt, const _CharT&>) struct _LIBCPP_TEMPLATE_VIS + __format_to_n_buffer_base { + using _Size = iter_difference_t<_OutIt>; + +public: + _LIBCPP_HIDE_FROM_ABI explicit __format_to_n_buffer_base(_OutIt __out_it, + _Size __n) + : __writer_(_VSTD::move(__out_it)), __n_(__n) {} + + _LIBCPP_HIDE_FROM_ABI void flush(_CharT* __ptr, size_t __size) { + if (_Size(__size_) <= __n_) + __writer_.flush(__ptr, _VSTD::min(_Size(__size), __n_ - __size_)); + __size_ += __size; + } + +protected: + __internal_storage<_CharT> __storage_; + __output_buffer<_CharT> __output_{__storage_.begin(), __storage_.capacity(), + this}; + typename __writer_selector<_OutIt, _CharT>::type __writer_; + + _Size __n_; + _Size __size_{0}; +}; + +/// The base of a buffer that counts and limits the number of insertions. +/// +/// This version is used when \c __enable_direct_output<_OutIt, _CharT> == true. +/// +/// This class limits the size available the the direct writer so it will not +/// exceed the maximum number of code units. +template +requires(output_iterator<_OutIt, const _CharT&>) class _LIBCPP_TEMPLATE_VIS + __format_to_n_buffer_base<_OutIt, _CharT, true> { + using _Size = iter_difference_t<_OutIt>; + +public: + _LIBCPP_HIDE_FROM_ABI explicit __format_to_n_buffer_base(_OutIt __out_it, + _Size __n) + : __output_(_VSTD::__unwrap_iter(__out_it), __n, this), + __writer_(_VSTD::move(__out_it)) { + _LIBCPP_ASSERT(__n > 0, + "The buffer doesn't work properly with negative sizes. For " + "efficiency the caller is also expected to handle __n == 0"); + } + + _LIBCPP_HIDE_FROM_ABI void flush(_CharT* __ptr, size_t __size) { + // A flush to the direct writer happens in two occasions: + // - The format function has written the maximum number of allowed code + // units. At this point it's no longer valid to write to this writer. So + // switch to the internal storage. This internal storage doesn't need to + // be written anywhere so the flush for that storage writes no output. + // - The format_to_n function is finished. In this case there's no need to + // switch the buffer, but for simplicity the buffers are still switched. + if (__size_ == 0) { + __writer_.flush(__ptr, __size); + __output_.reset(__storage_.begin(), __storage_.capacity()); + } + + __size_ += __size; + } + +protected: + __internal_storage<_CharT> __storage_; + __output_buffer<_CharT> __output_; + __writer_direct<_OutIt, _CharT> __writer_; + + _Size __size_{0}; +}; + +/// The buffer that counts and limits the number of insertions. +template +requires(output_iterator<_OutIt, const _CharT&>) struct _LIBCPP_TEMPLATE_VIS + __format_to_n_buffer final + : public __format_to_n_buffer_base< + _OutIt, _CharT, __enable_direct_output<_OutIt, _CharT>> { + using _Base = + __format_to_n_buffer_base<_OutIt, _CharT, + __enable_direct_output<_OutIt, _CharT>>; + using _Size = iter_difference_t<_OutIt>; + +public: + _LIBCPP_HIDE_FROM_ABI explicit __format_to_n_buffer(_OutIt __out_it, + _Size __n) + : _Base(_VSTD::move(__out_it), __n) {} + _LIBCPP_HIDE_FROM_ABI auto make_output_iterator() { + return this->__output_.make_output_iterator(); + } + + _LIBCPP_HIDE_FROM_ABI format_to_n_result<_OutIt> result() && { + this->__output_.flush(); + return {_VSTD::move(this->__writer_).out(), this->__size_}; + } +}; } // namespace __format #endif // !defined(_LIBCPP_HAS_NO_CONCEPTS) @@ -292,4 +400,6 @@ _LIBCPP_END_NAMESPACE_STD +_LIBCPP_POP_MACROS + #endif // _LIBCPP___FORMAT_BUFFER_H diff --git a/libcxx/include/format b/libcxx/include/format --- a/libcxx/include/format +++ b/libcxx/include/format @@ -280,6 +280,7 @@ #include <__format/formatter_integer.h> #include <__format/formatter_string.h> #include <__format/parser_std_format_spec.h> +#include <__iterator/incrementable_traits.h> #include <__variant/monostate.h> #include #include @@ -598,23 +599,19 @@ } #endif -template -struct _LIBCPP_TEMPLATE_VIS format_to_n_result { - _OutIt out; - iter_difference_t<_OutIt> size; -}; - template _OutIt, class... _Args> _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT format_to_n_result<_OutIt> format_to_n(_OutIt __out_it, iter_difference_t<_OutIt> __n, string_view __fmt, const _Args&... __args) { - // TODO FMT Improve PoC: using std::string is inefficient. - string __str = _VSTD::vformat(__fmt, _VSTD::make_format_args(__args...)); - iter_difference_t<_OutIt> __s = __str.size(); - iter_difference_t<_OutIt> __m = - _VSTD::clamp(__n, iter_difference_t<_OutIt>(0), __s); - __out_it = _VSTD::copy_n(__str.begin(), __m, _VSTD::move(__out_it)); - return {_VSTD::move(__out_it), __s}; + if (__n <= 0) [[unlikely]] + return {_VSTD::move(__out_it), + iter_difference_t<_OutIt>(formatted_size(__fmt, __args...))}; + + __format::__format_to_n_buffer<_OutIt, char> __buffer{_VSTD::move(__out_it), + __n}; + _VSTD::__vformat_to(__buffer.make_output_iterator(), __fmt, + basic_format_args{_VSTD::make_format_args(__args...)}); + return _VSTD::move(__buffer).result(); } #ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS @@ -622,13 +619,15 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT format_to_n_result<_OutIt> format_to_n(_OutIt __out_it, iter_difference_t<_OutIt> __n, wstring_view __fmt, const _Args&... __args) { - // TODO FMT Improve PoC: using std::string is inefficient. - wstring __str = _VSTD::vformat(__fmt, _VSTD::make_wformat_args(__args...)); - iter_difference_t<_OutIt> __s = __str.size(); - iter_difference_t<_OutIt> __m = - _VSTD::clamp(__n, iter_difference_t<_OutIt>(0), __s); - __out_it = _VSTD::copy_n(__str.begin(), __m, _VSTD::move(__out_it)); - return {_VSTD::move(__out_it), __s}; + if (__n <= 0) [[unlikely]] + return {_VSTD::move(__out_it), + iter_difference_t<_OutIt>(formatted_size(__fmt, __args...))}; + + __format::__format_to_n_buffer<_OutIt, wchar_t> __buffer{ + _VSTD::move(__out_it), __n}; + _VSTD::__vformat_to(__buffer.make_output_iterator(), __fmt, + basic_format_args{_VSTD::make_wformat_args(__args...)}); + return _VSTD::move(__buffer).result(); } #endif @@ -745,14 +744,16 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT format_to_n_result<_OutIt> format_to_n(_OutIt __out_it, iter_difference_t<_OutIt> __n, locale __loc, string_view __fmt, const _Args&... __args) { - // TODO FMT Improve PoC: using std::string is inefficient. - string __str = _VSTD::vformat(_VSTD::move(__loc), __fmt, - _VSTD::make_format_args(__args...)); - iter_difference_t<_OutIt> __s = __str.size(); - iter_difference_t<_OutIt> __m = - _VSTD::clamp(__n, iter_difference_t<_OutIt>(0), __s); - __out_it = _VSTD::copy_n(__str.begin(), __m, _VSTD::move(__out_it)); - return {_VSTD::move(__out_it), __s}; + if (__n <= 0) [[unlikely]] + return {_VSTD::move(__out_it), + iter_difference_t<_OutIt>(formatted_size(__fmt, __args...))}; + + __format::__format_to_n_buffer<_OutIt, char> __buffer{_VSTD::move(__out_it), + __n}; + _VSTD::__vformat_to(__buffer.make_output_iterator(), _VSTD::move(__loc), + __fmt, + basic_format_args{_VSTD::make_format_args(__args...)}); + return _VSTD::move(__buffer).result(); } #ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS @@ -760,14 +761,16 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT format_to_n_result<_OutIt> format_to_n(_OutIt __out_it, iter_difference_t<_OutIt> __n, locale __loc, wstring_view __fmt, const _Args&... __args) { - // TODO FMT Improve PoC: using std::string is inefficient. - wstring __str = _VSTD::vformat(_VSTD::move(__loc), __fmt, - _VSTD::make_wformat_args(__args...)); - iter_difference_t<_OutIt> __s = __str.size(); - iter_difference_t<_OutIt> __m = - _VSTD::clamp(__n, iter_difference_t<_OutIt>(0), __s); - __out_it = _VSTD::copy_n(__str.begin(), __m, _VSTD::move(__out_it)); - return {_VSTD::move(__out_it), __s}; + if (__n <= 0) [[unlikely]] + return {_VSTD::move(__out_it), + iter_difference_t<_OutIt>(formatted_size(__fmt, __args...))}; + + __format::__format_to_n_buffer<_OutIt, wchar_t> __buffer{ + _VSTD::move(__out_it), __n}; + _VSTD::__vformat_to(__buffer.make_output_iterator(), _VSTD::move(__loc), + __fmt, + basic_format_args{_VSTD::make_wformat_args(__args...)}); + return _VSTD::move(__buffer).result(); } #endif