brintos

brintos / llvm-project-archived public Read only

0
0
Text · 11.1 KiB · 06d2b4c Raw
266 lines · c
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-exception7//8//===----------------------------------------------------------------------===//9 10#ifndef _LIBCPP___FORMAT_RANGE_FORMATTER_H11#define _LIBCPP___FORMAT_RANGE_FORMATTER_H12 13#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)14#  pragma GCC system_header15#endif16 17#include <__algorithm/ranges_copy.h>18#include <__chrono/statically_widen.h>19#include <__concepts/same_as.h>20#include <__config>21#include <__format/buffer.h>22#include <__format/concepts.h>23#include <__format/fmt_pair_like.h>24#include <__format/format_context.h>25#include <__format/format_error.h>26#include <__format/formatter.h>27#include <__format/formatter_output.h>28#include <__format/parser_std_format_spec.h>29#include <__iterator/back_insert_iterator.h>30#include <__ranges/concepts.h>31#include <__ranges/data.h>32#include <__ranges/from_range.h>33#include <__ranges/size.h>34#include <__type_traits/remove_cvref.h>35#include <string_view>36 37_LIBCPP_BEGIN_NAMESPACE_STD38 39#if _LIBCPP_STD_VER >= 2340 41template <class _Tp, class _CharT = char>42  requires same_as<remove_cvref_t<_Tp>, _Tp> && formattable<_Tp, _CharT>43struct range_formatter {44  _LIBCPP_HIDE_FROM_ABI constexpr void set_separator(basic_string_view<_CharT> __separator) noexcept {45    __separator_ = __separator;46  }47  _LIBCPP_HIDE_FROM_ABI constexpr void48  set_brackets(basic_string_view<_CharT> __opening_bracket, basic_string_view<_CharT> __closing_bracket) noexcept {49    __opening_bracket_ = __opening_bracket;50    __closing_bracket_ = __closing_bracket;51  }52 53  _LIBCPP_HIDE_FROM_ABI constexpr formatter<_Tp, _CharT>& underlying() noexcept { return __underlying_; }54  _LIBCPP_HIDE_FROM_ABI constexpr const formatter<_Tp, _CharT>& underlying() const noexcept { return __underlying_; }55 56  template <class _ParseContext>57  _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {58    auto __begin = __parser_.__parse(__ctx, __format_spec::__fields_range);59    auto __end   = __ctx.end();60    // Note the cases where __begin == __end in this code only happens when the61    // replacement-field has no terminating }, or when the parse is manually62    // called with a format-spec. The former is an error and the latter means63    // using a formatter without the format functions or print.64    if (__begin == __end) [[unlikely]]65      return __parse_empty_range_underlying_spec(__ctx, __begin);66 67    // The n field overrides a possible m type, therefore delay applying the68    // effect of n until the type has been procesed.69    __parse_type(__begin, __end);70    if (__parser_.__clear_brackets_)71      set_brackets({}, {});72    if (__begin == __end) [[unlikely]]73      return __parse_empty_range_underlying_spec(__ctx, __begin);74 75    bool __has_range_underlying_spec = *__begin == _CharT(':');76    if (__has_range_underlying_spec) {77      // range-underlying-spec:78      //   :  format-spec79      ++__begin;80    } else if (__begin != __end && *__begin != _CharT('}'))81      // When there is no underlaying range the current parse should have82      // consumed the format-spec. If not, the not consumed input will be83      // processed by the underlying. For example {:-} for a range in invalid,84      // the sign field is not present. Without this check the underlying_ will85      // get -} as input which my be valid.86      std::__throw_format_error("The format specifier should consume the input or end with a '}'");87 88    __ctx.advance_to(__begin);89    __begin = __underlying_.parse(__ctx);90 91    // This test should not be required if __has_range_underlying_spec is false.92    // However this test makes sure the underlying formatter left the parser in93    // a valid state. (Note this is not a full protection against evil parsers.94    // For example95    //   } this is test for the next argument {}96    //   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^97    // could consume more than it should.98    if (__begin != __end && *__begin != _CharT('}'))99      std::__throw_format_error("The format specifier should consume the input or end with a '}'");100 101    if (__parser_.__type_ != __format_spec::__type::__default) {102      // [format.range.formatter]/6103      //   If the range-type is s or ?s, then there shall be no n option and no104      //   range-underlying-spec.105      if (__parser_.__clear_brackets_) {106        if (__parser_.__type_ == __format_spec::__type::__string)107          std::__throw_format_error("The n option and type s can't be used together");108        std::__throw_format_error("The n option and type ?s can't be used together");109      }110      if (__has_range_underlying_spec) {111        if (__parser_.__type_ == __format_spec::__type::__string)112          std::__throw_format_error("Type s and an underlying format specification can't be used together");113        std::__throw_format_error("Type ?s and an underlying format specification can't be used together");114      }115    } else if (!__has_range_underlying_spec)116      std::__set_debug_format(__underlying_);117 118    return __begin;119  }120 121  template <ranges::input_range _Rp, class _FormatContext>122    requires formattable<ranges::range_reference_t<_Rp>, _CharT> &&123             same_as<remove_cvref_t<ranges::range_reference_t<_Rp>>, _Tp>124  _LIBCPP_HIDE_FROM_ABI typename _FormatContext::iterator format(_Rp&& __range, _FormatContext& __ctx) const {125    __format_spec::__parsed_specifications<_CharT> __specs = __parser_.__get_parsed_std_specifications(__ctx);126 127    if (!__specs.__has_width())128      return __format_range(__range, __ctx, __specs);129 130    // The size of the buffer needed is:131    // - open bracket characters132    // - close bracket character133    // - n elements where every element may have a different size134    // - (n -1) separators135    // The size of the element is hard to predict, knowing the type helps but136    // it depends on the format-spec. As an initial estimate we guess 6137    // characters.138    // Typically both brackets are 1 character and the separator is 2139    // characters. Which means there will be140    //   (n - 1) * 2 + 1 + 1 = n * 2 character141    // So estimate 8 times the range size as buffer.142    std::size_t __capacity_hint = 0;143    if constexpr (std::ranges::sized_range<_Rp>)144      __capacity_hint = 8 * ranges::size(__range);145    __format::__retarget_buffer<_CharT> __buffer{__capacity_hint};146    basic_format_context<typename __format::__retarget_buffer<_CharT>::__iterator, _CharT> __c{147        __buffer.__make_output_iterator(), __ctx};148 149    __format_range(__range, __c, __specs);150 151    return __formatter::__write_string_no_precision(__buffer.__view(), __ctx.out(), __specs);152  }153 154  template <ranges::input_range _Rp, class _FormatContext>155  typename _FormatContext::iterator _LIBCPP_HIDE_FROM_ABI156  __format_range(_Rp&& __range, _FormatContext& __ctx, __format_spec::__parsed_specifications<_CharT> __specs) const {157    if constexpr (same_as<_Tp, _CharT>) {158      switch (__specs.__std_.__type_) {159      case __format_spec::__type::__string:160      case __format_spec::__type::__debug:161        return __format_as_string(__range, __ctx, __specs.__std_.__type_ == __format_spec::__type::__debug);162      default:163        return __format_as_sequence(__range, __ctx);164      }165    } else166      return __format_as_sequence(__range, __ctx);167  }168 169  template <ranges::input_range _Rp, class _FormatContext>170  _LIBCPP_HIDE_FROM_ABI typename _FormatContext::iterator171  __format_as_string(_Rp&& __range, _FormatContext& __ctx, bool __debug_format) const {172    // When the range is contiguous use a basic_string_view instead to avoid a173    // copy of the underlying data. The basic_string_view formatter174    // specialization is the "basic" string formatter in libc++.175    if constexpr (ranges::contiguous_range<_Rp> && std::ranges::sized_range<_Rp>) {176      std::formatter<basic_string_view<_CharT>, _CharT> __formatter;177      if (__debug_format)178        __formatter.set_debug_format();179      return __formatter.format(180          basic_string_view<_CharT>{181              ranges::data(__range),182              ranges::size(__range),183          },184          __ctx);185    } else {186      std::formatter<basic_string<_CharT>, _CharT> __formatter;187      if (__debug_format)188        __formatter.set_debug_format();189      return __formatter.format(basic_string<_CharT>{from_range, __range}, __ctx);190    }191  }192 193  template <ranges::input_range _Rp, class _FormatContext>194  _LIBCPP_HIDE_FROM_ABI typename _FormatContext::iterator195  __format_as_sequence(_Rp&& __range, _FormatContext& __ctx) const {196    __ctx.advance_to(ranges::copy(__opening_bracket_, __ctx.out()).out);197    bool __use_separator = false;198    for (auto&& __e : __range) {199      if (__use_separator)200        __ctx.advance_to(ranges::copy(__separator_, __ctx.out()).out);201      else202        __use_separator = true;203 204      __ctx.advance_to(__underlying_.format(__e, __ctx));205    }206 207    return ranges::copy(__closing_bracket_, __ctx.out()).out;208  }209 210  __format_spec::__parser<_CharT> __parser_{.__alignment_ = __format_spec::__alignment::__left};211 212private:213  template <contiguous_iterator _Iterator>214  _LIBCPP_HIDE_FROM_ABI constexpr void __parse_type(_Iterator& __begin, _Iterator __end) {215    switch (*__begin) {216    case _CharT('m'):217      if constexpr (__fmt_pair_like<_Tp>) {218        set_brackets(_LIBCPP_STATICALLY_WIDEN(_CharT, "{"), _LIBCPP_STATICALLY_WIDEN(_CharT, "}"));219        set_separator(_LIBCPP_STATICALLY_WIDEN(_CharT, ", "));220        ++__begin;221      } else222        std::__throw_format_error("Type m requires a pair or a tuple with two elements");223      break;224 225    case _CharT('s'):226      if constexpr (same_as<_Tp, _CharT>) {227        __parser_.__type_ = __format_spec::__type::__string;228        ++__begin;229      } else230        std::__throw_format_error("Type s requires character type as formatting argument");231      break;232 233    case _CharT('?'):234      ++__begin;235      if (__begin == __end || *__begin != _CharT('s'))236        std::__throw_format_error("The format specifier should consume the input or end with a '}'");237      if constexpr (same_as<_Tp, _CharT>) {238        __parser_.__type_ = __format_spec::__type::__debug;239        ++__begin;240      } else241        std::__throw_format_error("Type ?s requires character type as formatting argument");242    }243  }244 245  template <class _ParseContext>246  _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator247  __parse_empty_range_underlying_spec(_ParseContext& __ctx, typename _ParseContext::iterator __begin) {248    __ctx.advance_to(__begin);249    [[maybe_unused]] typename _ParseContext::iterator __result = __underlying_.parse(__ctx);250    _LIBCPP_ASSERT_INTERNAL(__result == __begin,251                            "the underlying's parse function should not advance the input beyond the end of the input");252    return __begin;253  }254 255  formatter<_Tp, _CharT> __underlying_;256  basic_string_view<_CharT> __separator_       = _LIBCPP_STATICALLY_WIDEN(_CharT, ", ");257  basic_string_view<_CharT> __opening_bracket_ = _LIBCPP_STATICALLY_WIDEN(_CharT, "[");258  basic_string_view<_CharT> __closing_bracket_ = _LIBCPP_STATICALLY_WIDEN(_CharT, "]");259};260 261#endif // _LIBCPP_STD_VER >= 23262 263_LIBCPP_END_NAMESPACE_STD264 265#endif // _LIBCPP___FORMAT_RANGE_FORMATTER_H266