#define _LIBCPP___FORMAT_BUFFER_H
#include <__algorithm/copy_n.h>
+#include <__algorithm/fill_n.h>
#include <__algorithm/max.h>
#include <__algorithm/min.h>
+#include <__algorithm/transform.h>
#include <__algorithm/unwrap_iter.h>
#include <__config>
#include <__format/enable_insertable.h>
#include <__utility/move.h>
#include <concepts>
#include <cstddef>
+#include <string_view>
#include <type_traits>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
return back_insert_iterator{*this};
}
- // TODO FMT It would be nice to have an overload taking a
- // basic_string_view<_CharT> and append it directly.
_LIBCPP_HIDE_FROM_ABI void push_back(_CharT __c) {
__ptr_[__size_++] = __c;
flush();
}
+ /// Copies the input __str to the buffer.
+ ///
+ /// Since some of the input is generated by std::to_chars, there needs to be a
+ /// conversion when _CharT is wchar_t.
+ template <__formatter::__char_type _InCharT>
+ _LIBCPP_HIDE_FROM_ABI void __copy(basic_string_view<_InCharT> __str) {
+ // When the underlying iterator is a simple iterator the __capacity_ is
+ // infinite. For a string or container back_inserter it isn't. This means
+ // adding a large string the the buffer can cause some overhead. In that
+ // case a better approach could be:
+ // - flush the buffer
+ // - container.append(__str.begin(), __str.end());
+ // The same holds true for the fill.
+ // For transform it might be slightly harder, however the use case for
+ // transform is slightly less common; it converts hexadecimal values to
+ // upper case. For integral these strings are short.
+ // TODO FMT Look at the improvements above.
+ size_t __n = __str.size();
+
+ __flush_on_overflow(__n);
+ if (__n <= __capacity_) {
+ _VSTD::copy_n(__str.data(), __n, _VSTD::addressof(__ptr_[__size_]));
+ __size_ += __n;
+ return;
+ }
+
+ // The output doesn't fit in the internal buffer.
+ // Copy the data in "__capacity_" sized chunks.
+ _LIBCPP_ASSERT(__size_ == 0, "the buffer should be flushed by __flush_on_overflow");
+ const _InCharT* __first = __str.data();
+ do {
+ size_t __chunk = _VSTD::min(__n, __capacity_);
+ _VSTD::copy_n(__first, __chunk, _VSTD::addressof(__ptr_[__size_]));
+ __size_ = __chunk;
+ __first += __chunk;
+ __n -= __chunk;
+ flush();
+ } while (__n);
+ }
+
+ /// A std::transform wrapper.
+ ///
+ /// Like @ref __copy it may need to do type conversion.
+ template <__formatter::__char_type _InCharT, class _UnaryOperation>
+ _LIBCPP_HIDE_FROM_ABI void __transform(const _InCharT* __first, const _InCharT* __last, _UnaryOperation __operation) {
+ _LIBCPP_ASSERT(__first <= __last, "not a valid range");
+
+ size_t __n = static_cast<size_t>(__last - __first);
+ __flush_on_overflow(__n);
+ if (__n <= __capacity_) {
+ _VSTD::transform(__first, __last, _VSTD::addressof(__ptr_[__size_]), _VSTD::move(__operation));
+ __size_ += __n;
+ return;
+ }
+
+ // The output doesn't fit in the internal buffer.
+ // Transform the data in "__capacity_" sized chunks.
+ _LIBCPP_ASSERT(__size_ == 0, "the buffer should be flushed by __flush_on_overflow");
+ do {
+ size_t __chunk = _VSTD::min(__n, __capacity_);
+ _VSTD::transform(__first, __first + __chunk, _VSTD::addressof(__ptr_[__size_]), __operation);
+ __size_ = __chunk;
+ __first += __chunk;
+ __n -= __chunk;
+ flush();
+ } while (__n);
+ }
+
+ /// A \c fill_n wrapper.
+ _LIBCPP_HIDE_FROM_ABI void __fill(size_t __n, _CharT __value) {
+ __flush_on_overflow(__n);
+ if (__n <= __capacity_) {
+ _VSTD::fill_n(_VSTD::addressof(__ptr_[__size_]), __n, __value);
+ __size_ += __n;
+ return;
+ }
+
+ // The output doesn't fit in the internal buffer.
+ // Fill the buffer in "__capacity_" sized chunks.
+ _LIBCPP_ASSERT(__size_ == 0, "the buffer should be flushed by __flush_on_overflow");
+ do {
+ size_t __chunk = _VSTD::min(__n, __capacity_);
+ _VSTD::fill_n(_VSTD::addressof(__ptr_[__size_]), __chunk, __value);
+ __size_ = __chunk;
+ __n -= __chunk;
+ flush();
+ } while (__n);
+ }
+
_LIBCPP_HIDE_FROM_ABI void flush() {
__flush_(__ptr_, __size_, __obj_);
__size_ = 0;
size_t __size_{0};
void (*__flush_)(_CharT*, size_t, void*);
void* __obj_;
+
+ /// Flushes the buffer when the output operation would overflow the buffer.
+ ///
+ /// A simple approach for the overflow detection would be something along the
+ /// lines:
+ /// \code
+ /// // The internal buffer is large enough.
+ /// if (__n <= __capacity_) {
+ /// // Flush when we really would overflow.
+ /// if (__size_ + __n >= __capacity_)
+ /// flush();
+ /// ...
+ /// }
+ /// \endcode
+ ///
+ /// This approach works for all cases but one:
+ /// A __format_to_n_buffer_base where \ref __enable_direct_output is true.
+ /// In that case the \ref __capacity_ of the buffer changes during the first
+ /// \ref flush. During that operation the output buffer switches from its
+ /// __writer_ to its __storage_. The \ref __capacity_ of the former depends
+ /// on the value of n, of the latter is a fixed size. For example:
+ /// - a format_to_n call with a 10'000 char buffer,
+ /// - the buffer is filled with 9'500 chars,
+ /// - adding 1'000 elements would overflow the buffer so the buffer gets
+ /// changed and the \ref __capacity_ decreases from 10'000 to
+ /// __buffer_size (256 at the time of writing).
+ ///
+ /// This means that the \ref flush for this class may need to copy a part of
+ /// the internal buffer to the proper output. In this example there will be
+ /// 500 characters that need this copy operation.
+ ///
+ /// Note it would be more efficient to write 500 chars directly and then swap
+ /// the buffers. This would make the code more complex and \ref format_to_n is
+ /// not the most common use case. Therefore the optimization isn't done.
+ _LIBCPP_HIDE_FROM_ABI void __flush_on_overflow(size_t __n) {
+ if (__size_ + __n >= __capacity_)
+ flush();
+ }
};
/// A storage using an internal buffer.
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_(_VSTD::max(_Size(0), __n)) {}
+ _LIBCPP_HIDE_FROM_ABI explicit __format_to_n_buffer_base(_OutIt __out_it, _Size __max_size)
+ : __writer_(_VSTD::move(__out_it)), __max_size_(_VSTD::max(_Size(0), __max_size)) {}
_LIBCPP_HIDE_FROM_ABI void flush(_CharT* __ptr, size_t __size) {
- if (_Size(__size_) <= __n_)
- __writer_.flush(__ptr, _VSTD::min(_Size(__size), __n_ - __size_));
+ if (_Size(__size_) <= __max_size_)
+ __writer_.flush(__ptr, _VSTD::min(_Size(__size), __max_size_ - __size_));
__size_ += __size;
}
__output_buffer<_CharT> __output_{__storage_.begin(), __storage_.__buffer_size, this};
typename __writer_selector<_OutIt, _CharT>::type __writer_;
- _Size __n_;
+ _Size __max_size_;
_Size __size_{0};
};
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)) {
- if (__n <= 0) [[unlikely]]
+ _LIBCPP_HIDE_FROM_ABI explicit __format_to_n_buffer_base(_OutIt __out_it, _Size __max_size)
+ : __output_(_VSTD::__unwrap_iter(__out_it), __max_size, this),
+ __writer_(_VSTD::move(__out_it)),
+ __max_size_(__max_size) {
+ if (__max_size <= 0) [[unlikely]]
__output_.reset(__storage_.begin(), __storage_.__buffer_size);
}
_LIBCPP_HIDE_FROM_ABI void flush(_CharT* __ptr, size_t __size) {
- // A flush to the direct writer happens in two occasions:
+ // A flush to the direct writer happens in the following 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.
+ // - Like above, but the next "mass write" operation would overflow the
+ // buffer. In that case the buffer is pre-emptively switched. The still
+ // valid code units will be written separately.
// - 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.
- // When the __n <= 0 the constructor already switched the buffers.
+ // When the __max_size <= 0 the constructor already switched the buffers.
if (__size_ == 0 && __ptr != __storage_.begin()) {
__writer_.flush(__ptr, __size);
__output_.reset(__storage_.begin(), __storage_.__buffer_size);
+ } else if (__size_ < __max_size_) {
+ // Copies a part of the internal buffer to the output up to n characters.
+ // See __output_buffer<_CharT>::__flush_on_overflow for more information.
+ _Size __s = _VSTD::min(_Size(__size), __max_size_ - __size_);
+ std::copy_n(__ptr, __s, __writer_.out());
+ __writer_.flush(__ptr, __s);
}
__size_ += __size;
__output_buffer<_CharT> __output_;
__writer_direct<_OutIt, _CharT> __writer_;
+ _Size __max_size_;
_Size __size_{0};
};
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 explicit __format_to_n_buffer(_OutIt __out_it, _Size __max_size)
+ : _Base(_VSTD::move(__out_it), __max_size) {}
_LIBCPP_HIDE_FROM_ABI auto make_output_iterator() { return this->__output_.make_output_iterator(); }
_LIBCPP_HIDE_FROM_ABI format_to_n_result<_OutIt> result() && {
#ifndef _LIBCPP___FORMAT_FORMATTER_FLOATING_POINT_H
#define _LIBCPP___FORMAT_FORMATTER_FLOATING_POINT_H
-#include <__algorithm/copy.h>
#include <__algorithm/copy_n.h>
-#include <__algorithm/fill_n.h>
#include <__algorithm/find.h>
#include <__algorithm/min.h>
#include <__algorithm/rotate.h>
// sign and (zero padding or alignment)
if (__zero_padding && __first != __buffer.begin())
*__out_it++ = *__buffer.begin();
- __out_it = _VSTD::fill_n(_VSTD::move(__out_it), __padding.__before_, __specs.__fill_);
+ __out_it = __formatter::__fill(_VSTD::move(__out_it), __padding.__before_, __specs.__fill_);
if (!__zero_padding && __first != __buffer.begin())
*__out_it++ = *__buffer.begin();
// integral part
if (__grouping.empty()) {
- __out_it = _VSTD::copy_n(__first, __digits, _VSTD::move(__out_it));
+ __out_it = __formatter::__copy(__first, __digits, _VSTD::move(__out_it));
} else {
auto __r = __grouping.rbegin();
auto __e = __grouping.rend() - 1;
// This loop achieves that process by testing the termination condition
// midway in the loop.
while (true) {
- __out_it = _VSTD::copy_n(__first, *__r, _VSTD::move(__out_it));
+ __out_it = __formatter::__copy(__first, *__r, _VSTD::move(__out_it));
__first += *__r;
if (__r == __e)
// fractional part
if (__result.__radix_point != __result.__last) {
*__out_it++ = __np.decimal_point();
- __out_it = _VSTD::copy(__result.__radix_point + 1, __result.__exponent, _VSTD::move(__out_it));
- __out_it = _VSTD::fill_n(_VSTD::move(__out_it), __buffer.__num_trailing_zeros(), _CharT('0'));
+ __out_it = __formatter::__copy(__result.__radix_point + 1, __result.__exponent, _VSTD::move(__out_it));
+ __out_it = __formatter::__fill(_VSTD::move(__out_it), __buffer.__num_trailing_zeros(), _CharT('0'));
}
// exponent
if (__result.__exponent != __result.__last)
- __out_it = _VSTD::copy(__result.__exponent, __result.__last, _VSTD::move(__out_it));
+ __out_it = __formatter::__copy(__result.__exponent, __result.__last, _VSTD::move(__out_it));
// alignment
- return _VSTD::fill_n(_VSTD::move(__out_it), __padding.__after_, __specs.__fill_);
+ return __formatter::__fill(_VSTD::move(__out_it), __padding.__after_, __specs.__fill_);
}
# endif // _LIBCPP_HAS_NO_LOCALIZATION
if (__size + __num_trailing_zeros >= __specs.__width_) {
if (__num_trailing_zeros && __result.__exponent != __result.__last)
// Insert trailing zeros before exponent character.
- return _VSTD::copy(
+ return __formatter::__copy(
__result.__exponent,
__result.__last,
- _VSTD::fill_n(
- _VSTD::copy(__buffer.begin(), __result.__exponent, __ctx.out()), __num_trailing_zeros, _CharT('0')));
+ __formatter::__fill(__formatter::__copy(__buffer.begin(), __result.__exponent, __ctx.out()),
+ __num_trailing_zeros,
+ _CharT('0')));
- return _VSTD::fill_n(
- _VSTD::copy(__buffer.begin(), __result.__last, __ctx.out()), __num_trailing_zeros, _CharT('0'));
+ return __formatter::__fill(
+ __formatter::__copy(__buffer.begin(), __result.__last, __ctx.out()), __num_trailing_zeros, _CharT('0'));
}
auto __out_it = __ctx.out();
// The zero padding is done like:
// - Write [sign][prefix]
// - Write data right aligned with '0' as fill character.
- __out_it = _VSTD::copy(__begin, __first, _VSTD::move(__out_it));
+ __out_it = __formatter::__copy(__begin, __first, _VSTD::move(__out_it));
__specs.__alignment_ = __format_spec::__alignment::__right;
__specs.__fill_ = _CharT('0');
int32_t __size = __first - __begin;
#include <__algorithm/copy_n.h>
#include <__algorithm/fill_n.h>
#include <__algorithm/transform.h>
+#include <__concepts/same_as.h>
#include <__config>
+#include <__format/buffer.h>
#include <__format/formatter.h>
#include <__format/parser_std_format_spec.h>
#include <__format/unicode.h>
+#include <__iterator/back_insert_iterator.h>
#include <__utility/move.h>
#include <__utility/unreachable.h>
#include <cstddef>
__libcpp_unreachable();
}
+/// Copy wrapper.
+///
+/// This uses a "mass output function" of __format::__output_buffer when possible.
+template <__formatter::__char_type _CharT, __formatter::__char_type _OutCharT = _CharT>
+_LIBCPP_HIDE_FROM_ABI auto __copy(basic_string_view<_CharT> __str, output_iterator<const _OutCharT&> auto __out_it)
+ -> decltype(__out_it) {
+ if constexpr (_VSTD::same_as<decltype(__out_it), _VSTD::back_insert_iterator<__format::__output_buffer<_OutCharT>>>) {
+ __out_it.__get_container()->__copy(__str);
+ return __out_it;
+ } else {
+ return std::copy_n(__str.data(), __str.size(), _VSTD::move(__out_it));
+ }
+}
+
+template <__formatter::__char_type _CharT, __formatter::__char_type _OutCharT = _CharT>
+_LIBCPP_HIDE_FROM_ABI auto
+__copy(const _CharT* __first, const _CharT* __last, output_iterator<const _OutCharT&> auto __out_it)
+ -> decltype(__out_it) {
+ return __formatter::__copy(basic_string_view{__first, __last}, _VSTD::move(__out_it));
+}
+
+template <__formatter::__char_type _CharT, __formatter::__char_type _OutCharT = _CharT>
+_LIBCPP_HIDE_FROM_ABI auto __copy(const _CharT* __first, size_t __n, output_iterator<const _OutCharT&> auto __out_it)
+ -> decltype(__out_it) {
+ return __formatter::__copy(basic_string_view{__first, __n}, _VSTD::move(__out_it));
+}
+
+/// Transform wrapper.
+///
+/// This uses a "mass output function" of __format::__output_buffer when possible.
+template <__formatter::__char_type _CharT, __formatter::__char_type _OutCharT = _CharT, class _UnaryOperation>
+_LIBCPP_HIDE_FROM_ABI auto
+__transform(const _CharT* __first,
+ const _CharT* __last,
+ output_iterator<const _OutCharT&> auto __out_it,
+ _UnaryOperation __operation) -> decltype(__out_it) {
+ if constexpr (_VSTD::same_as<decltype(__out_it), _VSTD::back_insert_iterator<__format::__output_buffer<_OutCharT>>>) {
+ __out_it.__get_container()->__transform(__first, __last, _VSTD::move(__operation));
+ return __out_it;
+ } else {
+ return std::transform(__first, __last, _VSTD::move(__out_it), __operation);
+ }
+}
+
+/// Fill wrapper.
+///
+/// This uses a "mass output function" of __format::__output_buffer when possible.
+template <__formatter::__char_type _CharT, output_iterator<const _CharT&> _OutIt>
+_LIBCPP_HIDE_FROM_ABI _OutIt __fill(_OutIt __out_it, size_t __n, _CharT __value) {
+ if constexpr (_VSTD::same_as<decltype(__out_it), _VSTD::back_insert_iterator<__format::__output_buffer<_CharT>>>) {
+ __out_it.__get_container()->__fill(__n, __value);
+ return __out_it;
+ } else {
+ return std::fill_n(_VSTD::move(__out_it), __n, __value);
+ }
+}
+
template <class _OutIt, class _CharT>
_LIBCPP_HIDE_FROM_ABI _OutIt __write_using_decimal_separators(_OutIt __out_it, const char* __begin, const char* __first,
const char* __last, string&& __grouping, _CharT __sep,
__padding_size_result __padding = {0, 0};
if (__specs.__alignment_ == __format_spec::__alignment::__zero_padding) {
// Write [sign][prefix].
- __out_it = _VSTD::copy(__begin, __first, _VSTD::move(__out_it));
+ __out_it = __formatter::__copy(__begin, __first, _VSTD::move(__out_it));
if (__specs.__width_ > __size) {
// Write zero padding.
__padding.__before_ = __specs.__width_ - __size;
- __out_it = _VSTD::fill_n(_VSTD::move(__out_it), __specs.__width_ - __size, _CharT('0'));
+ __out_it = __formatter::__fill(_VSTD::move(__out_it), __specs.__width_ - __size, _CharT('0'));
}
} else {
if (__specs.__width_ > __size) {
// Determine padding and write padding.
__padding = __padding_size(__size, __specs.__width_, __specs.__alignment_);
- __out_it = _VSTD::fill_n(_VSTD::move(__out_it), __padding.__before_, __specs.__fill_);
+ __out_it = __formatter::__fill(_VSTD::move(__out_it), __padding.__before_, __specs.__fill_);
}
// Write [sign][prefix].
- __out_it = _VSTD::copy(__begin, __first, _VSTD::move(__out_it));
+ __out_it = __formatter::__copy(__begin, __first, _VSTD::move(__out_it));
}
auto __r = __grouping.rbegin();
while (true) {
if (__specs.__std_.__type_ == __format_spec::__type::__hexadecimal_upper_case) {
__last = __first + *__r;
- __out_it = _VSTD::transform(__first, __last, _VSTD::move(__out_it), __hex_to_upper);
+ __out_it = __formatter::__transform(__first, __last, _VSTD::move(__out_it), __hex_to_upper);
__first = __last;
} else {
- __out_it = _VSTD::copy_n(__first, *__r, _VSTD::move(__out_it));
+ __out_it = __formatter::__copy(__first, *__r, _VSTD::move(__out_it));
__first += *__r;
}
*__out_it++ = __sep;
}
- return _VSTD::fill_n(_VSTD::move(__out_it), __padding.__after_, __specs.__fill_);
+ return __formatter::__fill(_VSTD::move(__out_it), __padding.__after_, __specs.__fill_);
}
/// Writes the input to the output with the required padding.
/// Since the output column width is specified the function can be used for
/// ASCII and Unicode output.
///
-/// \pre [\a __first, \a __last) is a valid range.
/// \pre \a __size <= \a __width. Using this function when this pre-condition
/// doesn't hold incurs an unwanted overhead.
///
-/// \param __first Pointer to the first element to write.
-/// \param __last Pointer beyond the last element to write.
+/// \param __str The string to write.
/// \param __out_it The output iterator to write to.
/// \param __specs The parsed formatting specifications.
/// \param __size The (estimated) output column width. When the elements
/// conversion, which means the [\a __first, \a __last) always contains elements
/// of the type \c char.
template <class _CharT, class _ParserCharT>
-_LIBCPP_HIDE_FROM_ABI auto __write(
- const _CharT* __first,
- const _CharT* __last,
- output_iterator<const _CharT&> auto __out_it,
- __format_spec::__parsed_specifications<_ParserCharT> __specs,
- ptrdiff_t __size) -> decltype(__out_it) {
- _LIBCPP_ASSERT(__first <= __last, "Not a valid range");
-
+_LIBCPP_HIDE_FROM_ABI auto
+__write(basic_string_view<_CharT> __str,
+ output_iterator<const _CharT&> auto __out_it,
+ __format_spec::__parsed_specifications<_ParserCharT> __specs,
+ ptrdiff_t __size) -> decltype(__out_it) {
if (__size >= __specs.__width_)
- return _VSTD::copy(__first, __last, _VSTD::move(__out_it));
+ return __formatter::__copy(__str, _VSTD::move(__out_it));
__padding_size_result __padding = __formatter::__padding_size(__size, __specs.__width_, __specs.__std_.__alignment_);
- __out_it = _VSTD::fill_n(_VSTD::move(__out_it), __padding.__before_, __specs.__fill_);
- __out_it = _VSTD::copy(__first, __last, _VSTD::move(__out_it));
- return _VSTD::fill_n(_VSTD::move(__out_it), __padding.__after_, __specs.__fill_);
+ __out_it = __formatter::__fill(_VSTD::move(__out_it), __padding.__before_, __specs.__fill_);
+ __out_it = __formatter::__copy(__str, _VSTD::move(__out_it));
+ return __formatter::__fill(_VSTD::move(__out_it), __padding.__after_, __specs.__fill_);
+}
+
+template <class _CharT, class _ParserCharT>
+_LIBCPP_HIDE_FROM_ABI auto
+__write(const _CharT* __first,
+ const _CharT* __last,
+ output_iterator<const _CharT&> auto __out_it,
+ __format_spec::__parsed_specifications<_ParserCharT> __specs,
+ ptrdiff_t __size) -> decltype(__out_it) {
+ _LIBCPP_ASSERT(__first <= __last, "Not a valid range");
+ return __formatter::__write(basic_string_view{__first, __last}, _VSTD::move(__out_it), __specs, __size);
}
/// \overload
///
/// Calls the function above where \a __size = \a __last - \a __first.
template <class _CharT, class _ParserCharT>
-_LIBCPP_HIDE_FROM_ABI auto __write(const _CharT* __first, const _CharT* __last,
- output_iterator<const _CharT&> auto __out_it,
- __format_spec::__parsed_specifications<_ParserCharT> __specs) -> decltype(__out_it) {
- return __write(__first, __last, _VSTD::move(__out_it), __specs, __last - __first);
+_LIBCPP_HIDE_FROM_ABI auto
+__write(const _CharT* __first,
+ const _CharT* __last,
+ output_iterator<const _CharT&> auto __out_it,
+ __format_spec::__parsed_specifications<_ParserCharT> __specs) -> decltype(__out_it) {
+ _LIBCPP_ASSERT(__first <= __last, "Not a valid range");
+ return __formatter::__write(__first, __last, _VSTD::move(__out_it), __specs, __last - __first);
}
template <class _CharT, class _ParserCharT, class _UnaryOperation>
ptrdiff_t __size = __last - __first;
if (__size >= __specs.__width_)
- return _VSTD::transform(__first, __last, _VSTD::move(__out_it), __op);
+ return __formatter::__transform(__first, __last, _VSTD::move(__out_it), __op);
__padding_size_result __padding = __padding_size(__size, __specs.__width_, __specs.__alignment_);
- __out_it = _VSTD::fill_n(_VSTD::move(__out_it), __padding.__before_, __specs.__fill_);
- __out_it = _VSTD::transform(__first, __last, _VSTD::move(__out_it), __op);
- return _VSTD::fill_n(_VSTD::move(__out_it), __padding.__after_, __specs.__fill_);
+ __out_it = __formatter::__fill(_VSTD::move(__out_it), __padding.__before_, __specs.__fill_);
+ __out_it = __formatter::__transform(__first, __last, _VSTD::move(__out_it), __op);
+ return __formatter::__fill(_VSTD::move(__out_it), __padding.__after_, __specs.__fill_);
}
/// Writes additional zero's for the precision before the exponent.
__padding_size_result __padding =
__padding_size(__size + __num_trailing_zeros, __specs.__width_, __specs.__alignment_);
- __out_it = _VSTD::fill_n(_VSTD::move(__out_it), __padding.__before_, __specs.__fill_);
- __out_it = _VSTD::copy(__first, __exponent, _VSTD::move(__out_it));
- __out_it = _VSTD::fill_n(_VSTD::move(__out_it), __num_trailing_zeros, _CharT('0'));
- __out_it = _VSTD::copy(__exponent, __last, _VSTD::move(__out_it));
- return _VSTD::fill_n(_VSTD::move(__out_it), __padding.__after_, __specs.__fill_);
+ __out_it = __formatter::__fill(_VSTD::move(__out_it), __padding.__before_, __specs.__fill_);
+ __out_it = __formatter::__copy(__first, __exponent, _VSTD::move(__out_it));
+ __out_it = __formatter::__fill(_VSTD::move(__out_it), __num_trailing_zeros, _CharT('0'));
+ __out_it = __formatter::__copy(__exponent, __last, _VSTD::move(__out_it));
+ return __formatter::__fill(_VSTD::move(__out_it), __padding.__after_, __specs.__fill_);
}
/// Writes a string using format's width estimation algorithm.
// No padding -> copy the string
if (!__specs.__has_width())
- return _VSTD::copy(__str.begin(), __str.end(), _VSTD::move(__out_it));
+ return __formatter::__copy(__str, _VSTD::move(__out_it));
// Note when the estimated width is larger than size there's no padding. So
// there's no reason to get the real size when the estimate is larger than or
size_t __size =
__format_spec::__estimate_column_width(__str, __specs.__width_, __format_spec::__column_width_rounding::__up)
.__width_;
-
- return __formatter::__write(__str.begin(), __str.end(), _VSTD::move(__out_it), __specs, __size);
+ return __formatter::__write(__str, _VSTD::move(__out_it), __specs, __size);
}
template <class _CharT>
test_termination_condition(
STR("340282366920938463463374607431768211455"), STR("}"), A(std::numeric_limits<__uint128_t>::max()));
#endif
+ // Test __formatter::__transform (libc++ specific).
+ test_termination_condition(STR("FF"), STR("X}"), A(255));
}
template <class CharT>
format_test_pointer<const void*, CharT>(check, check_exception);
}
+/// Tests special buffer functions with a "large" input.
+///
+/// This is a test specific for libc++, however the code should behave the same
+/// on all implementations.
+/// In \c __format::__output_buffer there are some special functions to optimize
+/// outputting multiple characters, \c __copy, \c __transform, \c __fill. This
+/// test validates whether the functions behave properly when the output size
+/// doesn't fit in its internal buffer.
+template <class CharT, class TestFunction>
+void format_test_buffer_optimizations(TestFunction check) {
+#ifdef _LIBCPP_VERSION
+ // Used to validate our test sets are the proper size.
+ // To test the chunked operations it needs to be larger than the internal
+ // buffer. Picked a nice looking number.
+ constexpr int minimum = 3 * std::__format::__internal_storage<CharT>::__buffer_size;
+#else
+ constexpr int minimum = 1;
+#endif
+
+ // Copy
+ std::basic_string<CharT> str = STR(
+ "The quick brown fox jumps over the lazy dog."
+ "The quick brown fox jumps over the lazy dog."
+ "The quick brown fox jumps over the lazy dog."
+ "The quick brown fox jumps over the lazy dog."
+ "The quick brown fox jumps over the lazy dog."
+ "The quick brown fox jumps over the lazy dog."
+ "The quick brown fox jumps over the lazy dog."
+ "The quick brown fox jumps over the lazy dog."
+ "The quick brown fox jumps over the lazy dog."
+ "The quick brown fox jumps over the lazy dog."
+ "The quick brown fox jumps over the lazy dog."
+ "The quick brown fox jumps over the lazy dog."
+ "The quick brown fox jumps over the lazy dog."
+ "The quick brown fox jumps over the lazy dog."
+ "The quick brown fox jumps over the lazy dog."
+ "The quick brown fox jumps over the lazy dog."
+ "The quick brown fox jumps over the lazy dog."
+ "The quick brown fox jumps over the lazy dog."
+ "The quick brown fox jumps over the lazy dog."
+ "The quick brown fox jumps over the lazy dog."
+ "The quick brown fox jumps over the lazy dog."
+ "The quick brown fox jumps over the lazy dog."
+ "The quick brown fox jumps over the lazy dog."
+ "The quick brown fox jumps over the lazy dog."
+ "The quick brown fox jumps over the lazy dog."
+ "The quick brown fox jumps over the lazy dog."
+ "The quick brown fox jumps over the lazy dog."
+ "The quick brown fox jumps over the lazy dog."
+ "The quick brown fox jumps over the lazy dog."
+ "The quick brown fox jumps over the lazy dog.");
+ assert(str.size() > minimum);
+ check.template operator()<"{}">(std::basic_string_view<CharT>{str}, str);
+
+ // Fill
+ std::basic_string<CharT> fill(minimum, CharT('*'));
+ check.template operator()<"{:*<{}}">(std::basic_string_view<CharT>{str + fill}, str, str.size() + minimum);
+ check.template operator()<"{:*^{}}">(
+ std::basic_string_view<CharT>{fill + str + fill}, str, minimum + str.size() + minimum);
+ check.template operator()<"{:*>{}}">(std::basic_string_view<CharT>{fill + str}, str, minimum + str.size());
+}
+
template <class CharT, class TestFunction, class ExceptionTest>
void format_tests(TestFunction check, ExceptionTest check_exception) {
// *** Test escaping ***
// *** Test handle formatter argument ***
format_test_handle<CharT>(check, check_exception);
+
+ // *** Test the interal buffer optimizations ***
+ format_test_buffer_optimizations<CharT>(check);
}
#ifndef TEST_HAS_NO_WIDE_CHARACTERS