This patch replaces the _Iter_sink specialization for contiguous
iterators of _CharT, with separate _Ptr_sink. This allow further
reduce the number of instantiations as single _Ptr_sink<_CharT>
specialization is used for all such iterators.
To make _Ptr_sink independent of iterator type, we no longer store
value of the iterator as _M_first member (the address pointed to is
still stored inside span). To produce the actual iterator position,
_Ptr_sink::_M_finish requires original pointer to be passed. As any
contiguous iterator is copyable, there is no issue in passing them
to both _Ptr_sink constructor and _M_finish method.
The __do_vformat_to method is reworked, to use _Ptr_sink when possible
(__contiguous_char_iterator is true). The implementation approach is
also changed to split the function into three constexpr branches:
_Sink_iter, contiguous char iterator, other iterators. The latter two
wrap iterators in _Ptr_sink and _Iter_sink respectively and forward
to _Sink_iter specialization.
To reduce the duplication, we introduce __do_format_to_n helper function,
that also handles wrapping iterator into _Ptr_sink and _Iter_sink as
appropriate.
libstdc++-v3/ChangeLog:
* include/std/format (__format::_Ptr_sink): Reworked
from _Iter_sink specialization below.
(__format::_Iter_sink<_CharT, contiguous_iterator _OutIter>):
Renamed and reworked to _Ptr_sink.
(__format::__contiguous_char_iter, __format::__do_vformat_to_n):
Define.
(__format::__do_vformat_to): Use _Ptr_sink when appropriate,
and delegate to _Sink_iter specialization.
(std::format_to_n): Delegate to __do_vformat_to_n.
(__format::_Counting_sink): Use _Ptr_sink as base class.
---
v2
- fixed typos in commit description
- remove __do_vformat_to_n forward declaration.
The changes to __do_vformat_to should reduce the code size, as all
non _Sink_iter are now simply delegated to _Sink_iter specialization
(so the code to handle {} is not repeated in each of them).
libstdc++-v3/include/std/format | 334 ++++++++++++++++++--------------
1 file changed, 185 insertions(+), 149 deletions(-)
diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format
index 9b5d5d499f5..9984ae09f9b 100644
--- a/libstdc++-v3/include/std/format
+++ b/libstdc++-v3/include/std/format
@@ -3699,19 +3699,19 @@ namespace __format
}
};
- // Partial specialization for contiguous iterators.
+ // Used for contiguous iterators.
// No buffer is used, characters are written straight to the iterator.
// We do not know the size of the output range, so the span size just grows
// as needed. The end of the span might be an invalid pointer outside the
// valid range, but we never actually call _M_span.end(). This class does
// not introduce any invalid pointer arithmetic or overflows that would not
// have happened anyway.
- template<typename _CharT, contiguous_iterator _OutIter>
- requires same_as<iter_value_t<_OutIter>, _CharT>
- class _Iter_sink<_CharT, _OutIter> : public _Sink<_CharT>
+ template<typename _CharT>
+ class _Ptr_sink : public _Sink<_CharT>
{
- _OutIter _M_first;
- iter_difference_t<_OutIter> _M_max = -1;
+ static constexpr size_t _S_no_limit = size_t(-1);
+
+ size_t _M_max;
protected:
size_t _M_count = 0;
private:
@@ -3726,7 +3726,7 @@ namespace __format
auto __s = this->_M_used();
- if (_M_max >= 0)
+ if (_M_max != _S_no_limit)
{
_M_count += __s.size();
// Span was already sized for the maximum character count,
@@ -3738,7 +3738,7 @@ namespace __format
{
// No maximum character count. Just extend the span to allow
// writing more characters to it.
- this->_M_reset({__s.data(), __s.size() + 1024}, __s.size());
+ _M_rebuf(__s.data(), __s.size() + 1024, __s.size());
}
}
@@ -3756,74 +3756,93 @@ namespace __format
auto __avail = this->_M_unused();
if (__n > __avail.size())
{
- if (_M_max >= 0)
+ if (_M_max != _S_no_limit)
return {}; // cannot grow
auto __s = this->_M_used();
- this->_M_reset({__s.data(), __s.size() + __n}, __s.size());
+ _M_rebuf(__s.data(), __s.size() + __n, __s.size());
}
return { this };
}
private:
- static span<_CharT>
- _S_make_span(_CharT* __ptr, iter_difference_t<_OutIter> __n,
- span<_CharT> __buf) noexcept
- {
- if (__n == 0)
- return __buf; // Only write to the internal buffer.
-
- if (__n > 0)
- {
- if constexpr (!is_integral_v<iter_difference_t<_OutIter>>
- || sizeof(__n) > sizeof(size_t))
- {
- // __int128 or __detail::__max_diff_type
- auto __m = iter_difference_t<_OutIter>((size_t)-1);
- if (__n > __m)
- __n = __m;
- }
- return {__ptr, (size_t)__n};
- }
+ template<typename _IterDifference>
+ static size_t
+ _S_trim_max(_IterDifference __max)
+ {
+ if (__max < 0)
+ return _S_no_limit;
+ if constexpr (!is_integral_v<_IterDifference> || sizeof(__max) >
sizeof(size_t))
+ // __int128 or __detail::__max_diff_type
+ if (_IterDifference((size_t)-1) < __max)
+ return _S_no_limit;
+ return size_t(__max);
+ }
-#if __has_builtin(__builtin_dynamic_object_size)
- if (size_t __bytes = __builtin_dynamic_object_size(__ptr, 2))
- return {__ptr, __bytes / sizeof(_CharT)};
-#endif
- // Avoid forming a pointer to a different memory page.
- const auto __off = reinterpret_cast<__UINTPTR_TYPE__>(__ptr) % 1024;
- __n = (1024 - __off) / sizeof(_CharT);
- if (__n > 0) [[likely]]
- return {__ptr, static_cast<size_t>(__n)};
- else // Misaligned/packed buffer of wchar_t?
- return {__ptr, 1};
+ [[__gnu__::__always_inline__]]
+ void
+ _M_rebuf(_CharT* __ptr, size_t __total, size_t __inuse = 0)
+ {
+ std::span<_CharT> __span(__ptr, __total);
+ this->_M_reset(__span, __inuse);
}
public:
explicit
- _Iter_sink(_OutIter __out, iter_difference_t<_OutIter> __n = -1) noexcept
- : _Sink<_CharT>(_S_make_span(std::to_address(__out), __n, _M_buf)),
- _M_first(__out), _M_max(__n)
- { }
-
- format_to_n_result<_OutIter>
- _M_finish() &&
- {
- auto __s = this->_M_used();
- if (__s.data() == _M_buf)
- {
- // Switched to internal buffer, so must have written _M_max.
- iter_difference_t<_OutIter> __count(_M_count + __s.size());
- return { _M_first + _M_max, __count };
- }
- else // Not using internal buffer yet
+ _Ptr_sink(_CharT* __ptr, size_t __n = _S_no_limit) noexcept
+ : _Sink<_CharT>(_M_buf), _M_max(__n)
+ {
+ if (__n == 0)
+ return; // Only write to the internal buffer.
+ else if (__n != _S_no_limit)
+ _M_rebuf(__ptr, __n);
+#if __has_builtin(__builtin_dynamic_object_size)
+ else if (size_t __bytes = __builtin_dynamic_object_size(__ptr, 2))
+ _M_rebuf(__ptr, __bytes / sizeof(_CharT));
+#endif
+ else
{
- iter_difference_t<_OutIter> __count(__s.size());
- return { _M_first + __count, __count };
+ // Avoid forming a pointer to a different memory page.
+ const auto __off = reinterpret_cast<__UINTPTR_TYPE__>(__ptr) % 1024;
+ __n = (1024 - __off) / sizeof(_CharT);
+ if (__n > 0) [[likely]]
+ _M_rebuf(__ptr, __n);
+ else // Misaligned/packed buffer of wchar_t?
+ _M_rebuf(__ptr, 1);
}
}
+
+ template<contiguous_iterator _OutIter>
+ explicit
+ _Ptr_sink(_OutIter __out, iter_difference_t<_OutIter> __n = -1)
+ : _Ptr_sink(std::to_address(__out), _S_trim_max(__n))
+ { }
+
+ template<contiguous_iterator _OutIter>
+ format_to_n_result<_OutIter>
+ _M_finish(_OutIter __first) const
+ {
+ auto __s = this->_M_used();
+ if (__s.data() == _M_buf)
+ {
+ // Switched to internal buffer, so must have written _M_max.
+ iter_difference_t<_OutIter> __max(_M_max);
+ iter_difference_t<_OutIter> __count(_M_count + __s.size());
+ return { __first + __max, __count };
+ }
+ else // Not using internal buffer yet
+ {
+ iter_difference_t<_OutIter> __count(__s.size());
+ return { __first + __count, __count };
+ }
+ }
};
+ template<typename _CharT, typename _OutIter>
+ concept __contiguous_char_iter
+ = contiguous_iterator<_OutIter>
+ && same_as<iter_value_t<_OutIter>, _CharT>;
+
// A sink for handling the padded outputs (_M_padwidth) or truncated
// (_M_maxwidth). The handling is done by writting to buffer (_Str_strink)
// until sufficient number of characters is written. After that if sequence
@@ -5250,92 +5269,113 @@ namespace __format
const basic_format_args<_Context>& __args,
const locale* __loc)
{
- _Iter_sink<_CharT, _Out> __sink(std::move(__out));
- _Sink_iter<_CharT> __sink_out;
-
if constexpr (is_same_v<_Out, _Sink_iter<_CharT>>)
- __sink_out = __out; // Already a sink iterator, safe to use post-move.
- else
- __sink_out = __sink.out();
-
- if constexpr (is_same_v<_CharT, char>)
- // Fast path for "{}" format strings and simple format arg types.
- if (__fmt.size() == 2 && __fmt[0] == '{' && __fmt[1] == '}')
- {
- bool __done = false;
- __format::__visit_format_arg([&](auto& __arg) {
- using _Tp = remove_cvref_t<decltype(__arg)>;
- if constexpr (is_same_v<_Tp, bool>)
- {
- size_t __len = 4 + !__arg;
- const char* __chars[] = { "false", "true" };
- if (auto __res = __sink_out._M_reserve(__len))
+ {
+ if constexpr (is_same_v<_CharT, char>)
+ // Fast path for "{}" format strings and simple format arg types.
+ if (__fmt.size() == 2 && __fmt[0] == '{' && __fmt[1] == '}')
+ {
+ bool __done = false;
+ __format::__visit_format_arg([&](auto& __arg) {
+ using _Tp = remove_cvref_t<decltype(__arg)>;
+ if constexpr (is_same_v<_Tp, bool>)
{
- __builtin_memcpy(__res.get(), __chars[__arg], __len);
- __res._M_bump(__len);
- __done = true;
+ size_t __len = 4 + !__arg;
+ const char* __chars[] = { "false", "true" };
+ if (auto __res = __out._M_reserve(__len))
+ {
+ __builtin_memcpy(__res.get(), __chars[__arg], __len);
+ __res._M_bump(__len);
+ __done = true;
+ }
}
- }
- else if constexpr (is_same_v<_Tp, char>)
- {
- if (auto __res = __sink_out._M_reserve(1))
+ else if constexpr (is_same_v<_Tp, char>)
{
- *__res.get() = __arg;
- __res._M_bump(1);
- __done = true;
+ if (auto __res = __out._M_reserve(1))
+ {
+ *__res.get() = __arg;
+ __res._M_bump(1);
+ __done = true;
+ }
}
- }
- else if constexpr (is_integral_v<_Tp>)
- {
- make_unsigned_t<_Tp> __uval;
- const bool __neg = __arg < 0;
- if (__neg)
- __uval = make_unsigned_t<_Tp>(~__arg) + 1u;
- else
- __uval = __arg;
- const auto __n = __detail::__to_chars_len(__uval);
- if (auto __res = __sink_out._M_reserve(__n + __neg))
+ else if constexpr (is_integral_v<_Tp>)
{
- auto __ptr = __res.get();
- *__ptr = '-';
- __detail::__to_chars_10_impl(__ptr + (int)__neg, __n,
- __uval);
- __res._M_bump(__n + __neg);
- __done = true;
+ make_unsigned_t<_Tp> __uval;
+ const bool __neg = __arg < 0;
+ if (__neg)
+ __uval = make_unsigned_t<_Tp>(~__arg) + 1u;
+ else
+ __uval = __arg;
+ const auto __n = __detail::__to_chars_len(__uval);
+ if (auto __res = __out._M_reserve(__n + __neg))
+ {
+ auto __ptr = __res.get();
+ *__ptr = '-';
+ __detail::__to_chars_10_impl(__ptr + (int)__neg, __n,
+ __uval);
+ __res._M_bump(__n + __neg);
+ __done = true;
+ }
}
- }
- else if constexpr (is_convertible_v<_Tp, string_view>)
- {
- string_view __sv = __arg;
- if (auto __res = __sink_out._M_reserve(__sv.size()))
+ else if constexpr (is_convertible_v<_Tp, string_view>)
{
- __builtin_memcpy(__res.get(), __sv.data(), __sv.size());
- __res._M_bump(__sv.size());
- __done = true;
+ string_view __sv = __arg;
+ if (auto __res = __out._M_reserve(__sv.size()))
+ {
+ __builtin_memcpy(__res.get(), __sv.data(),
__sv.size());
+ __res._M_bump(__sv.size());
+ __done = true;
+ }
}
- }
- }, __args.get(0));
-
- if (__done)
- {
- if constexpr (is_same_v<_Out, _Sink_iter<_CharT>>)
- return __sink_out;
- else
- return std::move(__sink)._M_finish().out;
+ }, __args.get(0));
+
+ if (__done)
+ return __out;
}
- }
-
- auto __ctx = __loc == nullptr
- ? _Context(__args, __sink_out)
- : _Context(__args, __sink_out, *__loc);
- _Formatting_scanner<_Sink_iter<_CharT>, _CharT> __scanner(__ctx, __fmt);
- __scanner._M_scan();
+
+ auto __ctx = __loc == nullptr
+ ? _Context(__args, __out)
+ : _Context(__args, __out, *__loc);
+ _Formatting_scanner<_Sink_iter<_CharT>, _CharT> __scanner(__ctx,
__fmt);
+ __scanner._M_scan();
+ return __out;
+ }
+ else if constexpr (__contiguous_char_iter<_CharT, _Out>)
+ {
+ _Ptr_sink<_CharT> __sink(__out);
+ __format::__do_vformat_to(__sink.out(), __fmt, __args, __loc);
+ return std::move(__sink)._M_finish(__out).out;
+ }
+ else
+ {
+ _Iter_sink<_CharT, _Out> __sink(std::move(__out));
+ __format::__do_vformat_to(__sink.out(), __fmt, __args, __loc);
+ return std::move(__sink)._M_finish().out;
+ }
+ }
- if constexpr (is_same_v<_Out, _Sink_iter<_CharT>>)
- return __ctx.out();
+ template<typename _Out, typename _CharT>
+ format_to_n_result<_Out>
+ __do_vformat_to_n(_Out __out, iter_difference_t<_Out> __n,
+ basic_string_view<_CharT> __fmt,
+ const type_identity_t<
+ basic_format_args<__format_context<_CharT>>>& __args,
+ const locale* __loc)
+ {
+ if constexpr (__contiguous_char_iter<_CharT, _Out>)
+ {
+ _Ptr_sink<_CharT> __sink(__out, __n);
+ __format::__do_vformat_to(__sink.out(), __fmt, __args, __loc);
+ return std::move(__sink)._M_finish(__out);
+ }
else
- return std::move(__sink)._M_finish().out;
+ {
+ _Iter_sink<_CharT, _Out> __sink(std::move(__out), __n);
+ __format::__do_vformat_to(__sink.out(), __fmt, __args, __loc);
+ return std::move(__sink)._M_finish();
+ }
}
+
#pragma GCC diagnostic pop
} // namespace __format
@@ -5541,10 +5581,9 @@ namespace __format
format_to_n(_Out __out, iter_difference_t<_Out> __n,
format_string<_Args...> __fmt, _Args&&... __args)
{
- __format::_Iter_sink<char, _Out> __sink(std::move(__out), __n);
- std::vformat_to(__sink.out(), __fmt.get(),
- std::make_format_args(__args...));
- return std::move(__sink)._M_finish();
+ return __format::__do_vformat_to_n(
+ std::move(__out), __n, __fmt.get(),
+ std::make_format_args(__args...));
}
#ifdef _GLIBCXX_USE_WCHAR_T
@@ -5554,10 +5593,9 @@ namespace __format
format_to_n(_Out __out, iter_difference_t<_Out> __n,
wformat_string<_Args...> __fmt, _Args&&... __args)
{
- __format::_Iter_sink<wchar_t, _Out> __sink(std::move(__out), __n);
- std::vformat_to(__sink.out(), __fmt.get(),
- std::make_wformat_args(__args...));
- return std::move(__sink)._M_finish();
+ return __format::__do_vformat_to_n(
+ std::move(__out), __n, __fmt.get(),
+ std::make_wformat_args(__args...));
}
#endif
@@ -5567,10 +5605,9 @@ namespace __format
format_to_n(_Out __out, iter_difference_t<_Out> __n, const locale& __loc,
format_string<_Args...> __fmt, _Args&&... __args)
{
- __format::_Iter_sink<char, _Out> __sink(std::move(__out), __n);
- std::vformat_to(__sink.out(), __loc, __fmt.get(),
- std::make_format_args(__args...));
- return std::move(__sink)._M_finish();
+ return __format::__do_vformat_to_n(
+ std::move(__out), __n, __fmt.get(),
+ std::make_format_args(__args...), &__loc);
}
#ifdef _GLIBCXX_USE_WCHAR_T
@@ -5580,10 +5617,9 @@ namespace __format
format_to_n(_Out __out, iter_difference_t<_Out> __n, const locale& __loc,
wformat_string<_Args...> __fmt, _Args&&... __args)
{
- __format::_Iter_sink<wchar_t, _Out> __sink(std::move(__out), __n);
- std::vformat_to(__sink.out(), __loc, __fmt.get(),
- std::make_wformat_args(__args...));
- return std::move(__sink)._M_finish();
+ return __format::__do_vformat_to_n(
+ std::move(__out), __n, __fmt.get(),
+ std::make_wformat_args(__args...), &__loc);
}
#endif
@@ -5592,10 +5628,10 @@ namespace __format
{
#if 1
template<typename _CharT>
- class _Counting_sink final : public _Iter_sink<_CharT, _CharT*>
+ class _Counting_sink final : public _Ptr_sink<_CharT>
{
public:
- _Counting_sink() : _Iter_sink<_CharT, _CharT*>(nullptr, 0) { }
+ _Counting_sink() : _Ptr_sink<_CharT>(nullptr, 0) { }
[[__gnu__::__always_inline__]]
size_t
--
2.53.0