Changeset - r27416:8a08da0bf98e
[Not reviewed]
master
0 5 4
Rubidium - 16 months ago 2023-05-20 09:50:13
rubidium@openttd.org
Codechange: update to fmt 10.0.0 and add formatting support for chrono and std types
4 files changed:
0 comments (0 inline, 0 general)
src/3rdparty/fmt/CMakeLists.txt
Show inline comments
 
add_files(
 
    chrono.h
 
    core.h
 
    format.h
 
    format-inl.h
 
    ostream.h
 
    ranges.h
 
    std.h
 
)
src/3rdparty/fmt/LICENSE.rst
Show inline comments
 
Copyright (c) 2012 - present, Victor Zverovich
 
Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
 

	
 
Permission is hereby granted, free of charge, to any person obtaining
 
a copy of this software and associated documentation files (the
src/3rdparty/fmt/chrono.h
Show inline comments
 
new file 100644
 
// Formatting library for C++ - chrono support
 
//
 
// Copyright (c) 2012 - present, Victor Zverovich
 
// All rights reserved.
 
//
 
// For the license information refer to format.h.
 

	
 
#ifndef FMT_CHRONO_H_
 
#define FMT_CHRONO_H_
 

	
 
#include <algorithm>
 
#include <chrono>
 
#include <cmath>    // std::isfinite
 
#include <cstring>  // std::memcpy
 
#include <ctime>
 
#include <iterator>
 
#include <locale>
 
#include <ostream>
 
#include <type_traits>
 

	
 
#include "format.h"
 

	
 
FMT_BEGIN_NAMESPACE
 

	
 
// Check if std::chrono::local_t is available.
 
#ifndef FMT_USE_LOCAL_TIME
 
#  ifdef __cpp_lib_chrono
 
#    define FMT_USE_LOCAL_TIME (__cpp_lib_chrono >= 201907L)
 
#  else
 
#    define FMT_USE_LOCAL_TIME 0
 
#  endif
 
#endif
 

	
 
// Check if std::chrono::utc_timestamp is available.
 
#ifndef FMT_USE_UTC_TIME
 
#  ifdef __cpp_lib_chrono
 
#    define FMT_USE_UTC_TIME (__cpp_lib_chrono >= 201907L)
 
#  else
 
#    define FMT_USE_UTC_TIME 0
 
#  endif
 
#endif
 

	
 
// Enable tzset.
 
#ifndef FMT_USE_TZSET
 
// UWP doesn't provide _tzset.
 
#  if FMT_HAS_INCLUDE("winapifamily.h")
 
#    include <winapifamily.h>
 
#  endif
 
#  if defined(_WIN32) && (!defined(WINAPI_FAMILY) || \
 
                          (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
 
#    define FMT_USE_TZSET 1
 
#  else
 
#    define FMT_USE_TZSET 0
 
#  endif
 
#endif
 

	
 
// Enable safe chrono durations, unless explicitly disabled.
 
#ifndef FMT_SAFE_DURATION_CAST
 
#  define FMT_SAFE_DURATION_CAST 1
 
#endif
 
#if FMT_SAFE_DURATION_CAST
 

	
 
// For conversion between std::chrono::durations without undefined
 
// behaviour or erroneous results.
 
// This is a stripped down version of duration_cast, for inclusion in fmt.
 
// See https://github.com/pauldreik/safe_duration_cast
 
//
 
// Copyright Paul Dreik 2019
 
namespace safe_duration_cast {
 

	
 
template <typename To, typename From,
 
          FMT_ENABLE_IF(!std::is_same<From, To>::value &&
 
                        std::numeric_limits<From>::is_signed ==
 
                            std::numeric_limits<To>::is_signed)>
 
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
 
  ec = 0;
 
  using F = std::numeric_limits<From>;
 
  using T = std::numeric_limits<To>;
 
  static_assert(F::is_integer, "From must be integral");
 
  static_assert(T::is_integer, "To must be integral");
 

	
 
  // A and B are both signed, or both unsigned.
 
  if (detail::const_check(F::digits <= T::digits)) {
 
    // From fits in To without any problem.
 
  } else {
 
    // From does not always fit in To, resort to a dynamic check.
 
    if (from < (T::min)() || from > (T::max)()) {
 
      // outside range.
 
      ec = 1;
 
      return {};
 
    }
 
  }
 
  return static_cast<To>(from);
 
}
 

	
 
/**
 
 * converts From to To, without loss. If the dynamic value of from
 
 * can't be converted to To without loss, ec is set.
 
 */
 
template <typename To, typename From,
 
          FMT_ENABLE_IF(!std::is_same<From, To>::value &&
 
                        std::numeric_limits<From>::is_signed !=
 
                            std::numeric_limits<To>::is_signed)>
 
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
 
  ec = 0;
 
  using F = std::numeric_limits<From>;
 
  using T = std::numeric_limits<To>;
 
  static_assert(F::is_integer, "From must be integral");
 
  static_assert(T::is_integer, "To must be integral");
 

	
 
  if (detail::const_check(F::is_signed && !T::is_signed)) {
 
    // From may be negative, not allowed!
 
    if (fmt::detail::is_negative(from)) {
 
      ec = 1;
 
      return {};
 
    }
 
    // From is positive. Can it always fit in To?
 
    if (detail::const_check(F::digits > T::digits) &&
 
        from > static_cast<From>(detail::max_value<To>())) {
 
      ec = 1;
 
      return {};
 
    }
 
  }
 

	
 
  if (detail::const_check(!F::is_signed && T::is_signed &&
 
                          F::digits >= T::digits) &&
 
      from > static_cast<From>(detail::max_value<To>())) {
 
    ec = 1;
 
    return {};
 
  }
 
  return static_cast<To>(from);  // Lossless conversion.
 
}
 

	
 
template <typename To, typename From,
 
          FMT_ENABLE_IF(std::is_same<From, To>::value)>
 
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
 
  ec = 0;
 
  return from;
 
}  // function
 

	
 
// clang-format off
 
/**
 
 * converts From to To if possible, otherwise ec is set.
 
 *
 
 * input                            |    output
 
 * ---------------------------------|---------------
 
 * NaN                              | NaN
 
 * Inf                              | Inf
 
 * normal, fits in output           | converted (possibly lossy)
 
 * normal, does not fit in output   | ec is set
 
 * subnormal                        | best effort
 
 * -Inf                             | -Inf
 
 */
 
// clang-format on
 
template <typename To, typename From,
 
          FMT_ENABLE_IF(!std::is_same<From, To>::value)>
 
FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
 
  ec = 0;
 
  using T = std::numeric_limits<To>;
 
  static_assert(std::is_floating_point<From>::value, "From must be floating");
 
  static_assert(std::is_floating_point<To>::value, "To must be floating");
 

	
 
  // catch the only happy case
 
  if (std::isfinite(from)) {
 
    if (from >= T::lowest() && from <= (T::max)()) {
 
      return static_cast<To>(from);
 
    }
 
    // not within range.
 
    ec = 1;
 
    return {};
 
  }
 

	
 
  // nan and inf will be preserved
 
  return static_cast<To>(from);
 
}  // function
 

	
 
template <typename To, typename From,
 
          FMT_ENABLE_IF(std::is_same<From, To>::value)>
 
FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
 
  ec = 0;
 
  static_assert(std::is_floating_point<From>::value, "From must be floating");
 
  return from;
 
}
 

	
 
/**
 
 * safe duration cast between integral durations
 
 */
 
template <typename To, typename FromRep, typename FromPeriod,
 
          FMT_ENABLE_IF(std::is_integral<FromRep>::value),
 
          FMT_ENABLE_IF(std::is_integral<typename To::rep>::value)>
 
To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
 
                      int& ec) {
 
  using From = std::chrono::duration<FromRep, FromPeriod>;
 
  ec = 0;
 
  // the basic idea is that we need to convert from count() in the from type
 
  // to count() in the To type, by multiplying it with this:
 
  struct Factor
 
      : std::ratio_divide<typename From::period, typename To::period> {};
 

	
 
  static_assert(Factor::num > 0, "num must be positive");
 
  static_assert(Factor::den > 0, "den must be positive");
 

	
 
  // the conversion is like this: multiply from.count() with Factor::num
 
  // /Factor::den and convert it to To::rep, all this without
 
  // overflow/underflow. let's start by finding a suitable type that can hold
 
  // both To, From and Factor::num
 
  using IntermediateRep =
 
      typename std::common_type<typename From::rep, typename To::rep,
 
                                decltype(Factor::num)>::type;
 

	
 
  // safe conversion to IntermediateRep
 
  IntermediateRep count =
 
      lossless_integral_conversion<IntermediateRep>(from.count(), ec);
 
  if (ec) return {};
 
  // multiply with Factor::num without overflow or underflow
 
  if (detail::const_check(Factor::num != 1)) {
 
    const auto max1 = detail::max_value<IntermediateRep>() / Factor::num;
 
    if (count > max1) {
 
      ec = 1;
 
      return {};
 
    }
 
    const auto min1 =
 
        (std::numeric_limits<IntermediateRep>::min)() / Factor::num;
 
    if (detail::const_check(!std::is_unsigned<IntermediateRep>::value) &&
 
        count < min1) {
 
      ec = 1;
 
      return {};
 
    }
 
    count *= Factor::num;
 
  }
 

	
 
  if (detail::const_check(Factor::den != 1)) count /= Factor::den;
 
  auto tocount = lossless_integral_conversion<typename To::rep>(count, ec);
 
  return ec ? To() : To(tocount);
 
}
 

	
 
/**
 
 * safe duration_cast between floating point durations
 
 */
 
template <typename To, typename FromRep, typename FromPeriod,
 
          FMT_ENABLE_IF(std::is_floating_point<FromRep>::value),
 
          FMT_ENABLE_IF(std::is_floating_point<typename To::rep>::value)>
 
To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
 
                      int& ec) {
 
  using From = std::chrono::duration<FromRep, FromPeriod>;
 
  ec = 0;
 
  if (std::isnan(from.count())) {
 
    // nan in, gives nan out. easy.
 
    return To{std::numeric_limits<typename To::rep>::quiet_NaN()};
 
  }
 
  // maybe we should also check if from is denormal, and decide what to do about
 
  // it.
 

	
 
  // +-inf should be preserved.
 
  if (std::isinf(from.count())) {
 
    return To{from.count()};
 
  }
 

	
 
  // the basic idea is that we need to convert from count() in the from type
 
  // to count() in the To type, by multiplying it with this:
 
  struct Factor
 
      : std::ratio_divide<typename From::period, typename To::period> {};
 

	
 
  static_assert(Factor::num > 0, "num must be positive");
 
  static_assert(Factor::den > 0, "den must be positive");
 

	
 
  // the conversion is like this: multiply from.count() with Factor::num
 
  // /Factor::den and convert it to To::rep, all this without
 
  // overflow/underflow. let's start by finding a suitable type that can hold
 
  // both To, From and Factor::num
 
  using IntermediateRep =
 
      typename std::common_type<typename From::rep, typename To::rep,
 
                                decltype(Factor::num)>::type;
 

	
 
  // force conversion of From::rep -> IntermediateRep to be safe,
 
  // even if it will never happen be narrowing in this context.
 
  IntermediateRep count =
 
      safe_float_conversion<IntermediateRep>(from.count(), ec);
 
  if (ec) {
 
    return {};
 
  }
 

	
 
  // multiply with Factor::num without overflow or underflow
 
  if (detail::const_check(Factor::num != 1)) {
 
    constexpr auto max1 = detail::max_value<IntermediateRep>() /
 
                          static_cast<IntermediateRep>(Factor::num);
 
    if (count > max1) {
 
      ec = 1;
 
      return {};
 
    }
 
    constexpr auto min1 = std::numeric_limits<IntermediateRep>::lowest() /
 
                          static_cast<IntermediateRep>(Factor::num);
 
    if (count < min1) {
 
      ec = 1;
 
      return {};
 
    }
 
    count *= static_cast<IntermediateRep>(Factor::num);
 
  }
 

	
 
  // this can't go wrong, right? den>0 is checked earlier.
 
  if (detail::const_check(Factor::den != 1)) {
 
    using common_t = typename std::common_type<IntermediateRep, intmax_t>::type;
 
    count /= static_cast<common_t>(Factor::den);
 
  }
 

	
 
  // convert to the to type, safely
 
  using ToRep = typename To::rep;
 

	
 
  const ToRep tocount = safe_float_conversion<ToRep>(count, ec);
 
  if (ec) {
 
    return {};
 
  }
 
  return To{tocount};
 
}
 
}  // namespace safe_duration_cast
 
#endif
 

	
 
// Prevents expansion of a preceding token as a function-style macro.
 
// Usage: f FMT_NOMACRO()
 
#define FMT_NOMACRO
 

	
 
namespace detail {
 
template <typename T = void> struct null {};
 
inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); }
 
inline null<> localtime_s(...) { return null<>(); }
 
inline null<> gmtime_r(...) { return null<>(); }
 
inline null<> gmtime_s(...) { return null<>(); }
 

	
 
inline const std::locale& get_classic_locale() {
 
  static const auto& locale = std::locale::classic();
 
  return locale;
 
}
 

	
 
template <typename CodeUnit> struct codecvt_result {
 
  static constexpr const size_t max_size = 32;
 
  CodeUnit buf[max_size];
 
  CodeUnit* end;
 
};
 
template <typename CodeUnit>
 
constexpr const size_t codecvt_result<CodeUnit>::max_size;
 

	
 
template <typename CodeUnit>
 
void write_codecvt(codecvt_result<CodeUnit>& out, string_view in_buf,
 
                   const std::locale& loc) {
 
#if FMT_CLANG_VERSION
 
#  pragma clang diagnostic push
 
#  pragma clang diagnostic ignored "-Wdeprecated"
 
  auto& f = std::use_facet<std::codecvt<CodeUnit, char, std::mbstate_t>>(loc);
 
#  pragma clang diagnostic pop
 
#else
 
  auto& f = std::use_facet<std::codecvt<CodeUnit, char, std::mbstate_t>>(loc);
 
#endif
 
  auto mb = std::mbstate_t();
 
  const char* from_next = nullptr;
 
  auto result = f.in(mb, in_buf.begin(), in_buf.end(), from_next,
 
                     std::begin(out.buf), std::end(out.buf), out.end);
 
  if (result != std::codecvt_base::ok)
 
    FMT_THROW(format_error("failed to format time"));
 
}
 

	
 
template <typename OutputIt>
 
auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc)
 
    -> OutputIt {
 
  if (detail::is_utf8() && loc != get_classic_locale()) {
 
    // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and
 
    // gcc-4.
 
#if FMT_MSC_VERSION != 0 || \
 
    (defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI))
 
    // The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5
 
    // and newer.
 
    using code_unit = wchar_t;
 
#else
 
    using code_unit = char32_t;
 
#endif
 

	
 
    using unit_t = codecvt_result<code_unit>;
 
    unit_t unit;
 
    write_codecvt(unit, in, loc);
 
    // In UTF-8 is used one to four one-byte code units.
 
    unicode_to_utf8<code_unit, basic_memory_buffer<char, unit_t::max_size * 4>>
 
        u;
 
    if (!u.convert({unit.buf, to_unsigned(unit.end - unit.buf)}))
 
      FMT_THROW(format_error("failed to format time"));
 
    return copy_str<char>(u.c_str(), u.c_str() + u.size(), out);
 
  }
 
  return copy_str<char>(in.data(), in.data() + in.size(), out);
 
}
 

	
 
template <typename Char, typename OutputIt,
 
          FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
 
auto write_tm_str(OutputIt out, string_view sv, const std::locale& loc)
 
    -> OutputIt {
 
  codecvt_result<Char> unit;
 
  write_codecvt(unit, sv, loc);
 
  return copy_str<Char>(unit.buf, unit.end, out);
 
}
 

	
 
template <typename Char, typename OutputIt,
 
          FMT_ENABLE_IF(std::is_same<Char, char>::value)>
 
auto write_tm_str(OutputIt out, string_view sv, const std::locale& loc)
 
    -> OutputIt {
 
  return write_encoded_tm_str(out, sv, loc);
 
}
 

	
 
template <typename Char>
 
inline void do_write(buffer<Char>& buf, const std::tm& time,
 
                     const std::locale& loc, char format, char modifier) {
 
  auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
 
  auto&& os = std::basic_ostream<Char>(&format_buf);
 
  os.imbue(loc);
 
  using iterator = std::ostreambuf_iterator<Char>;
 
  const auto& facet = std::use_facet<std::time_put<Char, iterator>>(loc);
 
  auto end = facet.put(os, os, Char(' '), &time, format, modifier);
 
  if (end.failed()) FMT_THROW(format_error("failed to format time"));
 
}
 

	
 
template <typename Char, typename OutputIt,
 
          FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
 
auto write(OutputIt out, const std::tm& time, const std::locale& loc,
 
           char format, char modifier = 0) -> OutputIt {
 
  auto&& buf = get_buffer<Char>(out);
 
  do_write<Char>(buf, time, loc, format, modifier);
 
  return get_iterator(buf, out);
 
}
 

	
 
template <typename Char, typename OutputIt,
 
          FMT_ENABLE_IF(std::is_same<Char, char>::value)>
 
auto write(OutputIt out, const std::tm& time, const std::locale& loc,
 
           char format, char modifier = 0) -> OutputIt {
 
  auto&& buf = basic_memory_buffer<Char>();
 
  do_write<char>(buf, time, loc, format, modifier);
 
  return write_encoded_tm_str(out, string_view(buf.data(), buf.size()), loc);
 
}
 

	
 
}  // namespace detail
 

	
 
FMT_BEGIN_EXPORT
 

	
 
/**
 
  Converts given time since epoch as ``std::time_t`` value into calendar time,
 
  expressed in local time. Unlike ``std::localtime``, this function is
 
  thread-safe on most platforms.
 
 */
 
inline std::tm localtime(std::time_t time) {
 
  struct dispatcher {
 
    std::time_t time_;
 
    std::tm tm_;
 

	
 
    dispatcher(std::time_t t) : time_(t) {}
 

	
 
    bool run() {
 
      using namespace fmt::detail;
 
      return handle(localtime_r(&time_, &tm_));
 
    }
 

	
 
    bool handle(std::tm* tm) { return tm != nullptr; }
 

	
 
    bool handle(detail::null<>) {
 
      using namespace fmt::detail;
 
      return fallback(localtime_s(&tm_, &time_));
 
    }
 

	
 
    bool fallback(int res) { return res == 0; }
 

	
 
#if !FMT_MSC_VERSION
 
    bool fallback(detail::null<>) {
 
      using namespace fmt::detail;
 
      std::tm* tm = std::localtime(&time_);
 
      if (tm) tm_ = *tm;
 
      return tm != nullptr;
 
    }
 
#endif
 
  };
 
  dispatcher lt(time);
 
  // Too big time values may be unsupported.
 
  if (!lt.run()) FMT_THROW(format_error("time_t value out of range"));
 
  return lt.tm_;
 
}
 

	
 
#if FMT_USE_LOCAL_TIME
 
template <typename Duration>
 
inline auto localtime(std::chrono::local_time<Duration> time) -> std::tm {
 
  return localtime(std::chrono::system_clock::to_time_t(
 
      std::chrono::current_zone()->to_sys(time)));
 
}
 
#endif
 

	
 
/**
 
  Converts given time since epoch as ``std::time_t`` value into calendar time,
 
  expressed in Coordinated Universal Time (UTC). Unlike ``std::gmtime``, this
 
  function is thread-safe on most platforms.
 
 */
 
inline std::tm gmtime(std::time_t time) {
 
  struct dispatcher {
 
    std::time_t time_;
 
    std::tm tm_;
 

	
 
    dispatcher(std::time_t t) : time_(t) {}
 

	
 
    bool run() {
 
      using namespace fmt::detail;
 
      return handle(gmtime_r(&time_, &tm_));
 
    }
 

	
 
    bool handle(std::tm* tm) { return tm != nullptr; }
 

	
 
    bool handle(detail::null<>) {
 
      using namespace fmt::detail;
 
      return fallback(gmtime_s(&tm_, &time_));
 
    }
 

	
 
    bool fallback(int res) { return res == 0; }
 

	
 
#if !FMT_MSC_VERSION
 
    bool fallback(detail::null<>) {
 
      std::tm* tm = std::gmtime(&time_);
 
      if (tm) tm_ = *tm;
 
      return tm != nullptr;
 
    }
 
#endif
 
  };
 
  dispatcher gt(time);
 
  // Too big time values may be unsupported.
 
  if (!gt.run()) FMT_THROW(format_error("time_t value out of range"));
 
  return gt.tm_;
 
}
 

	
 
inline std::tm gmtime(
 
    std::chrono::time_point<std::chrono::system_clock> time_point) {
 
  return gmtime(std::chrono::system_clock::to_time_t(time_point));
 
}
 

	
 
FMT_BEGIN_DETAIL_NAMESPACE
 

	
 
// DEPRECATED!
 
template <typename Char>
 
FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end,
 
                               format_specs<Char>& specs) -> const Char* {
 
  FMT_ASSERT(begin != end, "");
 
  auto align = align::none;
 
  auto p = begin + code_point_length(begin);
 
  if (end - p <= 0) p = begin;
 
  for (;;) {
 
    switch (to_ascii(*p)) {
 
    case '<':
 
      align = align::left;
 
      break;
 
    case '>':
 
      align = align::right;
 
      break;
 
    case '^':
 
      align = align::center;
 
      break;
 
    }
 
    if (align != align::none) {
 
      if (p != begin) {
 
        auto c = *begin;
 
        if (c == '}') return begin;
 
        if (c == '{') {
 
          throw_format_error("invalid fill character '{'");
 
          return begin;
 
        }
 
        specs.fill = {begin, to_unsigned(p - begin)};
 
        begin = p + 1;
 
      } else {
 
        ++begin;
 
      }
 
      break;
 
    } else if (p == begin) {
 
      break;
 
    }
 
    p = begin;
 
  }
 
  specs.align = align;
 
  return begin;
 
}
 

	
 
// Writes two-digit numbers a, b and c separated by sep to buf.
 
// The method by Pavel Novikov based on
 
// https://johnnylee-sde.github.io/Fast-unsigned-integer-to-time-string/.
 
inline void write_digit2_separated(char* buf, unsigned a, unsigned b,
 
                                   unsigned c, char sep) {
 
  unsigned long long digits =
 
      a | (b << 24) | (static_cast<unsigned long long>(c) << 48);
 
  // Convert each value to BCD.
 
  // We have x = a * 10 + b and we want to convert it to BCD y = a * 16 + b.
 
  // The difference is
 
  //   y - x = a * 6
 
  // a can be found from x:
 
  //   a = floor(x / 10)
 
  // then
 
  //   y = x + a * 6 = x + floor(x / 10) * 6
 
  // floor(x / 10) is (x * 205) >> 11 (needs 16 bits).
 
  digits += (((digits * 205) >> 11) & 0x000f00000f00000f) * 6;
 
  // Put low nibbles to high bytes and high nibbles to low bytes.
 
  digits = ((digits & 0x00f00000f00000f0) >> 4) |
 
           ((digits & 0x000f00000f00000f) << 8);
 
  auto usep = static_cast<unsigned long long>(sep);
 
  // Add ASCII '0' to each digit byte and insert separators.
 
  digits |= 0x3030003030003030 | (usep << 16) | (usep << 40);
 

	
 
  constexpr const size_t len = 8;
 
  if (const_check(is_big_endian())) {
 
    char tmp[len];
 
    std::memcpy(tmp, &digits, len);
 
    std::reverse_copy(tmp, tmp + len, buf);
 
  } else {
 
    std::memcpy(buf, &digits, len);
 
  }
 
}
 

	
 
template <typename Period> FMT_CONSTEXPR inline const char* get_units() {
 
  if (std::is_same<Period, std::atto>::value) return "as";
 
  if (std::is_same<Period, std::femto>::value) return "fs";
 
  if (std::is_same<Period, std::pico>::value) return "ps";
 
  if (std::is_same<Period, std::nano>::value) return "ns";
 
  if (std::is_same<Period, std::micro>::value) return "µs";
 
  if (std::is_same<Period, std::milli>::value) return "ms";
 
  if (std::is_same<Period, std::centi>::value) return "cs";
 
  if (std::is_same<Period, std::deci>::value) return "ds";
 
  if (std::is_same<Period, std::ratio<1>>::value) return "s";
 
  if (std::is_same<Period, std::deca>::value) return "das";
 
  if (std::is_same<Period, std::hecto>::value) return "hs";
 
  if (std::is_same<Period, std::kilo>::value) return "ks";
 
  if (std::is_same<Period, std::mega>::value) return "Ms";
 
  if (std::is_same<Period, std::giga>::value) return "Gs";
 
  if (std::is_same<Period, std::tera>::value) return "Ts";
 
  if (std::is_same<Period, std::peta>::value) return "Ps";
 
  if (std::is_same<Period, std::exa>::value) return "Es";
 
  if (std::is_same<Period, std::ratio<60>>::value) return "m";
 
  if (std::is_same<Period, std::ratio<3600>>::value) return "h";
 
  return nullptr;
 
}
 

	
 
enum class numeric_system {
 
  standard,
 
  // Alternative numeric system, e.g. 十二 instead of 12 in ja_JP locale.
 
  alternative
 
};
 

	
 
// Glibc extensions for formatting numeric values.
 
enum class pad_type {
 
  unspecified,
 
  // Do not pad a numeric result string.
 
  none,
 
  // Pad a numeric result string with zeros even if the conversion specifier
 
  // character uses space-padding by default.
 
  zero,
 
  // Pad a numeric result string with spaces.
 
  space,
 
};
 

	
 
template <typename OutputIt>
 
auto write_padding(OutputIt out, pad_type pad, int width) -> OutputIt {
 
  if (pad == pad_type::none) return out;
 
  return std::fill_n(out, width, pad == pad_type::space ? ' ' : '0');
 
}
 

	
 
template <typename OutputIt>
 
auto write_padding(OutputIt out, pad_type pad) -> OutputIt {
 
  if (pad != pad_type::none) *out++ = pad == pad_type::space ? ' ' : '0';
 
  return out;
 
}
 

	
 
// Parses a put_time-like format string and invokes handler actions.
 
template <typename Char, typename Handler>
 
FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
 
                                              const Char* end,
 
                                              Handler&& handler) {
 
  if (begin == end || *begin == '}') return begin;
 
  if (*begin != '%') FMT_THROW(format_error("invalid format"));
 
  auto ptr = begin;
 
  pad_type pad = pad_type::unspecified;
 
  while (ptr != end) {
 
    auto c = *ptr;
 
    if (c == '}') break;
 
    if (c != '%') {
 
      ++ptr;
 
      continue;
 
    }
 
    if (begin != ptr) handler.on_text(begin, ptr);
 
    ++ptr;  // consume '%'
 
    if (ptr == end) FMT_THROW(format_error("invalid format"));
 
    c = *ptr;
 
    switch (c) {
 
    case '_':
 
      pad = pad_type::space;
 
      ++ptr;
 
      break;
 
    case '-':
 
      pad = pad_type::none;
 
      ++ptr;
 
      break;
 
    case '0':
 
      pad = pad_type::zero;
 
      ++ptr;
 
      break;
 
    }
 
    if (ptr == end) FMT_THROW(format_error("invalid format"));
 
    c = *ptr++;
 
    switch (c) {
 
    case '%':
 
      handler.on_text(ptr - 1, ptr);
 
      break;
 
    case 'n': {
 
      const Char newline[] = {'\n'};
 
      handler.on_text(newline, newline + 1);
 
      break;
 
    }
 
    case 't': {
 
      const Char tab[] = {'\t'};
 
      handler.on_text(tab, tab + 1);
 
      break;
 
    }
 
    // Year:
 
    case 'Y':
 
      handler.on_year(numeric_system::standard);
 
      break;
 
    case 'y':
 
      handler.on_short_year(numeric_system::standard);
 
      break;
 
    case 'C':
 
      handler.on_century(numeric_system::standard);
 
      break;
 
    case 'G':
 
      handler.on_iso_week_based_year();
 
      break;
 
    case 'g':
 
      handler.on_iso_week_based_short_year();
 
      break;
 
    // Day of the week:
 
    case 'a':
 
      handler.on_abbr_weekday();
 
      break;
 
    case 'A':
 
      handler.on_full_weekday();
 
      break;
 
    case 'w':
 
      handler.on_dec0_weekday(numeric_system::standard);
 
      break;
 
    case 'u':
 
      handler.on_dec1_weekday(numeric_system::standard);
 
      break;
 
    // Month:
 
    case 'b':
 
    case 'h':
 
      handler.on_abbr_month();
 
      break;
 
    case 'B':
 
      handler.on_full_month();
 
      break;
 
    case 'm':
 
      handler.on_dec_month(numeric_system::standard);
 
      break;
 
    // Day of the year/month:
 
    case 'U':
 
      handler.on_dec0_week_of_year(numeric_system::standard);
 
      break;
 
    case 'W':
 
      handler.on_dec1_week_of_year(numeric_system::standard);
 
      break;
 
    case 'V':
 
      handler.on_iso_week_of_year(numeric_system::standard);
 
      break;
 
    case 'j':
 
      handler.on_day_of_year();
 
      break;
 
    case 'd':
 
      handler.on_day_of_month(numeric_system::standard);
 
      break;
 
    case 'e':
 
      handler.on_day_of_month_space(numeric_system::standard);
 
      break;
 
    // Hour, minute, second:
 
    case 'H':
 
      handler.on_24_hour(numeric_system::standard, pad);
 
      break;
 
    case 'I':
 
      handler.on_12_hour(numeric_system::standard, pad);
 
      break;
 
    case 'M':
 
      handler.on_minute(numeric_system::standard, pad);
 
      break;
 
    case 'S':
 
      handler.on_second(numeric_system::standard, pad);
 
      break;
 
    // Other:
 
    case 'c':
 
      handler.on_datetime(numeric_system::standard);
 
      break;
 
    case 'x':
 
      handler.on_loc_date(numeric_system::standard);
 
      break;
 
    case 'X':
 
      handler.on_loc_time(numeric_system::standard);
 
      break;
 
    case 'D':
 
      handler.on_us_date();
 
      break;
 
    case 'F':
 
      handler.on_iso_date();
 
      break;
 
    case 'r':
 
      handler.on_12_hour_time();
 
      break;
 
    case 'R':
 
      handler.on_24_hour_time();
 
      break;
 
    case 'T':
 
      handler.on_iso_time();
 
      break;
 
    case 'p':
 
      handler.on_am_pm();
 
      break;
 
    case 'Q':
 
      handler.on_duration_value();
 
      break;
 
    case 'q':
 
      handler.on_duration_unit();
 
      break;
 
    case 'z':
 
      handler.on_utc_offset(numeric_system::standard);
 
      break;
 
    case 'Z':
 
      handler.on_tz_name();
 
      break;
 
    // Alternative representation:
 
    case 'E': {
 
      if (ptr == end) FMT_THROW(format_error("invalid format"));
 
      c = *ptr++;
 
      switch (c) {
 
      case 'Y':
 
        handler.on_year(numeric_system::alternative);
 
        break;
 
      case 'y':
 
        handler.on_offset_year();
 
        break;
 
      case 'C':
 
        handler.on_century(numeric_system::alternative);
 
        break;
 
      case 'c':
 
        handler.on_datetime(numeric_system::alternative);
 
        break;
 
      case 'x':
 
        handler.on_loc_date(numeric_system::alternative);
 
        break;
 
      case 'X':
 
        handler.on_loc_time(numeric_system::alternative);
 
        break;
 
      case 'z':
 
        handler.on_utc_offset(numeric_system::alternative);
 
        break;
 
      default:
 
        FMT_THROW(format_error("invalid format"));
 
      }
 
      break;
 
    }
 
    case 'O':
 
      if (ptr == end) FMT_THROW(format_error("invalid format"));
 
      c = *ptr++;
 
      switch (c) {
 
      case 'y':
 
        handler.on_short_year(numeric_system::alternative);
 
        break;
 
      case 'm':
 
        handler.on_dec_month(numeric_system::alternative);
 
        break;
 
      case 'U':
 
        handler.on_dec0_week_of_year(numeric_system::alternative);
 
        break;
 
      case 'W':
 
        handler.on_dec1_week_of_year(numeric_system::alternative);
 
        break;
 
      case 'V':
 
        handler.on_iso_week_of_year(numeric_system::alternative);
 
        break;
 
      case 'd':
 
        handler.on_day_of_month(numeric_system::alternative);
 
        break;
 
      case 'e':
 
        handler.on_day_of_month_space(numeric_system::alternative);
 
        break;
 
      case 'w':
 
        handler.on_dec0_weekday(numeric_system::alternative);
 
        break;
 
      case 'u':
 
        handler.on_dec1_weekday(numeric_system::alternative);
 
        break;
 
      case 'H':
 
        handler.on_24_hour(numeric_system::alternative, pad);
 
        break;
 
      case 'I':
 
        handler.on_12_hour(numeric_system::alternative, pad);
 
        break;
 
      case 'M':
 
        handler.on_minute(numeric_system::alternative, pad);
 
        break;
 
      case 'S':
 
        handler.on_second(numeric_system::alternative, pad);
 
        break;
 
      case 'z':
 
        handler.on_utc_offset(numeric_system::alternative);
 
        break;
 
      default:
 
        FMT_THROW(format_error("invalid format"));
 
      }
 
      break;
 
    default:
 
      FMT_THROW(format_error("invalid format"));
 
    }
 
    begin = ptr;
 
  }
 
  if (begin != ptr) handler.on_text(begin, ptr);
 
  return ptr;
 
}
 

	
 
template <typename Derived> struct null_chrono_spec_handler {
 
  FMT_CONSTEXPR void unsupported() {
 
    static_cast<Derived*>(this)->unsupported();
 
  }
 
  FMT_CONSTEXPR void on_year(numeric_system) { unsupported(); }
 
  FMT_CONSTEXPR void on_short_year(numeric_system) { unsupported(); }
 
  FMT_CONSTEXPR void on_offset_year() { unsupported(); }
 
  FMT_CONSTEXPR void on_century(numeric_system) { unsupported(); }
 
  FMT_CONSTEXPR void on_iso_week_based_year() { unsupported(); }
 
  FMT_CONSTEXPR void on_iso_week_based_short_year() { unsupported(); }
 
  FMT_CONSTEXPR void on_abbr_weekday() { unsupported(); }
 
  FMT_CONSTEXPR void on_full_weekday() { unsupported(); }
 
  FMT_CONSTEXPR void on_dec0_weekday(numeric_system) { unsupported(); }
 
  FMT_CONSTEXPR void on_dec1_weekday(numeric_system) { unsupported(); }
 
  FMT_CONSTEXPR void on_abbr_month() { unsupported(); }
 
  FMT_CONSTEXPR void on_full_month() { unsupported(); }
 
  FMT_CONSTEXPR void on_dec_month(numeric_system) { unsupported(); }
 
  FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) { unsupported(); }
 
  FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) { unsupported(); }
 
  FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) { unsupported(); }
 
  FMT_CONSTEXPR void on_day_of_year() { unsupported(); }
 
  FMT_CONSTEXPR void on_day_of_month(numeric_system) { unsupported(); }
 
  FMT_CONSTEXPR void on_day_of_month_space(numeric_system) { unsupported(); }
 
  FMT_CONSTEXPR void on_24_hour(numeric_system) { unsupported(); }
 
  FMT_CONSTEXPR void on_12_hour(numeric_system) { unsupported(); }
 
  FMT_CONSTEXPR void on_minute(numeric_system) { unsupported(); }
 
  FMT_CONSTEXPR void on_second(numeric_system) { unsupported(); }
 
  FMT_CONSTEXPR void on_datetime(numeric_system) { unsupported(); }
 
  FMT_CONSTEXPR void on_loc_date(numeric_system) { unsupported(); }
 
  FMT_CONSTEXPR void on_loc_time(numeric_system) { unsupported(); }
 
  FMT_CONSTEXPR void on_us_date() { unsupported(); }
 
  FMT_CONSTEXPR void on_iso_date() { unsupported(); }
 
  FMT_CONSTEXPR void on_12_hour_time() { unsupported(); }
 
  FMT_CONSTEXPR void on_24_hour_time() { unsupported(); }
 
  FMT_CONSTEXPR void on_iso_time() { unsupported(); }
 
  FMT_CONSTEXPR void on_am_pm() { unsupported(); }
 
  FMT_CONSTEXPR void on_duration_value() { unsupported(); }
 
  FMT_CONSTEXPR void on_duration_unit() { unsupported(); }
 
  FMT_CONSTEXPR void on_utc_offset(numeric_system) { unsupported(); }
 
  FMT_CONSTEXPR void on_tz_name() { unsupported(); }
 
};
 

	
 
struct tm_format_checker : null_chrono_spec_handler<tm_format_checker> {
 
  FMT_NORETURN void unsupported() { FMT_THROW(format_error("no format")); }
 

	
 
  template <typename Char>
 
  FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
 
  FMT_CONSTEXPR void on_year(numeric_system) {}
 
  FMT_CONSTEXPR void on_short_year(numeric_system) {}
 
  FMT_CONSTEXPR void on_offset_year() {}
 
  FMT_CONSTEXPR void on_century(numeric_system) {}
 
  FMT_CONSTEXPR void on_iso_week_based_year() {}
 
  FMT_CONSTEXPR void on_iso_week_based_short_year() {}
 
  FMT_CONSTEXPR void on_abbr_weekday() {}
 
  FMT_CONSTEXPR void on_full_weekday() {}
 
  FMT_CONSTEXPR void on_dec0_weekday(numeric_system) {}
 
  FMT_CONSTEXPR void on_dec1_weekday(numeric_system) {}
 
  FMT_CONSTEXPR void on_abbr_month() {}
 
  FMT_CONSTEXPR void on_full_month() {}
 
  FMT_CONSTEXPR void on_dec_month(numeric_system) {}
 
  FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) {}
 
  FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) {}
 
  FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) {}
 
  FMT_CONSTEXPR void on_day_of_year() {}
 
  FMT_CONSTEXPR void on_day_of_month(numeric_system) {}
 
  FMT_CONSTEXPR void on_day_of_month_space(numeric_system) {}
 
  FMT_CONSTEXPR void on_24_hour(numeric_system, pad_type) {}
 
  FMT_CONSTEXPR void on_12_hour(numeric_system, pad_type) {}
 
  FMT_CONSTEXPR void on_minute(numeric_system, pad_type) {}
 
  FMT_CONSTEXPR void on_second(numeric_system, pad_type) {}
 
  FMT_CONSTEXPR void on_datetime(numeric_system) {}
 
  FMT_CONSTEXPR void on_loc_date(numeric_system) {}
 
  FMT_CONSTEXPR void on_loc_time(numeric_system) {}
 
  FMT_CONSTEXPR void on_us_date() {}
 
  FMT_CONSTEXPR void on_iso_date() {}
 
  FMT_CONSTEXPR void on_12_hour_time() {}
 
  FMT_CONSTEXPR void on_24_hour_time() {}
 
  FMT_CONSTEXPR void on_iso_time() {}
 
  FMT_CONSTEXPR void on_am_pm() {}
 
  FMT_CONSTEXPR void on_utc_offset(numeric_system) {}
 
  FMT_CONSTEXPR void on_tz_name() {}
 
};
 

	
 
inline const char* tm_wday_full_name(int wday) {
 
  static constexpr const char* full_name_list[] = {
 
      "Sunday",   "Monday", "Tuesday", "Wednesday",
 
      "Thursday", "Friday", "Saturday"};
 
  return wday >= 0 && wday <= 6 ? full_name_list[wday] : "?";
 
}
 
inline const char* tm_wday_short_name(int wday) {
 
  static constexpr const char* short_name_list[] = {"Sun", "Mon", "Tue", "Wed",
 
                                                    "Thu", "Fri", "Sat"};
 
  return wday >= 0 && wday <= 6 ? short_name_list[wday] : "???";
 
}
 

	
 
inline const char* tm_mon_full_name(int mon) {
 
  static constexpr const char* full_name_list[] = {
 
      "January", "February", "March",     "April",   "May",      "June",
 
      "July",    "August",   "September", "October", "November", "December"};
 
  return mon >= 0 && mon <= 11 ? full_name_list[mon] : "?";
 
}
 
inline const char* tm_mon_short_name(int mon) {
 
  static constexpr const char* short_name_list[] = {
 
      "Jan", "Feb", "Mar", "Apr", "May", "Jun",
 
      "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
 
  };
 
  return mon >= 0 && mon <= 11 ? short_name_list[mon] : "???";
 
}
 

	
 
template <typename T, typename = void>
 
struct has_member_data_tm_gmtoff : std::false_type {};
 
template <typename T>
 
struct has_member_data_tm_gmtoff<T, void_t<decltype(T::tm_gmtoff)>>
 
    : std::true_type {};
 

	
 
template <typename T, typename = void>
 
struct has_member_data_tm_zone : std::false_type {};
 
template <typename T>
 
struct has_member_data_tm_zone<T, void_t<decltype(T::tm_zone)>>
 
    : std::true_type {};
 

	
 
#if FMT_USE_TZSET
 
inline void tzset_once() {
 
  static bool init = []() -> bool {
 
    _tzset();
 
    return true;
 
  }();
 
  ignore_unused(init);
 
}
 
#endif
 

	
 
// Converts value to Int and checks that it's in the range [0, upper).
 
template <typename T, typename Int, FMT_ENABLE_IF(std::is_integral<T>::value)>
 
inline Int to_nonnegative_int(T value, Int upper) {
 
  FMT_ASSERT(std::is_unsigned<Int>::value ||
 
                 (value >= 0 && to_unsigned(value) <= to_unsigned(upper)),
 
             "invalid value");
 
  (void)upper;
 
  return static_cast<Int>(value);
 
}
 
template <typename T, typename Int, FMT_ENABLE_IF(!std::is_integral<T>::value)>
 
inline Int to_nonnegative_int(T value, Int upper) {
 
  if (value < 0 || value > static_cast<T>(upper))
 
    FMT_THROW(format_error("invalid value"));
 
  return static_cast<Int>(value);
 
}
 

	
 
constexpr long long pow10(std::uint32_t n) {
 
  return n == 0 ? 1 : 10 * pow10(n - 1);
 
}
 

	
 
// Counts the number of fractional digits in the range [0, 18] according to the
 
// C++20 spec. If more than 18 fractional digits are required then returns 6 for
 
// microseconds precision.
 
template <long long Num, long long Den, int N = 0,
 
          bool Enabled = (N < 19) && (Num <= max_value<long long>() / 10)>
 
struct count_fractional_digits {
 
  static constexpr int value =
 
      Num % Den == 0 ? N : count_fractional_digits<Num * 10, Den, N + 1>::value;
 
};
 

	
 
// Base case that doesn't instantiate any more templates
 
// in order to avoid overflow.
 
template <long long Num, long long Den, int N>
 
struct count_fractional_digits<Num, Den, N, false> {
 
  static constexpr int value = (Num % Den == 0) ? N : 6;
 
};
 

	
 
// Format subseconds which are given as an integer type with an appropriate
 
// number of digits.
 
template <typename Char, typename OutputIt, typename Duration>
 
void write_fractional_seconds(OutputIt& out, Duration d, int precision = -1) {
 
  constexpr auto num_fractional_digits =
 
      count_fractional_digits<Duration::period::num,
 
                              Duration::period::den>::value;
 

	
 
  using subsecond_precision = std::chrono::duration<
 
      typename std::common_type<typename Duration::rep,
 
                                std::chrono::seconds::rep>::type,
 
      std::ratio<1, detail::pow10(num_fractional_digits)>>;
 

	
 
  const auto fractional =
 
      d - std::chrono::duration_cast<std::chrono::seconds>(d);
 
  const auto subseconds =
 
      std::chrono::treat_as_floating_point<
 
          typename subsecond_precision::rep>::value
 
          ? fractional.count()
 
          : std::chrono::duration_cast<subsecond_precision>(fractional).count();
 
  auto n = static_cast<uint32_or_64_or_128_t<long long>>(subseconds);
 
  const int num_digits = detail::count_digits(n);
 

	
 
  int leading_zeroes = (std::max)(0, num_fractional_digits - num_digits);
 
  if (precision < 0) {
 
    FMT_ASSERT(!std::is_floating_point<typename Duration::rep>::value, "");
 
    if (std::ratio_less<typename subsecond_precision::period,
 
                        std::chrono::seconds::period>::value) {
 
      *out++ = '.';
 
      out = std::fill_n(out, leading_zeroes, '0');
 
      out = format_decimal<Char>(out, n, num_digits).end;
 
    }
 
  } else {
 
    *out++ = '.';
 
    leading_zeroes = (std::min)(leading_zeroes, precision);
 
    out = std::fill_n(out, leading_zeroes, '0');
 
    int remaining = precision - leading_zeroes;
 
    if (remaining != 0 && remaining < num_digits) {
 
      n /= to_unsigned(detail::pow10(to_unsigned(num_digits - remaining)));
 
      out = format_decimal<Char>(out, n, remaining).end;
 
      return;
 
    }
 
    out = format_decimal<Char>(out, n, num_digits).end;
 
    remaining -= num_digits;
 
    out = std::fill_n(out, remaining, '0');
 
  }
 
}
 

	
 
// Format subseconds which are given as a floating point type with an
 
// appropriate number of digits. We cannot pass the Duration here, as we
 
// explicitly need to pass the Rep value in the chrono_formatter.
 
template <typename Duration>
 
void write_floating_seconds(memory_buffer& buf, Duration duration,
 
                            int num_fractional_digits = -1) {
 
  using rep = typename Duration::rep;
 
  FMT_ASSERT(std::is_floating_point<rep>::value, "");
 

	
 
  auto val = duration.count();
 

	
 
  if (num_fractional_digits < 0) {
 
    // For `std::round` with fallback to `round`:
 
    // On some toolchains `std::round` is not available (e.g. GCC 6).
 
    using namespace std;
 
    num_fractional_digits =
 
        count_fractional_digits<Duration::period::num,
 
                                Duration::period::den>::value;
 
    if (num_fractional_digits < 6 && static_cast<rep>(round(val)) != val)
 
      num_fractional_digits = 6;
 
  }
 

	
 
  format_to(std::back_inserter(buf), FMT_STRING("{:.{}f}"),
 
            std::fmod(val * static_cast<rep>(Duration::period::num) /
 
                          static_cast<rep>(Duration::period::den),
 
                      static_cast<rep>(60)),
 
            num_fractional_digits);
 
}
 

	
 
template <typename OutputIt, typename Char,
 
          typename Duration = std::chrono::seconds>
 
class tm_writer {
 
 private:
 
  static constexpr int days_per_week = 7;
 

	
 
  const std::locale& loc_;
 
  const bool is_classic_;
 
  OutputIt out_;
 
  const Duration* subsecs_;
 
  const std::tm& tm_;
 

	
 
  auto tm_sec() const noexcept -> int {
 
    FMT_ASSERT(tm_.tm_sec >= 0 && tm_.tm_sec <= 61, "");
 
    return tm_.tm_sec;
 
  }
 
  auto tm_min() const noexcept -> int {
 
    FMT_ASSERT(tm_.tm_min >= 0 && tm_.tm_min <= 59, "");
 
    return tm_.tm_min;
 
  }
 
  auto tm_hour() const noexcept -> int {
 
    FMT_ASSERT(tm_.tm_hour >= 0 && tm_.tm_hour <= 23, "");
 
    return tm_.tm_hour;
 
  }
 
  auto tm_mday() const noexcept -> int {
 
    FMT_ASSERT(tm_.tm_mday >= 1 && tm_.tm_mday <= 31, "");
 
    return tm_.tm_mday;
 
  }
 
  auto tm_mon() const noexcept -> int {
 
    FMT_ASSERT(tm_.tm_mon >= 0 && tm_.tm_mon <= 11, "");
 
    return tm_.tm_mon;
 
  }
 
  auto tm_year() const noexcept -> long long { return 1900ll + tm_.tm_year; }
 
  auto tm_wday() const noexcept -> int {
 
    FMT_ASSERT(tm_.tm_wday >= 0 && tm_.tm_wday <= 6, "");
 
    return tm_.tm_wday;
 
  }
 
  auto tm_yday() const noexcept -> int {
 
    FMT_ASSERT(tm_.tm_yday >= 0 && tm_.tm_yday <= 365, "");
 
    return tm_.tm_yday;
 
  }
 

	
 
  auto tm_hour12() const noexcept -> int {
 
    const auto h = tm_hour();
 
    const auto z = h < 12 ? h : h - 12;
 
    return z == 0 ? 12 : z;
 
  }
 

	
 
  // POSIX and the C Standard are unclear or inconsistent about what %C and %y
 
  // do if the year is negative or exceeds 9999. Use the convention that %C
 
  // concatenated with %y yields the same output as %Y, and that %Y contains at
 
  // least 4 characters, with more only if necessary.
 
  auto split_year_lower(long long year) const noexcept -> int {
 
    auto l = year % 100;
 
    if (l < 0) l = -l;  // l in [0, 99]
 
    return static_cast<int>(l);
 
  }
 

	
 
  // Algorithm:
 
  // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_the_week_number_from_a_month_and_day_of_the_month_or_ordinal_date
 
  auto iso_year_weeks(long long curr_year) const noexcept -> int {
 
    const auto prev_year = curr_year - 1;
 
    const auto curr_p =
 
        (curr_year + curr_year / 4 - curr_year / 100 + curr_year / 400) %
 
        days_per_week;
 
    const auto prev_p =
 
        (prev_year + prev_year / 4 - prev_year / 100 + prev_year / 400) %
 
        days_per_week;
 
    return 52 + ((curr_p == 4 || prev_p == 3) ? 1 : 0);
 
  }
 
  auto iso_week_num(int tm_yday, int tm_wday) const noexcept -> int {
 
    return (tm_yday + 11 - (tm_wday == 0 ? days_per_week : tm_wday)) /
 
           days_per_week;
 
  }
 
  auto tm_iso_week_year() const noexcept -> long long {
 
    const auto year = tm_year();
 
    const auto w = iso_week_num(tm_yday(), tm_wday());
 
    if (w < 1) return year - 1;
 
    if (w > iso_year_weeks(year)) return year + 1;
 
    return year;
 
  }
 
  auto tm_iso_week_of_year() const noexcept -> int {
 
    const auto year = tm_year();
 
    const auto w = iso_week_num(tm_yday(), tm_wday());
 
    if (w < 1) return iso_year_weeks(year - 1);
 
    if (w > iso_year_weeks(year)) return 1;
 
    return w;
 
  }
 

	
 
  void write1(int value) {
 
    *out_++ = static_cast<char>('0' + to_unsigned(value) % 10);
 
  }
 
  void write2(int value) {
 
    const char* d = digits2(to_unsigned(value) % 100);
 
    *out_++ = *d++;
 
    *out_++ = *d;
 
  }
 
  void write2(int value, pad_type pad) {
 
    unsigned int v = to_unsigned(value) % 100;
 
    if (v >= 10) {
 
      const char* d = digits2(v);
 
      *out_++ = *d++;
 
      *out_++ = *d;
 
    } else {
 
      out_ = detail::write_padding(out_, pad);
 
      *out_++ = static_cast<char>('0' + v);
 
    }
 
  }
 

	
 
  void write_year_extended(long long year) {
 
    // At least 4 characters.
 
    int width = 4;
 
    if (year < 0) {
 
      *out_++ = '-';
 
      year = 0 - year;
 
      --width;
 
    }
 
    uint32_or_64_or_128_t<long long> n = to_unsigned(year);
 
    const int num_digits = count_digits(n);
 
    if (width > num_digits) out_ = std::fill_n(out_, width - num_digits, '0');
 
    out_ = format_decimal<Char>(out_, n, num_digits).end;
 
  }
 
  void write_year(long long year) {
 
    if (year >= 0 && year < 10000) {
 
      write2(static_cast<int>(year / 100));
 
      write2(static_cast<int>(year % 100));
 
    } else {
 
      write_year_extended(year);
 
    }
 
  }
 

	
 
  void write_utc_offset(long offset, numeric_system ns) {
 
    if (offset < 0) {
 
      *out_++ = '-';
 
      offset = -offset;
 
    } else {
 
      *out_++ = '+';
 
    }
 
    offset /= 60;
 
    write2(static_cast<int>(offset / 60));
 
    if (ns != numeric_system::standard) *out_++ = ':';
 
    write2(static_cast<int>(offset % 60));
 
  }
 
  template <typename T, FMT_ENABLE_IF(has_member_data_tm_gmtoff<T>::value)>
 
  void format_utc_offset_impl(const T& tm, numeric_system ns) {
 
    write_utc_offset(tm.tm_gmtoff, ns);
 
  }
 
  template <typename T, FMT_ENABLE_IF(!has_member_data_tm_gmtoff<T>::value)>
 
  void format_utc_offset_impl(const T& tm, numeric_system ns) {
 
#if defined(_WIN32) && defined(_UCRT)
 
#  if FMT_USE_TZSET
 
    tzset_once();
 
#  endif
 
    long offset = 0;
 
    _get_timezone(&offset);
 
    if (tm.tm_isdst) {
 
      long dstbias = 0;
 
      _get_dstbias(&dstbias);
 
      offset += dstbias;
 
    }
 
    write_utc_offset(-offset, ns);
 
#else
 
    if (ns == numeric_system::standard) return format_localized('z');
 

	
 
    // Extract timezone offset from timezone conversion functions.
 
    std::tm gtm = tm;
 
    std::time_t gt = std::mktime(&gtm);
 
    std::tm ltm = gmtime(gt);
 
    std::time_t lt = std::mktime(&ltm);
 
    long offset = gt - lt;
 
    write_utc_offset(offset, ns);
 
#endif
 
  }
 

	
 
  template <typename T, FMT_ENABLE_IF(has_member_data_tm_zone<T>::value)>
 
  void format_tz_name_impl(const T& tm) {
 
    if (is_classic_)
 
      out_ = write_tm_str<Char>(out_, tm.tm_zone, loc_);
 
    else
 
      format_localized('Z');
 
  }
 
  template <typename T, FMT_ENABLE_IF(!has_member_data_tm_zone<T>::value)>
 
  void format_tz_name_impl(const T&) {
 
    format_localized('Z');
 
  }
 

	
 
  void format_localized(char format, char modifier = 0) {
 
    out_ = write<Char>(out_, tm_, loc_, format, modifier);
 
  }
 

	
 
 public:
 
  tm_writer(const std::locale& loc, OutputIt out, const std::tm& tm,
 
            const Duration* subsecs = nullptr)
 
      : loc_(loc),
 
        is_classic_(loc_ == get_classic_locale()),
 
        out_(out),
 
        subsecs_(subsecs),
 
        tm_(tm) {}
 

	
 
  OutputIt out() const { return out_; }
 

	
 
  FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
 
    out_ = copy_str<Char>(begin, end, out_);
 
  }
 

	
 
  void on_abbr_weekday() {
 
    if (is_classic_)
 
      out_ = write(out_, tm_wday_short_name(tm_wday()));
 
    else
 
      format_localized('a');
 
  }
 
  void on_full_weekday() {
 
    if (is_classic_)
 
      out_ = write(out_, tm_wday_full_name(tm_wday()));
 
    else
 
      format_localized('A');
 
  }
 
  void on_dec0_weekday(numeric_system ns) {
 
    if (is_classic_ || ns == numeric_system::standard) return write1(tm_wday());
 
    format_localized('w', 'O');
 
  }
 
  void on_dec1_weekday(numeric_system ns) {
 
    if (is_classic_ || ns == numeric_system::standard) {
 
      auto wday = tm_wday();
 
      write1(wday == 0 ? days_per_week : wday);
 
    } else {
 
      format_localized('u', 'O');
 
    }
 
  }
 

	
 
  void on_abbr_month() {
 
    if (is_classic_)
 
      out_ = write(out_, tm_mon_short_name(tm_mon()));
 
    else
 
      format_localized('b');
 
  }
 
  void on_full_month() {
 
    if (is_classic_)
 
      out_ = write(out_, tm_mon_full_name(tm_mon()));
 
    else
 
      format_localized('B');
 
  }
 

	
 
  void on_datetime(numeric_system ns) {
 
    if (is_classic_) {
 
      on_abbr_weekday();
 
      *out_++ = ' ';
 
      on_abbr_month();
 
      *out_++ = ' ';
 
      on_day_of_month_space(numeric_system::standard);
 
      *out_++ = ' ';
 
      on_iso_time();
 
      *out_++ = ' ';
 
      on_year(numeric_system::standard);
 
    } else {
 
      format_localized('c', ns == numeric_system::standard ? '\0' : 'E');
 
    }
 
  }
 
  void on_loc_date(numeric_system ns) {
 
    if (is_classic_)
 
      on_us_date();
 
    else
 
      format_localized('x', ns == numeric_system::standard ? '\0' : 'E');
 
  }
 
  void on_loc_time(numeric_system ns) {
 
    if (is_classic_)
 
      on_iso_time();
 
    else
 
      format_localized('X', ns == numeric_system::standard ? '\0' : 'E');
 
  }
 
  void on_us_date() {
 
    char buf[8];
 
    write_digit2_separated(buf, to_unsigned(tm_mon() + 1),
 
                           to_unsigned(tm_mday()),
 
                           to_unsigned(split_year_lower(tm_year())), '/');
 
    out_ = copy_str<Char>(std::begin(buf), std::end(buf), out_);
 
  }
 
  void on_iso_date() {
 
    auto year = tm_year();
 
    char buf[10];
 
    size_t offset = 0;
 
    if (year >= 0 && year < 10000) {
 
      copy2(buf, digits2(static_cast<size_t>(year / 100)));
 
    } else {
 
      offset = 4;
 
      write_year_extended(year);
 
      year = 0;
 
    }
 
    write_digit2_separated(buf + 2, static_cast<unsigned>(year % 100),
 
                           to_unsigned(tm_mon() + 1), to_unsigned(tm_mday()),
 
                           '-');
 
    out_ = copy_str<Char>(std::begin(buf) + offset, std::end(buf), out_);
 
  }
 

	
 
  void on_utc_offset(numeric_system ns) { format_utc_offset_impl(tm_, ns); }
 
  void on_tz_name() { format_tz_name_impl(tm_); }
 

	
 
  void on_year(numeric_system ns) {
 
    if (is_classic_ || ns == numeric_system::standard)
 
      return write_year(tm_year());
 
    format_localized('Y', 'E');
 
  }
 
  void on_short_year(numeric_system ns) {
 
    if (is_classic_ || ns == numeric_system::standard)
 
      return write2(split_year_lower(tm_year()));
 
    format_localized('y', 'O');
 
  }
 
  void on_offset_year() {
 
    if (is_classic_) return write2(split_year_lower(tm_year()));
 
    format_localized('y', 'E');
 
  }
 

	
 
  void on_century(numeric_system ns) {
 
    if (is_classic_ || ns == numeric_system::standard) {
 
      auto year = tm_year();
 
      auto upper = year / 100;
 
      if (year >= -99 && year < 0) {
 
        // Zero upper on negative year.
 
        *out_++ = '-';
 
        *out_++ = '0';
 
      } else if (upper >= 0 && upper < 100) {
 
        write2(static_cast<int>(upper));
 
      } else {
 
        out_ = write<Char>(out_, upper);
 
      }
 
    } else {
 
      format_localized('C', 'E');
 
    }
 
  }
 

	
 
  void on_dec_month(numeric_system ns) {
 
    if (is_classic_ || ns == numeric_system::standard)
 
      return write2(tm_mon() + 1);
 
    format_localized('m', 'O');
 
  }
 

	
 
  void on_dec0_week_of_year(numeric_system ns) {
 
    if (is_classic_ || ns == numeric_system::standard)
 
      return write2((tm_yday() + days_per_week - tm_wday()) / days_per_week);
 
    format_localized('U', 'O');
 
  }
 
  void on_dec1_week_of_year(numeric_system ns) {
 
    if (is_classic_ || ns == numeric_system::standard) {
 
      auto wday = tm_wday();
 
      write2((tm_yday() + days_per_week -
 
              (wday == 0 ? (days_per_week - 1) : (wday - 1))) /
 
             days_per_week);
 
    } else {
 
      format_localized('W', 'O');
 
    }
 
  }
 
  void on_iso_week_of_year(numeric_system ns) {
 
    if (is_classic_ || ns == numeric_system::standard)
 
      return write2(tm_iso_week_of_year());
 
    format_localized('V', 'O');
 
  }
 

	
 
  void on_iso_week_based_year() { write_year(tm_iso_week_year()); }
 
  void on_iso_week_based_short_year() {
 
    write2(split_year_lower(tm_iso_week_year()));
 
  }
 

	
 
  void on_day_of_year() {
 
    auto yday = tm_yday() + 1;
 
    write1(yday / 100);
 
    write2(yday % 100);
 
  }
 
  void on_day_of_month(numeric_system ns) {
 
    if (is_classic_ || ns == numeric_system::standard) return write2(tm_mday());
 
    format_localized('d', 'O');
 
  }
 
  void on_day_of_month_space(numeric_system ns) {
 
    if (is_classic_ || ns == numeric_system::standard) {
 
      auto mday = to_unsigned(tm_mday()) % 100;
 
      const char* d2 = digits2(mday);
 
      *out_++ = mday < 10 ? ' ' : d2[0];
 
      *out_++ = d2[1];
 
    } else {
 
      format_localized('e', 'O');
 
    }
 
  }
 

	
 
  void on_24_hour(numeric_system ns, pad_type pad) {
 
    if (is_classic_ || ns == numeric_system::standard)
 
      return write2(tm_hour(), pad);
 
    format_localized('H', 'O');
 
  }
 
  void on_12_hour(numeric_system ns, pad_type pad) {
 
    if (is_classic_ || ns == numeric_system::standard)
 
      return write2(tm_hour12(), pad);
 
    format_localized('I', 'O');
 
  }
 
  void on_minute(numeric_system ns, pad_type pad) {
 
    if (is_classic_ || ns == numeric_system::standard)
 
      return write2(tm_min(), pad);
 
    format_localized('M', 'O');
 
  }
 

	
 
  void on_second(numeric_system ns, pad_type pad) {
 
    if (is_classic_ || ns == numeric_system::standard) {
 
      write2(tm_sec(), pad);
 
      if (subsecs_) {
 
        if (std::is_floating_point<typename Duration::rep>::value) {
 
          auto buf = memory_buffer();
 
          write_floating_seconds(buf, *subsecs_);
 
          if (buf.size() > 1) {
 
            // Remove the leading "0", write something like ".123".
 
            out_ = std::copy(buf.begin() + 1, buf.end(), out_);
 
          }
 
        } else {
 
          write_fractional_seconds<Char>(out_, *subsecs_);
 
        }
 
      }
 
    } else {
 
      // Currently no formatting of subseconds when a locale is set.
 
      format_localized('S', 'O');
 
    }
 
  }
 

	
 
  void on_12_hour_time() {
 
    if (is_classic_) {
 
      char buf[8];
 
      write_digit2_separated(buf, to_unsigned(tm_hour12()),
 
                             to_unsigned(tm_min()), to_unsigned(tm_sec()), ':');
 
      out_ = copy_str<Char>(std::begin(buf), std::end(buf), out_);
 
      *out_++ = ' ';
 
      on_am_pm();
 
    } else {
 
      format_localized('r');
 
    }
 
  }
 
  void on_24_hour_time() {
 
    write2(tm_hour());
 
    *out_++ = ':';
 
    write2(tm_min());
 
  }
 
  void on_iso_time() {
 
    on_24_hour_time();
 
    *out_++ = ':';
 
    on_second(numeric_system::standard, pad_type::unspecified);
 
  }
 

	
 
  void on_am_pm() {
 
    if (is_classic_) {
 
      *out_++ = tm_hour() < 12 ? 'A' : 'P';
 
      *out_++ = 'M';
 
    } else {
 
      format_localized('p');
 
    }
 
  }
 

	
 
  // These apply to chrono durations but not tm.
 
  void on_duration_value() {}
 
  void on_duration_unit() {}
 
};
 

	
 
struct chrono_format_checker : null_chrono_spec_handler<chrono_format_checker> {
 
  bool has_precision_integral = false;
 

	
 
  FMT_NORETURN void unsupported() { FMT_THROW(format_error("no date")); }
 

	
 
  template <typename Char>
 
  FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
 
  FMT_CONSTEXPR void on_24_hour(numeric_system, pad_type) {}
 
  FMT_CONSTEXPR void on_12_hour(numeric_system, pad_type) {}
 
  FMT_CONSTEXPR void on_minute(numeric_system, pad_type) {}
 
  FMT_CONSTEXPR void on_second(numeric_system, pad_type) {}
 
  FMT_CONSTEXPR void on_12_hour_time() {}
 
  FMT_CONSTEXPR void on_24_hour_time() {}
 
  FMT_CONSTEXPR void on_iso_time() {}
 
  FMT_CONSTEXPR void on_am_pm() {}
 
  FMT_CONSTEXPR void on_duration_value() const {
 
    if (has_precision_integral) {
 
      FMT_THROW(format_error("precision not allowed for this argument type"));
 
    }
 
  }
 
  FMT_CONSTEXPR void on_duration_unit() {}
 
};
 

	
 
template <typename T,
 
          FMT_ENABLE_IF(std::is_integral<T>::value&& has_isfinite<T>::value)>
 
inline bool isfinite(T) {
 
  return true;
 
}
 

	
 
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
 
inline T mod(T x, int y) {
 
  return x % static_cast<T>(y);
 
}
 
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
 
inline T mod(T x, int y) {
 
  return std::fmod(x, static_cast<T>(y));
 
}
 

	
 
// If T is an integral type, maps T to its unsigned counterpart, otherwise
 
// leaves it unchanged (unlike std::make_unsigned).
 
template <typename T, bool INTEGRAL = std::is_integral<T>::value>
 
struct make_unsigned_or_unchanged {
 
  using type = T;
 
};
 

	
 
template <typename T> struct make_unsigned_or_unchanged<T, true> {
 
  using type = typename std::make_unsigned<T>::type;
 
};
 

	
 
#if FMT_SAFE_DURATION_CAST
 
// throwing version of safe_duration_cast
 
template <typename To, typename FromRep, typename FromPeriod>
 
To fmt_safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from) {
 
  int ec;
 
  To to = safe_duration_cast::safe_duration_cast<To>(from, ec);
 
  if (ec) FMT_THROW(format_error("cannot format duration"));
 
  return to;
 
}
 
#endif
 

	
 
template <typename Rep, typename Period,
 
          FMT_ENABLE_IF(std::is_integral<Rep>::value)>
 
inline std::chrono::duration<Rep, std::milli> get_milliseconds(
 
    std::chrono::duration<Rep, Period> d) {
 
  // this may overflow and/or the result may not fit in the
 
  // target type.
 
#if FMT_SAFE_DURATION_CAST
 
  using CommonSecondsType =
 
      typename std::common_type<decltype(d), std::chrono::seconds>::type;
 
  const auto d_as_common = fmt_safe_duration_cast<CommonSecondsType>(d);
 
  const auto d_as_whole_seconds =
 
      fmt_safe_duration_cast<std::chrono::seconds>(d_as_common);
 
  // this conversion should be nonproblematic
 
  const auto diff = d_as_common - d_as_whole_seconds;
 
  const auto ms =
 
      fmt_safe_duration_cast<std::chrono::duration<Rep, std::milli>>(diff);
 
  return ms;
 
#else
 
  auto s = std::chrono::duration_cast<std::chrono::seconds>(d);
 
  return std::chrono::duration_cast<std::chrono::milliseconds>(d - s);
 
#endif
 
}
 

	
 
template <typename Char, typename Rep, typename OutputIt,
 
          FMT_ENABLE_IF(std::is_integral<Rep>::value)>
 
OutputIt format_duration_value(OutputIt out, Rep val, int) {
 
  return write<Char>(out, val);
 
}
 

	
 
template <typename Char, typename Rep, typename OutputIt,
 
          FMT_ENABLE_IF(std::is_floating_point<Rep>::value)>
 
OutputIt format_duration_value(OutputIt out, Rep val, int precision) {
 
  auto specs = format_specs<Char>();
 
  specs.precision = precision;
 
  specs.type = precision >= 0 ? presentation_type::fixed_lower
 
                              : presentation_type::general_lower;
 
  return write<Char>(out, val, specs);
 
}
 

	
 
template <typename Char, typename OutputIt>
 
OutputIt copy_unit(string_view unit, OutputIt out, Char) {
 
  return std::copy(unit.begin(), unit.end(), out);
 
}
 

	
 
template <typename OutputIt>
 
OutputIt copy_unit(string_view unit, OutputIt out, wchar_t) {
 
  // This works when wchar_t is UTF-32 because units only contain characters
 
  // that have the same representation in UTF-16 and UTF-32.
 
  utf8_to_utf16 u(unit);
 
  return std::copy(u.c_str(), u.c_str() + u.size(), out);
 
}
 

	
 
template <typename Char, typename Period, typename OutputIt>
 
OutputIt format_duration_unit(OutputIt out) {
 
  if (const char* unit = get_units<Period>())
 
    return copy_unit(string_view(unit), out, Char());
 
  *out++ = '[';
 
  out = write<Char>(out, Period::num);
 
  if (const_check(Period::den != 1)) {
 
    *out++ = '/';
 
    out = write<Char>(out, Period::den);
 
  }
 
  *out++ = ']';
 
  *out++ = 's';
 
  return out;
 
}
 

	
 
class get_locale {
 
 private:
 
  union {
 
    std::locale locale_;
 
  };
 
  bool has_locale_ = false;
 

	
 
 public:
 
  get_locale(bool localized, locale_ref loc) : has_locale_(localized) {
 
    if (localized)
 
      ::new (&locale_) std::locale(loc.template get<std::locale>());
 
  }
 
  ~get_locale() {
 
    if (has_locale_) locale_.~locale();
 
  }
 
  operator const std::locale&() const {
 
    return has_locale_ ? locale_ : get_classic_locale();
 
  }
 
};
 

	
 
template <typename FormatContext, typename OutputIt, typename Rep,
 
          typename Period>
 
struct chrono_formatter {
 
  FormatContext& context;
 
  OutputIt out;
 
  int precision;
 
  bool localized = false;
 
  // rep is unsigned to avoid overflow.
 
  using rep =
 
      conditional_t<std::is_integral<Rep>::value && sizeof(Rep) < sizeof(int),
 
                    unsigned, typename make_unsigned_or_unchanged<Rep>::type>;
 
  rep val;
 
  using seconds = std::chrono::duration<rep>;
 
  seconds s;
 
  using milliseconds = std::chrono::duration<rep, std::milli>;
 
  bool negative;
 

	
 
  using char_type = typename FormatContext::char_type;
 
  using tm_writer_type = tm_writer<OutputIt, char_type>;
 

	
 
  chrono_formatter(FormatContext& ctx, OutputIt o,
 
                   std::chrono::duration<Rep, Period> d)
 
      : context(ctx),
 
        out(o),
 
        val(static_cast<rep>(d.count())),
 
        negative(false) {
 
    if (d.count() < 0) {
 
      val = 0 - val;
 
      negative = true;
 
    }
 

	
 
    // this may overflow and/or the result may not fit in the
 
    // target type.
 
#if FMT_SAFE_DURATION_CAST
 
    // might need checked conversion (rep!=Rep)
 
    auto tmpval = std::chrono::duration<rep, Period>(val);
 
    s = fmt_safe_duration_cast<seconds>(tmpval);
 
#else
 
    s = std::chrono::duration_cast<seconds>(
 
        std::chrono::duration<rep, Period>(val));
 
#endif
 
  }
 

	
 
  // returns true if nan or inf, writes to out.
 
  bool handle_nan_inf() {
 
    if (isfinite(val)) {
 
      return false;
 
    }
 
    if (isnan(val)) {
 
      write_nan();
 
      return true;
 
    }
 
    // must be +-inf
 
    if (val > 0) {
 
      write_pinf();
 
    } else {
 
      write_ninf();
 
    }
 
    return true;
 
  }
 

	
 
  Rep hour() const { return static_cast<Rep>(mod((s.count() / 3600), 24)); }
 

	
 
  Rep hour12() const {
 
    Rep hour = static_cast<Rep>(mod((s.count() / 3600), 12));
 
    return hour <= 0 ? 12 : hour;
 
  }
 

	
 
  Rep minute() const { return static_cast<Rep>(mod((s.count() / 60), 60)); }
 
  Rep second() const { return static_cast<Rep>(mod(s.count(), 60)); }
 

	
 
  std::tm time() const {
 
    auto time = std::tm();
 
    time.tm_hour = to_nonnegative_int(hour(), 24);
 
    time.tm_min = to_nonnegative_int(minute(), 60);
 
    time.tm_sec = to_nonnegative_int(second(), 60);
 
    return time;
 
  }
 

	
 
  void write_sign() {
 
    if (negative) {
 
      *out++ = '-';
 
      negative = false;
 
    }
 
  }
 

	
 
  void write(Rep value, int width, pad_type pad = pad_type::unspecified) {
 
    write_sign();
 
    if (isnan(value)) return write_nan();
 
    uint32_or_64_or_128_t<int> n =
 
        to_unsigned(to_nonnegative_int(value, max_value<int>()));
 
    int num_digits = detail::count_digits(n);
 
    if (width > num_digits) {
 
      out = detail::write_padding(out, pad, width - num_digits);
 
    }
 
    out = format_decimal<char_type>(out, n, num_digits).end;
 
  }
 

	
 
  void write_nan() { std::copy_n("nan", 3, out); }
 
  void write_pinf() { std::copy_n("inf", 3, out); }
 
  void write_ninf() { std::copy_n("-inf", 4, out); }
 

	
 
  template <typename Callback, typename... Args>
 
  void format_tm(const tm& time, Callback cb, Args... args) {
 
    if (isnan(val)) return write_nan();
 
    get_locale loc(localized, context.locale());
 
    auto w = tm_writer_type(loc, out, time);
 
    (w.*cb)(args...);
 
    out = w.out();
 
  }
 

	
 
  void on_text(const char_type* begin, const char_type* end) {
 
    std::copy(begin, end, out);
 
  }
 

	
 
  // These are not implemented because durations don't have date information.
 
  void on_abbr_weekday() {}
 
  void on_full_weekday() {}
 
  void on_dec0_weekday(numeric_system) {}
 
  void on_dec1_weekday(numeric_system) {}
 
  void on_abbr_month() {}
 
  void on_full_month() {}
 
  void on_datetime(numeric_system) {}
 
  void on_loc_date(numeric_system) {}
 
  void on_loc_time(numeric_system) {}
 
  void on_us_date() {}
 
  void on_iso_date() {}
 
  void on_utc_offset(numeric_system) {}
 
  void on_tz_name() {}
 
  void on_year(numeric_system) {}
 
  void on_short_year(numeric_system) {}
 
  void on_offset_year() {}
 
  void on_century(numeric_system) {}
 
  void on_iso_week_based_year() {}
 
  void on_iso_week_based_short_year() {}
 
  void on_dec_month(numeric_system) {}
 
  void on_dec0_week_of_year(numeric_system) {}
 
  void on_dec1_week_of_year(numeric_system) {}
 
  void on_iso_week_of_year(numeric_system) {}
 
  void on_day_of_year() {}
 
  void on_day_of_month(numeric_system) {}
 
  void on_day_of_month_space(numeric_system) {}
 

	
 
  void on_24_hour(numeric_system ns, pad_type pad) {
 
    if (handle_nan_inf()) return;
 

	
 
    if (ns == numeric_system::standard) return write(hour(), 2, pad);
 
    auto time = tm();
 
    time.tm_hour = to_nonnegative_int(hour(), 24);
 
    format_tm(time, &tm_writer_type::on_24_hour, ns, pad);
 
  }
 

	
 
  void on_12_hour(numeric_system ns, pad_type pad) {
 
    if (handle_nan_inf()) return;
 

	
 
    if (ns == numeric_system::standard) return write(hour12(), 2, pad);
 
    auto time = tm();
 
    time.tm_hour = to_nonnegative_int(hour12(), 12);
 
    format_tm(time, &tm_writer_type::on_12_hour, ns, pad);
 
  }
 

	
 
  void on_minute(numeric_system ns, pad_type pad) {
 
    if (handle_nan_inf()) return;
 

	
 
    if (ns == numeric_system::standard) return write(minute(), 2, pad);
 
    auto time = tm();
 
    time.tm_min = to_nonnegative_int(minute(), 60);
 
    format_tm(time, &tm_writer_type::on_minute, ns, pad);
 
  }
 

	
 
  void on_second(numeric_system ns, pad_type pad) {
 
    if (handle_nan_inf()) return;
 

	
 
    if (ns == numeric_system::standard) {
 
      if (std::is_floating_point<rep>::value) {
 
        auto buf = memory_buffer();
 
        write_floating_seconds(buf, std::chrono::duration<rep, Period>(val),
 
                               precision);
 
        if (negative) *out++ = '-';
 
        if (buf.size() < 2 || buf[1] == '.') {
 
          out = detail::write_padding(out, pad);
 
        }
 
        out = std::copy(buf.begin(), buf.end(), out);
 
      } else {
 
        write(second(), 2, pad);
 
        write_fractional_seconds<char_type>(
 
            out, std::chrono::duration<rep, Period>(val), precision);
 
      }
 
      return;
 
    }
 
    auto time = tm();
 
    time.tm_sec = to_nonnegative_int(second(), 60);
 
    format_tm(time, &tm_writer_type::on_second, ns, pad);
 
  }
 

	
 
  void on_12_hour_time() {
 
    if (handle_nan_inf()) return;
 
    format_tm(time(), &tm_writer_type::on_12_hour_time);
 
  }
 

	
 
  void on_24_hour_time() {
 
    if (handle_nan_inf()) {
 
      *out++ = ':';
 
      handle_nan_inf();
 
      return;
 
    }
 

	
 
    write(hour(), 2);
 
    *out++ = ':';
 
    write(minute(), 2);
 
  }
 

	
 
  void on_iso_time() {
 
    on_24_hour_time();
 
    *out++ = ':';
 
    if (handle_nan_inf()) return;
 
    on_second(numeric_system::standard, pad_type::unspecified);
 
  }
 

	
 
  void on_am_pm() {
 
    if (handle_nan_inf()) return;
 
    format_tm(time(), &tm_writer_type::on_am_pm);
 
  }
 

	
 
  void on_duration_value() {
 
    if (handle_nan_inf()) return;
 
    write_sign();
 
    out = format_duration_value<char_type>(out, val, precision);
 
  }
 

	
 
  void on_duration_unit() {
 
    out = format_duration_unit<char_type, Period>(out);
 
  }
 
};
 

	
 
FMT_END_DETAIL_NAMESPACE
 

	
 
#if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907
 
using weekday = std::chrono::weekday;
 
#else
 
// A fallback version of weekday.
 
class weekday {
 
 private:
 
  unsigned char value;
 

	
 
 public:
 
  weekday() = default;
 
  explicit constexpr weekday(unsigned wd) noexcept
 
      : value(static_cast<unsigned char>(wd != 7 ? wd : 0)) {}
 
  constexpr unsigned c_encoding() const noexcept { return value; }
 
};
 

	
 
class year_month_day {};
 
#endif
 

	
 
// A rudimentary weekday formatter.
 
template <typename Char> struct formatter<weekday, Char> {
 
 private:
 
  bool localized = false;
 

	
 
 public:
 
  FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
 
      -> decltype(ctx.begin()) {
 
    auto begin = ctx.begin(), end = ctx.end();
 
    if (begin != end && *begin == 'L') {
 
      ++begin;
 
      localized = true;
 
    }
 
    return begin;
 
  }
 

	
 
  template <typename FormatContext>
 
  auto format(weekday wd, FormatContext& ctx) const -> decltype(ctx.out()) {
 
    auto time = std::tm();
 
    time.tm_wday = static_cast<int>(wd.c_encoding());
 
    detail::get_locale loc(localized, ctx.locale());
 
    auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time);
 
    w.on_abbr_weekday();
 
    return w.out();
 
  }
 
};
 

	
 
template <typename Rep, typename Period, typename Char>
 
struct formatter<std::chrono::duration<Rep, Period>, Char> {
 
 private:
 
  format_specs<Char> specs;
 
  int precision = -1;
 
  using arg_ref_type = detail::arg_ref<Char>;
 
  arg_ref_type width_ref;
 
  arg_ref_type precision_ref;
 
  bool localized = false;
 
  basic_string_view<Char> format_str;
 
  using duration = std::chrono::duration<Rep, Period>;
 

	
 
  using iterator = typename basic_format_parse_context<Char>::iterator;
 
  struct parse_range {
 
    iterator begin;
 
    iterator end;
 
  };
 

	
 
  FMT_CONSTEXPR parse_range do_parse(basic_format_parse_context<Char>& ctx) {
 
    auto begin = ctx.begin(), end = ctx.end();
 
    if (begin == end || *begin == '}') return {begin, begin};
 

	
 
    begin = detail::parse_align(begin, end, specs);
 
    if (begin == end) return {begin, begin};
 

	
 
    begin = detail::parse_dynamic_spec(begin, end, specs.width, width_ref, ctx);
 
    if (begin == end) return {begin, begin};
 

	
 
    auto checker = detail::chrono_format_checker();
 
    if (*begin == '.') {
 
      checker.has_precision_integral = !std::is_floating_point<Rep>::value;
 
      begin =
 
          detail::parse_precision(begin, end, precision, precision_ref, ctx);
 
    }
 
    if (begin != end && *begin == 'L') {
 
      ++begin;
 
      localized = true;
 
    }
 
    end = detail::parse_chrono_format(begin, end, checker);
 
    return {begin, end};
 
  }
 

	
 
 public:
 
  FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
 
      -> decltype(ctx.begin()) {
 
    auto range = do_parse(ctx);
 
    format_str = basic_string_view<Char>(
 
        &*range.begin, detail::to_unsigned(range.end - range.begin));
 
    return range.end;
 
  }
 

	
 
  template <typename FormatContext>
 
  auto format(const duration& d, FormatContext& ctx) const
 
      -> decltype(ctx.out()) {
 
    auto specs_copy = specs;
 
    auto precision_copy = precision;
 
    auto begin = format_str.begin(), end = format_str.end();
 
    // As a possible future optimization, we could avoid extra copying if width
 
    // is not specified.
 
    basic_memory_buffer<Char> buf;
 
    auto out = std::back_inserter(buf);
 
    detail::handle_dynamic_spec<detail::width_checker>(specs_copy.width,
 
                                                       width_ref, ctx);
 
    detail::handle_dynamic_spec<detail::precision_checker>(precision_copy,
 
                                                           precision_ref, ctx);
 
    if (begin == end || *begin == '}') {
 
      out = detail::format_duration_value<Char>(out, d.count(), precision_copy);
 
      detail::format_duration_unit<Char, Period>(out);
 
    } else {
 
      detail::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(
 
          ctx, out, d);
 
      f.precision = precision_copy;
 
      f.localized = localized;
 
      detail::parse_chrono_format(begin, end, f);
 
    }
 
    return detail::write(
 
        ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs_copy);
 
  }
 
};
 

	
 
template <typename Char, typename Duration>
 
struct formatter<std::chrono::time_point<std::chrono::system_clock, Duration>,
 
                 Char> : formatter<std::tm, Char> {
 
  FMT_CONSTEXPR formatter() {
 
    this->format_str = detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>{};
 
  }
 

	
 
  template <typename FormatContext>
 
  auto format(std::chrono::time_point<std::chrono::system_clock, Duration> val,
 
              FormatContext& ctx) const -> decltype(ctx.out()) {
 
    using period = typename Duration::period;
 
    if (period::num != 1 || period::den != 1 ||
 
        std::is_floating_point<typename Duration::rep>::value) {
 
      const auto epoch = val.time_since_epoch();
 
      auto subsecs = std::chrono::duration_cast<Duration>(
 
          epoch - std::chrono::duration_cast<std::chrono::seconds>(epoch));
 

	
 
      if (subsecs.count() < 0) {
 
        auto second = std::chrono::seconds(1);
 
        if (epoch.count() < ((Duration::min)() + second).count())
 
          FMT_THROW(format_error("duration is too small"));
 
        subsecs += second;
 
        val -= second;
 
      }
 

	
 
      return formatter<std::tm, Char>::do_format(
 
          gmtime(std::chrono::time_point_cast<std::chrono::seconds>(val)), ctx,
 
          &subsecs);
 
    }
 

	
 
    return formatter<std::tm, Char>::format(
 
        gmtime(std::chrono::time_point_cast<std::chrono::seconds>(val)), ctx);
 
  }
 
};
 

	
 
#if FMT_USE_LOCAL_TIME
 
template <typename Char, typename Duration>
 
struct formatter<std::chrono::local_time<Duration>, Char>
 
    : formatter<std::tm, Char> {
 
  FMT_CONSTEXPR formatter() {
 
    this->format_str = detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>{};
 
  }
 

	
 
  template <typename FormatContext>
 
  auto format(std::chrono::local_time<Duration> val, FormatContext& ctx) const
 
      -> decltype(ctx.out()) {
 
    using period = typename Duration::period;
 
    if (period::num != 1 || period::den != 1 ||
 
        std::is_floating_point<typename Duration::rep>::value) {
 
      const auto epoch = val.time_since_epoch();
 
      const auto subsecs = std::chrono::duration_cast<Duration>(
 
          epoch - std::chrono::duration_cast<std::chrono::seconds>(epoch));
 

	
 
      return formatter<std::tm, Char>::do_format(
 
          localtime(std::chrono::time_point_cast<std::chrono::seconds>(val)),
 
          ctx, &subsecs);
 
    }
 

	
 
    return formatter<std::tm, Char>::format(
 
        localtime(std::chrono::time_point_cast<std::chrono::seconds>(val)),
 
        ctx);
 
  }
 
};
 
#endif
 

	
 
#if FMT_USE_UTC_TIME
 
template <typename Char, typename Duration>
 
struct formatter<std::chrono::time_point<std::chrono::utc_clock, Duration>,
 
                 Char>
 
    : formatter<std::chrono::time_point<std::chrono::system_clock, Duration>,
 
                Char> {
 
  template <typename FormatContext>
 
  auto format(std::chrono::time_point<std::chrono::utc_clock, Duration> val,
 
              FormatContext& ctx) const -> decltype(ctx.out()) {
 
    return formatter<
 
        std::chrono::time_point<std::chrono::system_clock, Duration>,
 
        Char>::format(std::chrono::utc_clock::to_sys(val), ctx);
 
  }
 
};
 
#endif
 

	
 
template <typename Char> struct formatter<std::tm, Char> {
 
 private:
 
  format_specs<Char> specs;
 
  detail::arg_ref<Char> width_ref;
 

	
 
 protected:
 
  basic_string_view<Char> format_str;
 

	
 
  FMT_CONSTEXPR auto do_parse(basic_format_parse_context<Char>& ctx)
 
      -> decltype(ctx.begin()) {
 
    auto begin = ctx.begin(), end = ctx.end();
 
    if (begin == end || *begin == '}') return begin;
 

	
 
    begin = detail::parse_align(begin, end, specs);
 
    if (begin == end) return end;
 

	
 
    begin = detail::parse_dynamic_spec(begin, end, specs.width, width_ref, ctx);
 
    if (begin == end) return end;
 

	
 
    end = detail::parse_chrono_format(begin, end, detail::tm_format_checker());
 
    // Replace default format_str only if the new spec is not empty.
 
    if (end != begin) format_str = {begin, detail::to_unsigned(end - begin)};
 
    return end;
 
  }
 

	
 
  template <typename FormatContext, typename Duration>
 
  auto do_format(const std::tm& tm, FormatContext& ctx,
 
                 const Duration* subsecs) const -> decltype(ctx.out()) {
 
    auto specs_copy = specs;
 
    basic_memory_buffer<Char> buf;
 
    auto out = std::back_inserter(buf);
 
    detail::handle_dynamic_spec<detail::width_checker>(specs_copy.width,
 
                                                       width_ref, ctx);
 

	
 
    const auto loc_ref = ctx.locale();
 
    detail::get_locale loc(static_cast<bool>(loc_ref), loc_ref);
 
    auto w =
 
        detail::tm_writer<decltype(out), Char, Duration>(loc, out, tm, subsecs);
 
    detail::parse_chrono_format(format_str.begin(), format_str.end(), w);
 
    return detail::write(
 
        ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs_copy);
 
  }
 

	
 
 public:
 
  FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
 
      -> decltype(ctx.begin()) {
 
    return this->do_parse(ctx);
 
  }
 

	
 
  template <typename FormatContext>
 
  auto format(const std::tm& tm, FormatContext& ctx) const
 
      -> decltype(ctx.out()) {
 
    return do_format<FormatContext, std::chrono::seconds>(tm, ctx, nullptr);
 
  }
 
};
 

	
 
FMT_END_EXPORT
 
FMT_END_NAMESPACE
 

	
 
#endif  // FMT_CHRONO_H_
src/3rdparty/fmt/core.h
Show inline comments
 
// Formatting library for C++ - the core API
 
// Formatting library for C++ - the core API for char/UTF-8
 
//
 
// Copyright (c) 2012 - present, Victor Zverovich
 
// All rights reserved.
 
@@ -8,54 +8,59 @@
 
#ifndef FMT_CORE_H_
 
#define FMT_CORE_H_
 

	
 
#include <cstdio>  // std::FILE
 
#include <cstring>
 
#include <functional>
 
#include <cstddef>  // std::byte
 
#include <cstdio>   // std::FILE
 
#include <cstring>  // std::strlen
 
#include <iterator>
 
#include <memory>
 
#include <limits>
 
#include <string>
 
#include <type_traits>
 
#include <vector>
 

	
 
// The fmt library version in the form major * 10000 + minor * 100 + patch.
 
#define FMT_VERSION 70103
 

	
 
#ifdef __clang__
 
#define FMT_VERSION 100000
 

	
 
#if defined(__clang__) && !defined(__ibmxl__)
 
#  define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
 
#else
 
#  define FMT_CLANG_VERSION 0
 
#endif
 

	
 
#if defined(__GNUC__) && !defined(__clang__)
 
#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) && \
 
    !defined(__NVCOMPILER)
 
#  define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
 
#else
 
#  define FMT_GCC_VERSION 0
 
#endif
 

	
 
#if defined(__INTEL_COMPILER)
 
#ifndef FMT_GCC_PRAGMA
 
// Workaround _Pragma bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59884.
 
#  if FMT_GCC_VERSION >= 504
 
#    define FMT_GCC_PRAGMA(arg) _Pragma(arg)
 
#  else
 
#    define FMT_GCC_PRAGMA(arg)
 
#  endif
 
#endif
 

	
 
#ifdef __ICL
 
#  define FMT_ICC_VERSION __ICL
 
#elif defined(__INTEL_COMPILER)
 
#  define FMT_ICC_VERSION __INTEL_COMPILER
 
#else
 
#  define FMT_ICC_VERSION 0
 
#endif
 

	
 
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
 
#  define FMT_HAS_GXX_CXX11 FMT_GCC_VERSION
 
#else
 
#  define FMT_HAS_GXX_CXX11 0
 
#endif
 

	
 
#ifdef __NVCC__
 
#  define FMT_NVCC __NVCC__
 
#ifdef _MSC_VER
 
#  define FMT_MSC_VERSION _MSC_VER
 
#  define FMT_MSC_WARNING(...) __pragma(warning(__VA_ARGS__))
 
#else
 
#  define FMT_NVCC 0
 
#  define FMT_MSC_VERSION 0
 
#  define FMT_MSC_WARNING(...)
 
#endif
 

	
 
#ifdef _MSC_VER
 
#  define FMT_MSC_VER _MSC_VER
 
#  define FMT_SUPPRESS_MSC_WARNING(n) __pragma(warning(suppress : n))
 
#ifdef _MSVC_LANG
 
#  define FMT_CPLUSPLUS _MSVC_LANG
 
#else
 
#  define FMT_MSC_VER 0
 
#  define FMT_SUPPRESS_MSC_WARNING(n)
 
#  define FMT_CPLUSPLUS __cplusplus
 
#endif
 

	
 
#ifdef __has_feature
 
@@ -64,8 +69,7 @@
 
#  define FMT_HAS_FEATURE(x) 0
 
#endif
 

	
 
#if defined(__has_include) && !defined(__INTELLISENSE__) && \
 
    (!FMT_ICC_VERSION || FMT_ICC_VERSION >= 1600)
 
#if defined(__has_include) || FMT_ICC_VERSION >= 1600 || FMT_MSC_VERSION > 1900
 
#  define FMT_HAS_INCLUDE(x) __has_include(x)
 
#else
 
#  define FMT_HAS_INCLUDE(x) 0
 
@@ -78,98 +82,78 @@
 
#endif
 

	
 
#define FMT_HAS_CPP14_ATTRIBUTE(attribute) \
 
  (__cplusplus >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute))
 
  (FMT_CPLUSPLUS >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute))
 

	
 
#define FMT_HAS_CPP17_ATTRIBUTE(attribute) \
 
  (__cplusplus >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute))
 
  (FMT_CPLUSPLUS >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute))
 

	
 
// Check if relaxed C++14 constexpr is supported.
 
// GCC doesn't allow throw in constexpr until version 6 (bug 67371).
 
#ifndef FMT_USE_CONSTEXPR
 
#  define FMT_USE_CONSTEXPR                                           \
 
    (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VER >= 1910 || \
 
     (FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L)) &&           \
 
        !FMT_NVCC && !FMT_ICC_VERSION
 
#  if (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VERSION >= 1912 || \
 
       (FMT_GCC_VERSION >= 600 && FMT_CPLUSPLUS >= 201402L)) &&             \
 
      !FMT_ICC_VERSION && !defined(__NVCC__)
 
#    define FMT_USE_CONSTEXPR 1
 
#  else
 
#    define FMT_USE_CONSTEXPR 0
 
#  endif
 
#endif
 
#if FMT_USE_CONSTEXPR
 
#  define FMT_CONSTEXPR constexpr
 
#  define FMT_CONSTEXPR_DECL constexpr
 
#else
 
#  define FMT_CONSTEXPR inline
 
#  define FMT_CONSTEXPR_DECL
 
#  define FMT_CONSTEXPR
 
#endif
 

	
 
#if ((FMT_CPLUSPLUS >= 202002L) &&                            \
 
     (!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE > 9)) || \
 
    (FMT_CPLUSPLUS >= 201709L && FMT_GCC_VERSION >= 1002)
 
#  define FMT_CONSTEXPR20 constexpr
 
#else
 
#  define FMT_CONSTEXPR20
 
#endif
 

	
 
#ifndef FMT_OVERRIDE
 
#  if FMT_HAS_FEATURE(cxx_override_control) || \
 
      (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900
 
#    define FMT_OVERRIDE override
 
#  else
 
#    define FMT_OVERRIDE
 
// Check if constexpr std::char_traits<>::{compare,length} are supported.
 
#if defined(__GLIBCXX__)
 
#  if FMT_CPLUSPLUS >= 201703L && defined(_GLIBCXX_RELEASE) && \
 
      _GLIBCXX_RELEASE >= 7  // GCC 7+ libstdc++ has _GLIBCXX_RELEASE.
 
#    define FMT_CONSTEXPR_CHAR_TRAITS constexpr
 
#  endif
 
#elif defined(_LIBCPP_VERSION) && FMT_CPLUSPLUS >= 201703L && \
 
    _LIBCPP_VERSION >= 4000
 
#  define FMT_CONSTEXPR_CHAR_TRAITS constexpr
 
#elif FMT_MSC_VERSION >= 1914 && FMT_CPLUSPLUS >= 201703L
 
#  define FMT_CONSTEXPR_CHAR_TRAITS constexpr
 
#endif
 
#ifndef FMT_CONSTEXPR_CHAR_TRAITS
 
#  define FMT_CONSTEXPR_CHAR_TRAITS
 
#endif
 

	
 
// Check if exceptions are disabled.
 
#ifndef FMT_EXCEPTIONS
 
#  if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || \
 
      FMT_MSC_VER && !_HAS_EXCEPTIONS
 
      (FMT_MSC_VERSION && !_HAS_EXCEPTIONS)
 
#    define FMT_EXCEPTIONS 0
 
#  else
 
#    define FMT_EXCEPTIONS 1
 
#  endif
 
#endif
 

	
 
// Define FMT_USE_NOEXCEPT to make fmt use noexcept (C++11 feature).
 
#ifndef FMT_USE_NOEXCEPT
 
#  define FMT_USE_NOEXCEPT 0
 
#endif
 

	
 
#if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \
 
    (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900
 
#  define FMT_DETECTED_NOEXCEPT noexcept
 
#  define FMT_HAS_CXX11_NOEXCEPT 1
 
#else
 
#  define FMT_DETECTED_NOEXCEPT throw()
 
#  define FMT_HAS_CXX11_NOEXCEPT 0
 
#endif
 

	
 
#ifndef FMT_NOEXCEPT
 
#  if FMT_EXCEPTIONS || FMT_HAS_CXX11_NOEXCEPT
 
#    define FMT_NOEXCEPT FMT_DETECTED_NOEXCEPT
 
#  else
 
#    define FMT_NOEXCEPT
 
#  endif
 
#endif
 

	
 
// [[noreturn]] is disabled on MSVC and NVCC because of bogus unreachable code
 
// warnings.
 
#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VER && \
 
    !FMT_NVCC
 
// Disable [[noreturn]] on MSVC/NVCC because of bogus unreachable code warnings.
 
#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VERSION && \
 
    !defined(__NVCC__)
 
#  define FMT_NORETURN [[noreturn]]
 
#else
 
#  define FMT_NORETURN
 
#endif
 

	
 
#ifndef FMT_DEPRECATED
 
#  if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900
 
#    define FMT_DEPRECATED [[deprecated]]
 
#ifndef FMT_NODISCARD
 
#  if FMT_HAS_CPP17_ATTRIBUTE(nodiscard)
 
#    define FMT_NODISCARD [[nodiscard]]
 
#  else
 
#    if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__)
 
#      define FMT_DEPRECATED __attribute__((deprecated))
 
#    elif FMT_MSC_VER
 
#      define FMT_DEPRECATED __declspec(deprecated)
 
#    else
 
#      define FMT_DEPRECATED /* deprecated */
 
#    endif
 
#    define FMT_NODISCARD
 
#  endif
 
#endif
 

	
 
// Workaround broken [[deprecated]] in the Intel, PGI and NVCC compilers.
 
#if FMT_ICC_VERSION || defined(__PGI) || FMT_NVCC
 
#  define FMT_DEPRECATED_ALIAS
 
#else
 
#  define FMT_DEPRECATED_ALIAS FMT_DEPRECATED
 
#endif
 

	
 
#ifndef FMT_INLINE
 
#  if FMT_GCC_VERSION || FMT_CLANG_VERSION
 
#    define FMT_INLINE inline __attribute__((always_inline))
 
@@ -178,86 +162,107 @@
 
#  endif
 
#endif
 

	
 
#ifndef FMT_USE_INLINE_NAMESPACES
 
#  if FMT_HAS_FEATURE(cxx_inline_namespaces) || FMT_GCC_VERSION >= 404 || \
 
      (FMT_MSC_VER >= 1900 && !_MANAGED)
 
#    define FMT_USE_INLINE_NAMESPACES 1
 
#  else
 
#    define FMT_USE_INLINE_NAMESPACES 0
 
#  endif
 
// An inline std::forward replacement.
 
#define FMT_FORWARD(...) static_cast<decltype(__VA_ARGS__)&&>(__VA_ARGS__)
 

	
 
#ifdef _MSC_VER
 
#  define FMT_UNCHECKED_ITERATOR(It) \
 
    using _Unchecked_type = It  // Mark iterator as checked.
 
#else
 
#  define FMT_UNCHECKED_ITERATOR(It) using unchecked_type = It
 
#endif
 

	
 
#ifndef FMT_BEGIN_NAMESPACE
 
#  if FMT_USE_INLINE_NAMESPACES
 
#    define FMT_INLINE_NAMESPACE inline namespace
 
#    define FMT_END_NAMESPACE \
 
      }                       \
 
      }
 
#  else
 
#    define FMT_INLINE_NAMESPACE namespace
 
#    define FMT_END_NAMESPACE \
 
      }                       \
 
      using namespace v7;     \
 
      }
 
#  endif
 
#  define FMT_BEGIN_NAMESPACE \
 
    namespace fmt {           \
 
    FMT_INLINE_NAMESPACE v7 {
 
    inline namespace v10 {
 
#  define FMT_END_NAMESPACE \
 
    }                       \
 
    }
 
#endif
 

	
 
#ifndef FMT_MODULE_EXPORT
 
#  define FMT_MODULE_EXPORT
 
#  define FMT_BEGIN_EXPORT
 
#  define FMT_END_EXPORT
 
#endif
 

	
 
#if !defined(FMT_HEADER_ONLY) && defined(_WIN32)
 
#  define FMT_CLASS_API FMT_SUPPRESS_MSC_WARNING(4275)
 
#  ifdef FMT_EXPORT
 
#  ifdef FMT_LIB_EXPORT
 
#    define FMT_API __declspec(dllexport)
 
#    define FMT_EXTERN_TEMPLATE_API FMT_API
 
#    define FMT_EXPORTED
 
#  elif defined(FMT_SHARED)
 
#    define FMT_API __declspec(dllimport)
 
#    define FMT_EXTERN_TEMPLATE_API FMT_API
 
#  endif
 
#else
 
#  define FMT_CLASS_API
 
#  if defined(FMT_LIB_EXPORT) || defined(FMT_SHARED)
 
#    if defined(__GNUC__) || defined(__clang__)
 
#      define FMT_API __attribute__((visibility("default")))
 
#    endif
 
#  endif
 
#endif
 
#ifndef FMT_API
 
#  define FMT_API
 
#endif
 
#ifndef FMT_EXTERN_TEMPLATE_API
 
#  define FMT_EXTERN_TEMPLATE_API
 
#endif
 
#ifndef FMT_INSTANTIATION_DEF_API
 
#  define FMT_INSTANTIATION_DEF_API FMT_API
 
#endif
 

	
 
#ifndef FMT_HEADER_ONLY
 
#  define FMT_EXTERN extern
 
#else
 
#  define FMT_EXTERN
 
#endif
 

	
 
// libc++ supports string_view in pre-c++17.
 
#if (FMT_HAS_INCLUDE(<string_view>) &&                       \
 
     (__cplusplus > 201402L || defined(_LIBCPP_VERSION))) || \
 
    (defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910)
 
#if FMT_HAS_INCLUDE(<string_view>) && \
 
    (FMT_CPLUSPLUS >= 201703L || defined(_LIBCPP_VERSION))
 
#  include <string_view>
 
#  define FMT_USE_STRING_VIEW
 
#elif FMT_HAS_INCLUDE("experimental/string_view") && __cplusplus >= 201402L
 
#elif FMT_HAS_INCLUDE("experimental/string_view") && FMT_CPLUSPLUS >= 201402L
 
#  include <experimental/string_view>
 
#  define FMT_USE_EXPERIMENTAL_STRING_VIEW
 
#endif
 

	
 
#ifndef FMT_UNICODE
 
#  define FMT_UNICODE !FMT_MSC_VER
 
#  define FMT_UNICODE !FMT_MSC_VERSION
 
#endif
 

	
 
#ifndef FMT_CONSTEVAL
 
#  if ((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) && \
 
       (!defined(__apple_build_version__) ||                     \
 
        __apple_build_version__ >= 14000029L) &&                 \
 
       FMT_CPLUSPLUS >= 202002L) ||                              \
 
      (defined(__cpp_consteval) &&                               \
 
       (!FMT_MSC_VERSION || _MSC_FULL_VER >= 193030704))
 
// consteval is broken in MSVC before VS2022 and Apple clang before 14.
 
#    define FMT_CONSTEVAL consteval
 
#    define FMT_HAS_CONSTEVAL
 
#  else
 
#    define FMT_CONSTEVAL
 
#  endif
 
#endif
 
#if FMT_UNICODE && FMT_MSC_VER
 
#  pragma execution_character_set("utf-8")
 

	
 
#ifndef FMT_USE_NONTYPE_TEMPLATE_ARGS
 
#  if defined(__cpp_nontype_template_args) &&                  \
 
      ((FMT_GCC_VERSION >= 903 && FMT_CPLUSPLUS >= 201709L) || \
 
       __cpp_nontype_template_args >= 201911L) &&              \
 
      !defined(__NVCOMPILER) && !defined(__LCC__)
 
#    define FMT_USE_NONTYPE_TEMPLATE_ARGS 1
 
#  else
 
#    define FMT_USE_NONTYPE_TEMPLATE_ARGS 0
 
#  endif
 
#endif
 

	
 
#if defined __cpp_inline_variables && __cpp_inline_variables >= 201606L
 
#  define FMT_INLINE_VARIABLE inline
 
#else
 
#  define FMT_INLINE_VARIABLE
 
#endif
 

	
 
// Enable minimal optimizations for more compact code in debug mode.
 
FMT_GCC_PRAGMA("GCC push_options")
 
#if !defined(__OPTIMIZE__) && !defined(__NVCOMPILER) && !defined(__LCC__) && \
 
    !defined(__CUDACC__)
 
FMT_GCC_PRAGMA("GCC optimize(\"Og\")")
 
#endif
 

	
 
FMT_BEGIN_NAMESPACE
 

	
 
// Implementations of enable_if_t and other metafunctions for older systems.
 
template <bool B, class T = void>
 
template <bool B, typename T = void>
 
using enable_if_t = typename std::enable_if<B, T>::type;
 
template <bool B, class T, class F>
 
template <bool B, typename T, typename F>
 
using conditional_t = typename std::conditional<B, T, F>::type;
 
template <bool B> using bool_constant = std::integral_constant<bool, B>;
 
template <typename T>
 
@@ -268,31 +273,70 @@ template <typename T>
 
using remove_cvref_t = typename std::remove_cv<remove_reference_t<T>>::type;
 
template <typename T> struct type_identity { using type = T; };
 
template <typename T> using type_identity_t = typename type_identity<T>::type;
 

	
 
struct monostate {};
 
template <typename T>
 
using underlying_t = typename std::underlying_type<T>::type;
 

	
 
struct monostate {
 
  constexpr monostate() {}
 
};
 

	
 
// An enable_if helper to be used in template parameters which results in much
 
// shorter symbols: https://godbolt.org/z/sWw4vP. Extra parentheses are needed
 
// to workaround a bug in MSVC 2019 (see #1140 and #1186).
 
#define FMT_ENABLE_IF(...) enable_if_t<(__VA_ARGS__), int> = 0
 
#ifdef FMT_DOC
 
#  define FMT_ENABLE_IF(...)
 
#else
 
#  define FMT_ENABLE_IF(...) fmt::enable_if_t<(__VA_ARGS__), int> = 0
 
#endif
 

	
 
#ifdef __cpp_lib_byte
 
inline auto format_as(std::byte b) -> unsigned char {
 
  return static_cast<unsigned char>(b);
 
}
 
#endif
 

	
 
namespace detail {
 

	
 
// A helper function to suppress "conditional expression is constant" warnings.
 
template <typename T> constexpr T const_check(T value) { return value; }
 
// Suppresses "unused variable" warnings with the method described in
 
// https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/.
 
// (void)var does not work on many Intel compilers.
 
template <typename... T> FMT_CONSTEXPR void ignore_unused(const T&...) {}
 

	
 
constexpr FMT_INLINE auto is_constant_evaluated(
 
    bool default_value = false) noexcept -> bool {
 
// Workaround for incompatibility between libstdc++ consteval-based
 
// std::is_constant_evaluated() implementation and clang-14.
 
// https://github.com/fmtlib/fmt/issues/3247
 
#if FMT_CPLUSPLUS >= 202002L && defined(_GLIBCXX_RELEASE) && \
 
    _GLIBCXX_RELEASE >= 12 &&                                \
 
    (FMT_CLANG_VERSION >= 1400 && FMT_CLANG_VERSION < 1500)
 
  ignore_unused(default_value);
 
  return __builtin_is_constant_evaluated();
 
#elif defined(__cpp_lib_is_constant_evaluated)
 
  ignore_unused(default_value);
 
  return std::is_constant_evaluated();
 
#else
 
  return default_value;
 
#endif
 
}
 

	
 
// Suppresses "conditional expression is constant" warnings.
 
template <typename T> constexpr FMT_INLINE auto const_check(T value) -> T {
 
  return value;
 
}
 

	
 
FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
 
                                      const char* message);
 

	
 
#ifndef FMT_ASSERT
 
#  ifdef NDEBUG
 
// FMT_ASSERT is not empty to avoid -Werror=empty-body.
 
#    define FMT_ASSERT(condition, message) ((void)0)
 
// FMT_ASSERT is not empty to avoid -Wempty-body.
 
#    define FMT_ASSERT(condition, message) \
 
      fmt::detail::ignore_unused((condition), (message))
 
#  else
 
#    define FMT_ASSERT(condition, message)                                    \
 
      ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \
 
           ? (void)0                                                          \
 
           : ::fmt::detail::assert_fail(__FILE__, __LINE__, (message)))
 
           : fmt::detail::assert_fail(__FILE__, __LINE__, (message)))
 
#  endif
 
#endif
 

	
 
@@ -307,44 +351,42 @@ template <typename T> struct std_string_
 

	
 
#ifdef FMT_USE_INT128
 
// Do nothing.
 
#elif defined(__SIZEOF_INT128__) && !FMT_NVCC && \
 
    !(FMT_CLANG_VERSION && FMT_MSC_VER)
 
#elif defined(__SIZEOF_INT128__) && !defined(__NVCC__) && \
 
    !(FMT_CLANG_VERSION && FMT_MSC_VERSION)
 
#  define FMT_USE_INT128 1
 
using int128_t = __int128_t;
 
using uint128_t = __uint128_t;
 
using int128_opt = __int128_t;  // An optional native 128-bit integer.
 
using uint128_opt = __uint128_t;
 
template <typename T> inline auto convert_for_visit(T value) -> T {
 
  return value;
 
}
 
#else
 
#  define FMT_USE_INT128 0
 
#endif
 
#if !FMT_USE_INT128
 
struct int128_t {};
 
struct uint128_t {};
 
enum class int128_opt {};
 
enum class uint128_opt {};
 
// Reduce template instantiations.
 
template <typename T> auto convert_for_visit(T) -> monostate { return {}; }
 
#endif
 

	
 
// Casts a nonnegative integer to unsigned.
 
template <typename Int>
 
FMT_CONSTEXPR typename std::make_unsigned<Int>::type to_unsigned(Int value) {
 
  FMT_ASSERT(value >= 0, "negative value");
 
FMT_CONSTEXPR auto to_unsigned(Int value) ->
 
    typename std::make_unsigned<Int>::type {
 
  FMT_ASSERT(std::is_unsigned<Int>::value || value >= 0, "negative value");
 
  return static_cast<typename std::make_unsigned<Int>::type>(value);
 
}
 

	
 
FMT_SUPPRESS_MSC_WARNING(4566) constexpr unsigned char micro[] = "\u00B5";
 

	
 
template <typename Char> constexpr bool is_unicode() {
 
  return FMT_UNICODE || sizeof(Char) != 1 ||
 
         (sizeof(micro) == 3 && micro[0] == 0xC2 && micro[1] == 0xB5);
 
FMT_CONSTEXPR inline auto is_utf8() -> bool {
 
  FMT_MSC_WARNING(suppress : 4566) constexpr unsigned char section[] = "\u00A7";
 

	
 
  // Avoid buggy sign extensions in MSVC's constant evaluation mode (#2297).
 
  using uchar = unsigned char;
 
  return FMT_UNICODE || (sizeof(section) == 3 && uchar(section[0]) == 0xC2 &&
 
                         uchar(section[1]) == 0xA7);
 
}
 

	
 
#ifdef __cpp_char8_t
 
using char8_type = char8_t;
 
#else
 
enum char8_type : unsigned char {};
 
#endif
 
}  // namespace detail
 

	
 
#ifdef FMT_USE_INTERNAL
 
namespace internal = detail;  // DEPRECATED
 
#endif
 

	
 
/**
 
  An implementation of ``std::basic_string_view`` for pre-C++17. It provides a
 
  subset of the API. ``fmt::basic_string_view`` is used for format strings even
 
@@ -352,6 +394,7 @@ namespace internal = detail;  // DEPRECA
 
  compiled with a different ``-std`` option than the client code (which is not
 
  recommended).
 
 */
 
FMT_MODULE_EXPORT
 
template <typename Char> class basic_string_view {
 
 private:
 
  const Char* data_;
 
@@ -361,12 +404,11 @@ template <typename Char> class basic_str
 
  using value_type = Char;
 
  using iterator = const Char*;
 

	
 
  constexpr basic_string_view() FMT_NOEXCEPT : data_(nullptr), size_(0) {}
 
  constexpr basic_string_view() noexcept : data_(nullptr), size_(0) {}
 

	
 
  /** Constructs a string reference object from a C string and a size. */
 
  constexpr basic_string_view(const Char* s, size_t count) FMT_NOEXCEPT
 
      : data_(s),
 
        size_(count) {}
 
  constexpr basic_string_view(const Char* s, size_t count) noexcept
 
      : data_(s), size_(count) {}
 

	
 
  /**
 
    \rst
 
@@ -374,42 +416,58 @@ template <typename Char> class basic_str
 
    the size with ``std::char_traits<Char>::length``.
 
    \endrst
 
   */
 
#if __cplusplus >= 201703L  // C++17's char_traits::length() is constexpr.
 
  FMT_CONSTEXPR
 
#endif
 
  FMT_CONSTEXPR_CHAR_TRAITS
 
  FMT_INLINE
 
  basic_string_view(const Char* s)
 
      : data_(s), size_(std::char_traits<Char>::length(s)) {}
 
      : data_(s),
 
        size_(detail::const_check(std::is_same<Char, char>::value &&
 
                                  !detail::is_constant_evaluated(true))
 
                  ? std::strlen(reinterpret_cast<const char*>(s))
 
                  : std::char_traits<Char>::length(s)) {}
 

	
 
  /** Constructs a string reference from a ``std::basic_string`` object. */
 
  template <typename Traits, typename Alloc>
 
  FMT_CONSTEXPR basic_string_view(
 
      const std::basic_string<Char, Traits, Alloc>& s) FMT_NOEXCEPT
 
      : data_(s.data()),
 
        size_(s.size()) {}
 
      const std::basic_string<Char, Traits, Alloc>& s) noexcept
 
      : data_(s.data()), size_(s.size()) {}
 

	
 
  template <typename S, FMT_ENABLE_IF(std::is_same<
 
                                      S, detail::std_string_view<Char>>::value)>
 
  FMT_CONSTEXPR basic_string_view(S s) FMT_NOEXCEPT : data_(s.data()),
 
                                                      size_(s.size()) {}
 
  FMT_CONSTEXPR basic_string_view(S s) noexcept
 
      : data_(s.data()), size_(s.size()) {}
 

	
 
  /** Returns a pointer to the string data. */
 
  constexpr const Char* data() const { return data_; }
 
  constexpr auto data() const noexcept -> const Char* { return data_; }
 

	
 
  /** Returns the string size. */
 
  constexpr size_t size() const { return size_; }
 

	
 
  constexpr iterator begin() const { return data_; }
 
  constexpr iterator end() const { return data_ + size_; }
 

	
 
  constexpr const Char& operator[](size_t pos) const { return data_[pos]; }
 

	
 
  FMT_CONSTEXPR void remove_prefix(size_t n) {
 
  constexpr auto size() const noexcept -> size_t { return size_; }
 

	
 
  constexpr auto begin() const noexcept -> iterator { return data_; }
 
  constexpr auto end() const noexcept -> iterator { return data_ + size_; }
 

	
 
  constexpr auto operator[](size_t pos) const noexcept -> const Char& {
 
    return data_[pos];
 
  }
 

	
 
  FMT_CONSTEXPR void remove_prefix(size_t n) noexcept {
 
    data_ += n;
 
    size_ -= n;
 
  }
 

	
 
  FMT_CONSTEXPR_CHAR_TRAITS bool starts_with(
 
      basic_string_view<Char> sv) const noexcept {
 
    return size_ >= sv.size_ &&
 
           std::char_traits<Char>::compare(data_, sv.data_, sv.size_) == 0;
 
  }
 
  FMT_CONSTEXPR_CHAR_TRAITS bool starts_with(Char c) const noexcept {
 
    return size_ >= 1 && std::char_traits<Char>::eq(*data_, c);
 
  }
 
  FMT_CONSTEXPR_CHAR_TRAITS bool starts_with(const Char* s) const {
 
    return starts_with(basic_string_view<Char>(s));
 
  }
 

	
 
  // Lexicographically compare this string reference to other.
 
  int compare(basic_string_view other) const {
 
  FMT_CONSTEXPR_CHAR_TRAITS auto compare(basic_string_view other) const -> int {
 
    size_t str_size = size_ < other.size_ ? size_ : other.size_;
 
    int result = std::char_traits<Char>::compare(data_, other.data_, str_size);
 
    if (result == 0)
 
@@ -417,97 +475,77 @@ template <typename Char> class basic_str
 
    return result;
 
  }
 

	
 
  friend bool operator==(basic_string_view lhs, basic_string_view rhs) {
 
  FMT_CONSTEXPR_CHAR_TRAITS friend auto operator==(basic_string_view lhs,
 
                                                   basic_string_view rhs)
 
      -> bool {
 
    return lhs.compare(rhs) == 0;
 
  }
 
  friend bool operator!=(basic_string_view lhs, basic_string_view rhs) {
 
  friend auto operator!=(basic_string_view lhs, basic_string_view rhs) -> bool {
 
    return lhs.compare(rhs) != 0;
 
  }
 
  friend bool operator<(basic_string_view lhs, basic_string_view rhs) {
 
  friend auto operator<(basic_string_view lhs, basic_string_view rhs) -> bool {
 
    return lhs.compare(rhs) < 0;
 
  }
 
  friend bool operator<=(basic_string_view lhs, basic_string_view rhs) {
 
  friend auto operator<=(basic_string_view lhs, basic_string_view rhs) -> bool {
 
    return lhs.compare(rhs) <= 0;
 
  }
 
  friend bool operator>(basic_string_view lhs, basic_string_view rhs) {
 
  friend auto operator>(basic_string_view lhs, basic_string_view rhs) -> bool {
 
    return lhs.compare(rhs) > 0;
 
  }
 
  friend bool operator>=(basic_string_view lhs, basic_string_view rhs) {
 
  friend auto operator>=(basic_string_view lhs, basic_string_view rhs) -> bool {
 
    return lhs.compare(rhs) >= 0;
 
  }
 
};
 

	
 
FMT_MODULE_EXPORT
 
using string_view = basic_string_view<char>;
 
using wstring_view = basic_string_view<wchar_t>;
 

	
 
/** Specifies if ``T`` is a character type. Can be specialized by users. */
 
FMT_MODULE_EXPORT
 
template <typename T> struct is_char : std::false_type {};
 
template <> struct is_char<char> : std::true_type {};
 
template <> struct is_char<wchar_t> : std::true_type {};
 
template <> struct is_char<detail::char8_type> : std::true_type {};
 
template <> struct is_char<char16_t> : std::true_type {};
 
template <> struct is_char<char32_t> : std::true_type {};
 

	
 
/**
 
  \rst
 
  Returns a string view of `s`. In order to add custom string type support to
 
  {fmt} provide an overload of `to_string_view` for it in the same namespace as
 
  the type for the argument-dependent lookup to work.
 

	
 
  **Example**::
 

	
 
    namespace my_ns {
 
    inline string_view to_string_view(const my_string& s) {
 
      return {s.data(), s.length()};
 
    }
 
    }
 
    std::string message = fmt::format(my_string("The answer is {}"), 42);
 
  \endrst
 
 */
 
template <typename Char, FMT_ENABLE_IF(is_char<Char>::value)>
 
inline basic_string_view<Char> to_string_view(const Char* s) {
 
  return s;
 
}
 

	
 
template <typename Char, typename Traits, typename Alloc>
 
inline basic_string_view<Char> to_string_view(
 
    const std::basic_string<Char, Traits, Alloc>& s) {
 
  return s;
 
}
 

	
 
template <typename Char>
 
inline basic_string_view<Char> to_string_view(basic_string_view<Char> s) {
 
  return s;
 
}
 

	
 
template <typename Char,
 
          FMT_ENABLE_IF(!std::is_empty<detail::std_string_view<Char>>::value)>
 
inline basic_string_view<Char> to_string_view(detail::std_string_view<Char> s) {
 
  return s;
 
}
 

	
 
// A base class for compile-time strings. It is defined in the fmt namespace to
 
// make formatting functions visible via ADL, e.g. format(FMT_STRING("{}"), 42).
 

	
 
namespace detail {
 

	
 
// A base class for compile-time strings.
 
struct compile_string {};
 

	
 
template <typename S>
 
struct is_compile_string : std::is_base_of<compile_string, S> {};
 

	
 
template <typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
 
constexpr basic_string_view<typename S::char_type> to_string_view(const S& s) {
 
template <typename Char, FMT_ENABLE_IF(is_char<Char>::value)>
 
FMT_INLINE auto to_string_view(const Char* s) -> basic_string_view<Char> {
 
  return s;
 
}
 
template <typename Char, typename Traits, typename Alloc>
 
inline auto to_string_view(const std::basic_string<Char, Traits, Alloc>& s)
 
    -> basic_string_view<Char> {
 
  return s;
 
}
 

	
 
namespace detail {
 
template <typename Char>
 
constexpr auto to_string_view(basic_string_view<Char> s)
 
    -> basic_string_view<Char> {
 
  return s;
 
}
 
template <typename Char,
 
          FMT_ENABLE_IF(!std::is_empty<std_string_view<Char>>::value)>
 
inline auto to_string_view(std_string_view<Char> s) -> basic_string_view<Char> {
 
  return s;
 
}
 
template <typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
 
constexpr auto to_string_view(const S& s)
 
    -> basic_string_view<typename S::char_type> {
 
  return basic_string_view<typename S::char_type>(s);
 
}
 
void to_string_view(...);
 
using fmt::v7::to_string_view;
 

	
 
// Specifies whether S is a string type convertible to fmt::basic_string_view.
 
// It should be a constexpr function but MSVC 2017 fails to compile it in
 
// enable_if and MSVC 2015 fails to compile it as an alias template.
 
// ADL is intentionally disabled as to_string_view is not an extension point.
 
template <typename S>
 
struct is_string : std::is_class<decltype(to_string_view(std::declval<S>()))> {
 
};
 
struct is_string
 
    : std::is_class<decltype(detail::to_string_view(std::declval<S>()))> {};
 

	
 
template <typename S, typename = void> struct char_t_impl {};
 
template <typename S> struct char_t_impl<S, enable_if_t<is_string<S>::value>> {
 
@@ -515,464 +553,6 @@ template <typename S> struct char_t_impl
 
  using type = typename result::value_type;
 
};
 

	
 
// Reports a compile-time error if S is not a valid format string.
 
template <typename..., typename S, FMT_ENABLE_IF(!is_compile_string<S>::value)>
 
FMT_INLINE void check_format_string(const S&) {
 
#ifdef FMT_ENFORCE_COMPILE_STRING
 
  static_assert(is_compile_string<S>::value,
 
                "FMT_ENFORCE_COMPILE_STRING requires all format strings to use "
 
                "FMT_STRING.");
 
#endif
 
}
 
template <typename..., typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
 
void check_format_string(S);
 

	
 
struct error_handler {
 
  constexpr error_handler() = default;
 

	
 
  // This function is intentionally not constexpr to give a compile-time error.
 
  FMT_NORETURN FMT_API void on_error(const char* message);
 
};
 
}  // namespace detail
 

	
 
/** String's character type. */
 
template <typename S> using char_t = typename detail::char_t_impl<S>::type;
 

	
 
/**
 
  \rst
 
  Parsing context consisting of a format string range being parsed and an
 
  argument counter for automatic indexing.
 

	
 
  You can use one of the following type aliases for common character types:
 

	
 
  +-----------------------+-------------------------------------+
 
  | Type                  | Definition                          |
 
  +=======================+=====================================+
 
  | format_parse_context  | basic_format_parse_context<char>    |
 
  +-----------------------+-------------------------------------+
 
  | wformat_parse_context | basic_format_parse_context<wchar_t> |
 
  +-----------------------+-------------------------------------+
 
  \endrst
 
 */
 
template <typename Char, typename ErrorHandler = detail::error_handler>
 
class basic_format_parse_context : private ErrorHandler {
 
 private:
 
  basic_string_view<Char> format_str_;
 
  int next_arg_id_;
 

	
 
 public:
 
  using char_type = Char;
 
  using iterator = typename basic_string_view<Char>::iterator;
 

	
 
  explicit constexpr basic_format_parse_context(
 
      basic_string_view<Char> format_str, ErrorHandler eh = {},
 
      int next_arg_id = 0)
 
      : ErrorHandler(eh), format_str_(format_str), next_arg_id_(next_arg_id) {}
 

	
 
  /**
 
    Returns an iterator to the beginning of the format string range being
 
    parsed.
 
   */
 
  constexpr iterator begin() const FMT_NOEXCEPT { return format_str_.begin(); }
 

	
 
  /**
 
    Returns an iterator past the end of the format string range being parsed.
 
   */
 
  constexpr iterator end() const FMT_NOEXCEPT { return format_str_.end(); }
 

	
 
  /** Advances the begin iterator to ``it``. */
 
  FMT_CONSTEXPR void advance_to(iterator it) {
 
    format_str_.remove_prefix(detail::to_unsigned(it - begin()));
 
  }
 

	
 
  /**
 
    Reports an error if using the manual argument indexing; otherwise returns
 
    the next argument index and switches to the automatic indexing.
 
   */
 
  FMT_CONSTEXPR int next_arg_id() {
 
    // Don't check if the argument id is valid to avoid overhead and because it
 
    // will be checked during formatting anyway.
 
    if (next_arg_id_ >= 0) return next_arg_id_++;
 
    on_error("cannot switch from manual to automatic argument indexing");
 
    return 0;
 
  }
 

	
 
  /**
 
    Reports an error if using the automatic argument indexing; otherwise
 
    switches to the manual indexing.
 
   */
 
  FMT_CONSTEXPR void check_arg_id(int) {
 
    if (next_arg_id_ > 0)
 
      on_error("cannot switch from automatic to manual argument indexing");
 
    else
 
      next_arg_id_ = -1;
 
  }
 

	
 
  FMT_CONSTEXPR void check_arg_id(basic_string_view<Char>) {}
 

	
 
  FMT_CONSTEXPR void on_error(const char* message) {
 
    ErrorHandler::on_error(message);
 
  }
 

	
 
  constexpr ErrorHandler error_handler() const { return *this; }
 
};
 

	
 
using format_parse_context = basic_format_parse_context<char>;
 
using wformat_parse_context = basic_format_parse_context<wchar_t>;
 

	
 
template <typename Context> class basic_format_arg;
 
template <typename Context> class basic_format_args;
 
template <typename Context> class dynamic_format_arg_store;
 

	
 
// A formatter for objects of type T.
 
template <typename T, typename Char = char, typename Enable = void>
 
struct formatter {
 
  // A deleted default constructor indicates a disabled formatter.
 
  formatter() = delete;
 
};
 

	
 
// Specifies if T has an enabled formatter specialization. A type can be
 
// formattable even if it doesn't have a formatter e.g. via a conversion.
 
template <typename T, typename Context>
 
using has_formatter =
 
    std::is_constructible<typename Context::template formatter_type<T>>;
 

	
 
// Checks whether T is a container with contiguous storage.
 
template <typename T> struct is_contiguous : std::false_type {};
 
template <typename Char>
 
struct is_contiguous<std::basic_string<Char>> : std::true_type {};
 

	
 
namespace detail {
 

	
 
// Extracts a reference to the container from back_insert_iterator.
 
template <typename Container>
 
inline Container& get_container(std::back_insert_iterator<Container> it) {
 
  using bi_iterator = std::back_insert_iterator<Container>;
 
  struct accessor : bi_iterator {
 
    accessor(bi_iterator iter) : bi_iterator(iter) {}
 
    using bi_iterator::container;
 
  };
 
  return *accessor(it).container;
 
}
 

	
 
/**
 
  \rst
 
  A contiguous memory buffer with an optional growing ability. It is an internal
 
  class and shouldn't be used directly, only via `~fmt::basic_memory_buffer`.
 
  \endrst
 
 */
 
template <typename T> class buffer {
 
 private:
 
  T* ptr_;
 
  size_t size_;
 
  size_t capacity_;
 

	
 
 protected:
 
  // Don't initialize ptr_ since it is not accessed to save a few cycles.
 
  FMT_SUPPRESS_MSC_WARNING(26495)
 
  buffer(size_t sz) FMT_NOEXCEPT : size_(sz), capacity_(sz) {}
 

	
 
  buffer(T* p = nullptr, size_t sz = 0, size_t cap = 0) FMT_NOEXCEPT
 
      : ptr_(p),
 
        size_(sz),
 
        capacity_(cap) {}
 

	
 
  ~buffer() = default;
 

	
 
  /** Sets the buffer data and capacity. */
 
  void set(T* buf_data, size_t buf_capacity) FMT_NOEXCEPT {
 
    ptr_ = buf_data;
 
    capacity_ = buf_capacity;
 
  }
 

	
 
  /** Increases the buffer capacity to hold at least *capacity* elements. */
 
  virtual void grow(size_t capacity) = 0;
 

	
 
 public:
 
  using value_type = T;
 
  using const_reference = const T&;
 

	
 
  buffer(const buffer&) = delete;
 
  void operator=(const buffer&) = delete;
 

	
 
  T* begin() FMT_NOEXCEPT { return ptr_; }
 
  T* end() FMT_NOEXCEPT { return ptr_ + size_; }
 

	
 
  const T* begin() const FMT_NOEXCEPT { return ptr_; }
 
  const T* end() const FMT_NOEXCEPT { return ptr_ + size_; }
 

	
 
  /** Returns the size of this buffer. */
 
  size_t size() const FMT_NOEXCEPT { return size_; }
 

	
 
  /** Returns the capacity of this buffer. */
 
  size_t capacity() const FMT_NOEXCEPT { return capacity_; }
 

	
 
  /** Returns a pointer to the buffer data. */
 
  T* data() FMT_NOEXCEPT { return ptr_; }
 

	
 
  /** Returns a pointer to the buffer data. */
 
  const T* data() const FMT_NOEXCEPT { return ptr_; }
 

	
 
  /** Clears this buffer. */
 
  void clear() { size_ = 0; }
 

	
 
  // Tries resizing the buffer to contain *count* elements. If T is a POD type
 
  // the new elements may not be initialized.
 
  void try_resize(size_t count) {
 
    try_reserve(count);
 
    size_ = count <= capacity_ ? count : capacity_;
 
  }
 

	
 
  // Tries increasing the buffer capacity to *new_capacity*. It can increase the
 
  // capacity by a smaller amount than requested but guarantees there is space
 
  // for at least one additional element either by increasing the capacity or by
 
  // flushing the buffer if it is full.
 
  void try_reserve(size_t new_capacity) {
 
    if (new_capacity > capacity_) grow(new_capacity);
 
  }
 

	
 
  void push_back(const T& value) {
 
    try_reserve(size_ + 1);
 
    ptr_[size_++] = value;
 
  }
 

	
 
  /** Appends data to the end of the buffer. */
 
  template <typename U> void append(const U* begin, const U* end);
 

	
 
  template <typename I> T& operator[](I index) { return ptr_[index]; }
 
  template <typename I> const T& operator[](I index) const {
 
    return ptr_[index];
 
  }
 
};
 

	
 
struct buffer_traits {
 
  explicit buffer_traits(size_t) {}
 
  size_t count() const { return 0; }
 
  size_t limit(size_t size) { return size; }
 
};
 

	
 
class fixed_buffer_traits {
 
 private:
 
  size_t count_ = 0;
 
  size_t limit_;
 

	
 
 public:
 
  explicit fixed_buffer_traits(size_t limit) : limit_(limit) {}
 
  size_t count() const { return count_; }
 
  size_t limit(size_t size) {
 
    size_t n = limit_ > count_ ? limit_ - count_ : 0;
 
    count_ += size;
 
    return size < n ? size : n;
 
  }
 
};
 

	
 
// A buffer that writes to an output iterator when flushed.
 
template <typename OutputIt, typename T, typename Traits = buffer_traits>
 
class iterator_buffer final : public Traits, public buffer<T> {
 
 private:
 
  OutputIt out_;
 
  enum { buffer_size = 256 };
 
  T data_[buffer_size];
 

	
 
 protected:
 
  void grow(size_t) final FMT_OVERRIDE {
 
    if (this->size() == buffer_size) flush();
 
  }
 
  void flush();
 

	
 
 public:
 
  explicit iterator_buffer(OutputIt out, size_t n = buffer_size)
 
      : Traits(n),
 
        buffer<T>(data_, 0, buffer_size),
 
        out_(out) {}
 
  ~iterator_buffer() { flush(); }
 

	
 
  OutputIt out() {
 
    flush();
 
    return out_;
 
  }
 
  size_t count() const { return Traits::count() + this->size(); }
 
};
 

	
 
template <typename T> class iterator_buffer<T*, T> final : public buffer<T> {
 
 protected:
 
  void grow(size_t) final FMT_OVERRIDE {}
 

	
 
 public:
 
  explicit iterator_buffer(T* out, size_t = 0) : buffer<T>(out, 0, ~size_t()) {}
 

	
 
  T* out() { return &*this->end(); }
 
};
 

	
 
// A buffer that writes to a container with the contiguous storage.
 
template <typename Container>
 
class iterator_buffer<std::back_insert_iterator<Container>,
 
                      enable_if_t<is_contiguous<Container>::value,
 
                                  typename Container::value_type>>
 
    final : public buffer<typename Container::value_type> {
 
 private:
 
  Container& container_;
 

	
 
 protected:
 
  void grow(size_t capacity) final FMT_OVERRIDE {
 
    container_.resize(capacity);
 
    this->set(&container_[0], capacity);
 
  }
 

	
 
 public:
 
  explicit iterator_buffer(Container& c)
 
      : buffer<typename Container::value_type>(c.size()), container_(c) {}
 
  explicit iterator_buffer(std::back_insert_iterator<Container> out, size_t = 0)
 
      : iterator_buffer(get_container(out)) {}
 
  std::back_insert_iterator<Container> out() {
 
    return std::back_inserter(container_);
 
  }
 
};
 

	
 
// A buffer that counts the number of code units written discarding the output.
 
template <typename T = char> class counting_buffer final : public buffer<T> {
 
 private:
 
  enum { buffer_size = 256 };
 
  T data_[buffer_size];
 
  size_t count_ = 0;
 

	
 
 protected:
 
  void grow(size_t) final FMT_OVERRIDE {
 
    if (this->size() != buffer_size) return;
 
    count_ += this->size();
 
    this->clear();
 
  }
 

	
 
 public:
 
  counting_buffer() : buffer<T>(data_, 0, buffer_size) {}
 

	
 
  size_t count() { return count_ + this->size(); }
 
};
 

	
 
// An output iterator that appends to the buffer.
 
// It is used to reduce symbol sizes for the common case.
 
template <typename T>
 
class buffer_appender : public std::back_insert_iterator<buffer<T>> {
 
  using base = std::back_insert_iterator<buffer<T>>;
 

	
 
 public:
 
  explicit buffer_appender(buffer<T>& buf) : base(buf) {}
 
  buffer_appender(base it) : base(it) {}
 

	
 
  buffer_appender& operator++() {
 
    base::operator++();
 
    return *this;
 
  }
 

	
 
  buffer_appender operator++(int) {
 
    buffer_appender tmp = *this;
 
    ++*this;
 
    return tmp;
 
  }
 
};
 

	
 
// Maps an output iterator into a buffer.
 
template <typename T, typename OutputIt>
 
iterator_buffer<OutputIt, T> get_buffer(OutputIt);
 
template <typename T> buffer<T>& get_buffer(buffer_appender<T>);
 

	
 
template <typename OutputIt> OutputIt get_buffer_init(OutputIt out) {
 
  return out;
 
}
 
template <typename T> buffer<T>& get_buffer_init(buffer_appender<T> out) {
 
  return get_container(out);
 
}
 

	
 
template <typename Buffer>
 
auto get_iterator(Buffer& buf) -> decltype(buf.out()) {
 
  return buf.out();
 
}
 
template <typename T> buffer_appender<T> get_iterator(buffer<T>& buf) {
 
  return buffer_appender<T>(buf);
 
}
 

	
 
template <typename T, typename Char = char, typename Enable = void>
 
struct fallback_formatter {
 
  fallback_formatter() = delete;
 
};
 

	
 
// Specifies if T has an enabled fallback_formatter specialization.
 
template <typename T, typename Context>
 
using has_fallback_formatter =
 
    std::is_constructible<fallback_formatter<T, typename Context::char_type>>;
 

	
 
struct view {};
 

	
 
template <typename Char, typename T> struct named_arg : view {
 
  const Char* name;
 
  const T& value;
 
  named_arg(const Char* n, const T& v) : name(n), value(v) {}
 
};
 

	
 
template <typename Char> struct named_arg_info {
 
  const Char* name;
 
  int id;
 
};
 

	
 
template <typename T, typename Char, size_t NUM_ARGS, size_t NUM_NAMED_ARGS>
 
struct arg_data {
 
  // args_[0].named_args points to named_args_ to avoid bloating format_args.
 
  // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
 
  T args_[1 + (NUM_ARGS != 0 ? NUM_ARGS : +1)];
 
  named_arg_info<Char> named_args_[NUM_NAMED_ARGS];
 

	
 
  template <typename... U>
 
  arg_data(const U&... init) : args_{T(named_args_, NUM_NAMED_ARGS), init...} {}
 
  arg_data(const arg_data& other) = delete;
 
  const T* args() const { return args_ + 1; }
 
  named_arg_info<Char>* named_args() { return named_args_; }
 
};
 

	
 
template <typename T, typename Char, size_t NUM_ARGS>
 
struct arg_data<T, Char, NUM_ARGS, 0> {
 
  // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
 
  T args_[NUM_ARGS != 0 ? NUM_ARGS : +1];
 

	
 
  template <typename... U>
 
  FMT_INLINE arg_data(const U&... init) : args_{init...} {}
 
  FMT_INLINE const T* args() const { return args_; }
 
  FMT_INLINE std::nullptr_t named_args() { return nullptr; }
 
};
 

	
 
template <typename Char>
 
inline void init_named_args(named_arg_info<Char>*, int, int) {}
 

	
 
template <typename Char, typename T, typename... Tail>
 
void init_named_args(named_arg_info<Char>* named_args, int arg_count,
 
                     int named_arg_count, const T&, const Tail&... args) {
 
  init_named_args(named_args, arg_count + 1, named_arg_count, args...);
 
}
 

	
 
template <typename Char, typename T, typename... Tail>
 
void init_named_args(named_arg_info<Char>* named_args, int arg_count,
 
                     int named_arg_count, const named_arg<Char, T>& arg,
 
                     const Tail&... args) {
 
  named_args[named_arg_count++] = {arg.name, arg_count};
 
  init_named_args(named_args, arg_count + 1, named_arg_count, args...);
 
}
 

	
 
template <typename... Args>
 
FMT_INLINE void init_named_args(std::nullptr_t, int, int, const Args&...) {}
 

	
 
template <typename T> struct is_named_arg : std::false_type {};
 

	
 
template <typename T, typename Char>
 
struct is_named_arg<named_arg<Char, T>> : std::true_type {};
 

	
 
template <bool B = false> constexpr size_t count() { return B ? 1 : 0; }
 
template <bool B1, bool B2, bool... Tail> constexpr size_t count() {
 
  return (B1 ? 1 : 0) + count<B2, Tail...>();
 
}
 

	
 
template <typename... Args> constexpr size_t count_named_args() {
 
  return count<is_named_arg<Args>::value...>();
 
}
 

	
 
enum class type {
 
  none_type,
 
  // Integer types should go first,
 
@@ -1009,8 +589,8 @@ FMT_TYPE_CONSTANT(int, int_type);
 
FMT_TYPE_CONSTANT(unsigned, uint_type);
 
FMT_TYPE_CONSTANT(long long, long_long_type);
 
FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type);
 
FMT_TYPE_CONSTANT(int128_t, int128_type);
 
FMT_TYPE_CONSTANT(uint128_t, uint128_type);
 
FMT_TYPE_CONSTANT(int128_opt, int128_type);
 
FMT_TYPE_CONSTANT(uint128_opt, uint128_type);
 
FMT_TYPE_CONSTANT(bool, bool_type);
 
FMT_TYPE_CONSTANT(Char, char_type);
 
FMT_TYPE_CONSTANT(float, float_type);
 
@@ -1023,11 +603,613 @@ FMT_TYPE_CONSTANT(const void*, pointer_t
 
constexpr bool is_integral_type(type t) {
 
  return t > type::none_type && t <= type::last_integer_type;
 
}
 

	
 
constexpr bool is_arithmetic_type(type t) {
 
  return t > type::none_type && t <= type::last_numeric_type;
 
}
 

	
 
constexpr auto set(type rhs) -> int { return 1 << static_cast<int>(rhs); }
 
constexpr auto in(type t, int set) -> bool {
 
  return ((set >> static_cast<int>(t)) & 1) != 0;
 
}
 

	
 
// Bitsets of types.
 
enum {
 
  sint_set =
 
      set(type::int_type) | set(type::long_long_type) | set(type::int128_type),
 
  uint_set = set(type::uint_type) | set(type::ulong_long_type) |
 
             set(type::uint128_type),
 
  bool_set = set(type::bool_type),
 
  char_set = set(type::char_type),
 
  float_set = set(type::float_type) | set(type::double_type) |
 
              set(type::long_double_type),
 
  string_set = set(type::string_type),
 
  cstring_set = set(type::cstring_type),
 
  pointer_set = set(type::pointer_type)
 
};
 

	
 
FMT_NORETURN FMT_API void throw_format_error(const char* message);
 

	
 
struct error_handler {
 
  constexpr error_handler() = default;
 

	
 
  // This function is intentionally not constexpr to give a compile-time error.
 
  FMT_NORETURN void on_error(const char* message) {
 
    throw_format_error(message);
 
  }
 
};
 
}  // namespace detail
 

	
 
/** String's character type. */
 
template <typename S> using char_t = typename detail::char_t_impl<S>::type;
 

	
 
/**
 
  \rst
 
  Parsing context consisting of a format string range being parsed and an
 
  argument counter for automatic indexing.
 
  You can use the ``format_parse_context`` type alias for ``char`` instead.
 
  \endrst
 
 */
 
FMT_MODULE_EXPORT
 
template <typename Char> class basic_format_parse_context {
 
 private:
 
  basic_string_view<Char> format_str_;
 
  int next_arg_id_;
 

	
 
  FMT_CONSTEXPR void do_check_arg_id(int id);
 

	
 
 public:
 
  using char_type = Char;
 
  using iterator = const Char*;
 

	
 
  explicit constexpr basic_format_parse_context(
 
      basic_string_view<Char> format_str, int next_arg_id = 0)
 
      : format_str_(format_str), next_arg_id_(next_arg_id) {}
 

	
 
  /**
 
    Returns an iterator to the beginning of the format string range being
 
    parsed.
 
   */
 
  constexpr auto begin() const noexcept -> iterator {
 
    return format_str_.begin();
 
  }
 

	
 
  /**
 
    Returns an iterator past the end of the format string range being parsed.
 
   */
 
  constexpr auto end() const noexcept -> iterator { return format_str_.end(); }
 

	
 
  /** Advances the begin iterator to ``it``. */
 
  FMT_CONSTEXPR void advance_to(iterator it) {
 
    format_str_.remove_prefix(detail::to_unsigned(it - begin()));
 
  }
 

	
 
  /**
 
    Reports an error if using the manual argument indexing; otherwise returns
 
    the next argument index and switches to the automatic indexing.
 
   */
 
  FMT_CONSTEXPR auto next_arg_id() -> int {
 
    if (next_arg_id_ < 0) {
 
      detail::throw_format_error(
 
          "cannot switch from manual to automatic argument indexing");
 
      return 0;
 
    }
 
    int id = next_arg_id_++;
 
    do_check_arg_id(id);
 
    return id;
 
  }
 

	
 
  /**
 
    Reports an error if using the automatic argument indexing; otherwise
 
    switches to the manual indexing.
 
   */
 
  FMT_CONSTEXPR void check_arg_id(int id) {
 
    if (next_arg_id_ > 0) {
 
      detail::throw_format_error(
 
          "cannot switch from automatic to manual argument indexing");
 
      return;
 
    }
 
    next_arg_id_ = -1;
 
    do_check_arg_id(id);
 
  }
 
  FMT_CONSTEXPR void check_arg_id(basic_string_view<Char>) {}
 
  FMT_CONSTEXPR void check_dynamic_spec(int arg_id);
 
};
 

	
 
FMT_MODULE_EXPORT
 
using format_parse_context = basic_format_parse_context<char>;
 

	
 
namespace detail {
 
// A parse context with extra data used only in compile-time checks.
 
template <typename Char>
 
class compile_parse_context : public basic_format_parse_context<Char> {
 
 private:
 
  int num_args_;
 
  const type* types_;
 
  using base = basic_format_parse_context<Char>;
 

	
 
 public:
 
  explicit FMT_CONSTEXPR compile_parse_context(
 
      basic_string_view<Char> format_str, int num_args, const type* types,
 
      int next_arg_id = 0)
 
      : base(format_str, next_arg_id), num_args_(num_args), types_(types) {}
 

	
 
  constexpr auto num_args() const -> int { return num_args_; }
 
  constexpr auto arg_type(int id) const -> type { return types_[id]; }
 

	
 
  FMT_CONSTEXPR auto next_arg_id() -> int {
 
    int id = base::next_arg_id();
 
    if (id >= num_args_) throw_format_error("argument not found");
 
    return id;
 
  }
 

	
 
  FMT_CONSTEXPR void check_arg_id(int id) {
 
    base::check_arg_id(id);
 
    if (id >= num_args_) throw_format_error("argument not found");
 
  }
 
  using base::check_arg_id;
 

	
 
  FMT_CONSTEXPR void check_dynamic_spec(int arg_id) {
 
    detail::ignore_unused(arg_id);
 
#if !defined(__LCC__)
 
    if (arg_id < num_args_ && types_ && !is_integral_type(types_[arg_id]))
 
      throw_format_error("width/precision is not integer");
 
#endif
 
  }
 
};
 
}  // namespace detail
 

	
 
template <typename Char>
 
FMT_CONSTEXPR void basic_format_parse_context<Char>::do_check_arg_id(int id) {
 
  // Argument id is only checked at compile-time during parsing because
 
  // formatting has its own validation.
 
  if (detail::is_constant_evaluated() &&
 
      (!FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200)) {
 
    using context = detail::compile_parse_context<Char>;
 
    if (id >= static_cast<context*>(this)->num_args())
 
      detail::throw_format_error("argument not found");
 
  }
 
}
 

	
 
template <typename Char>
 
FMT_CONSTEXPR void basic_format_parse_context<Char>::check_dynamic_spec(
 
    int arg_id) {
 
  if (detail::is_constant_evaluated() &&
 
      (!FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200)) {
 
    using context = detail::compile_parse_context<Char>;
 
    static_cast<context*>(this)->check_dynamic_spec(arg_id);
 
  }
 
}
 

	
 
FMT_MODULE_EXPORT template <typename Context> class basic_format_arg;
 
FMT_MODULE_EXPORT template <typename Context> class basic_format_args;
 
FMT_MODULE_EXPORT template <typename Context> class dynamic_format_arg_store;
 

	
 
// A formatter for objects of type T.
 
FMT_MODULE_EXPORT
 
template <typename T, typename Char = char, typename Enable = void>
 
struct formatter {
 
  // A deleted default constructor indicates a disabled formatter.
 
  formatter() = delete;
 
};
 

	
 
// Specifies if T has an enabled formatter specialization. A type can be
 
// formattable even if it doesn't have a formatter e.g. via a conversion.
 
template <typename T, typename Context>
 
using has_formatter =
 
    std::is_constructible<typename Context::template formatter_type<T>>;
 

	
 
// Checks whether T is a container with contiguous storage.
 
template <typename T> struct is_contiguous : std::false_type {};
 
template <typename Char>
 
struct is_contiguous<std::basic_string<Char>> : std::true_type {};
 

	
 
class appender;
 

	
 
namespace detail {
 

	
 
template <typename Context, typename T>
 
constexpr auto has_const_formatter_impl(T*)
 
    -> decltype(typename Context::template formatter_type<T>().format(
 
                    std::declval<const T&>(), std::declval<Context&>()),
 
                true) {
 
  return true;
 
}
 
template <typename Context>
 
constexpr auto has_const_formatter_impl(...) -> bool {
 
  return false;
 
}
 
template <typename T, typename Context>
 
constexpr auto has_const_formatter() -> bool {
 
  return has_const_formatter_impl<Context>(static_cast<T*>(nullptr));
 
}
 

	
 
// Extracts a reference to the container from back_insert_iterator.
 
template <typename Container>
 
inline auto get_container(std::back_insert_iterator<Container> it)
 
    -> Container& {
 
  using base = std::back_insert_iterator<Container>;
 
  struct accessor : base {
 
    accessor(base b) : base(b) {}
 
    using base::container;
 
  };
 
  return *accessor(it).container;
 
}
 

	
 
template <typename Char, typename InputIt, typename OutputIt>
 
FMT_CONSTEXPR auto copy_str(InputIt begin, InputIt end, OutputIt out)
 
    -> OutputIt {
 
  while (begin != end) *out++ = static_cast<Char>(*begin++);
 
  return out;
 
}
 

	
 
template <typename Char, typename T, typename U,
 
          FMT_ENABLE_IF(
 
              std::is_same<remove_const_t<T>, U>::value&& is_char<U>::value)>
 
FMT_CONSTEXPR auto copy_str(T* begin, T* end, U* out) -> U* {
 
  if (is_constant_evaluated()) return copy_str<Char, T*, U*>(begin, end, out);
 
  auto size = to_unsigned(end - begin);
 
  if (size > 0) memcpy(out, begin, size * sizeof(U));
 
  return out + size;
 
}
 

	
 
/**
 
  \rst
 
  A contiguous memory buffer with an optional growing ability. It is an internal
 
  class and shouldn't be used directly, only via `~fmt::basic_memory_buffer`.
 
  \endrst
 
 */
 
template <typename T> class buffer {
 
 private:
 
  T* ptr_;
 
  size_t size_;
 
  size_t capacity_;
 

	
 
 protected:
 
  // Don't initialize ptr_ since it is not accessed to save a few cycles.
 
  FMT_MSC_WARNING(suppress : 26495)
 
  buffer(size_t sz) noexcept : size_(sz), capacity_(sz) {}
 

	
 
  FMT_CONSTEXPR20 buffer(T* p = nullptr, size_t sz = 0, size_t cap = 0) noexcept
 
      : ptr_(p), size_(sz), capacity_(cap) {}
 

	
 
  FMT_CONSTEXPR20 ~buffer() = default;
 
  buffer(buffer&&) = default;
 

	
 
  /** Sets the buffer data and capacity. */
 
  FMT_CONSTEXPR void set(T* buf_data, size_t buf_capacity) noexcept {
 
    ptr_ = buf_data;
 
    capacity_ = buf_capacity;
 
  }
 

	
 
  /** Increases the buffer capacity to hold at least *capacity* elements. */
 
  virtual FMT_CONSTEXPR20 void grow(size_t capacity) = 0;
 

	
 
 public:
 
  using value_type = T;
 
  using const_reference = const T&;
 

	
 
  buffer(const buffer&) = delete;
 
  void operator=(const buffer&) = delete;
 

	
 
  FMT_INLINE auto begin() noexcept -> T* { return ptr_; }
 
  FMT_INLINE auto end() noexcept -> T* { return ptr_ + size_; }
 

	
 
  FMT_INLINE auto begin() const noexcept -> const T* { return ptr_; }
 
  FMT_INLINE auto end() const noexcept -> const T* { return ptr_ + size_; }
 

	
 
  /** Returns the size of this buffer. */
 
  constexpr auto size() const noexcept -> size_t { return size_; }
 

	
 
  /** Returns the capacity of this buffer. */
 
  constexpr auto capacity() const noexcept -> size_t { return capacity_; }
 

	
 
  /** Returns a pointer to the buffer data. */
 
  FMT_CONSTEXPR auto data() noexcept -> T* { return ptr_; }
 

	
 
  /** Returns a pointer to the buffer data. */
 
  FMT_CONSTEXPR auto data() const noexcept -> const T* { return ptr_; }
 

	
 
  /** Clears this buffer. */
 
  void clear() { size_ = 0; }
 

	
 
  // Tries resizing the buffer to contain *count* elements. If T is a POD type
 
  // the new elements may not be initialized.
 
  FMT_CONSTEXPR20 void try_resize(size_t count) {
 
    try_reserve(count);
 
    size_ = count <= capacity_ ? count : capacity_;
 
  }
 

	
 
  // Tries increasing the buffer capacity to *new_capacity*. It can increase the
 
  // capacity by a smaller amount than requested but guarantees there is space
 
  // for at least one additional element either by increasing the capacity or by
 
  // flushing the buffer if it is full.
 
  FMT_CONSTEXPR20 void try_reserve(size_t new_capacity) {
 
    if (new_capacity > capacity_) grow(new_capacity);
 
  }
 

	
 
  FMT_CONSTEXPR20 void push_back(const T& value) {
 
    try_reserve(size_ + 1);
 
    ptr_[size_++] = value;
 
  }
 

	
 
  /** Appends data to the end of the buffer. */
 
  template <typename U> void append(const U* begin, const U* end);
 

	
 
  template <typename Idx> FMT_CONSTEXPR auto operator[](Idx index) -> T& {
 
    return ptr_[index];
 
  }
 
  template <typename Idx>
 
  FMT_CONSTEXPR auto operator[](Idx index) const -> const T& {
 
    return ptr_[index];
 
  }
 
};
 

	
 
struct buffer_traits {
 
  explicit buffer_traits(size_t) {}
 
  auto count() const -> size_t { return 0; }
 
  auto limit(size_t size) -> size_t { return size; }
 
};
 

	
 
class fixed_buffer_traits {
 
 private:
 
  size_t count_ = 0;
 
  size_t limit_;
 

	
 
 public:
 
  explicit fixed_buffer_traits(size_t limit) : limit_(limit) {}
 
  auto count() const -> size_t { return count_; }
 
  auto limit(size_t size) -> size_t {
 
    size_t n = limit_ > count_ ? limit_ - count_ : 0;
 
    count_ += size;
 
    return size < n ? size : n;
 
  }
 
};
 

	
 
// A buffer that writes to an output iterator when flushed.
 
template <typename OutputIt, typename T, typename Traits = buffer_traits>
 
class iterator_buffer final : public Traits, public buffer<T> {
 
 private:
 
  OutputIt out_;
 
  enum { buffer_size = 256 };
 
  T data_[buffer_size];
 

	
 
 protected:
 
  FMT_CONSTEXPR20 void grow(size_t) override {
 
    if (this->size() == buffer_size) flush();
 
  }
 

	
 
  void flush() {
 
    auto size = this->size();
 
    this->clear();
 
    out_ = copy_str<T>(data_, data_ + this->limit(size), out_);
 
  }
 

	
 
 public:
 
  explicit iterator_buffer(OutputIt out, size_t n = buffer_size)
 
      : Traits(n), buffer<T>(data_, 0, buffer_size), out_(out) {}
 
  iterator_buffer(iterator_buffer&& other)
 
      : Traits(other), buffer<T>(data_, 0, buffer_size), out_(other.out_) {}
 
  ~iterator_buffer() { flush(); }
 

	
 
  auto out() -> OutputIt {
 
    flush();
 
    return out_;
 
  }
 
  auto count() const -> size_t { return Traits::count() + this->size(); }
 
};
 

	
 
template <typename T>
 
class iterator_buffer<T*, T, fixed_buffer_traits> final
 
    : public fixed_buffer_traits,
 
      public buffer<T> {
 
 private:
 
  T* out_;
 
  enum { buffer_size = 256 };
 
  T data_[buffer_size];
 

	
 
 protected:
 
  FMT_CONSTEXPR20 void grow(size_t) override {
 
    if (this->size() == this->capacity()) flush();
 
  }
 

	
 
  void flush() {
 
    size_t n = this->limit(this->size());
 
    if (this->data() == out_) {
 
      out_ += n;
 
      this->set(data_, buffer_size);
 
    }
 
    this->clear();
 
  }
 

	
 
 public:
 
  explicit iterator_buffer(T* out, size_t n = buffer_size)
 
      : fixed_buffer_traits(n), buffer<T>(out, 0, n), out_(out) {}
 
  iterator_buffer(iterator_buffer&& other)
 
      : fixed_buffer_traits(other),
 
        buffer<T>(std::move(other)),
 
        out_(other.out_) {
 
    if (this->data() != out_) {
 
      this->set(data_, buffer_size);
 
      this->clear();
 
    }
 
  }
 
  ~iterator_buffer() { flush(); }
 

	
 
  auto out() -> T* {
 
    flush();
 
    return out_;
 
  }
 
  auto count() const -> size_t {
 
    return fixed_buffer_traits::count() + this->size();
 
  }
 
};
 

	
 
template <typename T> class iterator_buffer<T*, T> final : public buffer<T> {
 
 protected:
 
  FMT_CONSTEXPR20 void grow(size_t) override {}
 

	
 
 public:
 
  explicit iterator_buffer(T* out, size_t = 0) : buffer<T>(out, 0, ~size_t()) {}
 

	
 
  auto out() -> T* { return &*this->end(); }
 
};
 

	
 
// A buffer that writes to a container with the contiguous storage.
 
template <typename Container>
 
class iterator_buffer<std::back_insert_iterator<Container>,
 
                      enable_if_t<is_contiguous<Container>::value,
 
                                  typename Container::value_type>>
 
    final : public buffer<typename Container::value_type> {
 
 private:
 
  Container& container_;
 

	
 
 protected:
 
  FMT_CONSTEXPR20 void grow(size_t capacity) override {
 
    container_.resize(capacity);
 
    this->set(&container_[0], capacity);
 
  }
 

	
 
 public:
 
  explicit iterator_buffer(Container& c)
 
      : buffer<typename Container::value_type>(c.size()), container_(c) {}
 
  explicit iterator_buffer(std::back_insert_iterator<Container> out, size_t = 0)
 
      : iterator_buffer(get_container(out)) {}
 

	
 
  auto out() -> std::back_insert_iterator<Container> {
 
    return std::back_inserter(container_);
 
  }
 
};
 

	
 
// A buffer that counts the number of code units written discarding the output.
 
template <typename T = char> class counting_buffer final : public buffer<T> {
 
 private:
 
  enum { buffer_size = 256 };
 
  T data_[buffer_size];
 
  size_t count_ = 0;
 

	
 
 protected:
 
  FMT_CONSTEXPR20 void grow(size_t) override {
 
    if (this->size() != buffer_size) return;
 
    count_ += this->size();
 
    this->clear();
 
  }
 

	
 
 public:
 
  counting_buffer() : buffer<T>(data_, 0, buffer_size) {}
 

	
 
  auto count() -> size_t { return count_ + this->size(); }
 
};
 

	
 
template <typename T>
 
using buffer_appender = conditional_t<std::is_same<T, char>::value, appender,
 
                                      std::back_insert_iterator<buffer<T>>>;
 

	
 
// Maps an output iterator to a buffer.
 
template <typename T, typename OutputIt>
 
auto get_buffer(OutputIt out) -> iterator_buffer<OutputIt, T> {
 
  return iterator_buffer<OutputIt, T>(out);
 
}
 
template <typename T, typename Buf,
 
          FMT_ENABLE_IF(std::is_base_of<buffer<char>, Buf>::value)>
 
auto get_buffer(std::back_insert_iterator<Buf> out) -> buffer<char>& {
 
  return get_container(out);
 
}
 

	
 
template <typename Buf, typename OutputIt>
 
FMT_INLINE auto get_iterator(Buf& buf, OutputIt) -> decltype(buf.out()) {
 
  return buf.out();
 
}
 
template <typename T, typename OutputIt>
 
auto get_iterator(buffer<T>&, OutputIt out) -> OutputIt {
 
  return out;
 
}
 

	
 
struct view {};
 

	
 
template <typename Char, typename T> struct named_arg : view {
 
  const Char* name;
 
  const T& value;
 
  named_arg(const Char* n, const T& v) : name(n), value(v) {}
 
};
 

	
 
template <typename Char> struct named_arg_info {
 
  const Char* name;
 
  int id;
 
};
 

	
 
template <typename T, typename Char, size_t NUM_ARGS, size_t NUM_NAMED_ARGS>
 
struct arg_data {
 
  // args_[0].named_args points to named_args_ to avoid bloating format_args.
 
  // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
 
  T args_[1 + (NUM_ARGS != 0 ? NUM_ARGS : +1)];
 
  named_arg_info<Char> named_args_[NUM_NAMED_ARGS];
 

	
 
  template <typename... U>
 
  arg_data(const U&... init) : args_{T(named_args_, NUM_NAMED_ARGS), init...} {}
 
  arg_data(const arg_data& other) = delete;
 
  auto args() const -> const T* { return args_ + 1; }
 
  auto named_args() -> named_arg_info<Char>* { return named_args_; }
 
};
 

	
 
template <typename T, typename Char, size_t NUM_ARGS>
 
struct arg_data<T, Char, NUM_ARGS, 0> {
 
  // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
 
  T args_[NUM_ARGS != 0 ? NUM_ARGS : +1];
 

	
 
  template <typename... U>
 
  FMT_CONSTEXPR FMT_INLINE arg_data(const U&... init) : args_{init...} {}
 
  FMT_CONSTEXPR FMT_INLINE auto args() const -> const T* { return args_; }
 
  FMT_CONSTEXPR FMT_INLINE auto named_args() -> std::nullptr_t {
 
    return nullptr;
 
  }
 
};
 

	
 
template <typename Char>
 
inline void init_named_args(named_arg_info<Char>*, int, int) {}
 

	
 
template <typename T> struct is_named_arg : std::false_type {};
 
template <typename T> struct is_statically_named_arg : std::false_type {};
 

	
 
template <typename T, typename Char>
 
struct is_named_arg<named_arg<Char, T>> : std::true_type {};
 

	
 
template <typename Char, typename T, typename... Tail,
 
          FMT_ENABLE_IF(!is_named_arg<T>::value)>
 
void init_named_args(named_arg_info<Char>* named_args, int arg_count,
 
                     int named_arg_count, const T&, const Tail&... args) {
 
  init_named_args(named_args, arg_count + 1, named_arg_count, args...);
 
}
 

	
 
template <typename Char, typename T, typename... Tail,
 
          FMT_ENABLE_IF(is_named_arg<T>::value)>
 
void init_named_args(named_arg_info<Char>* named_args, int arg_count,
 
                     int named_arg_count, const T& arg, const Tail&... args) {
 
  named_args[named_arg_count++] = {arg.name, arg_count};
 
  init_named_args(named_args, arg_count + 1, named_arg_count, args...);
 
}
 

	
 
template <typename... Args>
 
FMT_CONSTEXPR FMT_INLINE void init_named_args(std::nullptr_t, int, int,
 
                                              const Args&...) {}
 

	
 
template <bool B = false> constexpr auto count() -> size_t { return B ? 1 : 0; }
 
template <bool B1, bool B2, bool... Tail> constexpr auto count() -> size_t {
 
  return (B1 ? 1 : 0) + count<B2, Tail...>();
 
}
 

	
 
template <typename... Args> constexpr auto count_named_args() -> size_t {
 
  return count<is_named_arg<Args>::value...>();
 
}
 

	
 
template <typename... Args>
 
constexpr auto count_statically_named_args() -> size_t {
 
  return count<is_statically_named_arg<Args>::value...>();
 
}
 

	
 
struct unformattable {};
 
struct unformattable_char : unformattable {};
 
struct unformattable_pointer : unformattable {};
 

	
 
template <typename Char> struct string_value {
 
  const Char* data;
 
  size_t size;
 
@@ -1040,8 +1222,8 @@ template <typename Char> struct named_ar
 

	
 
template <typename Context> struct custom_value {
 
  using parse_context = typename Context::parse_context_type;
 
  const void* value;
 
  void (*format)(const void* arg, parse_context& parse_ctx, Context& ctx);
 
  void* value;
 
  void (*format)(void* arg, parse_context& parse_ctx, Context& ctx);
 
};
 

	
 
// A formatting argument value.
 
@@ -1050,12 +1232,13 @@ template <typename Context> class value 
 
  using char_type = typename Context::char_type;
 

	
 
  union {
 
    monostate no_value;
 
    int int_value;
 
    unsigned uint_value;
 
    long long long_long_value;
 
    unsigned long long ulong_long_value;
 
    int128_t int128_value;
 
    uint128_t uint128_value;
 
    int128_opt int128_value;
 
    uint128_opt uint128_value;
 
    bool bool_value;
 
    char_type char_value;
 
    float float_value;
 
@@ -1067,19 +1250,23 @@ template <typename Context> class value 
 
    named_arg_value<char_type> named_args;
 
  };
 

	
 
  constexpr FMT_INLINE value(int val = 0) : int_value(val) {}
 
  constexpr FMT_INLINE value() : no_value() {}
 
  constexpr FMT_INLINE value(int val) : int_value(val) {}
 
  constexpr FMT_INLINE value(unsigned val) : uint_value(val) {}
 
  FMT_INLINE value(long long val) : long_long_value(val) {}
 
  FMT_INLINE value(unsigned long long val) : ulong_long_value(val) {}
 
  FMT_INLINE value(int128_t val) : int128_value(val) {}
 
  FMT_INLINE value(uint128_t val) : uint128_value(val) {}
 
  FMT_INLINE value(float val) : float_value(val) {}
 
  FMT_INLINE value(double val) : double_value(val) {}
 
  constexpr FMT_INLINE value(long long val) : long_long_value(val) {}
 
  constexpr FMT_INLINE value(unsigned long long val) : ulong_long_value(val) {}
 
  FMT_INLINE value(int128_opt val) : int128_value(val) {}
 
  FMT_INLINE value(uint128_opt val) : uint128_value(val) {}
 
  constexpr FMT_INLINE value(float val) : float_value(val) {}
 
  constexpr FMT_INLINE value(double val) : double_value(val) {}
 
  FMT_INLINE value(long double val) : long_double_value(val) {}
 
  FMT_INLINE value(bool val) : bool_value(val) {}
 
  FMT_INLINE value(char_type val) : char_value(val) {}
 
  FMT_INLINE value(const char_type* val) { string.data = val; }
 
  FMT_INLINE value(basic_string_view<char_type> val) {
 
  constexpr FMT_INLINE value(bool val) : bool_value(val) {}
 
  constexpr FMT_INLINE value(char_type val) : char_value(val) {}
 
  FMT_CONSTEXPR FMT_INLINE value(const char_type* val) {
 
    string.data = val;
 
    if (is_constant_evaluated()) string.size = {};
 
  }
 
  FMT_CONSTEXPR FMT_INLINE value(basic_string_view<char_type> val) {
 
    string.data = val.data();
 
    string.size = val.size();
 
  }
 
@@ -1087,31 +1274,35 @@ template <typename Context> class value 
 
  FMT_INLINE value(const named_arg_info<char_type>* args, size_t size)
 
      : named_args{args, size} {}
 

	
 
  template <typename T> FMT_INLINE value(const T& val) {
 
    custom.value = &val;
 
  template <typename T> FMT_CONSTEXPR FMT_INLINE value(T& val) {
 
    using value_type = remove_cvref_t<T>;
 
    custom.value = const_cast<value_type*>(&val);
 
    // Get the formatter type through the context to allow different contexts
 
    // have different extension points, e.g. `formatter<T>` for `format` and
 
    // `printf_formatter<T>` for `printf`.
 
    custom.format = format_custom_arg<
 
        T, conditional_t<has_formatter<T, Context>::value,
 
                         typename Context::template formatter_type<T>,
 
                         fallback_formatter<T, char_type>>>;
 
        value_type, typename Context::template formatter_type<value_type>>;
 
  }
 
  value(unformattable);
 
  value(unformattable_char);
 
  value(unformattable_pointer);
 

	
 
 private:
 
  // Formats an argument of a custom type, such as a user-defined class.
 
  template <typename T, typename Formatter>
 
  static void format_custom_arg(const void* arg,
 
  static void format_custom_arg(void* arg,
 
                                typename Context::parse_context_type& parse_ctx,
 
                                Context& ctx) {
 
    Formatter f;
 
    auto f = Formatter();
 
    parse_ctx.advance_to(f.parse(parse_ctx));
 
    ctx.advance_to(f.format(*static_cast<const T*>(arg), ctx));
 
    using qualified_type =
 
        conditional_t<has_const_formatter<T, Context>(), const T, T>;
 
    ctx.advance_to(f.format(*static_cast<qualified_type*>(arg), ctx));
 
  }
 
};
 

	
 
template <typename Context, typename T>
 
FMT_CONSTEXPR basic_format_arg<Context> make_arg(const T& value);
 
FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg<Context>;
 

	
 
// To minimize the number of types we need to deal with, long is translated
 
// either to int or to long long depending on its size.
 
@@ -1119,117 +1310,164 @@ enum { long_short = sizeof(long) == size
 
using long_type = conditional_t<long_short, int, long long>;
 
using ulong_type = conditional_t<long_short, unsigned, unsigned long long>;
 

	
 
struct unformattable {};
 
template <typename T> struct format_as_result {
 
  template <typename U,
 
            FMT_ENABLE_IF(std::is_enum<U>::value || std::is_class<U>::value)>
 
  static auto map(U*) -> decltype(format_as(std::declval<U>()));
 
  static auto map(...) -> void;
 

	
 
  using type = decltype(map(static_cast<T*>(nullptr)));
 
};
 
template <typename T> using format_as_t = typename format_as_result<T>::type;
 

	
 
template <typename T>
 
struct has_format_as
 
    : bool_constant<!std::is_same<format_as_t<T>, void>::value> {};
 

	
 
// Maps formatting arguments to core types.
 
// arg_mapper reports errors by returning unformattable instead of using
 
// static_assert because it's used in the is_formattable trait.
 
template <typename Context> struct arg_mapper {
 
  using char_type = typename Context::char_type;
 

	
 
  FMT_CONSTEXPR int map(signed char val) { return val; }
 
  FMT_CONSTEXPR unsigned map(unsigned char val) { return val; }
 
  FMT_CONSTEXPR int map(short val) { return val; }
 
  FMT_CONSTEXPR unsigned map(unsigned short val) { return val; }
 
  FMT_CONSTEXPR int map(int val) { return val; }
 
  FMT_CONSTEXPR unsigned map(unsigned val) { return val; }
 
  FMT_CONSTEXPR long_type map(long val) { return val; }
 
  FMT_CONSTEXPR ulong_type map(unsigned long val) { return val; }
 
  FMT_CONSTEXPR long long map(long long val) { return val; }
 
  FMT_CONSTEXPR unsigned long long map(unsigned long long val) { return val; }
 
  FMT_CONSTEXPR int128_t map(int128_t val) { return val; }
 
  FMT_CONSTEXPR uint128_t map(uint128_t val) { return val; }
 
  FMT_CONSTEXPR bool map(bool val) { return val; }
 

	
 
  template <typename T, FMT_ENABLE_IF(is_char<T>::value)>
 
  FMT_CONSTEXPR char_type map(T val) {
 
    static_assert(
 
        std::is_same<T, char>::value || std::is_same<T, char_type>::value,
 
        "mixing character types is disallowed");
 
  FMT_CONSTEXPR FMT_INLINE auto map(signed char val) -> int { return val; }
 
  FMT_CONSTEXPR FMT_INLINE auto map(unsigned char val) -> unsigned {
 
    return val;
 
  }
 
  FMT_CONSTEXPR FMT_INLINE auto map(short val) -> int { return val; }
 
  FMT_CONSTEXPR FMT_INLINE auto map(unsigned short val) -> unsigned {
 
    return val;
 
  }
 
  FMT_CONSTEXPR FMT_INLINE auto map(int val) -> int { return val; }
 
  FMT_CONSTEXPR FMT_INLINE auto map(unsigned val) -> unsigned { return val; }
 
  FMT_CONSTEXPR FMT_INLINE auto map(long val) -> long_type { return val; }
 
  FMT_CONSTEXPR FMT_INLINE auto map(unsigned long val) -> ulong_type {
 
    return val;
 
  }
 
  FMT_CONSTEXPR FMT_INLINE auto map(long long val) -> long long { return val; }
 
  FMT_CONSTEXPR FMT_INLINE auto map(unsigned long long val)
 
      -> unsigned long long {
 
    return val;
 
  }
 
  FMT_CONSTEXPR FMT_INLINE auto map(int128_opt val) -> int128_opt {
 
    return val;
 
  }
 
  FMT_CONSTEXPR FMT_INLINE auto map(uint128_opt val) -> uint128_opt {
 
    return val;
 
  }
 

	
 
  FMT_CONSTEXPR float map(float val) { return val; }
 
  FMT_CONSTEXPR double map(double val) { return val; }
 
  FMT_CONSTEXPR long double map(long double val) { return val; }
 

	
 
  FMT_CONSTEXPR const char_type* map(char_type* val) { return val; }
 
  FMT_CONSTEXPR const char_type* map(const char_type* val) { return val; }
 
  template <typename T, FMT_ENABLE_IF(is_string<T>::value)>
 
  FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) {
 
    static_assert(std::is_same<char_type, char_t<T>>::value,
 
                  "mixing character types is disallowed");
 
  FMT_CONSTEXPR FMT_INLINE auto map(bool val) -> bool { return val; }
 

	
 
  template <typename T, FMT_ENABLE_IF(std::is_same<T, char>::value ||
 
                                      std::is_same<T, char_type>::value)>
 
  FMT_CONSTEXPR FMT_INLINE auto map(T val) -> char_type {
 
    return val;
 
  }
 
  template <typename T, enable_if_t<(std::is_same<T, wchar_t>::value ||
 
#ifdef __cpp_char8_t
 
                                     std::is_same<T, char8_t>::value ||
 
#endif
 
                                     std::is_same<T, char16_t>::value ||
 
                                     std::is_same<T, char32_t>::value) &&
 
                                        !std::is_same<T, char_type>::value,
 
                                    int> = 0>
 
  FMT_CONSTEXPR FMT_INLINE auto map(T) -> unformattable_char {
 
    return {};
 
  }
 

	
 
  FMT_CONSTEXPR FMT_INLINE auto map(float val) -> float { return val; }
 
  FMT_CONSTEXPR FMT_INLINE auto map(double val) -> double { return val; }
 
  FMT_CONSTEXPR FMT_INLINE auto map(long double val) -> long double {
 
    return val;
 
  }
 

	
 
  FMT_CONSTEXPR FMT_INLINE auto map(char_type* val) -> const char_type* {
 
    return val;
 
  }
 
  FMT_CONSTEXPR FMT_INLINE auto map(const char_type* val) -> const char_type* {
 
    return val;
 
  }
 
  template <typename T,
 
            FMT_ENABLE_IF(is_string<T>::value && !std::is_pointer<T>::value &&
 
                          std::is_same<char_type, char_t<T>>::value)>
 
  FMT_CONSTEXPR FMT_INLINE auto map(const T& val)
 
      -> basic_string_view<char_type> {
 
    return to_string_view(val);
 
  }
 
  template <typename T,
 
            FMT_ENABLE_IF(
 
                std::is_constructible<basic_string_view<char_type>, T>::value &&
 
                !is_string<T>::value && !has_formatter<T, Context>::value &&
 
                !has_fallback_formatter<T, Context>::value)>
 
  FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) {
 
    return basic_string_view<char_type>(val);
 
            FMT_ENABLE_IF(is_string<T>::value && !std::is_pointer<T>::value &&
 
                          !std::is_same<char_type, char_t<T>>::value)>
 
  FMT_CONSTEXPR FMT_INLINE auto map(const T&) -> unformattable_char {
 
    return {};
 
  }
 

	
 
  FMT_CONSTEXPR FMT_INLINE auto map(void* val) -> const void* { return val; }
 
  FMT_CONSTEXPR FMT_INLINE auto map(const void* val) -> const void* {
 
    return val;
 
  }
 
  FMT_CONSTEXPR FMT_INLINE auto map(std::nullptr_t val) -> const void* {
 
    return val;
 
  }
 

	
 
  // Use SFINAE instead of a const T* parameter to avoid a conflict with the
 
  // array overload.
 
  template <
 
      typename T,
 
      FMT_ENABLE_IF(
 
          std::is_constructible<std_string_view<char_type>, T>::value &&
 
          !std::is_constructible<basic_string_view<char_type>, T>::value &&
 
          !is_string<T>::value && !has_formatter<T, Context>::value &&
 
          !has_fallback_formatter<T, Context>::value)>
 
  FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) {
 
    return std_string_view<char_type>(val);
 
  }
 
  FMT_CONSTEXPR const char* map(const signed char* val) {
 
    static_assert(std::is_same<char_type, char>::value, "invalid string type");
 
    return reinterpret_cast<const char*>(val);
 
          std::is_pointer<T>::value || std::is_member_pointer<T>::value ||
 
          std::is_function<typename std::remove_pointer<T>::type>::value ||
 
          (std::is_convertible<const T&, const void*>::value &&
 
           !std::is_convertible<const T&, const char_type*>::value &&
 
           !has_formatter<T, Context>::value))>
 
  FMT_CONSTEXPR auto map(const T&) -> unformattable_pointer {
 
    return {};
 
  }
 
  FMT_CONSTEXPR const char* map(const unsigned char* val) {
 
    static_assert(std::is_same<char_type, char>::value, "invalid string type");
 
    return reinterpret_cast<const char*>(val);
 
  }
 
  FMT_CONSTEXPR const char* map(signed char* val) {
 
    const auto* const_val = val;
 
    return map(const_val);
 
  }
 
  FMT_CONSTEXPR const char* map(unsigned char* val) {
 
    const auto* const_val = val;
 
    return map(const_val);
 

	
 
  template <typename T, std::size_t N,
 
            FMT_ENABLE_IF(!std::is_same<T, wchar_t>::value)>
 
  FMT_CONSTEXPR FMT_INLINE auto map(const T (&values)[N]) -> const T (&)[N] {
 
    return values;
 
  }
 

	
 
  FMT_CONSTEXPR const void* map(void* val) { return val; }
 
  FMT_CONSTEXPR const void* map(const void* val) { return val; }
 
  FMT_CONSTEXPR const void* map(std::nullptr_t val) { return val; }
 
  template <typename T> FMT_CONSTEXPR int map(const T*) {
 
    // Formatting of arbitrary pointers is disallowed. If you want to output
 
    // a pointer cast it to "void *" or "const void *". In particular, this
 
    // forbids formatting of "[const] volatile char *" which is printed as bool
 
    // by iostreams.
 
    static_assert(!sizeof(T), "formatting of non-void pointers is disallowed");
 
    return 0;
 
  // Only map owning types because mapping views can be unsafe.
 
  template <typename T, typename U = format_as_t<T>,
 
            FMT_ENABLE_IF(std::is_arithmetic<U>::value)>
 
  FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> decltype(this->map(U())) {
 
    return map(format_as(val));
 
  }
 

	
 
  template <typename T,
 
            FMT_ENABLE_IF(std::is_enum<T>::value &&
 
                          !has_formatter<T, Context>::value &&
 
                          !has_fallback_formatter<T, Context>::value)>
 
  FMT_CONSTEXPR auto map(const T& val)
 
      -> decltype(std::declval<arg_mapper>().map(
 
          static_cast<typename std::underlying_type<T>::type>(val))) {
 
    return map(static_cast<typename std::underlying_type<T>::type>(val));
 
  }
 
  template <typename T,
 
            FMT_ENABLE_IF(!is_string<T>::value && !is_char<T>::value &&
 
                          (has_formatter<T, Context>::value ||
 
                           has_fallback_formatter<T, Context>::value))>
 
  FMT_CONSTEXPR const T& map(const T& val) {
 
  template <typename T, typename U = remove_cvref_t<T>>
 
  struct formattable
 
      : bool_constant<has_const_formatter<U, Context>() ||
 
                      (has_formatter<U, Context>::value &&
 
                       !std::is_const<remove_reference_t<T>>::value)> {};
 

	
 
  template <typename T, FMT_ENABLE_IF(formattable<T>::value)>
 
  FMT_CONSTEXPR FMT_INLINE auto do_map(T&& val) -> T& {
 
    return val;
 
  }
 

	
 
  template <typename T>
 
  FMT_CONSTEXPR auto map(const named_arg<char_type, T>& val)
 
      -> decltype(std::declval<arg_mapper>().map(val.value)) {
 
    return map(val.value);
 
  template <typename T, FMT_ENABLE_IF(!formattable<T>::value)>
 
  FMT_CONSTEXPR FMT_INLINE auto do_map(T&&) -> unformattable {
 
    return {};
 
  }
 

	
 
  unformattable map(...) { return {}; }
 
  template <typename T, typename U = remove_cvref_t<T>,
 
            FMT_ENABLE_IF((std::is_class<U>::value || std::is_enum<U>::value ||
 
                           std::is_union<U>::value) &&
 
                          !is_string<U>::value && !is_char<U>::value &&
 
                          !is_named_arg<U>::value &&
 
                          !std::is_arithmetic<format_as_t<U>>::value)>
 
  FMT_CONSTEXPR FMT_INLINE auto map(T&& val)
 
      -> decltype(this->do_map(std::forward<T>(val))) {
 
    return do_map(std::forward<T>(val));
 
  }
 

	
 
  template <typename T, FMT_ENABLE_IF(is_named_arg<T>::value)>
 
  FMT_CONSTEXPR FMT_INLINE auto map(const T& named_arg)
 
      -> decltype(this->map(named_arg.value)) {
 
    return map(named_arg.value);
 
  }
 

	
 
  auto map(...) -> unformattable { return {}; }
 
};
 

	
 
// A type constant after applying arg_mapper<Context>.
 
@@ -1245,6 +1483,20 @@ enum : unsigned long long { is_unpacked_
 
enum : unsigned long long { has_named_args_bit = 1ULL << 62 };
 
}  // namespace detail
 

	
 
// An output iterator that appends to a buffer.
 
// It is used to reduce symbol sizes for the common case.
 
class appender : public std::back_insert_iterator<detail::buffer<char>> {
 
  using base = std::back_insert_iterator<detail::buffer<char>>;
 

	
 
 public:
 
  using std::back_insert_iterator<detail::buffer<char>>::back_insert_iterator;
 
  appender(base it) noexcept : base(it) {}
 
  FMT_UNCHECKED_ITERATOR(appender);
 

	
 
  auto operator++() noexcept -> appender& { return *this; }
 
  auto operator++(int) noexcept -> appender { return *this; }
 
};
 

	
 
// A formatting argument. It is a trivially copyable/constructible type to
 
// allow storage in basic_memory_buffer.
 
template <typename Context> class basic_format_arg {
 
@@ -1253,8 +1505,8 @@ template <typename Context> class basic_
 
  detail::type type_;
 

	
 
  template <typename ContextType, typename T>
 
  friend FMT_CONSTEXPR basic_format_arg<ContextType> detail::make_arg(
 
      const T& value);
 
  friend FMT_CONSTEXPR auto detail::make_arg(T&& value)
 
      -> basic_format_arg<ContextType>;
 

	
 
  template <typename Visitor, typename Ctx>
 
  friend FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis,
 
@@ -1288,14 +1540,16 @@ template <typename Context> class basic_
 

	
 
  constexpr basic_format_arg() : type_(detail::type::none_type) {}
 

	
 
  constexpr explicit operator bool() const FMT_NOEXCEPT {
 
  constexpr explicit operator bool() const noexcept {
 
    return type_ != detail::type::none_type;
 
  }
 

	
 
  detail::type type() const { return type_; }
 

	
 
  bool is_integral() const { return detail::is_integral_type(type_); }
 
  bool is_arithmetic() const { return detail::is_arithmetic_type(type_); }
 
  auto type() const -> detail::type { return type_; }
 

	
 
  auto is_integral() const -> bool { return detail::is_integral_type(type_); }
 
  auto is_arithmetic() const -> bool {
 
    return detail::is_arithmetic_type(type_);
 
  }
 
};
 

	
 
/**
 
@@ -1305,10 +1559,10 @@ template <typename Context> class basic_
 
  ``vis(value)`` will be called with the value of type ``double``.
 
  \endrst
 
 */
 
FMT_MODULE_EXPORT
 
template <typename Visitor, typename Context>
 
FMT_CONSTEXPR_DECL FMT_INLINE auto visit_format_arg(
 
FMT_CONSTEXPR FMT_INLINE auto visit_format_arg(
 
    Visitor&& vis, const basic_format_arg<Context>& arg) -> decltype(vis(0)) {
 
  using char_type = typename Context::char_type;
 
  switch (arg.type_) {
 
  case detail::type::none_type:
 
    break;
 
@@ -1320,16 +1574,10 @@ FMT_CONSTEXPR_DECL FMT_INLINE auto visit
 
    return vis(arg.value_.long_long_value);
 
  case detail::type::ulong_long_type:
 
    return vis(arg.value_.ulong_long_value);
 
#if FMT_USE_INT128
 
  case detail::type::int128_type:
 
    return vis(arg.value_.int128_value);
 
    return vis(detail::convert_for_visit(arg.value_.int128_value));
 
  case detail::type::uint128_type:
 
    return vis(arg.value_.uint128_value);
 
#else
 
  case detail::type::int128_type:
 
  case detail::type::uint128_type:
 
    break;
 
#endif
 
    return vis(detail::convert_for_visit(arg.value_.uint128_value));
 
  case detail::type::bool_type:
 
    return vis(arg.value_.bool_value);
 
  case detail::type::char_type:
 
@@ -1343,8 +1591,8 @@ FMT_CONSTEXPR_DECL FMT_INLINE auto visit
 
  case detail::type::cstring_type:
 
    return vis(arg.value_.string.data);
 
  case detail::type::string_type:
 
    return vis(basic_string_view<char_type>(arg.value_.string.data,
 
                                            arg.value_.string.size));
 
    using sv = basic_string_view<typename Context::char_type>;
 
    return vis(sv(arg.value_.string.data, arg.value_.string.size));
 
  case detail::type::pointer_type:
 
    return vis(arg.value_.pointer);
 
  case detail::type::custom_type:
 
@@ -1353,14 +1601,26 @@ FMT_CONSTEXPR_DECL FMT_INLINE auto visit
 
  return vis(monostate());
 
}
 

	
 
template <typename T> struct formattable : std::false_type {};
 

	
 
namespace detail {
 

	
 
template <typename Char, typename InputIt>
 
auto copy_str(InputIt begin, InputIt end, appender out) -> appender {
 
  get_container(out).append(begin, end);
 
  return out;
 
}
 

	
 
template <typename Char, typename R, typename OutputIt>
 
FMT_CONSTEXPR auto copy_str(R&& rng, OutputIt out) -> OutputIt {
 
  return detail::copy_str<Char>(rng.begin(), rng.end(), out);
 
}
 

	
 
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 500
 
// A workaround for gcc 4.8 to make void_t work in a SFINAE context.
 
template <typename... Ts> struct void_t_impl { using type = void; };
 
template <typename... Ts>
 
using void_t = typename detail::void_t_impl<Ts...>::type;
 
template <typename...> struct void_t_impl { using type = void; };
 
template <typename... T> using void_t = typename void_t_impl<T...>::type;
 
#else
 
template <typename...> using void_t = void;
 
#endif
 

	
 
template <typename It, typename T, typename Enable = void>
 
struct is_output_iterator : std::false_type {};
 
@@ -1372,125 +1632,95 @@ struct is_output_iterator<
 
           decltype(*std::declval<It>() = std::declval<T>())>>
 
    : std::true_type {};
 

	
 
template <typename OutputIt>
 
struct is_back_insert_iterator : std::false_type {};
 
template <typename It> struct is_back_insert_iterator : std::false_type {};
 
template <typename Container>
 
struct is_back_insert_iterator<std::back_insert_iterator<Container>>
 
    : std::true_type {};
 

	
 
template <typename OutputIt>
 
template <typename It>
 
struct is_contiguous_back_insert_iterator : std::false_type {};
 
template <typename Container>
 
struct is_contiguous_back_insert_iterator<std::back_insert_iterator<Container>>
 
    : is_contiguous<Container> {};
 
template <typename Char>
 
struct is_contiguous_back_insert_iterator<buffer_appender<Char>>
 
    : std::true_type {};
 

	
 
// A type-erased reference to an std::locale to avoid heavy <locale> include.
 
template <>
 
struct is_contiguous_back_insert_iterator<appender> : std::true_type {};
 

	
 
// A type-erased reference to an std::locale to avoid a heavy <locale> include.
 
class locale_ref {
 
 private:
 
  const void* locale_;  // A type-erased pointer to std::locale.
 

	
 
 public:
 
  locale_ref() : locale_(nullptr) {}
 
  constexpr FMT_INLINE locale_ref() : locale_(nullptr) {}
 
  template <typename Locale> explicit locale_ref(const Locale& loc);
 

	
 
  explicit operator bool() const FMT_NOEXCEPT { return locale_ != nullptr; }
 

	
 
  template <typename Locale> Locale get() const;
 
  explicit operator bool() const noexcept { return locale_ != nullptr; }
 

	
 
  template <typename Locale> auto get() const -> Locale;
 
};
 

	
 
template <typename> constexpr unsigned long long encode_types() { return 0; }
 
template <typename> constexpr auto encode_types() -> unsigned long long {
 
  return 0;
 
}
 

	
 
template <typename Context, typename Arg, typename... Args>
 
constexpr unsigned long long encode_types() {
 
constexpr auto encode_types() -> unsigned long long {
 
  return static_cast<unsigned>(mapped_type_constant<Arg, Context>::value) |
 
         (encode_types<Context, Args...>() << packed_arg_bits);
 
}
 

	
 
template <typename Context, typename T>
 
FMT_CONSTEXPR basic_format_arg<Context> make_arg(const T& value) {
 
  basic_format_arg<Context> arg;
 
  arg.type_ = mapped_type_constant<T, Context>::value;
 
  arg.value_ = arg_mapper<Context>().map(value);
 
  return arg;
 
}
 

	
 
template <typename T> int check(unformattable) {
 
FMT_CONSTEXPR FMT_INLINE auto make_value(T&& val) -> value<Context> {
 
  auto&& arg = arg_mapper<Context>().map(FMT_FORWARD(val));
 
  using arg_type = remove_cvref_t<decltype(arg)>;
 

	
 
  constexpr bool formattable_char =
 
      !std::is_same<arg_type, unformattable_char>::value;
 
  static_assert(formattable_char, "Mixing character types is disallowed.");
 

	
 
  // Formatting of arbitrary pointers is disallowed. If you want to format a
 
  // pointer cast it to `void*` or `const void*`. In particular, this forbids
 
  // formatting of `[const] volatile char*` printed as bool by iostreams.
 
  constexpr bool formattable_pointer =
 
      !std::is_same<arg_type, unformattable_pointer>::value;
 
  static_assert(formattable_pointer,
 
                "Formatting of non-void pointers is disallowed.");
 

	
 
  constexpr bool formattable = !std::is_same<arg_type, unformattable>::value;
 
  static_assert(
 
      formattable<T>(),
 
      formattable,
 
      "Cannot format an argument. To make type T formattable provide a "
 
      "formatter<T> specialization: https://fmt.dev/latest/api.html#udt");
 
  return 0;
 
}
 
template <typename T, typename U> inline const U& check(const U& val) {
 
  return val;
 
  return {arg};
 
}
 

	
 
// The type template parameter is there to avoid an ODR violation when using
 
// a fallback formatter in one translation unit and an implicit conversion in
 
// another (not recommended).
 
template <typename Context, typename T>
 
FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg<Context> {
 
  auto arg = basic_format_arg<Context>();
 
  arg.type_ = mapped_type_constant<T, Context>::value;
 
  arg.value_ = make_value<Context>(value);
 
  return arg;
 
}
 

	
 
// The DEPRECATED type template parameter is there to avoid an ODR violation
 
// when using a fallback formatter in one translation unit and an implicit
 
// conversion in another (not recommended).
 
template <bool IS_PACKED, typename Context, type, typename T,
 
          FMT_ENABLE_IF(IS_PACKED)>
 
inline value<Context> make_arg(const T& val) {
 
  return check<T>(arg_mapper<Context>().map(val));
 
FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value<Context> {
 
  return make_value<Context>(val);
 
}
 

	
 
template <bool IS_PACKED, typename Context, type, typename T,
 
          FMT_ENABLE_IF(!IS_PACKED)>
 
inline basic_format_arg<Context> make_arg(const T& value) {
 
FMT_CONSTEXPR inline auto make_arg(T&& value) -> basic_format_arg<Context> {
 
  return make_arg<Context>(value);
 
}
 

	
 
template <typename T> struct is_reference_wrapper : std::false_type {};
 
template <typename T>
 
struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
 

	
 
template <typename T> const T& unwrap(const T& v) { return v; }
 
template <typename T> const T& unwrap(const std::reference_wrapper<T>& v) {
 
  return static_cast<const T&>(v);
 
}
 

	
 
class dynamic_arg_list {
 
  // Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
 
  // templates it doesn't complain about inability to deduce single translation
 
  // unit for placing vtable. So storage_node_base is made a fake template.
 
  template <typename = void> struct node {
 
    virtual ~node() = default;
 
    std::unique_ptr<node<>> next;
 
  };
 

	
 
  template <typename T> struct typed_node : node<> {
 
    T value;
 

	
 
    template <typename Arg>
 
    FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {}
 

	
 
    template <typename Char>
 
    FMT_CONSTEXPR typed_node(const basic_string_view<Char>& arg)
 
        : value(arg.data(), arg.size()) {}
 
  };
 

	
 
  std::unique_ptr<node<>> head_;
 

	
 
 public:
 
  template <typename T, typename Arg> const T& push(const Arg& arg) {
 
    auto new_node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
 
    auto& value = new_node->value;
 
    new_node->next = std::move(head_);
 
    head_ = std::move(new_node);
 
    return value;
 
  }
 
};
 
}  // namespace detail
 
FMT_BEGIN_EXPORT
 

	
 
// Formatting context.
 
template <typename OutputIt, typename Char> class basic_format_context {
 
 public:
 
  /** The character type for the output. */
 
  using char_type = Char;
 

	
 
 private:
 
  OutputIt out_;
 
  basic_format_args<basic_format_context> args_;
 
@@ -1499,48 +1729,56 @@ template <typename OutputIt, typename Ch
 
 public:
 
  using iterator = OutputIt;
 
  using format_arg = basic_format_arg<basic_format_context>;
 
  using format_args = basic_format_args<basic_format_context>;
 
  using parse_context_type = basic_format_parse_context<Char>;
 
  template <typename T> using formatter_type = formatter<T, char_type>;
 

	
 
  template <typename T> using formatter_type = formatter<T, Char>;
 

	
 
  /** The character type for the output. */
 
  using char_type = Char;
 

	
 
  basic_format_context(basic_format_context&&) = default;
 
  basic_format_context(const basic_format_context&) = delete;
 
  void operator=(const basic_format_context&) = delete;
 
  /**
 
   Constructs a ``basic_format_context`` object. References to the arguments are
 
   stored in the object so make sure they have appropriate lifetimes.
 
    Constructs a ``basic_format_context`` object. References to the arguments
 
    are stored in the object so make sure they have appropriate lifetimes.
 
   */
 
  basic_format_context(OutputIt out,
 
                       basic_format_args<basic_format_context> ctx_args,
 
                       detail::locale_ref loc = detail::locale_ref())
 
  constexpr basic_format_context(OutputIt out, format_args ctx_args,
 
                                 detail::locale_ref loc = {})
 
      : out_(out), args_(ctx_args), loc_(loc) {}
 

	
 
  format_arg arg(int id) const { return args_.get(id); }
 
  format_arg arg(basic_string_view<char_type> name) { return args_.get(name); }
 
  int arg_id(basic_string_view<char_type> name) { return args_.get_id(name); }
 
  const basic_format_args<basic_format_context>& args() const { return args_; }
 

	
 
  detail::error_handler error_handler() { return {}; }
 
  constexpr auto arg(int id) const -> format_arg { return args_.get(id); }
 
  FMT_CONSTEXPR auto arg(basic_string_view<Char> name) -> format_arg {
 
    return args_.get(name);
 
  }
 
  FMT_CONSTEXPR auto arg_id(basic_string_view<Char> name) -> int {
 
    return args_.get_id(name);
 
  }
 
  auto args() const -> const format_args& { return args_; }
 

	
 
  FMT_CONSTEXPR auto error_handler() -> detail::error_handler { return {}; }
 
  void on_error(const char* message) { error_handler().on_error(message); }
 

	
 
  // Returns an iterator to the beginning of the output range.
 
  iterator out() { return out_; }
 
  FMT_CONSTEXPR auto out() -> iterator { return out_; }
 

	
 
  // Advances the begin iterator to ``it``.
 
  void advance_to(iterator it) {
 
    if (!detail::is_back_insert_iterator<iterator>()) out_ = it;
 
  }
 

	
 
  detail::locale_ref locale() { return loc_; }
 
  FMT_CONSTEXPR auto locale() -> detail::locale_ref { return loc_; }
 
};
 

	
 
template <typename Char>
 
using buffer_context =
 
    basic_format_context<detail::buffer_appender<Char>, Char>;
 
using format_context = buffer_context<char>;
 
using wformat_context = buffer_context<wchar_t>;
 

	
 
// Workaround an alias issue: https://stackoverflow.com/q/62767544/471164.
 
#define FMT_BUFFER_CONTEXT(Char) \
 
  basic_format_context<detail::buffer_appender<Char>, Char>
 

	
 
template <typename T, typename Char = char>
 
using is_formattable = bool_constant<!std::is_base_of<
 
    detail::unformattable, decltype(detail::arg_mapper<buffer_context<Char>>()
 
                                        .map(std::declval<T>()))>::value>;
 

	
 
/**
 
  \rst
 
@@ -1578,14 +1816,16 @@ class format_arg_store
 
           : 0);
 

	
 
 public:
 
  format_arg_store(const Args&... args)
 
  template <typename... T>
 
  FMT_CONSTEXPR FMT_INLINE format_arg_store(T&&... args)
 
      :
 
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
 
        basic_format_args<Context>(*this),
 
#endif
 
        data_{detail::make_arg<
 
            is_packed, Context,
 
            detail::mapped_type_constant<Args, Context>::value>(args)...} {
 
            detail::mapped_type_constant<remove_cvref_t<T>, Context>::value>(
 
            FMT_FORWARD(args))...} {
 
    detail::init_named_args(data_.named_args(), 0, 0, args...);
 
  }
 
};
 
@@ -1598,37 +1838,17 @@ class format_arg_store
 
  See `~fmt::arg` for lifetime considerations.
 
  \endrst
 
 */
 
template <typename Context = format_context, typename... Args>
 
inline format_arg_store<Context, Args...> make_format_args(
 
    const Args&... args) {
 
  return {args...};
 
template <typename Context = format_context, typename... T>
 
constexpr auto make_format_args(T&&... args)
 
    -> format_arg_store<Context, remove_cvref_t<T>...> {
 
  return {FMT_FORWARD(args)...};
 
}
 

	
 
/**
 
  \rst
 
  Constructs a `~fmt::format_arg_store` object that contains references
 
  to arguments and can be implicitly converted to `~fmt::format_args`.
 
  If ``format_str`` is a compile-time string then `make_args_checked` checks
 
  its validity at compile time.
 
  \endrst
 
 */
 
template <typename... Args, typename S, typename Char = char_t<S>>
 
inline auto make_args_checked(const S& format_str,
 
                              const remove_reference_t<Args>&... args)
 
    -> format_arg_store<buffer_context<Char>, remove_reference_t<Args>...> {
 
  static_assert(
 
      detail::count<(
 
              std::is_base_of<detail::view, remove_reference_t<Args>>::value &&
 
              std::is_reference<Args>::value)...>() == 0,
 
      "passing views as lvalues is disallowed");
 
  detail::check_format_string<Args...>(format_str);
 
  return {args...};
 
}
 

	
 
/**
 
  \rst
 
  Returns a named argument to be used in a formatting function. It should only
 
  be used in a call to a formatting function.
 
  Returns a named argument to be used in a formatting function.
 
  It should only be used in a call to a formatting function or
 
  `dynamic_format_arg_store::push_back`.
 

	
 
  **Example**::
 

	
 
@@ -1636,183 +1856,11 @@ inline auto make_args_checked(const S& f
 
  \endrst
 
 */
 
template <typename Char, typename T>
 
inline detail::named_arg<Char, T> arg(const Char* name, const T& arg) {
 
inline auto arg(const Char* name, const T& arg) -> detail::named_arg<Char, T> {
 
  static_assert(!detail::is_named_arg<T>(), "nested named arguments");
 
  return {name, arg};
 
}
 

	
 
/**
 
  \rst
 
  A dynamic version of `fmt::format_arg_store`.
 
  It's equipped with a storage to potentially temporary objects which lifetimes
 
  could be shorter than the format arguments object.
 

	
 
  It can be implicitly converted into `~fmt::basic_format_args` for passing
 
  into type-erased formatting functions such as `~fmt::vformat`.
 
  \endrst
 
 */
 
template <typename Context>
 
class dynamic_format_arg_store
 
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
 
    // Workaround a GCC template argument substitution bug.
 
    : public basic_format_args<Context>
 
#endif
 
{
 
 private:
 
  using char_type = typename Context::char_type;
 

	
 
  template <typename T> struct need_copy {
 
    static constexpr detail::type mapped_type =
 
        detail::mapped_type_constant<T, Context>::value;
 

	
 
    enum {
 
      value = !(detail::is_reference_wrapper<T>::value ||
 
                std::is_same<T, basic_string_view<char_type>>::value ||
 
                std::is_same<T, detail::std_string_view<char_type>>::value ||
 
                (mapped_type != detail::type::cstring_type &&
 
                 mapped_type != detail::type::string_type &&
 
                 mapped_type != detail::type::custom_type))
 
    };
 
  };
 

	
 
  template <typename T>
 
  using stored_type = conditional_t<detail::is_string<T>::value,
 
                                    std::basic_string<char_type>, T>;
 

	
 
  // Storage of basic_format_arg must be contiguous.
 
  std::vector<basic_format_arg<Context>> data_;
 
  std::vector<detail::named_arg_info<char_type>> named_info_;
 

	
 
  // Storage of arguments not fitting into basic_format_arg must grow
 
  // without relocation because items in data_ refer to it.
 
  detail::dynamic_arg_list dynamic_args_;
 

	
 
  friend class basic_format_args<Context>;
 

	
 
  unsigned long long get_types() const {
 
    return detail::is_unpacked_bit | data_.size() |
 
           (named_info_.empty()
 
                ? 0ULL
 
                : static_cast<unsigned long long>(detail::has_named_args_bit));
 
  }
 

	
 
  const basic_format_arg<Context>* data() const {
 
    return named_info_.empty() ? data_.data() : data_.data() + 1;
 
  }
 

	
 
  template <typename T> void emplace_arg(const T& arg) {
 
    data_.emplace_back(detail::make_arg<Context>(arg));
 
  }
 

	
 
  template <typename T>
 
  void emplace_arg(const detail::named_arg<char_type, T>& arg) {
 
    if (named_info_.empty()) {
 
      constexpr const detail::named_arg_info<char_type>* zero_ptr{nullptr};
 
      data_.insert(data_.begin(), {zero_ptr, 0});
 
    }
 
    data_.emplace_back(detail::make_arg<Context>(detail::unwrap(arg.value)));
 
    auto pop_one = [](std::vector<basic_format_arg<Context>>* data) {
 
      data->pop_back();
 
    };
 
    std::unique_ptr<std::vector<basic_format_arg<Context>>, decltype(pop_one)>
 
        guard{&data_, pop_one};
 
    named_info_.push_back({arg.name, static_cast<int>(data_.size() - 2u)});
 
    data_[0].value_.named_args = {named_info_.data(), named_info_.size()};
 
    guard.release();
 
  }
 

	
 
 public:
 
  /**
 
    \rst
 
    Adds an argument into the dynamic store for later passing to a formatting
 
    function.
 

	
 
    Note that custom types and string types (but not string views) are copied
 
    into the store dynamically allocating memory if necessary.
 

	
 
    **Example**::
 

	
 
      fmt::dynamic_format_arg_store<fmt::format_context> store;
 
      store.push_back(42);
 
      store.push_back("abc");
 
      store.push_back(1.5f);
 
      std::string result = fmt::vformat("{} and {} and {}", store);
 
    \endrst
 
  */
 
  template <typename T> void push_back(const T& arg) {
 
    if (detail::const_check(need_copy<T>::value))
 
      emplace_arg(dynamic_args_.push<stored_type<T>>(arg));
 
    else
 
      emplace_arg(detail::unwrap(arg));
 
  }
 

	
 
  /**
 
    \rst
 
    Adds a reference to the argument into the dynamic store for later passing to
 
    a formatting function. Supports named arguments wrapped in
 
    ``std::reference_wrapper`` via ``std::ref()``/``std::cref()``.
 

	
 
    **Example**::
 

	
 
      fmt::dynamic_format_arg_store<fmt::format_context> store;
 
      char str[] = "1234567890";
 
      store.push_back(std::cref(str));
 
      int a1_val{42};
 
      auto a1 = fmt::arg("a1_", a1_val);
 
      store.push_back(std::cref(a1));
 

	
 
      // Changing str affects the output but only for string and custom types.
 
      str[0] = 'X';
 

	
 
      std::string result = fmt::vformat("{} and {a1_}");
 
      assert(result == "X234567890 and 42");
 
    \endrst
 
  */
 
  template <typename T> void push_back(std::reference_wrapper<T> arg) {
 
    static_assert(
 
        detail::is_named_arg<typename std::remove_cv<T>::type>::value ||
 
            need_copy<T>::value,
 
        "objects of built-in types and string views are always copied");
 
    emplace_arg(arg.get());
 
  }
 

	
 
  /**
 
    Adds named argument into the dynamic store for later passing to a formatting
 
    function. ``std::reference_wrapper`` is supported to avoid copying of the
 
    argument.
 
  */
 
  template <typename T>
 
  void push_back(const detail::named_arg<char_type, T>& arg) {
 
    const char_type* arg_name =
 
        dynamic_args_.push<std::basic_string<char_type>>(arg.name).c_str();
 
    if (detail::const_check(need_copy<T>::value)) {
 
      emplace_arg(
 
          fmt::arg(arg_name, dynamic_args_.push<stored_type<T>>(arg.value)));
 
    } else {
 
      emplace_arg(fmt::arg(arg_name, arg.value));
 
    }
 
  }
 

	
 
  /** Erase all elements from the store */
 
  void clear() {
 
    data_.clear();
 
    named_info_.clear();
 
    dynamic_args_ = detail::dynamic_arg_list();
 
  }
 

	
 
  /**
 
    \rst
 
    Reserves space to store at least *new_cap* arguments including
 
    *new_cap_named* named arguments.
 
    \endrst
 
  */
 
  void reserve(size_t new_cap, size_t new_cap_named) {
 
    FMT_ASSERT(new_cap >= new_cap_named,
 
               "Set of arguments includes set of named arguments");
 
    data_.reserve(new_cap);
 
    named_info_.reserve(new_cap_named);
 
  }
 
};
 
FMT_END_EXPORT
 

	
 
/**
 
  \rst
 
@@ -1845,25 +1893,27 @@ template <typename Context> class basic_
 
    const format_arg* args_;
 
  };
 

	
 
  bool is_packed() const { return (desc_ & detail::is_unpacked_bit) == 0; }
 
  bool has_named_args() const {
 
  constexpr auto is_packed() const -> bool {
 
    return (desc_ & detail::is_unpacked_bit) == 0;
 
  }
 
  auto has_named_args() const -> bool {
 
    return (desc_ & detail::has_named_args_bit) != 0;
 
  }
 

	
 
  detail::type type(int index) const {
 
  FMT_CONSTEXPR auto type(int index) const -> detail::type {
 
    int shift = index * detail::packed_arg_bits;
 
    unsigned int mask = (1 << detail::packed_arg_bits) - 1;
 
    return static_cast<detail::type>((desc_ >> shift) & mask);
 
  }
 

	
 
  basic_format_args(unsigned long long desc,
 
                    const detail::value<Context>* values)
 
  constexpr FMT_INLINE basic_format_args(unsigned long long desc,
 
                                         const detail::value<Context>* values)
 
      : desc_(desc), values_(values) {}
 
  basic_format_args(unsigned long long desc, const format_arg* args)
 
  constexpr basic_format_args(unsigned long long desc, const format_arg* args)
 
      : desc_(desc), args_(args) {}
 

	
 
 public:
 
  basic_format_args() : desc_(0) {}
 
  constexpr basic_format_args() : desc_(0), args_(nullptr) {}
 

	
 
  /**
 
   \rst
 
@@ -1871,8 +1921,10 @@ template <typename Context> class basic_
 
   \endrst
 
   */
 
  template <typename... Args>
 
  FMT_INLINE basic_format_args(const format_arg_store<Context, Args...>& store)
 
      : basic_format_args(store.desc, store.data_.args()) {}
 
  constexpr FMT_INLINE basic_format_args(
 
      const format_arg_store<Context, Args...>& store)
 
      : basic_format_args(format_arg_store<Context, Args...>::desc,
 
                          store.data_.args()) {}
 

	
 
  /**
 
   \rst
 
@@ -1880,7 +1932,8 @@ template <typename Context> class basic_
 
   `~fmt::dynamic_format_arg_store`.
 
   \endrst
 
   */
 
  FMT_INLINE basic_format_args(const dynamic_format_arg_store<Context>& store)
 
  constexpr FMT_INLINE basic_format_args(
 
      const dynamic_format_arg_store<Context>& store)
 
      : basic_format_args(store.get_types(), store.data()) {}
 

	
 
  /**
 
@@ -1888,12 +1941,12 @@ template <typename Context> class basic_
 
   Constructs a `basic_format_args` object from a dynamic set of arguments.
 
   \endrst
 
   */
 
  basic_format_args(const format_arg* args, int count)
 
  constexpr basic_format_args(const format_arg* args, int count)
 
      : basic_format_args(detail::is_unpacked_bit | detail::to_unsigned(count),
 
                          args) {}
 

	
 
  /** Returns the argument with the specified id. */
 
  format_arg get(int id) const {
 
  FMT_CONSTEXPR auto get(int id) const -> format_arg {
 
    format_arg arg;
 
    if (!is_packed()) {
 
      if (id < max_size()) arg = args_[id];
 
@@ -1906,12 +1959,14 @@ template <typename Context> class basic_
 
    return arg;
 
  }
 

	
 
  template <typename Char> format_arg get(basic_string_view<Char> name) const {
 
  template <typename Char>
 
  auto get(basic_string_view<Char> name) const -> format_arg {
 
    int id = get_id(name);
 
    return id >= 0 ? get(id) : format_arg();
 
  }
 

	
 
  template <typename Char> int get_id(basic_string_view<Char> name) const {
 
  template <typename Char>
 
  auto get_id(basic_string_view<Char> name) const -> int {
 
    if (!has_named_args()) return -1;
 
    const auto& named_args =
 
        (is_packed() ? values_[-1] : args_[-1].value_).named_args;
 
@@ -1921,49 +1976,711 @@ template <typename Context> class basic_
 
    return -1;
 
  }
 

	
 
  int max_size() const {
 
  auto max_size() const -> int {
 
    unsigned long long max_packed = detail::max_packed_args;
 
    return static_cast<int>(is_packed() ? max_packed
 
                                        : desc_ & ~detail::is_unpacked_bit);
 
  }
 
};
 

	
 
#ifdef FMT_ARM_ABI_COMPATIBILITY
 
/** An alias to ``basic_format_args<format_context>``. */
 
// Separate types would result in shorter symbols but break ABI compatibility
 
// A separate type would result in shorter symbols but break ABI compatibility
 
// between clang and gcc on ARM (#1919).
 
using format_args = basic_format_args<format_context>;
 
using wformat_args = basic_format_args<wformat_context>;
 
FMT_MODULE_EXPORT using format_args = basic_format_args<format_context>;
 

	
 
// We cannot use enum classes as bit fields because of a gcc bug, so we put them
 
// in namespaces instead (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414).
 
// Additionally, if an underlying type is specified, older gcc incorrectly warns
 
// that the type is too small. Both bugs are fixed in gcc 9.3.
 
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 903
 
#  define FMT_ENUM_UNDERLYING_TYPE(type)
 
#else
 
// DEPRECATED! These are kept for ABI compatibility.
 
// It is a separate type rather than an alias to make symbols readable.
 
struct format_args : basic_format_args<format_context> {
 
  template <typename... Args>
 
  FMT_INLINE format_args(const Args&... args) : basic_format_args(args...) {}
 
#  define FMT_ENUM_UNDERLYING_TYPE(type) : type
 
#endif
 
namespace align {
 
enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, left, right, center,
 
                                                  numeric};
 
}
 
using align_t = align::type;
 
namespace sign {
 
enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, minus, plus, space};
 
}
 
using sign_t = sign::type;
 

	
 
namespace detail {
 

	
 
// Workaround an array initialization issue in gcc 4.8.
 
template <typename Char> struct fill_t {
 
 private:
 
  enum { max_size = 4 };
 
  Char data_[max_size] = {Char(' '), Char(0), Char(0), Char(0)};
 
  unsigned char size_ = 1;
 

	
 
 public:
 
  FMT_CONSTEXPR void operator=(basic_string_view<Char> s) {
 
    auto size = s.size();
 
    FMT_ASSERT(size <= max_size, "invalid fill");
 
    for (size_t i = 0; i < size; ++i) data_[i] = s[i];
 
    size_ = static_cast<unsigned char>(size);
 
  }
 

	
 
  constexpr auto size() const -> size_t { return size_; }
 
  constexpr auto data() const -> const Char* { return data_; }
 

	
 
  FMT_CONSTEXPR auto operator[](size_t index) -> Char& { return data_[index]; }
 
  FMT_CONSTEXPR auto operator[](size_t index) const -> const Char& {
 
    return data_[index];
 
  }
 
};
 
struct wformat_args : basic_format_args<wformat_context> {
 
  using basic_format_args::basic_format_args;
 
}  // namespace detail
 

	
 
enum class presentation_type : unsigned char {
 
  none,
 
  dec,             // 'd'
 
  oct,             // 'o'
 
  hex_lower,       // 'x'
 
  hex_upper,       // 'X'
 
  bin_lower,       // 'b'
 
  bin_upper,       // 'B'
 
  hexfloat_lower,  // 'a'
 
  hexfloat_upper,  // 'A'
 
  exp_lower,       // 'e'
 
  exp_upper,       // 'E'
 
  fixed_lower,     // 'f'
 
  fixed_upper,     // 'F'
 
  general_lower,   // 'g'
 
  general_upper,   // 'G'
 
  chr,             // 'c'
 
  string,          // 's'
 
  pointer,         // 'p'
 
  debug            // '?'
 
};
 
#endif
 

	
 
// Format specifiers for built-in and string types.
 
template <typename Char = char> struct format_specs {
 
  int width;
 
  int precision;
 
  presentation_type type;
 
  align_t align : 4;
 
  sign_t sign : 3;
 
  bool alt : 1;  // Alternate form ('#').
 
  bool localized : 1;
 
  detail::fill_t<Char> fill;
 

	
 
  constexpr format_specs()
 
      : width(0),
 
        precision(-1),
 
        type(presentation_type::none),
 
        align(align::none),
 
        sign(sign::none),
 
        alt(false),
 
        localized(false) {}
 
};
 

	
 
namespace detail {
 

	
 
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
 
std::basic_string<Char> vformat(
 
    basic_string_view<Char> format_str,
 
    basic_format_args<buffer_context<type_identity_t<Char>>> args);
 

	
 
FMT_API std::string vformat(string_view format_str, format_args args);
 
enum class arg_id_kind { none, index, name };
 

	
 
// An argument reference.
 
template <typename Char> struct arg_ref {
 
  FMT_CONSTEXPR arg_ref() : kind(arg_id_kind::none), val() {}
 

	
 
  FMT_CONSTEXPR explicit arg_ref(int index)
 
      : kind(arg_id_kind::index), val(index) {}
 
  FMT_CONSTEXPR explicit arg_ref(basic_string_view<Char> name)
 
      : kind(arg_id_kind::name), val(name) {}
 

	
 
  FMT_CONSTEXPR auto operator=(int idx) -> arg_ref& {
 
    kind = arg_id_kind::index;
 
    val.index = idx;
 
    return *this;
 
  }
 

	
 
  arg_id_kind kind;
 
  union value {
 
    FMT_CONSTEXPR value(int idx = 0) : index(idx) {}
 
    FMT_CONSTEXPR value(basic_string_view<Char> n) : name(n) {}
 

	
 
    int index;
 
    basic_string_view<Char> name;
 
  } val;
 
};
 

	
 
// Format specifiers with width and precision resolved at formatting rather
 
// than parsing time to allow reusing the same parsed specifiers with
 
// different sets of arguments (precompilation of format strings).
 
template <typename Char = char>
 
struct dynamic_format_specs : format_specs<Char> {
 
  arg_ref<Char> width_ref;
 
  arg_ref<Char> precision_ref;
 
};
 

	
 
// Converts a character to ASCII. Returns '\0' on conversion failure.
 
template <typename Char, FMT_ENABLE_IF(std::is_integral<Char>::value)>
 
constexpr auto to_ascii(Char c) -> char {
 
  return c <= 0xff ? static_cast<char>(c) : '\0';
 
}
 
template <typename Char, FMT_ENABLE_IF(std::is_enum<Char>::value)>
 
constexpr auto to_ascii(Char c) -> char {
 
  return c <= 0xff ? static_cast<char>(c) : '\0';
 
}
 

	
 
// Returns the number of code units in a code point or 1 on error.
 
template <typename Char>
 
FMT_CONSTEXPR auto code_point_length(const Char* begin) -> int {
 
  if (const_check(sizeof(Char) != 1)) return 1;
 
  auto c = static_cast<unsigned char>(*begin);
 
  return static_cast<int>((0x3a55000000000000ull >> (2 * (c >> 3))) & 0x3) + 1;
 
}
 

	
 
// Return the result via the out param to workaround gcc bug 77539.
 
template <bool IS_CONSTEXPR, typename T, typename Ptr = const T*>
 
FMT_CONSTEXPR auto find(Ptr first, Ptr last, T value, Ptr& out) -> bool {
 
  for (out = first; out != last; ++out) {
 
    if (*out == value) return true;
 
  }
 
  return false;
 
}
 

	
 
template <>
 
inline auto find<false, char>(const char* first, const char* last, char value,
 
                              const char*& out) -> bool {
 
  out = static_cast<const char*>(
 
      std::memchr(first, value, to_unsigned(last - first)));
 
  return out != nullptr;
 
}
 

	
 
// Parses the range [begin, end) as an unsigned integer. This function assumes
 
// that the range is non-empty and the first character is a digit.
 
template <typename Char>
 
FMT_CONSTEXPR auto parse_nonnegative_int(const Char*& begin, const Char* end,
 
                                         int error_value) noexcept -> int {
 
  FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', "");
 
  unsigned value = 0, prev = 0;
 
  auto p = begin;
 
  do {
 
    prev = value;
 
    value = value * 10 + unsigned(*p - '0');
 
    ++p;
 
  } while (p != end && '0' <= *p && *p <= '9');
 
  auto num_digits = p - begin;
 
  begin = p;
 
  if (num_digits <= std::numeric_limits<int>::digits10)
 
    return static_cast<int>(value);
 
  // Check for overflow.
 
  const unsigned max = to_unsigned((std::numeric_limits<int>::max)());
 
  return num_digits == std::numeric_limits<int>::digits10 + 1 &&
 
                 prev * 10ull + unsigned(p[-1] - '0') <= max
 
             ? static_cast<int>(value)
 
             : error_value;
 
}
 

	
 
FMT_CONSTEXPR inline auto parse_align(char c) -> align_t {
 
  switch (c) {
 
  case '<':
 
    return align::left;
 
  case '>':
 
    return align::right;
 
  case '^':
 
    return align::center;
 
  }
 
  return align::none;
 
}
 

	
 
template <typename Char> constexpr auto is_name_start(Char c) -> bool {
 
  return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '_';
 
}
 

	
 
template <typename Char, typename Handler>
 
FMT_CONSTEXPR auto do_parse_arg_id(const Char* begin, const Char* end,
 
                                   Handler&& handler) -> const Char* {
 
  Char c = *begin;
 
  if (c >= '0' && c <= '9') {
 
    int index = 0;
 
    constexpr int max = (std::numeric_limits<int>::max)();
 
    if (c != '0')
 
      index = parse_nonnegative_int(begin, end, max);
 
    else
 
      ++begin;
 
    if (begin == end || (*begin != '}' && *begin != ':'))
 
      throw_format_error("invalid format string");
 
    else
 
      handler.on_index(index);
 
    return begin;
 
  }
 
  if (!is_name_start(c)) {
 
    throw_format_error("invalid format string");
 
    return begin;
 
  }
 
  auto it = begin;
 
  do {
 
    ++it;
 
  } while (it != end && (is_name_start(*it) || ('0' <= *it && *it <= '9')));
 
  handler.on_name({begin, to_unsigned(it - begin)});
 
  return it;
 
}
 

	
 
template <typename Char, typename Handler>
 
FMT_CONSTEXPR FMT_INLINE auto parse_arg_id(const Char* begin, const Char* end,
 
                                           Handler&& handler) -> const Char* {
 
  FMT_ASSERT(begin != end, "");
 
  Char c = *begin;
 
  if (c != '}' && c != ':') return do_parse_arg_id(begin, end, handler);
 
  handler.on_auto();
 
  return begin;
 
}
 

	
 
template <typename Char> struct dynamic_spec_id_handler {
 
  basic_format_parse_context<Char>& ctx;
 
  arg_ref<Char>& ref;
 

	
 
  FMT_CONSTEXPR void on_auto() {
 
    int id = ctx.next_arg_id();
 
    ref = arg_ref<Char>(id);
 
    ctx.check_dynamic_spec(id);
 
  }
 
  FMT_CONSTEXPR void on_index(int id) {
 
    ref = arg_ref<Char>(id);
 
    ctx.check_arg_id(id);
 
    ctx.check_dynamic_spec(id);
 
  }
 
  FMT_CONSTEXPR void on_name(basic_string_view<Char> id) {
 
    ref = arg_ref<Char>(id);
 
    ctx.check_arg_id(id);
 
  }
 
};
 

	
 
// Parses [integer | "{" [arg_id] "}"].
 
template <typename Char>
 
FMT_CONSTEXPR auto parse_dynamic_spec(const Char* begin, const Char* end,
 
                                      int& value, arg_ref<Char>& ref,
 
                                      basic_format_parse_context<Char>& ctx)
 
    -> const Char* {
 
  FMT_ASSERT(begin != end, "");
 
  if ('0' <= *begin && *begin <= '9') {
 
    int val = parse_nonnegative_int(begin, end, -1);
 
    if (val != -1)
 
      value = val;
 
    else
 
      throw_format_error("number is too big");
 
  } else if (*begin == '{') {
 
    ++begin;
 
    auto handler = dynamic_spec_id_handler<Char>{ctx, ref};
 
    if (begin != end) begin = parse_arg_id(begin, end, handler);
 
    if (begin != end && *begin == '}') return ++begin;
 
    throw_format_error("invalid format string");
 
  }
 
  return begin;
 
}
 

	
 
template <typename Char>
 
void vformat_to(
 
    buffer<Char>& buf, basic_string_view<Char> format_str,
 
    basic_format_args<FMT_BUFFER_CONTEXT(type_identity_t<Char>)> args,
 
    detail::locale_ref loc = {});
 

	
 
template <typename Char, typename Args,
 
          FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
 
inline void vprint_mojibake(std::FILE*, basic_string_view<Char>, const Args&) {}
 
FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end,
 
                                   int& value, arg_ref<Char>& ref,
 
                                   basic_format_parse_context<Char>& ctx)
 
    -> const Char* {
 
  ++begin;
 
  if (begin == end || *begin == '}') {
 
    throw_format_error("invalid precision");
 
    return begin;
 
  }
 
  return parse_dynamic_spec(begin, end, value, ref, ctx);
 
}
 

	
 
enum class state { start, align, sign, hash, zero, width, precision, locale };
 

	
 
// Parses standard format specifiers.
 
template <typename Char>
 
FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(
 
    const Char* begin, const Char* end, dynamic_format_specs<Char>& specs,
 
    basic_format_parse_context<Char>& ctx, type arg_type) -> const Char* {
 
  auto c = '\0';
 
  if (end - begin > 1) {
 
    auto next = to_ascii(begin[1]);
 
    c = parse_align(next) == align::none ? to_ascii(*begin) : '\0';
 
  } else {
 
    if (begin == end) return begin;
 
    c = to_ascii(*begin);
 
  }
 

	
 
  struct {
 
    state current_state = state::start;
 
    FMT_CONSTEXPR void operator()(state s, bool valid = true) {
 
      if (current_state >= s || !valid)
 
        throw_format_error("invalid format specifier");
 
      current_state = s;
 
    }
 
  } enter_state;
 

	
 
  using pres = presentation_type;
 
  constexpr auto integral_set = sint_set | uint_set | bool_set | char_set;
 
  struct {
 
    const Char*& begin;
 
    dynamic_format_specs<Char>& specs;
 
    type arg_type;
 

	
 
    FMT_CONSTEXPR auto operator()(pres type, int set) -> const Char* {
 
      if (!in(arg_type, set)) throw_format_error("invalid format specifier");
 
      specs.type = type;
 
      return begin + 1;
 
    }
 
  } parse_presentation_type{begin, specs, arg_type};
 

	
 
  for (;;) {
 
    switch (c) {
 
    case '<':
 
    case '>':
 
    case '^':
 
      enter_state(state::align);
 
      specs.align = parse_align(c);
 
      ++begin;
 
      break;
 
    case '+':
 
    case '-':
 
    case ' ':
 
      enter_state(state::sign, in(arg_type, sint_set | float_set));
 
      switch (c) {
 
      case '+':
 
        specs.sign = sign::plus;
 
        break;
 
      case '-':
 
        specs.sign = sign::minus;
 
        break;
 
      case ' ':
 
        specs.sign = sign::space;
 
        break;
 
      }
 
      ++begin;
 
      break;
 
    case '#':
 
      enter_state(state::hash, is_arithmetic_type(arg_type));
 
      specs.alt = true;
 
      ++begin;
 
      break;
 
    case '0':
 
      enter_state(state::zero);
 
      if (!is_arithmetic_type(arg_type))
 
        throw_format_error("format specifier requires numeric argument");
 
      if (specs.align == align::none) {
 
        // Ignore 0 if align is specified for compatibility with std::format.
 
        specs.align = align::numeric;
 
        specs.fill[0] = Char('0');
 
      }
 
      ++begin;
 
      break;
 
    case '1':
 
    case '2':
 
    case '3':
 
    case '4':
 
    case '5':
 
    case '6':
 
    case '7':
 
    case '8':
 
    case '9':
 
    case '{':
 
      enter_state(state::width);
 
      begin = parse_dynamic_spec(begin, end, specs.width, specs.width_ref, ctx);
 
      break;
 
    case '.':
 
      enter_state(state::precision,
 
                  in(arg_type, float_set | string_set | cstring_set));
 
      begin = parse_precision(begin, end, specs.precision, specs.precision_ref,
 
                              ctx);
 
      break;
 
    case 'L':
 
      enter_state(state::locale, is_arithmetic_type(arg_type));
 
      specs.localized = true;
 
      ++begin;
 
      break;
 
    case 'd':
 
      return parse_presentation_type(pres::dec, integral_set);
 
    case 'o':
 
      return parse_presentation_type(pres::oct, integral_set);
 
    case 'x':
 
      return parse_presentation_type(pres::hex_lower, integral_set);
 
    case 'X':
 
      return parse_presentation_type(pres::hex_upper, integral_set);
 
    case 'b':
 
      return parse_presentation_type(pres::bin_lower, integral_set);
 
    case 'B':
 
      return parse_presentation_type(pres::bin_upper, integral_set);
 
    case 'a':
 
      return parse_presentation_type(pres::hexfloat_lower, float_set);
 
    case 'A':
 
      return parse_presentation_type(pres::hexfloat_upper, float_set);
 
    case 'e':
 
      return parse_presentation_type(pres::exp_lower, float_set);
 
    case 'E':
 
      return parse_presentation_type(pres::exp_upper, float_set);
 
    case 'f':
 
      return parse_presentation_type(pres::fixed_lower, float_set);
 
    case 'F':
 
      return parse_presentation_type(pres::fixed_upper, float_set);
 
    case 'g':
 
      return parse_presentation_type(pres::general_lower, float_set);
 
    case 'G':
 
      return parse_presentation_type(pres::general_upper, float_set);
 
    case 'c':
 
      return parse_presentation_type(pres::chr, integral_set);
 
    case 's':
 
      return parse_presentation_type(pres::string,
 
                                     bool_set | string_set | cstring_set);
 
    case 'p':
 
      return parse_presentation_type(pres::pointer, pointer_set | cstring_set);
 
    case '?':
 
      return parse_presentation_type(pres::debug,
 
                                     char_set | string_set | cstring_set);
 
    case '}':
 
      return begin;
 
    default: {
 
      if (*begin == '}') return begin;
 
      // Parse fill and alignment.
 
      auto fill_end = begin + code_point_length(begin);
 
      if (end - fill_end <= 0) {
 
        throw_format_error("invalid format specifier");
 
        return begin;
 
      }
 
      if (*begin == '{') {
 
        throw_format_error("invalid fill character '{'");
 
        return begin;
 
      }
 
      auto align = parse_align(to_ascii(*fill_end));
 
      enter_state(state::align, align != align::none);
 
      specs.fill = {begin, to_unsigned(fill_end - begin)};
 
      specs.align = align;
 
      begin = fill_end + 1;
 
    }
 
    }
 
    if (begin == end) return begin;
 
    c = to_ascii(*begin);
 
  }
 
}
 

	
 
template <typename Char, typename Handler>
 
FMT_CONSTEXPR auto parse_replacement_field(const Char* begin, const Char* end,
 
                                           Handler&& handler) -> const Char* {
 
  struct id_adapter {
 
    Handler& handler;
 
    int arg_id;
 

	
 
    FMT_CONSTEXPR void on_auto() { arg_id = handler.on_arg_id(); }
 
    FMT_CONSTEXPR void on_index(int id) { arg_id = handler.on_arg_id(id); }
 
    FMT_CONSTEXPR void on_name(basic_string_view<Char> id) {
 
      arg_id = handler.on_arg_id(id);
 
    }
 
  };
 

	
 
  ++begin;
 
  if (begin == end) return handler.on_error("invalid format string"), end;
 
  if (*begin == '}') {
 
    handler.on_replacement_field(handler.on_arg_id(), begin);
 
  } else if (*begin == '{') {
 
    handler.on_text(begin, begin + 1);
 
  } else {
 
    auto adapter = id_adapter{handler, 0};
 
    begin = parse_arg_id(begin, end, adapter);
 
    Char c = begin != end ? *begin : Char();
 
    if (c == '}') {
 
      handler.on_replacement_field(adapter.arg_id, begin);
 
    } else if (c == ':') {
 
      begin = handler.on_format_specs(adapter.arg_id, begin + 1, end);
 
      if (begin == end || *begin != '}')
 
        return handler.on_error("unknown format specifier"), end;
 
    } else {
 
      return handler.on_error("missing '}' in format string"), end;
 
    }
 
  }
 
  return begin + 1;
 
}
 

	
 
template <bool IS_CONSTEXPR, typename Char, typename Handler>
 
FMT_CONSTEXPR FMT_INLINE void parse_format_string(
 
    basic_string_view<Char> format_str, Handler&& handler) {
 
  auto begin = format_str.data();
 
  auto end = begin + format_str.size();
 
  if (end - begin < 32) {
 
    // Use a simple loop instead of memchr for small strings.
 
    const Char* p = begin;
 
    while (p != end) {
 
      auto c = *p++;
 
      if (c == '{') {
 
        handler.on_text(begin, p - 1);
 
        begin = p = parse_replacement_field(p - 1, end, handler);
 
      } else if (c == '}') {
 
        if (p == end || *p != '}')
 
          return handler.on_error("unmatched '}' in format string");
 
        handler.on_text(begin, p);
 
        begin = ++p;
 
      }
 
    }
 
    handler.on_text(begin, end);
 
    return;
 
  }
 
  struct writer {
 
    FMT_CONSTEXPR void operator()(const Char* from, const Char* to) {
 
      if (from == to) return;
 
      for (;;) {
 
        const Char* p = nullptr;
 
        if (!find<IS_CONSTEXPR>(from, to, Char('}'), p))
 
          return handler_.on_text(from, to);
 
        ++p;
 
        if (p == to || *p != '}')
 
          return handler_.on_error("unmatched '}' in format string");
 
        handler_.on_text(from, p);
 
        from = p + 1;
 
      }
 
    }
 
    Handler& handler_;
 
  } write = {handler};
 
  while (begin != end) {
 
    // Doing two passes with memchr (one for '{' and another for '}') is up to
 
    // 2.5x faster than the naive one-pass implementation on big format strings.
 
    const Char* p = begin;
 
    if (*begin != '{' && !find<IS_CONSTEXPR>(begin + 1, end, Char('{'), p))
 
      return write(begin, end);
 
    write(begin, p);
 
    begin = parse_replacement_field(p, end, handler);
 
  }
 
}
 

	
 
template <typename T, bool = is_named_arg<T>::value> struct strip_named_arg {
 
  using type = T;
 
};
 
template <typename T> struct strip_named_arg<T, true> {
 
  using type = remove_cvref_t<decltype(T::value)>;
 
};
 

	
 
template <typename T, typename ParseContext>
 
FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx)
 
    -> decltype(ctx.begin()) {
 
  using char_type = typename ParseContext::char_type;
 
  using context = buffer_context<char_type>;
 
  using mapped_type = conditional_t<
 
      mapped_type_constant<T, context>::value != type::custom_type,
 
      decltype(arg_mapper<context>().map(std::declval<const T&>())),
 
      typename strip_named_arg<T>::type>;
 
  return formatter<mapped_type, char_type>().parse(ctx);
 
}
 

	
 
// Checks char specs and returns true iff the presentation type is char-like.
 
template <typename Char>
 
FMT_CONSTEXPR auto check_char_specs(const format_specs<Char>& specs) -> bool {
 
  if (specs.type != presentation_type::none &&
 
      specs.type != presentation_type::chr &&
 
      specs.type != presentation_type::debug) {
 
    return false;
 
  }
 
  if (specs.align == align::numeric || specs.sign != sign::none || specs.alt)
 
    throw_format_error("invalid format specifier for char");
 
  return true;
 
}
 

	
 
constexpr FMT_INLINE_VARIABLE int invalid_arg_index = -1;
 

	
 
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
 
template <int N, typename T, typename... Args, typename Char>
 
constexpr auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
 
  if constexpr (is_statically_named_arg<T>()) {
 
    if (name == T::name) return N;
 
  }
 
  if constexpr (sizeof...(Args) > 0)
 
    return get_arg_index_by_name<N + 1, Args...>(name);
 
  (void)name;  // Workaround an MSVC bug about "unused" parameter.
 
  return invalid_arg_index;
 
}
 
#endif
 

	
 
template <typename... Args, typename Char>
 
FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
 
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
 
  if constexpr (sizeof...(Args) > 0)
 
    return get_arg_index_by_name<0, Args...>(name);
 
#endif
 
  (void)name;
 
  return invalid_arg_index;
 
}
 

	
 
template <typename Char, typename... Args> class format_string_checker {
 
 private:
 
  using parse_context_type = compile_parse_context<Char>;
 
  static constexpr int num_args = sizeof...(Args);
 

	
 
  // Format specifier parsing function.
 
  // In the future basic_format_parse_context will replace compile_parse_context
 
  // here and will use is_constant_evaluated and downcasting to access the data
 
  // needed for compile-time checks: https://godbolt.org/z/GvWzcTjh1.
 
  using parse_func = const Char* (*)(parse_context_type&);
 

	
 
  parse_context_type context_;
 
  parse_func parse_funcs_[num_args > 0 ? static_cast<size_t>(num_args) : 1];
 
  type types_[num_args > 0 ? static_cast<size_t>(num_args) : 1];
 

	
 
 public:
 
  explicit FMT_CONSTEXPR format_string_checker(basic_string_view<Char> fmt)
 
      : context_(fmt, num_args, types_),
 
        parse_funcs_{&parse_format_specs<Args, parse_context_type>...},
 
        types_{mapped_type_constant<Args, buffer_context<Char>>::value...} {}
 

	
 
  FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
 

	
 
  FMT_CONSTEXPR auto on_arg_id() -> int { return context_.next_arg_id(); }
 
  FMT_CONSTEXPR auto on_arg_id(int id) -> int {
 
    return context_.check_arg_id(id), id;
 
  }
 
  FMT_CONSTEXPR auto on_arg_id(basic_string_view<Char> id) -> int {
 
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
 
    auto index = get_arg_index_by_name<Args...>(id);
 
    if (index == invalid_arg_index) on_error("named argument is not found");
 
    return index;
 
#else
 
    (void)id;
 
    on_error("compile-time checks for named arguments require C++20 support");
 
    return 0;
 
#endif
 
  }
 

	
 
  FMT_CONSTEXPR void on_replacement_field(int, const Char*) {}
 

	
 
  FMT_CONSTEXPR auto on_format_specs(int id, const Char* begin, const Char*)
 
      -> const Char* {
 
    context_.advance_to(begin);
 
    // id >= 0 check is a workaround for gcc 10 bug (#2065).
 
    return id >= 0 && id < num_args ? parse_funcs_[id](context_) : begin;
 
  }
 

	
 
  FMT_CONSTEXPR void on_error(const char* message) {
 
    throw_format_error(message);
 
  }
 
};
 

	
 
// Reports a compile-time error if S is not a valid format string.
 
template <typename..., typename S, FMT_ENABLE_IF(!is_compile_string<S>::value)>
 
FMT_INLINE void check_format_string(const S&) {
 
#ifdef FMT_ENFORCE_COMPILE_STRING
 
  static_assert(is_compile_string<S>::value,
 
                "FMT_ENFORCE_COMPILE_STRING requires all format strings to use "
 
                "FMT_STRING.");
 
#endif
 
}
 
template <typename... Args, typename S,
 
          FMT_ENABLE_IF(is_compile_string<S>::value)>
 
void check_format_string(S format_str) {
 
  using char_t = typename S::char_type;
 
  FMT_CONSTEXPR auto s = basic_string_view<char_t>(format_str);
 
  using checker = format_string_checker<char_t, remove_cvref_t<Args>...>;
 
  FMT_CONSTEXPR bool error = (parse_format_string<true>(s, checker(s)), true);
 
  ignore_unused(error);
 
}
 

	
 
template <typename Char = char> struct vformat_args {
 
  using type = basic_format_args<
 
      basic_format_context<std::back_insert_iterator<buffer<Char>>, Char>>;
 
};
 
template <> struct vformat_args<char> { using type = format_args; };
 

	
 
// Use vformat_args and avoid type_identity to keep symbols short.
 
template <typename Char>
 
void vformat_to(buffer<Char>& buf, basic_string_view<Char> fmt,
 
                typename vformat_args<Char>::type args, locale_ref loc = {});
 

	
 
FMT_API void vprint_mojibake(std::FILE*, string_view, format_args);
 
#ifndef _WIN32
 
@@ -1971,37 +2688,161 @@ inline void vprint_mojibake(std::FILE*, 
 
#endif
 
}  // namespace detail
 

	
 
FMT_BEGIN_EXPORT
 

	
 
// A formatter specialization for natively supported types.
 
template <typename T, typename Char>
 
struct formatter<T, Char,
 
                 enable_if_t<detail::type_constant<T, Char>::value !=
 
                             detail::type::custom_type>> {
 
 private:
 
  detail::dynamic_format_specs<Char> specs_;
 

	
 
 public:
 
  template <typename ParseContext>
 
  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const Char* {
 
    auto type = detail::type_constant<T, Char>::value;
 
    auto end =
 
        detail::parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, type);
 
    if (type == detail::type::char_type) detail::check_char_specs(specs_);
 
    return end;
 
  }
 

	
 
  template <detail::type U = detail::type_constant<T, Char>::value,
 
            FMT_ENABLE_IF(U == detail::type::string_type ||
 
                          U == detail::type::cstring_type ||
 
                          U == detail::type::char_type)>
 
  FMT_CONSTEXPR void set_debug_format(bool set = true) {
 
    specs_.type = set ? presentation_type::debug : presentation_type::none;
 
  }
 

	
 
  template <typename FormatContext>
 
  FMT_CONSTEXPR auto format(const T& val, FormatContext& ctx) const
 
      -> decltype(ctx.out());
 
};
 

	
 
#define FMT_FORMAT_AS(Type, Base)                                        \
 
  template <typename Char>                                               \
 
  struct formatter<Type, Char> : formatter<Base, Char> {                 \
 
    template <typename FormatContext>                                    \
 
    auto format(const Type& val, FormatContext& ctx) const               \
 
        -> decltype(ctx.out()) {                                         \
 
      return formatter<Base, Char>::format(static_cast<Base>(val), ctx); \
 
    }                                                                    \
 
  }
 

	
 
FMT_FORMAT_AS(signed char, int);
 
FMT_FORMAT_AS(unsigned char, unsigned);
 
FMT_FORMAT_AS(short, int);
 
FMT_FORMAT_AS(unsigned short, unsigned);
 
FMT_FORMAT_AS(long, long long);
 
FMT_FORMAT_AS(unsigned long, unsigned long long);
 
FMT_FORMAT_AS(Char*, const Char*);
 
FMT_FORMAT_AS(std::basic_string<Char>, basic_string_view<Char>);
 
FMT_FORMAT_AS(std::nullptr_t, const void*);
 
FMT_FORMAT_AS(detail::std_string_view<Char>, basic_string_view<Char>);
 

	
 
template <typename Char = char> struct runtime_format_string {
 
  basic_string_view<Char> str;
 
};
 

	
 
/** A compile-time format string. */
 
template <typename Char, typename... Args> class basic_format_string {
 
 private:
 
  basic_string_view<Char> str_;
 

	
 
 public:
 
  template <typename S,
 
            FMT_ENABLE_IF(
 
                std::is_convertible<const S&, basic_string_view<Char>>::value)>
 
  FMT_CONSTEVAL FMT_INLINE basic_format_string(const S& s) : str_(s) {
 
    static_assert(
 
        detail::count<
 
            (std::is_base_of<detail::view, remove_reference_t<Args>>::value &&
 
             std::is_reference<Args>::value)...>() == 0,
 
        "passing views as lvalues is disallowed");
 
#ifdef FMT_HAS_CONSTEVAL
 
    if constexpr (detail::count_named_args<Args...>() ==
 
                  detail::count_statically_named_args<Args...>()) {
 
      using checker =
 
          detail::format_string_checker<Char, remove_cvref_t<Args>...>;
 
      detail::parse_format_string<true>(str_, checker(s));
 
    }
 
#else
 
    detail::check_format_string<Args...>(s);
 
#endif
 
  }
 
  basic_format_string(runtime_format_string<Char> fmt) : str_(fmt.str) {}
 

	
 
  FMT_INLINE operator basic_string_view<Char>() const { return str_; }
 
  FMT_INLINE auto get() const -> basic_string_view<Char> { return str_; }
 
};
 

	
 
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
 
// Workaround broken conversion on older gcc.
 
template <typename...> using format_string = string_view;
 
inline auto runtime(string_view s) -> string_view { return s; }
 
#else
 
template <typename... Args>
 
using format_string = basic_format_string<char, type_identity_t<Args>...>;
 
/**
 
  \rst
 
  Creates a runtime format string.
 

	
 
  **Example**::
 

	
 
    // Check format string at runtime instead of compile-time.
 
    fmt::print(fmt::runtime("{:d}"), "I am not a number");
 
  \endrst
 
 */
 
inline auto runtime(string_view s) -> runtime_format_string<> { return {{s}}; }
 
#endif
 

	
 
FMT_API auto vformat(string_view fmt, format_args args) -> std::string;
 

	
 
/**
 
  \rst
 
  Formats ``args`` according to specifications in ``fmt`` and returns the result
 
  as a string.
 

	
 
  **Example**::
 

	
 
    #include <fmt/core.h>
 
    std::string message = fmt::format("The answer is {}.", 42);
 
  \endrst
 
*/
 
template <typename... T>
 
FMT_NODISCARD FMT_INLINE auto format(format_string<T...> fmt, T&&... args)
 
    -> std::string {
 
  return vformat(fmt, fmt::make_format_args(args...));
 
}
 

	
 
/** Formats a string and writes the output to ``out``. */
 
// GCC 8 and earlier cannot handle std::back_insert_iterator<Container> with
 
// vformat_to<ArgFormatter>(...) overload, so SFINAE on iterator type instead.
 
template <typename OutputIt, typename S, typename Char = char_t<S>,
 
          bool enable = detail::is_output_iterator<OutputIt, Char>::value>
 
auto vformat_to(OutputIt out, const S& format_str,
 
                basic_format_args<buffer_context<type_identity_t<Char>>> args)
 
    -> typename std::enable_if<enable, OutputIt>::type {
 
  decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out));
 
  detail::vformat_to(buf, to_string_view(format_str), args);
 
  return detail::get_iterator(buf);
 
template <typename OutputIt,
 
          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
 
auto vformat_to(OutputIt out, string_view fmt, format_args args) -> OutputIt {
 
  auto&& buf = detail::get_buffer<char>(out);
 
  detail::vformat_to(buf, fmt, args, {});
 
  return detail::get_iterator(buf, out);
 
}
 

	
 
/**
 
 \rst
 
 Formats arguments, writes the result to the output iterator ``out`` and returns
 
 the iterator past the end of the output range.
 
 Formats ``args`` according to specifications in ``fmt``, writes the result to
 
 the output iterator ``out`` and returns the iterator past the end of the output
 
 range. `format_to` does not append a terminating null character.
 

	
 
 **Example**::
 

	
 
   std::vector<char> out;
 
   auto out = std::vector<char>();
 
   fmt::format_to(std::back_inserter(out), "{}", 42);
 
 \endrst
 
 */
 
// We cannot use FMT_ENABLE_IF because of a bug in gcc 8.3.
 
template <typename OutputIt, typename S, typename... Args,
 
          bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value>
 
inline auto format_to(OutputIt out, const S& format_str, Args&&... args) ->
 
    typename std::enable_if<enable, OutputIt>::type {
 
  const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
 
  return vformat_to(out, to_string_view(format_str), vargs);
 
template <typename OutputIt, typename... T,
 
          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
 
FMT_INLINE auto format_to(OutputIt out, format_string<T...> fmt, T&&... args)
 
    -> OutputIt {
 
  return vformat_to(out, fmt, fmt::make_format_args(args...));
 
}
 

	
 
template <typename OutputIt> struct format_to_n_result {
 
@@ -2011,111 +2852,100 @@ template <typename OutputIt> struct form
 
  size_t size;
 
};
 

	
 
template <typename OutputIt, typename Char, typename... Args,
 
          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
 
inline format_to_n_result<OutputIt> vformat_to_n(
 
    OutputIt out, size_t n, basic_string_view<Char> format_str,
 
    basic_format_args<buffer_context<type_identity_t<Char>>> args) {
 
  detail::iterator_buffer<OutputIt, Char, detail::fixed_buffer_traits> buf(out,
 
                                                                           n);
 
  detail::vformat_to(buf, format_str, args);
 
template <typename OutputIt, typename... T,
 
          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
 
auto vformat_to_n(OutputIt out, size_t n, string_view fmt, format_args args)
 
    -> format_to_n_result<OutputIt> {
 
  using traits = detail::fixed_buffer_traits;
 
  auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n);
 
  detail::vformat_to(buf, fmt, args, {});
 
  return {buf.out(), buf.count()};
 
}
 

	
 
/**
 
 \rst
 
 Formats arguments, writes up to ``n`` characters of the result to the output
 
 iterator ``out`` and returns the total output size and the iterator past the
 
 end of the output range.
 
 \endrst
 
  \rst
 
  Formats ``args`` according to specifications in ``fmt``, writes up to ``n``
 
  characters of the result to the output iterator ``out`` and returns the total
 
  (not truncated) output size and the iterator past the end of the output range.
 
  `format_to_n` does not append a terminating null character.
 
  \endrst
 
 */
 
template <typename OutputIt, typename S, typename... Args,
 
          bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value>
 
inline auto format_to_n(OutputIt out, size_t n, const S& format_str,
 
                        const Args&... args) ->
 
    typename std::enable_if<enable, format_to_n_result<OutputIt>>::type {
 
  const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
 
  return vformat_to_n(out, n, to_string_view(format_str), vargs);
 
template <typename OutputIt, typename... T,
 
          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
 
FMT_INLINE auto format_to_n(OutputIt out, size_t n, format_string<T...> fmt,
 
                            T&&... args) -> format_to_n_result<OutputIt> {
 
  return vformat_to_n(out, n, fmt, fmt::make_format_args(args...));
 
}
 

	
 
/** Returns the number of chars in the output of ``format(fmt, args...)``. */
 
template <typename... T>
 
FMT_NODISCARD FMT_INLINE auto formatted_size(format_string<T...> fmt,
 
                                             T&&... args) -> size_t {
 
  auto buf = detail::counting_buffer<>();
 
  detail::vformat_to<char>(buf, fmt, fmt::make_format_args(args...), {});
 
  return buf.count();
 
}
 

	
 
FMT_API void vprint(string_view fmt, format_args args);
 
FMT_API void vprint(std::FILE* f, string_view fmt, format_args args);
 

	
 
/**
 
  Returns the number of characters in the output of
 
  ``format(format_str, args...)``.
 
  \rst
 
  Formats ``args`` according to specifications in ``fmt`` and writes the output
 
  to ``stdout``.
 

	
 
  **Example**::
 

	
 
    fmt::print("Elapsed time: {0:.2f} seconds", 1.23);
 
  \endrst
 
 */
 
template <typename... Args>
 
inline size_t formatted_size(string_view format_str, Args&&... args) {
 
  const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
 
  detail::counting_buffer<> buf;
 
  detail::vformat_to(buf, format_str, vargs);
 
  return buf.count();
 
}
 

	
 
template <typename S, typename Char = char_t<S>>
 
FMT_INLINE std::basic_string<Char> vformat(
 
    const S& format_str,
 
    basic_format_args<buffer_context<type_identity_t<Char>>> args) {
 
  return detail::vformat(to_string_view(format_str), args);
 
template <typename... T>
 
FMT_INLINE void print(format_string<T...> fmt, T&&... args) {
 
  const auto& vargs = fmt::make_format_args(args...);
 
  return detail::is_utf8() ? vprint(fmt, vargs)
 
                           : detail::vprint_mojibake(stdout, fmt, vargs);
 
}
 

	
 
/**
 
  \rst
 
  Formats arguments and returns the result as a string.
 

	
 
  **Example**::
 

	
 
    #include <fmt/core.h>
 
    std::string message = fmt::format("The answer is {}", 42);
 
  \endrst
 
*/
 
// Pass char_t as a default template parameter instead of using
 
// std::basic_string<char_t<S>> to reduce the symbol size.
 
template <typename S, typename... Args, typename Char = char_t<S>>
 
FMT_INLINE std::basic_string<Char> format(const S& format_str, Args&&... args) {
 
  const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
 
  return detail::vformat(to_string_view(format_str), vargs);
 
}
 

	
 
FMT_API void vprint(string_view, format_args);
 
FMT_API void vprint(std::FILE*, string_view, format_args);
 

	
 
/**
 
  \rst
 
  Formats ``args`` according to specifications in ``format_str`` and writes the
 
  output to the file ``f``. Strings are assumed to be Unicode-encoded unless the
 
  ``FMT_UNICODE`` macro is set to 0.
 
  Formats ``args`` according to specifications in ``fmt`` and writes the
 
  output to the file ``f``.
 

	
 
  **Example**::
 

	
 
    fmt::print(stderr, "Don't {}!", "panic");
 
  \endrst
 
 */
 
template <typename S, typename... Args, typename Char = char_t<S>>
 
inline void print(std::FILE* f, const S& format_str, Args&&... args) {
 
  const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
 
  return detail::is_unicode<Char>()
 
             ? vprint(f, to_string_view(format_str), vargs)
 
             : detail::vprint_mojibake(f, to_string_view(format_str), vargs);
 
template <typename... T>
 
FMT_INLINE void print(std::FILE* f, format_string<T...> fmt, T&&... args) {
 
  const auto& vargs = fmt::make_format_args(args...);
 
  return detail::is_utf8() ? vprint(f, fmt, vargs)
 
                           : detail::vprint_mojibake(f, fmt, vargs);
 
}
 

	
 
/**
 
  Formats ``args`` according to specifications in ``fmt`` and writes the
 
  output to the file ``f`` followed by a newline.
 
 */
 
template <typename... T>
 
FMT_INLINE void println(std::FILE* f, format_string<T...> fmt, T&&... args) {
 
  return fmt::print(f, "{}\n", fmt::format(fmt, std::forward<T>(args)...));
 
}
 

	
 
/**
 
  \rst
 
  Formats ``args`` according to specifications in ``format_str`` and writes
 
  the output to ``stdout``. Strings are assumed to be Unicode-encoded unless
 
  the ``FMT_UNICODE`` macro is set to 0.
 

	
 
  **Example**::
 

	
 
    fmt::print("Elapsed time: {0:.2f} seconds", 1.23);
 
  \endrst
 
  Formats ``args`` according to specifications in ``fmt`` and writes the output
 
  to ``stdout`` followed by a newline.
 
 */
 
template <typename S, typename... Args, typename Char = char_t<S>>
 
inline void print(const S& format_str, Args&&... args) {
 
  const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
 
  return detail::is_unicode<Char>()
 
             ? vprint(to_string_view(format_str), vargs)
 
             : detail::vprint_mojibake(stdout, to_string_view(format_str),
 
                                       vargs);
 
template <typename... T>
 
FMT_INLINE void println(format_string<T...> fmt, T&&... args) {
 
  return fmt::println(stdout, fmt, std::forward<T>(args)...);
 
}
 

	
 
FMT_END_EXPORT
 
FMT_GCC_PRAGMA("GCC pop_options")
 
FMT_END_NAMESPACE
 

	
 
#ifdef FMT_HEADER_ONLY
 
#  include "format.h"
 
#endif
 
#endif  // FMT_CORE_H_

Changeset was too big and was cut off... Show full diff anyway

0 comments (0 inline, 0 general)