1 // -*- C++ -*- 2 //===----------------------------------------------------------------------===// 3 // 4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 5 // See https://llvm.org/LICENSE.txt for license information. 6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 7 // 8 //===----------------------------------------------------------------------===// 9 10 #ifndef _LIBCPP___FORMAT_FORMATTER_OUTPUT_H 11 #define _LIBCPP___FORMAT_FORMATTER_OUTPUT_H 12 13 #include <__algorithm/ranges_copy.h> 14 #include <__algorithm/ranges_fill_n.h> 15 #include <__algorithm/ranges_transform.h> 16 #include <__bit/countl.h> 17 #include <__concepts/same_as.h> 18 #include <__config> 19 #include <__format/buffer.h> 20 #include <__format/concepts.h> 21 #include <__format/formatter.h> 22 #include <__format/parser_std_format_spec.h> 23 #include <__format/unicode.h> 24 #include <__iterator/back_insert_iterator.h> 25 #include <__iterator/concepts.h> 26 #include <__iterator/iterator_traits.h> 27 #include <__memory/addressof.h> 28 #include <__memory/pointer_traits.h> 29 #include <__utility/move.h> 30 #include <__utility/unreachable.h> 31 #include <cstddef> 32 #include <string_view> 33 34 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) 35 # pragma GCC system_header 36 #endif 37 38 _LIBCPP_BEGIN_NAMESPACE_STD 39 40 #if _LIBCPP_STD_VER >= 20 41 42 namespace __formatter { 43 44 _LIBCPP_HIDE_FROM_ABI constexpr char __hex_to_upper(char __c) { 45 switch (__c) { 46 case 'a': 47 return 'A'; 48 case 'b': 49 return 'B'; 50 case 'c': 51 return 'C'; 52 case 'd': 53 return 'D'; 54 case 'e': 55 return 'E'; 56 case 'f': 57 return 'F'; 58 } 59 return __c; 60 } 61 62 struct _LIBCPP_EXPORTED_FROM_ABI __padding_size_result { 63 size_t __before_; 64 size_t __after_; 65 }; 66 67 _LIBCPP_HIDE_FROM_ABI constexpr __padding_size_result 68 __padding_size(size_t __size, size_t __width, __format_spec::__alignment __align) { 69 _LIBCPP_ASSERT_INTERNAL(__width > __size, "don't call this function when no padding is required"); 70 _LIBCPP_ASSERT_INTERNAL( 71 __align != __format_spec::__alignment::__zero_padding, "the caller should have handled the zero-padding"); 72 73 size_t __fill = __width - __size; 74 switch (__align) { 75 case __format_spec::__alignment::__zero_padding: 76 __libcpp_unreachable(); 77 78 case __format_spec::__alignment::__left: 79 return {0, __fill}; 80 81 case __format_spec::__alignment::__center: { 82 // The extra padding is divided per [format.string.std]/3 83 // __before = floor(__fill, 2); 84 // __after = ceil(__fill, 2); 85 size_t __before = __fill / 2; 86 size_t __after = __fill - __before; 87 return {__before, __after}; 88 } 89 case __format_spec::__alignment::__default: 90 case __format_spec::__alignment::__right: 91 return {__fill, 0}; 92 } 93 __libcpp_unreachable(); 94 } 95 96 /// Copy wrapper. 97 /// 98 /// This uses a "mass output function" of __format::__output_buffer when possible. 99 template <__fmt_char_type _CharT, __fmt_char_type _OutCharT = _CharT> 100 _LIBCPP_HIDE_FROM_ABI auto __copy(basic_string_view<_CharT> __str, output_iterator<const _OutCharT&> auto __out_it) 101 -> decltype(__out_it) { 102 if constexpr (std::same_as<decltype(__out_it), std::back_insert_iterator<__format::__output_buffer<_OutCharT>>>) { 103 __out_it.__get_container()->__copy(__str); 104 return __out_it; 105 } else if constexpr (std::same_as<decltype(__out_it), typename __format::__retarget_buffer<_OutCharT>::__iterator>) { 106 __out_it.__buffer_->__copy(__str); 107 return __out_it; 108 } else { 109 return std::ranges::copy(__str, std::move(__out_it)).out; 110 } 111 } 112 113 template <contiguous_iterator _Iterator, 114 __fmt_char_type _CharT = typename iterator_traits<_Iterator>::value_type, 115 __fmt_char_type _OutCharT = _CharT> 116 _LIBCPP_HIDE_FROM_ABI auto __copy(_Iterator __first, _Iterator __last, output_iterator<const _OutCharT&> auto __out_it) 117 -> decltype(__out_it) { 118 return __formatter::__copy(basic_string_view{__first, __last}, std::move(__out_it)); 119 } 120 121 template <contiguous_iterator _Iterator, 122 __fmt_char_type _CharT = typename iterator_traits<_Iterator>::value_type, 123 __fmt_char_type _OutCharT = _CharT> 124 _LIBCPP_HIDE_FROM_ABI auto __copy(_Iterator __first, size_t __n, output_iterator<const _OutCharT&> auto __out_it) 125 -> decltype(__out_it) { 126 return __formatter::__copy(basic_string_view{std::to_address(__first), __n}, std::move(__out_it)); 127 } 128 129 /// Transform wrapper. 130 /// 131 /// This uses a "mass output function" of __format::__output_buffer when possible. 132 template <contiguous_iterator _Iterator, 133 __fmt_char_type _CharT = typename iterator_traits<_Iterator>::value_type, 134 __fmt_char_type _OutCharT = _CharT, 135 class _UnaryOperation> 136 _LIBCPP_HIDE_FROM_ABI auto __transform( 137 _Iterator __first, _Iterator __last, output_iterator<const _OutCharT&> auto __out_it, _UnaryOperation __operation) 138 -> decltype(__out_it) { 139 if constexpr (std::same_as<decltype(__out_it), std::back_insert_iterator<__format::__output_buffer<_OutCharT>>>) { 140 __out_it.__get_container()->__transform(__first, __last, std::move(__operation)); 141 return __out_it; 142 } else if constexpr (std::same_as<decltype(__out_it), typename __format::__retarget_buffer<_OutCharT>::__iterator>) { 143 __out_it.__buffer_->__transform(__first, __last, std::move(__operation)); 144 return __out_it; 145 } else { 146 return std::ranges::transform(__first, __last, std::move(__out_it), __operation).out; 147 } 148 } 149 150 /// Fill wrapper. 151 /// 152 /// This uses a "mass output function" of __format::__output_buffer when possible. 153 template <__fmt_char_type _CharT, output_iterator<const _CharT&> _OutIt> 154 _LIBCPP_HIDE_FROM_ABI _OutIt __fill(_OutIt __out_it, size_t __n, _CharT __value) { 155 if constexpr (std::same_as<decltype(__out_it), std::back_insert_iterator<__format::__output_buffer<_CharT>>>) { 156 __out_it.__get_container()->__fill(__n, __value); 157 return __out_it; 158 } else if constexpr (std::same_as<decltype(__out_it), typename __format::__retarget_buffer<_CharT>::__iterator>) { 159 __out_it.__buffer_->__fill(__n, __value); 160 return __out_it; 161 } else { 162 return std::ranges::fill_n(std::move(__out_it), __n, __value); 163 } 164 } 165 166 # ifndef _LIBCPP_HAS_NO_UNICODE 167 template <__fmt_char_type _CharT, output_iterator<const _CharT&> _OutIt> 168 requires(same_as<_CharT, char>) 169 _LIBCPP_HIDE_FROM_ABI _OutIt __fill(_OutIt __out_it, size_t __n, __format_spec::__code_point<_CharT> __value) { 170 std::size_t __bytes = std::countl_one(static_cast<unsigned char>(__value.__data[0])); 171 if (__bytes == 0) 172 return __formatter::__fill(std::move(__out_it), __n, __value.__data[0]); 173 174 for (size_t __i = 0; __i < __n; ++__i) 175 __out_it = __formatter::__copy( 176 std::addressof(__value.__data[0]), std::addressof(__value.__data[0]) + __bytes, std::move(__out_it)); 177 return __out_it; 178 } 179 180 # ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS 181 template <__fmt_char_type _CharT, output_iterator<const _CharT&> _OutIt> 182 requires(same_as<_CharT, wchar_t> && sizeof(wchar_t) == 2) 183 _LIBCPP_HIDE_FROM_ABI _OutIt __fill(_OutIt __out_it, size_t __n, __format_spec::__code_point<_CharT> __value) { 184 if (!__unicode::__is_high_surrogate(__value.__data[0])) 185 return __formatter::__fill(std::move(__out_it), __n, __value.__data[0]); 186 187 for (size_t __i = 0; __i < __n; ++__i) 188 __out_it = __formatter::__copy( 189 std::addressof(__value.__data[0]), std::addressof(__value.__data[0]) + 2, std::move(__out_it)); 190 return __out_it; 191 } 192 193 template <__fmt_char_type _CharT, output_iterator<const _CharT&> _OutIt> 194 requires(same_as<_CharT, wchar_t> && sizeof(wchar_t) == 4) 195 _LIBCPP_HIDE_FROM_ABI _OutIt __fill(_OutIt __out_it, size_t __n, __format_spec::__code_point<_CharT> __value) { 196 return __formatter::__fill(std::move(__out_it), __n, __value.__data[0]); 197 } 198 # endif // _LIBCPP_HAS_NO_WIDE_CHARACTERS 199 # else // _LIBCPP_HAS_NO_UNICODE 200 template <__fmt_char_type _CharT, output_iterator<const _CharT&> _OutIt> 201 _LIBCPP_HIDE_FROM_ABI _OutIt __fill(_OutIt __out_it, size_t __n, __format_spec::__code_point<_CharT> __value) { 202 return __formatter::__fill(std::move(__out_it), __n, __value.__data[0]); 203 } 204 # endif // _LIBCPP_HAS_NO_UNICODE 205 206 /// Writes the input to the output with the required padding. 207 /// 208 /// Since the output column width is specified the function can be used for 209 /// ASCII and Unicode output. 210 /// 211 /// \pre \a __size <= \a __width. Using this function when this pre-condition 212 /// doesn't hold incurs an unwanted overhead. 213 /// 214 /// \param __str The string to write. 215 /// \param __out_it The output iterator to write to. 216 /// \param __specs The parsed formatting specifications. 217 /// \param __size The (estimated) output column width. When the elements 218 /// to be written are ASCII the following condition holds 219 /// \a __size == \a __last - \a __first. 220 /// 221 /// \returns An iterator pointing beyond the last element written. 222 /// 223 /// \note The type of the elements in range [\a __first, \a __last) can differ 224 /// from the type of \a __specs. Integer output uses \c std::to_chars for its 225 /// conversion, which means the [\a __first, \a __last) always contains elements 226 /// of the type \c char. 227 template <class _CharT, class _ParserCharT> 228 _LIBCPP_HIDE_FROM_ABI auto 229 __write(basic_string_view<_CharT> __str, 230 output_iterator<const _CharT&> auto __out_it, 231 __format_spec::__parsed_specifications<_ParserCharT> __specs, 232 ptrdiff_t __size) -> decltype(__out_it) { 233 if (__size >= __specs.__width_) 234 return __formatter::__copy(__str, std::move(__out_it)); 235 236 __padding_size_result __padding = __formatter::__padding_size(__size, __specs.__width_, __specs.__std_.__alignment_); 237 __out_it = __formatter::__fill(std::move(__out_it), __padding.__before_, __specs.__fill_); 238 __out_it = __formatter::__copy(__str, std::move(__out_it)); 239 return __formatter::__fill(std::move(__out_it), __padding.__after_, __specs.__fill_); 240 } 241 242 template <contiguous_iterator _Iterator, class _ParserCharT> 243 _LIBCPP_HIDE_FROM_ABI auto 244 __write(_Iterator __first, 245 _Iterator __last, 246 output_iterator<const iter_value_t<_Iterator>&> auto __out_it, 247 __format_spec::__parsed_specifications<_ParserCharT> __specs, 248 ptrdiff_t __size) -> decltype(__out_it) { 249 _LIBCPP_ASSERT_VALID_INPUT_RANGE(__first <= __last, "Not a valid range"); 250 return __formatter::__write(basic_string_view{__first, __last}, std::move(__out_it), __specs, __size); 251 } 252 253 /// \overload 254 /// 255 /// Calls the function above where \a __size = \a __last - \a __first. 256 template <contiguous_iterator _Iterator, class _ParserCharT> 257 _LIBCPP_HIDE_FROM_ABI auto 258 __write(_Iterator __first, 259 _Iterator __last, 260 output_iterator<const iter_value_t<_Iterator>&> auto __out_it, 261 __format_spec::__parsed_specifications<_ParserCharT> __specs) -> decltype(__out_it) { 262 _LIBCPP_ASSERT_VALID_INPUT_RANGE(__first <= __last, "Not a valid range"); 263 return __formatter::__write(__first, __last, std::move(__out_it), __specs, __last - __first); 264 } 265 266 template <contiguous_iterator _Iterator, 267 class _CharT = typename iterator_traits<_Iterator>::value_type, 268 class _ParserCharT, 269 class _UnaryOperation> 270 _LIBCPP_HIDE_FROM_ABI auto __write_transformed( 271 _Iterator __first, 272 _Iterator __last, 273 output_iterator<const _CharT&> auto __out_it, 274 __format_spec::__parsed_specifications<_ParserCharT> __specs, 275 _UnaryOperation __op) -> decltype(__out_it) { 276 _LIBCPP_ASSERT_VALID_INPUT_RANGE(__first <= __last, "Not a valid range"); 277 278 ptrdiff_t __size = __last - __first; 279 if (__size >= __specs.__width_) 280 return __formatter::__transform(__first, __last, std::move(__out_it), __op); 281 282 __padding_size_result __padding = __formatter::__padding_size(__size, __specs.__width_, __specs.__alignment_); 283 __out_it = __formatter::__fill(std::move(__out_it), __padding.__before_, __specs.__fill_); 284 __out_it = __formatter::__transform(__first, __last, std::move(__out_it), __op); 285 return __formatter::__fill(std::move(__out_it), __padding.__after_, __specs.__fill_); 286 } 287 288 /// Writes a string using format's width estimation algorithm. 289 /// 290 /// \pre !__specs.__has_precision() 291 /// 292 /// \note When \c _LIBCPP_HAS_NO_UNICODE is defined the function assumes the 293 /// input is ASCII. 294 template <class _CharT> 295 _LIBCPP_HIDE_FROM_ABI auto __write_string_no_precision( 296 basic_string_view<_CharT> __str, 297 output_iterator<const _CharT&> auto __out_it, 298 __format_spec::__parsed_specifications<_CharT> __specs) -> decltype(__out_it) { 299 _LIBCPP_ASSERT_INTERNAL(!__specs.__has_precision(), "use __write_string"); 300 301 // No padding -> copy the string 302 if (!__specs.__has_width()) 303 return __formatter::__copy(__str, std::move(__out_it)); 304 305 // Note when the estimated width is larger than size there's no padding. So 306 // there's no reason to get the real size when the estimate is larger than or 307 // equal to the minimum field width. 308 size_t __size = 309 __format_spec::__estimate_column_width(__str, __specs.__width_, __format_spec::__column_width_rounding::__up) 310 .__width_; 311 return __formatter::__write(__str, std::move(__out_it), __specs, __size); 312 } 313 314 template <class _CharT> 315 _LIBCPP_HIDE_FROM_ABI int __truncate(basic_string_view<_CharT>& __str, int __precision) { 316 __format_spec::__column_width_result __result = 317 __format_spec::__estimate_column_width(__str, __precision, __format_spec::__column_width_rounding::__down); 318 __str = basic_string_view<_CharT>{__str.begin(), __result.__last_}; 319 return __result.__width_; 320 } 321 322 } // namespace __formatter 323 324 #endif //_LIBCPP_STD_VER >= 20 325 326 _LIBCPP_END_NAMESPACE_STD 327 328 #endif // _LIBCPP___FORMAT_FORMATTER_OUTPUT_H 329