File indexing completed on 2026-01-09 10:11:05
0001
0002
0003
0004
0005
0006
0007
0008 #ifndef FMT_BASE_H_
0009 #define FMT_BASE_H_
0010
0011 #if defined(FMT_IMPORT_STD) && !defined(FMT_MODULE)
0012 # define FMT_MODULE
0013 #endif
0014
0015 #ifndef FMT_MODULE
0016 # include <limits.h> // CHAR_BIT
0017 # include <stdio.h> // FILE
0018 # include <string.h> // memcmp
0019
0020 # include <type_traits> // std::enable_if
0021 #endif
0022
0023
0024 #define FMT_VERSION 110200
0025
0026
0027 #if defined(__clang__) && !defined(__ibmxl__)
0028 # define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
0029 #else
0030 # define FMT_CLANG_VERSION 0
0031 #endif
0032 #if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)
0033 # define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
0034 #else
0035 # define FMT_GCC_VERSION 0
0036 #endif
0037 #if defined(__ICL)
0038 # define FMT_ICC_VERSION __ICL
0039 #elif defined(__INTEL_COMPILER)
0040 # define FMT_ICC_VERSION __INTEL_COMPILER
0041 #else
0042 # define FMT_ICC_VERSION 0
0043 #endif
0044 #if defined(_MSC_VER)
0045 # define FMT_MSC_VERSION _MSC_VER
0046 #else
0047 # define FMT_MSC_VERSION 0
0048 #endif
0049
0050
0051 #ifdef _GLIBCXX_RELEASE
0052 # define FMT_GLIBCXX_RELEASE _GLIBCXX_RELEASE
0053 #else
0054 # define FMT_GLIBCXX_RELEASE 0
0055 #endif
0056 #ifdef _LIBCPP_VERSION
0057 # define FMT_LIBCPP_VERSION _LIBCPP_VERSION
0058 #else
0059 # define FMT_LIBCPP_VERSION 0
0060 #endif
0061
0062 #ifdef _MSVC_LANG
0063 # define FMT_CPLUSPLUS _MSVC_LANG
0064 #else
0065 # define FMT_CPLUSPLUS __cplusplus
0066 #endif
0067
0068
0069 #ifdef __has_feature
0070 # define FMT_HAS_FEATURE(x) __has_feature(x)
0071 #else
0072 # define FMT_HAS_FEATURE(x) 0
0073 #endif
0074 #ifdef __has_include
0075 # define FMT_HAS_INCLUDE(x) __has_include(x)
0076 #else
0077 # define FMT_HAS_INCLUDE(x) 0
0078 #endif
0079 #ifdef __has_builtin
0080 # define FMT_HAS_BUILTIN(x) __has_builtin(x)
0081 #else
0082 # define FMT_HAS_BUILTIN(x) 0
0083 #endif
0084 #ifdef __has_cpp_attribute
0085 # define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x)
0086 #else
0087 # define FMT_HAS_CPP_ATTRIBUTE(x) 0
0088 #endif
0089
0090 #define FMT_HAS_CPP14_ATTRIBUTE(attribute) \
0091 (FMT_CPLUSPLUS >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute))
0092
0093 #define FMT_HAS_CPP17_ATTRIBUTE(attribute) \
0094 (FMT_CPLUSPLUS >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute))
0095
0096
0097 #ifdef FMT_USE_CONSTEXPR
0098
0099 #elif FMT_GCC_VERSION >= 702 && FMT_CPLUSPLUS >= 201402L
0100
0101
0102 # define FMT_USE_CONSTEXPR 1
0103 #elif FMT_ICC_VERSION
0104 # define FMT_USE_CONSTEXPR 0
0105 #elif FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VERSION >= 1912
0106 # define FMT_USE_CONSTEXPR 1
0107 #else
0108 # define FMT_USE_CONSTEXPR 0
0109 #endif
0110 #if FMT_USE_CONSTEXPR
0111 # define FMT_CONSTEXPR constexpr
0112 #else
0113 # define FMT_CONSTEXPR
0114 #endif
0115
0116
0117 #if !defined(__cpp_lib_is_constant_evaluated)
0118 # define FMT_USE_CONSTEVAL 0
0119 #elif FMT_CPLUSPLUS < 201709L
0120 # define FMT_USE_CONSTEVAL 0
0121 #elif FMT_GLIBCXX_RELEASE && FMT_GLIBCXX_RELEASE < 10
0122 # define FMT_USE_CONSTEVAL 0
0123 #elif FMT_LIBCPP_VERSION && FMT_LIBCPP_VERSION < 10000
0124 # define FMT_USE_CONSTEVAL 0
0125 #elif defined(__apple_build_version__) && __apple_build_version__ < 14000029L
0126 # define FMT_USE_CONSTEVAL 0
0127 #elif FMT_MSC_VERSION && FMT_MSC_VERSION < 1929
0128 # define FMT_USE_CONSTEVAL 0
0129 #elif defined(__cpp_consteval)
0130 # define FMT_USE_CONSTEVAL 1
0131 #elif FMT_GCC_VERSION >= 1002 || FMT_CLANG_VERSION >= 1101
0132 # define FMT_USE_CONSTEVAL 1
0133 #else
0134 # define FMT_USE_CONSTEVAL 0
0135 #endif
0136 #if FMT_USE_CONSTEVAL
0137 # define FMT_CONSTEVAL consteval
0138 # define FMT_CONSTEXPR20 constexpr
0139 #else
0140 # define FMT_CONSTEVAL
0141 # define FMT_CONSTEXPR20
0142 #endif
0143
0144
0145 #ifdef FMT_USE_EXCEPTIONS
0146
0147 #elif defined(__GNUC__) && !defined(__EXCEPTIONS)
0148 # define FMT_USE_EXCEPTIONS 0
0149 #elif defined(__clang__) && !defined(__cpp_exceptions)
0150 # define FMT_USE_EXCEPTIONS 0
0151 #elif FMT_MSC_VERSION && !_HAS_EXCEPTIONS
0152 # define FMT_USE_EXCEPTIONS 0
0153 #else
0154 # define FMT_USE_EXCEPTIONS 1
0155 #endif
0156 #if FMT_USE_EXCEPTIONS
0157 # define FMT_TRY try
0158 # define FMT_CATCH(x) catch (x)
0159 #else
0160 # define FMT_TRY if (true)
0161 # define FMT_CATCH(x) if (false)
0162 #endif
0163
0164 #ifdef FMT_NO_UNIQUE_ADDRESS
0165
0166 #elif FMT_CPLUSPLUS < 202002L
0167
0168 #elif FMT_HAS_CPP_ATTRIBUTE(no_unique_address)
0169 # define FMT_NO_UNIQUE_ADDRESS [[no_unique_address]]
0170
0171 #elif FMT_MSC_VERSION >= 1929 && !FMT_CLANG_VERSION
0172 # define FMT_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]]
0173 #endif
0174 #ifndef FMT_NO_UNIQUE_ADDRESS
0175 # define FMT_NO_UNIQUE_ADDRESS
0176 #endif
0177
0178 #if FMT_HAS_CPP17_ATTRIBUTE(fallthrough)
0179 # define FMT_FALLTHROUGH [[fallthrough]]
0180 #elif defined(__clang__)
0181 # define FMT_FALLTHROUGH [[clang::fallthrough]]
0182 #elif FMT_GCC_VERSION >= 700 && \
0183 (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520)
0184 # define FMT_FALLTHROUGH [[gnu::fallthrough]]
0185 #else
0186 # define FMT_FALLTHROUGH
0187 #endif
0188
0189
0190 #if FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VERSION && !defined(__NVCC__)
0191 # define FMT_NORETURN [[noreturn]]
0192 #else
0193 # define FMT_NORETURN
0194 #endif
0195
0196 #ifdef FMT_NODISCARD
0197
0198 #elif FMT_HAS_CPP17_ATTRIBUTE(nodiscard)
0199 # define FMT_NODISCARD [[nodiscard]]
0200 #else
0201 # define FMT_NODISCARD
0202 #endif
0203
0204 #ifdef FMT_DEPRECATED
0205
0206 #elif FMT_HAS_CPP14_ATTRIBUTE(deprecated)
0207 # define FMT_DEPRECATED [[deprecated]]
0208 #else
0209 # define FMT_DEPRECATED
0210 #endif
0211
0212 #if FMT_GCC_VERSION || FMT_CLANG_VERSION
0213 # define FMT_VISIBILITY(value) __attribute__((visibility(value)))
0214 #else
0215 # define FMT_VISIBILITY(value)
0216 #endif
0217
0218
0219 #define FMT_PRAGMA_IMPL(x) _Pragma(#x)
0220 #if FMT_GCC_VERSION >= 504 && !defined(__NVCOMPILER)
0221
0222
0223 # define FMT_PRAGMA_GCC(x) FMT_PRAGMA_IMPL(GCC x)
0224 #else
0225 # define FMT_PRAGMA_GCC(x)
0226 #endif
0227 #if FMT_CLANG_VERSION
0228 # define FMT_PRAGMA_CLANG(x) FMT_PRAGMA_IMPL(clang x)
0229 #else
0230 # define FMT_PRAGMA_CLANG(x)
0231 #endif
0232 #if FMT_MSC_VERSION
0233 # define FMT_MSC_WARNING(...) __pragma(warning(__VA_ARGS__))
0234 #else
0235 # define FMT_MSC_WARNING(...)
0236 #endif
0237
0238
0239 FMT_PRAGMA_GCC(push_options)
0240 #if !defined(__OPTIMIZE__) && !defined(__CUDACC__) && !defined(FMT_MODULE)
0241 FMT_PRAGMA_GCC(optimize("Og"))
0242 # define FMT_GCC_OPTIMIZED
0243 #endif
0244 FMT_PRAGMA_CLANG(diagnostic push)
0245
0246 #ifdef FMT_ALWAYS_INLINE
0247
0248 #elif FMT_GCC_VERSION || FMT_CLANG_VERSION
0249 # define FMT_ALWAYS_INLINE inline __attribute__((always_inline))
0250 #else
0251 # define FMT_ALWAYS_INLINE inline
0252 #endif
0253
0254 #if defined(NDEBUG) || defined(FMT_GCC_OPTIMIZED)
0255 # define FMT_INLINE FMT_ALWAYS_INLINE
0256 #else
0257 # define FMT_INLINE inline
0258 #endif
0259
0260 #ifndef FMT_BEGIN_NAMESPACE
0261 # define FMT_BEGIN_NAMESPACE \
0262 namespace fmt { \
0263 inline namespace v11 {
0264 # define FMT_END_NAMESPACE \
0265 } \
0266 }
0267 #endif
0268
0269 #ifndef FMT_EXPORT
0270 # define FMT_EXPORT
0271 # define FMT_BEGIN_EXPORT
0272 # define FMT_END_EXPORT
0273 #endif
0274
0275 #ifdef _WIN32
0276 # define FMT_WIN32 1
0277 #else
0278 # define FMT_WIN32 0
0279 #endif
0280
0281 #if !defined(FMT_HEADER_ONLY) && FMT_WIN32
0282 # if defined(FMT_LIB_EXPORT)
0283 # define FMT_API __declspec(dllexport)
0284 # elif defined(FMT_SHARED)
0285 # define FMT_API __declspec(dllimport)
0286 # endif
0287 #elif defined(FMT_LIB_EXPORT) || defined(FMT_SHARED)
0288 # define FMT_API FMT_VISIBILITY("default")
0289 #endif
0290 #ifndef FMT_API
0291 # define FMT_API
0292 #endif
0293
0294 #ifndef FMT_OPTIMIZE_SIZE
0295 # define FMT_OPTIMIZE_SIZE 0
0296 #endif
0297
0298
0299
0300 #ifndef FMT_BUILTIN_TYPES
0301 # define FMT_BUILTIN_TYPES 1
0302 #endif
0303
0304 #define FMT_APPLY_VARIADIC(expr) \
0305 using unused = int[]; \
0306 (void)unused { 0, (expr, 0)... }
0307
0308 FMT_BEGIN_NAMESPACE
0309
0310
0311 template <bool B, typename T = void>
0312 using enable_if_t = typename std::enable_if<B, T>::type;
0313 template <bool B, typename T, typename F>
0314 using conditional_t = typename std::conditional<B, T, F>::type;
0315 template <bool B> using bool_constant = std::integral_constant<bool, B>;
0316 template <typename T>
0317 using remove_reference_t = typename std::remove_reference<T>::type;
0318 template <typename T>
0319 using remove_const_t = typename std::remove_const<T>::type;
0320 template <typename T>
0321 using remove_cvref_t = typename std::remove_cv<remove_reference_t<T>>::type;
0322 template <typename T>
0323 using make_unsigned_t = typename std::make_unsigned<T>::type;
0324 template <typename T>
0325 using underlying_t = typename std::underlying_type<T>::type;
0326 template <typename T> using decay_t = typename std::decay<T>::type;
0327 using nullptr_t = decltype(nullptr);
0328
0329 #if (FMT_GCC_VERSION && FMT_GCC_VERSION < 500) || FMT_MSC_VERSION
0330
0331 template <typename...> struct void_t_impl {
0332 using type = void;
0333 };
0334 template <typename... T> using void_t = typename void_t_impl<T...>::type;
0335 #else
0336 template <typename...> using void_t = void;
0337 #endif
0338
0339 struct monostate {
0340 constexpr monostate() {}
0341 };
0342
0343
0344
0345
0346 #ifdef FMT_DOC
0347 # define FMT_ENABLE_IF(...)
0348 #else
0349 # define FMT_ENABLE_IF(...) fmt::enable_if_t<(__VA_ARGS__), int> = 0
0350 #endif
0351
0352 template <typename T> constexpr auto min_of(T a, T b) -> T {
0353 return a < b ? a : b;
0354 }
0355 template <typename T> constexpr auto max_of(T a, T b) -> T {
0356 return a > b ? a : b;
0357 }
0358
0359 namespace detail {
0360
0361
0362
0363 template <typename... T> FMT_CONSTEXPR void ignore_unused(const T&...) {}
0364
0365 constexpr auto is_constant_evaluated(bool default_value = false) noexcept
0366 -> bool {
0367
0368
0369 #if FMT_CPLUSPLUS >= 202002L && FMT_GLIBCXX_RELEASE >= 12 && \
0370 (FMT_CLANG_VERSION >= 1400 && FMT_CLANG_VERSION < 1500)
0371 ignore_unused(default_value);
0372 return __builtin_is_constant_evaluated();
0373 #elif defined(__cpp_lib_is_constant_evaluated)
0374 ignore_unused(default_value);
0375 return std::is_constant_evaluated();
0376 #else
0377 return default_value;
0378 #endif
0379 }
0380
0381
0382 template <typename T> FMT_ALWAYS_INLINE constexpr auto const_check(T val) -> T {
0383 return val;
0384 }
0385
0386 FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
0387 const char* message);
0388
0389 #if defined(FMT_ASSERT)
0390
0391 #elif defined(NDEBUG)
0392
0393 # define FMT_ASSERT(condition, message) \
0394 fmt::detail::ignore_unused((condition), (message))
0395 #else
0396 # define FMT_ASSERT(condition, message) \
0397 ((condition) \
0398 ? (void)0 \
0399 : fmt::detail::assert_fail(__FILE__, __LINE__, (message)))
0400 #endif
0401
0402 #ifdef FMT_USE_INT128
0403
0404 #elif defined(__SIZEOF_INT128__) && !defined(__NVCC__) && \
0405 !(FMT_CLANG_VERSION && FMT_MSC_VERSION)
0406 # define FMT_USE_INT128 1
0407 using int128_opt = __int128_t;
0408 using uint128_opt = __uint128_t;
0409 inline auto map(int128_opt x) -> int128_opt { return x; }
0410 inline auto map(uint128_opt x) -> uint128_opt { return x; }
0411 #else
0412 # define FMT_USE_INT128 0
0413 #endif
0414 #if !FMT_USE_INT128
0415 enum class int128_opt {};
0416 enum class uint128_opt {};
0417
0418 inline auto map(int128_opt) -> monostate { return {}; }
0419 inline auto map(uint128_opt) -> monostate { return {}; }
0420 #endif
0421
0422 #ifndef FMT_USE_BITINT
0423 # define FMT_USE_BITINT (FMT_CLANG_VERSION >= 1500)
0424 #endif
0425
0426 #if FMT_USE_BITINT
0427 FMT_PRAGMA_CLANG(diagnostic ignored "-Wbit-int-extension")
0428 template <int N> using bitint = _BitInt(N);
0429 template <int N> using ubitint = unsigned _BitInt(N);
0430 #else
0431 template <int N> struct bitint {};
0432 template <int N> struct ubitint {};
0433 #endif
0434
0435
0436 template <typename Int>
0437 FMT_CONSTEXPR auto to_unsigned(Int value) -> make_unsigned_t<Int> {
0438 FMT_ASSERT(std::is_unsigned<Int>::value || value >= 0, "negative value");
0439 return static_cast<make_unsigned_t<Int>>(value);
0440 }
0441
0442 template <typename Char>
0443 using unsigned_char = conditional_t<sizeof(Char) == 1, unsigned char, unsigned>;
0444
0445
0446
0447 template <typename T, typename Enable = void>
0448 struct is_std_string_like : std::false_type {};
0449 template <typename T>
0450 struct is_std_string_like<T, void_t<decltype(std::declval<T>().find_first_of(
0451 typename T::value_type(), 0))>>
0452 : std::is_convertible<decltype(std::declval<T>().data()),
0453 const typename T::value_type*> {};
0454
0455
0456 enum { is_utf8_enabled = "\u00A7"[1] == '\xA7' };
0457 enum { use_utf8 = !FMT_WIN32 || is_utf8_enabled };
0458
0459 #ifndef FMT_UNICODE
0460 # define FMT_UNICODE 1
0461 #endif
0462
0463 static_assert(!FMT_UNICODE || use_utf8,
0464 "Unicode support requires compiling with /utf-8");
0465
0466 template <typename T> constexpr const char* narrow(const T*) { return nullptr; }
0467 constexpr FMT_ALWAYS_INLINE const char* narrow(const char* s) { return s; }
0468
0469 template <typename Char>
0470 FMT_CONSTEXPR auto compare(const Char* s1, const Char* s2, std::size_t n)
0471 -> int {
0472 if (!is_constant_evaluated() && sizeof(Char) == 1) return memcmp(s1, s2, n);
0473 for (; n != 0; ++s1, ++s2, --n) {
0474 if (*s1 < *s2) return -1;
0475 if (*s1 > *s2) return 1;
0476 }
0477 return 0;
0478 }
0479
0480 namespace adl {
0481 using namespace std;
0482
0483 template <typename Container>
0484 auto invoke_back_inserter()
0485 -> decltype(back_inserter(std::declval<Container&>()));
0486 }
0487
0488 template <typename It, typename Enable = std::true_type>
0489 struct is_back_insert_iterator : std::false_type {};
0490
0491 template <typename It>
0492 struct is_back_insert_iterator<
0493 It, bool_constant<std::is_same<
0494 decltype(adl::invoke_back_inserter<typename It::container_type>()),
0495 It>::value>> : std::true_type {};
0496
0497
0498 template <typename OutputIt>
0499 inline FMT_CONSTEXPR20 auto get_container(OutputIt it) ->
0500 typename OutputIt::container_type& {
0501 struct accessor : OutputIt {
0502 FMT_CONSTEXPR20 accessor(OutputIt base) : OutputIt(base) {}
0503 using OutputIt::container;
0504 };
0505 return *accessor(it).container;
0506 }
0507 }
0508
0509
0510 FMT_BEGIN_EXPORT
0511
0512
0513
0514
0515
0516
0517
0518
0519 template <typename Char> class basic_string_view {
0520 private:
0521 const Char* data_;
0522 size_t size_;
0523
0524 public:
0525 using value_type = Char;
0526 using iterator = const Char*;
0527
0528 constexpr basic_string_view() noexcept : data_(nullptr), size_(0) {}
0529
0530
0531 constexpr basic_string_view(const Char* s, size_t count) noexcept
0532 : data_(s), size_(count) {}
0533
0534 constexpr basic_string_view(nullptr_t) = delete;
0535
0536
0537 #if FMT_GCC_VERSION
0538 FMT_ALWAYS_INLINE
0539 #endif
0540 FMT_CONSTEXPR20 basic_string_view(const Char* s) : data_(s) {
0541 #if FMT_HAS_BUILTIN(__builtin_strlen) || FMT_GCC_VERSION || FMT_CLANG_VERSION
0542 if (std::is_same<Char, char>::value && !detail::is_constant_evaluated()) {
0543 size_ = __builtin_strlen(detail::narrow(s));
0544 return;
0545 }
0546 #endif
0547 size_t len = 0;
0548 while (*s++) ++len;
0549 size_ = len;
0550 }
0551
0552
0553
0554 template <typename S,
0555 FMT_ENABLE_IF(detail::is_std_string_like<S>::value&& std::is_same<
0556 typename S::value_type, Char>::value)>
0557 FMT_CONSTEXPR basic_string_view(const S& s) noexcept
0558 : data_(s.data()), size_(s.size()) {}
0559
0560
0561 constexpr auto data() const noexcept -> const Char* { return data_; }
0562
0563
0564 constexpr auto size() const noexcept -> size_t { return size_; }
0565
0566 constexpr auto begin() const noexcept -> iterator { return data_; }
0567 constexpr auto end() const noexcept -> iterator { return data_ + size_; }
0568
0569 constexpr auto operator[](size_t pos) const noexcept -> const Char& {
0570 return data_[pos];
0571 }
0572
0573 FMT_CONSTEXPR void remove_prefix(size_t n) noexcept {
0574 data_ += n;
0575 size_ -= n;
0576 }
0577
0578 FMT_CONSTEXPR auto starts_with(basic_string_view<Char> sv) const noexcept
0579 -> bool {
0580 return size_ >= sv.size_ && detail::compare(data_, sv.data_, sv.size_) == 0;
0581 }
0582 FMT_CONSTEXPR auto starts_with(Char c) const noexcept -> bool {
0583 return size_ >= 1 && *data_ == c;
0584 }
0585 FMT_CONSTEXPR auto starts_with(const Char* s) const -> bool {
0586 return starts_with(basic_string_view<Char>(s));
0587 }
0588
0589 FMT_CONSTEXPR auto compare(basic_string_view other) const -> int {
0590 int result =
0591 detail::compare(data_, other.data_, min_of(size_, other.size_));
0592 if (result != 0) return result;
0593 return size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1);
0594 }
0595
0596 FMT_CONSTEXPR friend auto operator==(basic_string_view lhs,
0597 basic_string_view rhs) -> bool {
0598 return lhs.compare(rhs) == 0;
0599 }
0600 friend auto operator!=(basic_string_view lhs, basic_string_view rhs) -> bool {
0601 return lhs.compare(rhs) != 0;
0602 }
0603 friend auto operator<(basic_string_view lhs, basic_string_view rhs) -> bool {
0604 return lhs.compare(rhs) < 0;
0605 }
0606 friend auto operator<=(basic_string_view lhs, basic_string_view rhs) -> bool {
0607 return lhs.compare(rhs) <= 0;
0608 }
0609 friend auto operator>(basic_string_view lhs, basic_string_view rhs) -> bool {
0610 return lhs.compare(rhs) > 0;
0611 }
0612 friend auto operator>=(basic_string_view lhs, basic_string_view rhs) -> bool {
0613 return lhs.compare(rhs) >= 0;
0614 }
0615 };
0616
0617 using string_view = basic_string_view<char>;
0618
0619
0620 template <typename T> struct is_xchar : std::false_type {};
0621 template <> struct is_xchar<wchar_t> : std::true_type {};
0622 template <> struct is_xchar<char16_t> : std::true_type {};
0623 template <> struct is_xchar<char32_t> : std::true_type {};
0624 #ifdef __cpp_char8_t
0625 template <> struct is_xchar<char8_t> : std::true_type {};
0626 #endif
0627
0628
0629 template <typename T> struct is_char : is_xchar<T> {};
0630 template <> struct is_char<char> : std::true_type {};
0631
0632 template <typename T> class basic_appender;
0633 using appender = basic_appender<char>;
0634
0635
0636 template <typename T> struct is_contiguous : std::false_type {};
0637
0638 class context;
0639 template <typename OutputIt, typename Char> class generic_context;
0640 template <typename Char> class parse_context;
0641
0642
0643 template <typename Char> using basic_format_parse_context = parse_context<Char>;
0644 using format_parse_context = parse_context<char>;
0645 template <typename OutputIt, typename Char>
0646 using basic_format_context =
0647 conditional_t<std::is_same<OutputIt, appender>::value, context,
0648 generic_context<OutputIt, Char>>;
0649 using format_context = context;
0650
0651 template <typename Char>
0652 using buffered_context =
0653 conditional_t<std::is_same<Char, char>::value, context,
0654 generic_context<basic_appender<Char>, Char>>;
0655
0656 template <typename Context> class basic_format_arg;
0657 template <typename Context> class basic_format_args;
0658
0659
0660
0661 using format_args = basic_format_args<context>;
0662
0663
0664 template <typename T, typename Char = char, typename Enable = void>
0665 struct formatter {
0666
0667 formatter() = delete;
0668 };
0669
0670
0671
0672
0673 FMT_NORETURN FMT_API void report_error(const char* message);
0674
0675 enum class presentation_type : unsigned char {
0676
0677 none = 0,
0678 debug = 1,
0679 string = 2,
0680
0681
0682 dec = 3,
0683 hex,
0684 oct,
0685 bin,
0686 chr,
0687
0688
0689 pointer = 3,
0690
0691
0692 exp = 1,
0693 fixed,
0694 general,
0695 hexfloat
0696 };
0697
0698 enum class align { none, left, right, center, numeric };
0699 enum class sign { none, minus, plus, space };
0700 enum class arg_id_kind { none, index, name };
0701
0702
0703 class basic_specs {
0704 private:
0705
0706
0707
0708
0709
0710
0711
0712
0713
0714
0715
0716
0717
0718
0719
0720
0721
0722 enum : unsigned {
0723 type_mask = 0x00007,
0724 align_mask = 0x00038,
0725 width_mask = 0x000C0,
0726 precision_mask = 0x00300,
0727 sign_mask = 0x00C00,
0728 uppercase_mask = 0x01000,
0729 alternate_mask = 0x02000,
0730 localized_mask = 0x04000,
0731 fill_size_mask = 0x38000,
0732
0733 align_shift = 3,
0734 width_shift = 6,
0735 precision_shift = 8,
0736 sign_shift = 10,
0737 fill_size_shift = 15,
0738
0739 max_fill_size = 4
0740 };
0741
0742 unsigned data_ = 1 << fill_size_shift;
0743 static_assert(sizeof(basic_specs::data_) * CHAR_BIT >= 18, "");
0744
0745
0746 char fill_data_[max_fill_size] = {' '};
0747
0748 FMT_CONSTEXPR void set_fill_size(size_t size) {
0749 data_ = (data_ & ~fill_size_mask) |
0750 (static_cast<unsigned>(size) << fill_size_shift);
0751 }
0752
0753 public:
0754 constexpr auto type() const -> presentation_type {
0755 return static_cast<presentation_type>(data_ & type_mask);
0756 }
0757 FMT_CONSTEXPR void set_type(presentation_type t) {
0758 data_ = (data_ & ~type_mask) | static_cast<unsigned>(t);
0759 }
0760
0761 constexpr auto align() const -> align {
0762 return static_cast<fmt::align>((data_ & align_mask) >> align_shift);
0763 }
0764 FMT_CONSTEXPR void set_align(fmt::align a) {
0765 data_ = (data_ & ~align_mask) | (static_cast<unsigned>(a) << align_shift);
0766 }
0767
0768 constexpr auto dynamic_width() const -> arg_id_kind {
0769 return static_cast<arg_id_kind>((data_ & width_mask) >> width_shift);
0770 }
0771 FMT_CONSTEXPR void set_dynamic_width(arg_id_kind w) {
0772 data_ = (data_ & ~width_mask) | (static_cast<unsigned>(w) << width_shift);
0773 }
0774
0775 FMT_CONSTEXPR auto dynamic_precision() const -> arg_id_kind {
0776 return static_cast<arg_id_kind>((data_ & precision_mask) >>
0777 precision_shift);
0778 }
0779 FMT_CONSTEXPR void set_dynamic_precision(arg_id_kind p) {
0780 data_ = (data_ & ~precision_mask) |
0781 (static_cast<unsigned>(p) << precision_shift);
0782 }
0783
0784 constexpr bool dynamic() const {
0785 return (data_ & (width_mask | precision_mask)) != 0;
0786 }
0787
0788 constexpr auto sign() const -> sign {
0789 return static_cast<fmt::sign>((data_ & sign_mask) >> sign_shift);
0790 }
0791 FMT_CONSTEXPR void set_sign(fmt::sign s) {
0792 data_ = (data_ & ~sign_mask) | (static_cast<unsigned>(s) << sign_shift);
0793 }
0794
0795 constexpr auto upper() const -> bool { return (data_ & uppercase_mask) != 0; }
0796 FMT_CONSTEXPR void set_upper() { data_ |= uppercase_mask; }
0797
0798 constexpr auto alt() const -> bool { return (data_ & alternate_mask) != 0; }
0799 FMT_CONSTEXPR void set_alt() { data_ |= alternate_mask; }
0800 FMT_CONSTEXPR void clear_alt() { data_ &= ~alternate_mask; }
0801
0802 constexpr auto localized() const -> bool {
0803 return (data_ & localized_mask) != 0;
0804 }
0805 FMT_CONSTEXPR void set_localized() { data_ |= localized_mask; }
0806
0807 constexpr auto fill_size() const -> size_t {
0808 return (data_ & fill_size_mask) >> fill_size_shift;
0809 }
0810
0811 template <typename Char, FMT_ENABLE_IF(std::is_same<Char, char>::value)>
0812 constexpr auto fill() const -> const Char* {
0813 return fill_data_;
0814 }
0815 template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
0816 constexpr auto fill() const -> const Char* {
0817 return nullptr;
0818 }
0819
0820 template <typename Char> constexpr auto fill_unit() const -> Char {
0821 using uchar = unsigned char;
0822 return static_cast<Char>(static_cast<uchar>(fill_data_[0]) |
0823 (static_cast<uchar>(fill_data_[1]) << 8) |
0824 (static_cast<uchar>(fill_data_[2]) << 16));
0825 }
0826
0827 FMT_CONSTEXPR void set_fill(char c) {
0828 fill_data_[0] = c;
0829 set_fill_size(1);
0830 }
0831
0832 template <typename Char>
0833 FMT_CONSTEXPR void set_fill(basic_string_view<Char> s) {
0834 auto size = s.size();
0835 set_fill_size(size);
0836 if (size == 1) {
0837 unsigned uchar = static_cast<detail::unsigned_char<Char>>(s[0]);
0838 fill_data_[0] = static_cast<char>(uchar);
0839 fill_data_[1] = static_cast<char>(uchar >> 8);
0840 fill_data_[2] = static_cast<char>(uchar >> 16);
0841 return;
0842 }
0843 FMT_ASSERT(size <= max_fill_size, "invalid fill");
0844 for (size_t i = 0; i < size; ++i)
0845 fill_data_[i & 3] = static_cast<char>(s[i]);
0846 }
0847
0848 FMT_CONSTEXPR void copy_fill_from(const basic_specs& specs) {
0849 set_fill_size(specs.fill_size());
0850 for (size_t i = 0; i < max_fill_size; ++i)
0851 fill_data_[i] = specs.fill_data_[i];
0852 }
0853 };
0854
0855
0856 struct format_specs : basic_specs {
0857 int width;
0858 int precision;
0859
0860 constexpr format_specs() : width(0), precision(-1) {}
0861 };
0862
0863
0864
0865
0866
0867 template <typename Char = char> class parse_context {
0868 private:
0869 basic_string_view<Char> fmt_;
0870 int next_arg_id_;
0871
0872 enum { use_constexpr_cast = !FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200 };
0873
0874 FMT_CONSTEXPR void do_check_arg_id(int arg_id);
0875
0876 public:
0877 using char_type = Char;
0878 using iterator = const Char*;
0879
0880 constexpr explicit parse_context(basic_string_view<Char> fmt,
0881 int next_arg_id = 0)
0882 : fmt_(fmt), next_arg_id_(next_arg_id) {}
0883
0884
0885
0886 constexpr auto begin() const noexcept -> iterator { return fmt_.begin(); }
0887
0888
0889 constexpr auto end() const noexcept -> iterator { return fmt_.end(); }
0890
0891
0892 FMT_CONSTEXPR void advance_to(iterator it) {
0893 fmt_.remove_prefix(detail::to_unsigned(it - begin()));
0894 }
0895
0896
0897
0898 FMT_CONSTEXPR auto next_arg_id() -> int {
0899 if (next_arg_id_ < 0) {
0900 report_error("cannot switch from manual to automatic argument indexing");
0901 return 0;
0902 }
0903 int id = next_arg_id_++;
0904 do_check_arg_id(id);
0905 return id;
0906 }
0907
0908
0909
0910 FMT_CONSTEXPR void check_arg_id(int id) {
0911 if (next_arg_id_ > 0) {
0912 report_error("cannot switch from automatic to manual argument indexing");
0913 return;
0914 }
0915 next_arg_id_ = -1;
0916 do_check_arg_id(id);
0917 }
0918 FMT_CONSTEXPR void check_arg_id(basic_string_view<Char>) {
0919 next_arg_id_ = -1;
0920 }
0921 FMT_CONSTEXPR void check_dynamic_spec(int arg_id);
0922 };
0923
0924 FMT_END_EXPORT
0925
0926 namespace detail {
0927
0928
0929
0930
0931 template <typename Char, FMT_ENABLE_IF(is_char<Char>::value)>
0932 constexpr auto to_string_view(const Char* s) -> basic_string_view<Char> {
0933 return s;
0934 }
0935 template <typename T, FMT_ENABLE_IF(is_std_string_like<T>::value)>
0936 constexpr auto to_string_view(const T& s)
0937 -> basic_string_view<typename T::value_type> {
0938 return s;
0939 }
0940 template <typename Char>
0941 constexpr auto to_string_view(basic_string_view<Char> s)
0942 -> basic_string_view<Char> {
0943 return s;
0944 }
0945
0946 template <typename T, typename Enable = void>
0947 struct has_to_string_view : std::false_type {};
0948
0949 template <typename T>
0950 struct has_to_string_view<
0951 T, void_t<decltype(detail::to_string_view(std::declval<T>()))>>
0952 : std::true_type {};
0953
0954
0955 template <typename S,
0956 typename V = decltype(detail::to_string_view(std::declval<S>()))>
0957 using char_t = typename V::value_type;
0958
0959 enum class type {
0960 none_type,
0961
0962 int_type,
0963 uint_type,
0964 long_long_type,
0965 ulong_long_type,
0966 int128_type,
0967 uint128_type,
0968 bool_type,
0969 char_type,
0970 last_integer_type = char_type,
0971
0972 float_type,
0973 double_type,
0974 long_double_type,
0975 last_numeric_type = long_double_type,
0976 cstring_type,
0977 string_type,
0978 pointer_type,
0979 custom_type
0980 };
0981
0982
0983 template <typename T, typename Char>
0984 struct type_constant : std::integral_constant<type, type::custom_type> {};
0985
0986 #define FMT_TYPE_CONSTANT(Type, constant) \
0987 template <typename Char> \
0988 struct type_constant<Type, Char> \
0989 : std::integral_constant<type, type::constant> {}
0990
0991 FMT_TYPE_CONSTANT(int, int_type);
0992 FMT_TYPE_CONSTANT(unsigned, uint_type);
0993 FMT_TYPE_CONSTANT(long long, long_long_type);
0994 FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type);
0995 FMT_TYPE_CONSTANT(int128_opt, int128_type);
0996 FMT_TYPE_CONSTANT(uint128_opt, uint128_type);
0997 FMT_TYPE_CONSTANT(bool, bool_type);
0998 FMT_TYPE_CONSTANT(Char, char_type);
0999 FMT_TYPE_CONSTANT(float, float_type);
1000 FMT_TYPE_CONSTANT(double, double_type);
1001 FMT_TYPE_CONSTANT(long double, long_double_type);
1002 FMT_TYPE_CONSTANT(const Char*, cstring_type);
1003 FMT_TYPE_CONSTANT(basic_string_view<Char>, string_type);
1004 FMT_TYPE_CONSTANT(const void*, pointer_type);
1005
1006 constexpr auto is_integral_type(type t) -> bool {
1007 return t > type::none_type && t <= type::last_integer_type;
1008 }
1009 constexpr auto is_arithmetic_type(type t) -> bool {
1010 return t > type::none_type && t <= type::last_numeric_type;
1011 }
1012
1013 constexpr auto set(type rhs) -> int { return 1 << static_cast<int>(rhs); }
1014 constexpr auto in(type t, int set) -> bool {
1015 return ((set >> static_cast<int>(t)) & 1) != 0;
1016 }
1017
1018
1019 enum {
1020 sint_set =
1021 set(type::int_type) | set(type::long_long_type) | set(type::int128_type),
1022 uint_set = set(type::uint_type) | set(type::ulong_long_type) |
1023 set(type::uint128_type),
1024 bool_set = set(type::bool_type),
1025 char_set = set(type::char_type),
1026 float_set = set(type::float_type) | set(type::double_type) |
1027 set(type::long_double_type),
1028 string_set = set(type::string_type),
1029 cstring_set = set(type::cstring_type),
1030 pointer_set = set(type::pointer_type)
1031 };
1032
1033 struct view {};
1034
1035 template <typename T, typename Enable = std::true_type>
1036 struct is_view : std::false_type {};
1037 template <typename T>
1038 struct is_view<T, bool_constant<sizeof(T) != 0>> : std::is_base_of<view, T> {};
1039
1040 template <typename Char, typename T> struct named_arg;
1041 template <typename T> struct is_named_arg : std::false_type {};
1042 template <typename T> struct is_static_named_arg : std::false_type {};
1043
1044 template <typename Char, typename T>
1045 struct is_named_arg<named_arg<Char, T>> : std::true_type {};
1046
1047 template <typename Char, typename T> struct named_arg : view {
1048 const Char* name;
1049 const T& value;
1050
1051 named_arg(const Char* n, const T& v) : name(n), value(v) {}
1052 static_assert(!is_named_arg<T>::value, "nested named arguments");
1053 };
1054
1055 template <bool B = false> constexpr auto count() -> int { return B ? 1 : 0; }
1056 template <bool B1, bool B2, bool... Tail> constexpr auto count() -> int {
1057 return (B1 ? 1 : 0) + count<B2, Tail...>();
1058 }
1059
1060 template <typename... Args> constexpr auto count_named_args() -> int {
1061 return count<is_named_arg<Args>::value...>();
1062 }
1063 template <typename... Args> constexpr auto count_static_named_args() -> int {
1064 return count<is_static_named_arg<Args>::value...>();
1065 }
1066
1067 template <typename Char> struct named_arg_info {
1068 const Char* name;
1069 int id;
1070 };
1071
1072
1073 template <typename Char>
1074 FMT_CONSTEXPR void check_for_duplicate(named_arg_info<Char>* named_args,
1075 int named_arg_index,
1076 basic_string_view<Char> arg_name) {
1077 for (int i = 0; i < named_arg_index; ++i) {
1078 if (named_args[i].name == arg_name) report_error("duplicate named arg");
1079 }
1080 }
1081
1082 template <typename Char, typename T, FMT_ENABLE_IF(!is_named_arg<T>::value)>
1083 void init_named_arg(named_arg_info<Char>*, int& arg_index, int&, const T&) {
1084 ++arg_index;
1085 }
1086 template <typename Char, typename T, FMT_ENABLE_IF(is_named_arg<T>::value)>
1087 void init_named_arg(named_arg_info<Char>* named_args, int& arg_index,
1088 int& named_arg_index, const T& arg) {
1089 check_for_duplicate<Char>(named_args, named_arg_index, arg.name);
1090 named_args[named_arg_index++] = {arg.name, arg_index++};
1091 }
1092
1093 template <typename T, typename Char,
1094 FMT_ENABLE_IF(!is_static_named_arg<T>::value)>
1095 FMT_CONSTEXPR void init_static_named_arg(named_arg_info<Char>*, int& arg_index,
1096 int&) {
1097 ++arg_index;
1098 }
1099 template <typename T, typename Char,
1100 FMT_ENABLE_IF(is_static_named_arg<T>::value)>
1101 FMT_CONSTEXPR void init_static_named_arg(named_arg_info<Char>* named_args,
1102 int& arg_index, int& named_arg_index) {
1103 check_for_duplicate<Char>(named_args, named_arg_index, T::name);
1104 named_args[named_arg_index++] = {T::name, arg_index++};
1105 }
1106
1107
1108
1109 enum { long_short = sizeof(long) == sizeof(int) && FMT_BUILTIN_TYPES };
1110 using long_type = conditional_t<long_short, int, long long>;
1111 using ulong_type = conditional_t<long_short, unsigned, unsigned long long>;
1112
1113 template <typename T>
1114 using format_as_result =
1115 remove_cvref_t<decltype(format_as(std::declval<const T&>()))>;
1116 template <typename T>
1117 using format_as_member_result =
1118 remove_cvref_t<decltype(formatter<T>::format_as(std::declval<const T&>()))>;
1119
1120 template <typename T, typename Enable = std::true_type>
1121 struct use_format_as : std::false_type {};
1122
1123 template <typename T, typename Enable = std::true_type>
1124 struct use_format_as_member : std::false_type {};
1125
1126
1127 template <typename T>
1128 struct use_format_as<
1129 T, bool_constant<std::is_arithmetic<format_as_result<T>>::value>>
1130 : std::true_type {};
1131 template <typename T>
1132 struct use_format_as_member<
1133 T, bool_constant<std::is_arithmetic<format_as_member_result<T>>::value>>
1134 : std::true_type {};
1135
1136 template <typename T, typename U = remove_const_t<T>>
1137 using use_formatter =
1138 bool_constant<(std::is_class<T>::value || std::is_enum<T>::value ||
1139 std::is_union<T>::value || std::is_array<T>::value) &&
1140 !has_to_string_view<T>::value && !is_named_arg<T>::value &&
1141 !use_format_as<T>::value && !use_format_as_member<U>::value>;
1142
1143 template <typename Char, typename T, typename U = remove_const_t<T>>
1144 auto has_formatter_impl(T* p, buffered_context<Char>* ctx = nullptr)
1145 -> decltype(formatter<U, Char>().format(*p, *ctx), std::true_type());
1146 template <typename Char> auto has_formatter_impl(...) -> std::false_type;
1147
1148
1149 template <typename T, typename Char> constexpr auto has_formatter() -> bool {
1150 return decltype(has_formatter_impl<Char>(static_cast<T*>(nullptr)))::value;
1151 }
1152
1153
1154
1155 template <typename Char> struct type_mapper {
1156 static auto map(signed char) -> int;
1157 static auto map(unsigned char) -> unsigned;
1158 static auto map(short) -> int;
1159 static auto map(unsigned short) -> unsigned;
1160 static auto map(int) -> int;
1161 static auto map(unsigned) -> unsigned;
1162 static auto map(long) -> long_type;
1163 static auto map(unsigned long) -> ulong_type;
1164 static auto map(long long) -> long long;
1165 static auto map(unsigned long long) -> unsigned long long;
1166 static auto map(int128_opt) -> int128_opt;
1167 static auto map(uint128_opt) -> uint128_opt;
1168 static auto map(bool) -> bool;
1169
1170 template <int N>
1171 static auto map(bitint<N>) -> conditional_t<N <= 64, long long, void>;
1172 template <int N>
1173 static auto map(ubitint<N>)
1174 -> conditional_t<N <= 64, unsigned long long, void>;
1175
1176 template <typename T, FMT_ENABLE_IF(is_char<T>::value)>
1177 static auto map(T) -> conditional_t<
1178 std::is_same<T, char>::value || std::is_same<T, Char>::value, Char, void>;
1179
1180 static auto map(float) -> float;
1181 static auto map(double) -> double;
1182 static auto map(long double) -> long double;
1183
1184 static auto map(Char*) -> const Char*;
1185 static auto map(const Char*) -> const Char*;
1186 template <typename T, typename C = char_t<T>,
1187 FMT_ENABLE_IF(!std::is_pointer<T>::value)>
1188 static auto map(const T&) -> conditional_t<std::is_same<C, Char>::value,
1189 basic_string_view<C>, void>;
1190
1191 static auto map(void*) -> const void*;
1192 static auto map(const void*) -> const void*;
1193 static auto map(volatile void*) -> const void*;
1194 static auto map(const volatile void*) -> const void*;
1195 static auto map(nullptr_t) -> const void*;
1196 template <typename T, FMT_ENABLE_IF(std::is_pointer<T>::value ||
1197 std::is_member_pointer<T>::value)>
1198 static auto map(const T&) -> void;
1199
1200 template <typename T, FMT_ENABLE_IF(use_format_as<T>::value)>
1201 static auto map(const T& x) -> decltype(map(format_as(x)));
1202 template <typename T, FMT_ENABLE_IF(use_format_as_member<T>::value)>
1203 static auto map(const T& x) -> decltype(map(formatter<T>::format_as(x)));
1204
1205 template <typename T, FMT_ENABLE_IF(use_formatter<T>::value)>
1206 static auto map(T&) -> conditional_t<has_formatter<T, Char>(), T&, void>;
1207
1208 template <typename T, FMT_ENABLE_IF(is_named_arg<T>::value)>
1209 static auto map(const T& named_arg) -> decltype(map(named_arg.value));
1210 };
1211
1212
1213 template <typename T, typename Char>
1214 using mapped_t = decltype(detail::type_mapper<Char>::map(std::declval<T&>()));
1215
1216
1217 template <typename T, typename Char = char>
1218 using mapped_type_constant = type_constant<mapped_t<T, Char>, Char>;
1219
1220 template <typename T, typename Context,
1221 type TYPE =
1222 mapped_type_constant<T, typename Context::char_type>::value>
1223 using stored_type_constant = std::integral_constant<
1224 type, Context::builtin_types || TYPE == type::int_type ? TYPE
1225 : type::custom_type>;
1226
1227 template <typename Char>
1228 class compile_parse_context : public parse_context<Char> {
1229 private:
1230 int num_args_;
1231 const type* types_;
1232 using base = parse_context<Char>;
1233
1234 public:
1235 FMT_CONSTEXPR explicit compile_parse_context(basic_string_view<Char> fmt,
1236 int num_args, const type* types,
1237 int next_arg_id = 0)
1238 : base(fmt, next_arg_id), num_args_(num_args), types_(types) {}
1239
1240 constexpr auto num_args() const -> int { return num_args_; }
1241 constexpr auto arg_type(int id) const -> type { return types_[id]; }
1242
1243 FMT_CONSTEXPR auto next_arg_id() -> int {
1244 int id = base::next_arg_id();
1245 if (id >= num_args_) report_error("argument not found");
1246 return id;
1247 }
1248
1249 FMT_CONSTEXPR void check_arg_id(int id) {
1250 base::check_arg_id(id);
1251 if (id >= num_args_) report_error("argument not found");
1252 }
1253 using base::check_arg_id;
1254
1255 FMT_CONSTEXPR void check_dynamic_spec(int arg_id) {
1256 ignore_unused(arg_id);
1257 if (arg_id < num_args_ && types_ && !is_integral_type(types_[arg_id]))
1258 report_error("width/precision is not integer");
1259 }
1260 };
1261
1262
1263 template <typename Char> union arg_ref {
1264 FMT_CONSTEXPR arg_ref(int idx = 0) : index(idx) {}
1265 FMT_CONSTEXPR arg_ref(basic_string_view<Char> n) : name(n) {}
1266
1267 int index;
1268 basic_string_view<Char> name;
1269 };
1270
1271
1272
1273
1274 template <typename Char = char> struct dynamic_format_specs : format_specs {
1275 arg_ref<Char> width_ref;
1276 arg_ref<Char> precision_ref;
1277 };
1278
1279
1280 template <typename Char, FMT_ENABLE_IF(std::is_integral<Char>::value)>
1281 constexpr auto to_ascii(Char c) -> char {
1282 return c <= 0xff ? static_cast<char>(c) : '\0';
1283 }
1284
1285
1286 template <typename Char>
1287 FMT_CONSTEXPR auto code_point_length(const Char* begin) -> int {
1288 if (const_check(sizeof(Char) != 1)) return 1;
1289 auto c = static_cast<unsigned char>(*begin);
1290 return static_cast<int>((0x3a55000000000000ull >> (2 * (c >> 3))) & 3) + 1;
1291 }
1292
1293
1294
1295 template <typename Char>
1296 FMT_CONSTEXPR auto parse_nonnegative_int(const Char*& begin, const Char* end,
1297 int error_value) noexcept -> int {
1298 FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', "");
1299 unsigned value = 0, prev = 0;
1300 auto p = begin;
1301 do {
1302 prev = value;
1303 value = value * 10 + unsigned(*p - '0');
1304 ++p;
1305 } while (p != end && '0' <= *p && *p <= '9');
1306 auto num_digits = p - begin;
1307 begin = p;
1308 int digits10 = static_cast<int>(sizeof(int) * CHAR_BIT * 3 / 10);
1309 if (num_digits <= digits10) return static_cast<int>(value);
1310
1311 unsigned max = INT_MAX;
1312 return num_digits == digits10 + 1 &&
1313 prev * 10ull + unsigned(p[-1] - '0') <= max
1314 ? static_cast<int>(value)
1315 : error_value;
1316 }
1317
1318 FMT_CONSTEXPR inline auto parse_align(char c) -> align {
1319 switch (c) {
1320 case '<': return align::left;
1321 case '>': return align::right;
1322 case '^': return align::center;
1323 }
1324 return align::none;
1325 }
1326
1327 template <typename Char> constexpr auto is_name_start(Char c) -> bool {
1328 return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '_';
1329 }
1330
1331 template <typename Char, typename Handler>
1332 FMT_CONSTEXPR auto parse_arg_id(const Char* begin, const Char* end,
1333 Handler&& handler) -> const Char* {
1334 Char c = *begin;
1335 if (c >= '0' && c <= '9') {
1336 int index = 0;
1337 if (c != '0')
1338 index = parse_nonnegative_int(begin, end, INT_MAX);
1339 else
1340 ++begin;
1341 if (begin == end || (*begin != '}' && *begin != ':'))
1342 report_error("invalid format string");
1343 else
1344 handler.on_index(index);
1345 return begin;
1346 }
1347 if (FMT_OPTIMIZE_SIZE > 1 || !is_name_start(c)) {
1348 report_error("invalid format string");
1349 return begin;
1350 }
1351 auto it = begin;
1352 do {
1353 ++it;
1354 } while (it != end && (is_name_start(*it) || ('0' <= *it && *it <= '9')));
1355 handler.on_name({begin, to_unsigned(it - begin)});
1356 return it;
1357 }
1358
1359 template <typename Char> struct dynamic_spec_handler {
1360 parse_context<Char>& ctx;
1361 arg_ref<Char>& ref;
1362 arg_id_kind& kind;
1363
1364 FMT_CONSTEXPR void on_index(int id) {
1365 ref = id;
1366 kind = arg_id_kind::index;
1367 ctx.check_arg_id(id);
1368 ctx.check_dynamic_spec(id);
1369 }
1370 FMT_CONSTEXPR void on_name(basic_string_view<Char> id) {
1371 ref = id;
1372 kind = arg_id_kind::name;
1373 ctx.check_arg_id(id);
1374 }
1375 };
1376
1377 template <typename Char> struct parse_dynamic_spec_result {
1378 const Char* end;
1379 arg_id_kind kind;
1380 };
1381
1382
1383 template <typename Char>
1384 FMT_CONSTEXPR auto parse_dynamic_spec(const Char* begin, const Char* end,
1385 int& value, arg_ref<Char>& ref,
1386 parse_context<Char>& ctx)
1387 -> parse_dynamic_spec_result<Char> {
1388 FMT_ASSERT(begin != end, "");
1389 auto kind = arg_id_kind::none;
1390 if ('0' <= *begin && *begin <= '9') {
1391 int val = parse_nonnegative_int(begin, end, -1);
1392 if (val == -1) report_error("number is too big");
1393 value = val;
1394 } else {
1395 if (*begin == '{') {
1396 ++begin;
1397 if (begin != end) {
1398 Char c = *begin;
1399 if (c == '}' || c == ':') {
1400 int id = ctx.next_arg_id();
1401 ref = id;
1402 kind = arg_id_kind::index;
1403 ctx.check_dynamic_spec(id);
1404 } else {
1405 begin = parse_arg_id(begin, end,
1406 dynamic_spec_handler<Char>{ctx, ref, kind});
1407 }
1408 }
1409 if (begin != end && *begin == '}') return {++begin, kind};
1410 }
1411 report_error("invalid format string");
1412 }
1413 return {begin, kind};
1414 }
1415
1416 template <typename Char>
1417 FMT_CONSTEXPR auto parse_width(const Char* begin, const Char* end,
1418 format_specs& specs, arg_ref<Char>& width_ref,
1419 parse_context<Char>& ctx) -> const Char* {
1420 auto result = parse_dynamic_spec(begin, end, specs.width, width_ref, ctx);
1421 specs.set_dynamic_width(result.kind);
1422 return result.end;
1423 }
1424
1425 template <typename Char>
1426 FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end,
1427 format_specs& specs,
1428 arg_ref<Char>& precision_ref,
1429 parse_context<Char>& ctx) -> const Char* {
1430 ++begin;
1431 if (begin == end) {
1432 report_error("invalid precision");
1433 return begin;
1434 }
1435 auto result =
1436 parse_dynamic_spec(begin, end, specs.precision, precision_ref, ctx);
1437 specs.set_dynamic_precision(result.kind);
1438 return result.end;
1439 }
1440
1441 enum class state { start, align, sign, hash, zero, width, precision, locale };
1442
1443
1444 template <typename Char>
1445 FMT_CONSTEXPR auto parse_format_specs(const Char* begin, const Char* end,
1446 dynamic_format_specs<Char>& specs,
1447 parse_context<Char>& ctx, type arg_type)
1448 -> const Char* {
1449 auto c = '\0';
1450 if (end - begin > 1) {
1451 auto next = to_ascii(begin[1]);
1452 c = parse_align(next) == align::none ? to_ascii(*begin) : '\0';
1453 } else {
1454 if (begin == end) return begin;
1455 c = to_ascii(*begin);
1456 }
1457
1458 struct {
1459 state current_state = state::start;
1460 FMT_CONSTEXPR void operator()(state s, bool valid = true) {
1461 if (current_state >= s || !valid)
1462 report_error("invalid format specifier");
1463 current_state = s;
1464 }
1465 } enter_state;
1466
1467 using pres = presentation_type;
1468 constexpr auto integral_set = sint_set | uint_set | bool_set | char_set;
1469 struct {
1470 const Char*& begin;
1471 format_specs& specs;
1472 type arg_type;
1473
1474 FMT_CONSTEXPR auto operator()(pres pres_type, int set) -> const Char* {
1475 if (!in(arg_type, set)) report_error("invalid format specifier");
1476 specs.set_type(pres_type);
1477 return begin + 1;
1478 }
1479 } parse_presentation_type{begin, specs, arg_type};
1480
1481 for (;;) {
1482 switch (c) {
1483 case '<':
1484 case '>':
1485 case '^':
1486 enter_state(state::align);
1487 specs.set_align(parse_align(c));
1488 ++begin;
1489 break;
1490 case '+':
1491 case ' ':
1492 specs.set_sign(c == ' ' ? sign::space : sign::plus);
1493 FMT_FALLTHROUGH;
1494 case '-':
1495 enter_state(state::sign, in(arg_type, sint_set | float_set));
1496 ++begin;
1497 break;
1498 case '#':
1499 enter_state(state::hash, is_arithmetic_type(arg_type));
1500 specs.set_alt();
1501 ++begin;
1502 break;
1503 case '0':
1504 enter_state(state::zero);
1505 if (!is_arithmetic_type(arg_type))
1506 report_error("format specifier requires numeric argument");
1507 if (specs.align() == align::none) {
1508
1509 specs.set_align(align::numeric);
1510 specs.set_fill('0');
1511 }
1512 ++begin;
1513 break;
1514
1515 case '1': case '2': case '3': case '4': case '5':
1516 case '6': case '7': case '8': case '9': case '{':
1517
1518 enter_state(state::width);
1519 begin = parse_width(begin, end, specs, specs.width_ref, ctx);
1520 break;
1521 case '.':
1522 enter_state(state::precision,
1523 in(arg_type, float_set | string_set | cstring_set));
1524 begin = parse_precision(begin, end, specs, specs.precision_ref, ctx);
1525 break;
1526 case 'L':
1527 enter_state(state::locale, is_arithmetic_type(arg_type));
1528 specs.set_localized();
1529 ++begin;
1530 break;
1531 case 'd': return parse_presentation_type(pres::dec, integral_set);
1532 case 'X': specs.set_upper(); FMT_FALLTHROUGH;
1533 case 'x': return parse_presentation_type(pres::hex, integral_set);
1534 case 'o': return parse_presentation_type(pres::oct, integral_set);
1535 case 'B': specs.set_upper(); FMT_FALLTHROUGH;
1536 case 'b': return parse_presentation_type(pres::bin, integral_set);
1537 case 'E': specs.set_upper(); FMT_FALLTHROUGH;
1538 case 'e': return parse_presentation_type(pres::exp, float_set);
1539 case 'F': specs.set_upper(); FMT_FALLTHROUGH;
1540 case 'f': return parse_presentation_type(pres::fixed, float_set);
1541 case 'G': specs.set_upper(); FMT_FALLTHROUGH;
1542 case 'g': return parse_presentation_type(pres::general, float_set);
1543 case 'A': specs.set_upper(); FMT_FALLTHROUGH;
1544 case 'a': return parse_presentation_type(pres::hexfloat, float_set);
1545 case 'c':
1546 if (arg_type == type::bool_type) report_error("invalid format specifier");
1547 return parse_presentation_type(pres::chr, integral_set);
1548 case 's':
1549 return parse_presentation_type(pres::string,
1550 bool_set | string_set | cstring_set);
1551 case 'p':
1552 return parse_presentation_type(pres::pointer, pointer_set | cstring_set);
1553 case '?':
1554 return parse_presentation_type(pres::debug,
1555 char_set | string_set | cstring_set);
1556 case '}': return begin;
1557 default: {
1558 if (*begin == '}') return begin;
1559
1560 auto fill_end = begin + code_point_length(begin);
1561 if (end - fill_end <= 0) {
1562 report_error("invalid format specifier");
1563 return begin;
1564 }
1565 if (*begin == '{') {
1566 report_error("invalid fill character '{'");
1567 return begin;
1568 }
1569 auto alignment = parse_align(to_ascii(*fill_end));
1570 enter_state(state::align, alignment != align::none);
1571 specs.set_fill(
1572 basic_string_view<Char>(begin, to_unsigned(fill_end - begin)));
1573 specs.set_align(alignment);
1574 begin = fill_end + 1;
1575 }
1576 }
1577 if (begin == end) return begin;
1578 c = to_ascii(*begin);
1579 }
1580 }
1581
1582 template <typename Char, typename Handler>
1583 FMT_CONSTEXPR FMT_INLINE auto parse_replacement_field(const Char* begin,
1584 const Char* end,
1585 Handler&& handler)
1586 -> const Char* {
1587 ++begin;
1588 if (begin == end) {
1589 handler.on_error("invalid format string");
1590 return end;
1591 }
1592 int arg_id = 0;
1593 switch (*begin) {
1594 case '}':
1595 handler.on_replacement_field(handler.on_arg_id(), begin);
1596 return begin + 1;
1597 case '{': handler.on_text(begin, begin + 1); return begin + 1;
1598 case ':': arg_id = handler.on_arg_id(); break;
1599 default: {
1600 struct id_adapter {
1601 Handler& handler;
1602 int arg_id;
1603
1604 FMT_CONSTEXPR void on_index(int id) { arg_id = handler.on_arg_id(id); }
1605 FMT_CONSTEXPR void on_name(basic_string_view<Char> id) {
1606 arg_id = handler.on_arg_id(id);
1607 }
1608 } adapter = {handler, 0};
1609 begin = parse_arg_id(begin, end, adapter);
1610 arg_id = adapter.arg_id;
1611 Char c = begin != end ? *begin : Char();
1612 if (c == '}') {
1613 handler.on_replacement_field(arg_id, begin);
1614 return begin + 1;
1615 }
1616 if (c != ':') {
1617 handler.on_error("missing '}' in format string");
1618 return end;
1619 }
1620 break;
1621 }
1622 }
1623 begin = handler.on_format_specs(arg_id, begin + 1, end);
1624 if (begin == end || *begin != '}')
1625 return handler.on_error("unknown format specifier"), end;
1626 return begin + 1;
1627 }
1628
1629 template <typename Char, typename Handler>
1630 FMT_CONSTEXPR void parse_format_string(basic_string_view<Char> fmt,
1631 Handler&& handler) {
1632 auto begin = fmt.data(), end = begin + fmt.size();
1633 auto p = begin;
1634 while (p != end) {
1635 auto c = *p++;
1636 if (c == '{') {
1637 handler.on_text(begin, p - 1);
1638 begin = p = parse_replacement_field(p - 1, end, handler);
1639 } else if (c == '}') {
1640 if (p == end || *p != '}')
1641 return handler.on_error("unmatched '}' in format string");
1642 handler.on_text(begin, p);
1643 begin = ++p;
1644 }
1645 }
1646 handler.on_text(begin, end);
1647 }
1648
1649
1650 FMT_CONSTEXPR inline auto check_char_specs(const format_specs& specs) -> bool {
1651 auto type = specs.type();
1652 if (type != presentation_type::none && type != presentation_type::chr &&
1653 type != presentation_type::debug) {
1654 return false;
1655 }
1656 if (specs.align() == align::numeric || specs.sign() != sign::none ||
1657 specs.alt()) {
1658 report_error("invalid format specifier for char");
1659 }
1660 return true;
1661 }
1662
1663
1664 struct compile_string {};
1665
1666 template <typename T, typename Char>
1667 FMT_VISIBILITY("hidden")
1668 FMT_CONSTEXPR auto invoke_parse(parse_context<Char>& ctx) -> const Char* {
1669 using mapped_type = remove_cvref_t<mapped_t<T, Char>>;
1670 constexpr bool formattable =
1671 std::is_constructible<formatter<mapped_type, Char>>::value;
1672 if (!formattable) return ctx.begin();
1673 using formatted_type = conditional_t<formattable, mapped_type, int>;
1674 return formatter<formatted_type, Char>().parse(ctx);
1675 }
1676
1677 template <typename... T> struct arg_pack {};
1678
1679 template <typename Char, int NUM_ARGS, int NUM_NAMED_ARGS, bool DYNAMIC_NAMES>
1680 class format_string_checker {
1681 private:
1682 type types_[max_of(1, NUM_ARGS)];
1683 named_arg_info<Char> named_args_[max_of(1, NUM_NAMED_ARGS)];
1684 compile_parse_context<Char> context_;
1685
1686 using parse_func = auto (*)(parse_context<Char>&) -> const Char*;
1687 parse_func parse_funcs_[max_of(1, NUM_ARGS)];
1688
1689 public:
1690 template <typename... T>
1691 FMT_CONSTEXPR explicit format_string_checker(basic_string_view<Char> fmt,
1692 arg_pack<T...>)
1693 : types_{mapped_type_constant<T, Char>::value...},
1694 named_args_{},
1695 context_(fmt, NUM_ARGS, types_),
1696 parse_funcs_{&invoke_parse<T, Char>...} {
1697 int arg_index = 0, named_arg_index = 0;
1698 FMT_APPLY_VARIADIC(
1699 init_static_named_arg<T>(named_args_, arg_index, named_arg_index));
1700 ignore_unused(arg_index, named_arg_index);
1701 }
1702
1703 FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
1704
1705 FMT_CONSTEXPR auto on_arg_id() -> int { return context_.next_arg_id(); }
1706 FMT_CONSTEXPR auto on_arg_id(int id) -> int {
1707 context_.check_arg_id(id);
1708 return id;
1709 }
1710 FMT_CONSTEXPR auto on_arg_id(basic_string_view<Char> id) -> int {
1711 for (int i = 0; i < NUM_NAMED_ARGS; ++i) {
1712 if (named_args_[i].name == id) return named_args_[i].id;
1713 }
1714 if (!DYNAMIC_NAMES) on_error("argument not found");
1715 return -1;
1716 }
1717
1718 FMT_CONSTEXPR void on_replacement_field(int id, const Char* begin) {
1719 on_format_specs(id, begin, begin);
1720 }
1721
1722 FMT_CONSTEXPR auto on_format_specs(int id, const Char* begin, const Char* end)
1723 -> const Char* {
1724 context_.advance_to(begin);
1725 if (id >= 0 && id < NUM_ARGS) return parse_funcs_[id](context_);
1726
1727
1728
1729
1730 for (int bracket_count = 0;
1731 begin != end && (bracket_count > 0 || *begin != '}'); ++begin) {
1732 if (*begin == '{')
1733 ++bracket_count;
1734 else if (*begin == '}')
1735 --bracket_count;
1736 }
1737 return begin;
1738 }
1739
1740 FMT_NORETURN FMT_CONSTEXPR void on_error(const char* message) {
1741 report_error(message);
1742 }
1743 };
1744
1745
1746
1747 template <typename T> class buffer {
1748 private:
1749 T* ptr_;
1750 size_t size_;
1751 size_t capacity_;
1752
1753 using grow_fun = void (*)(buffer& buf, size_t capacity);
1754 grow_fun grow_;
1755
1756 protected:
1757
1758 FMT_MSC_WARNING(suppress : 26495)
1759 FMT_CONSTEXPR buffer(grow_fun grow, size_t sz) noexcept
1760 : size_(sz), capacity_(sz), grow_(grow) {}
1761
1762 constexpr buffer(grow_fun grow, T* p = nullptr, size_t sz = 0,
1763 size_t cap = 0) noexcept
1764 : ptr_(p), size_(sz), capacity_(cap), grow_(grow) {}
1765
1766 FMT_CONSTEXPR20 ~buffer() = default;
1767 buffer(buffer&&) = default;
1768
1769
1770 FMT_CONSTEXPR void set(T* buf_data, size_t buf_capacity) noexcept {
1771 ptr_ = buf_data;
1772 capacity_ = buf_capacity;
1773 }
1774
1775 public:
1776 using value_type = T;
1777 using const_reference = const T&;
1778
1779 buffer(const buffer&) = delete;
1780 void operator=(const buffer&) = delete;
1781
1782 auto begin() noexcept -> T* { return ptr_; }
1783 auto end() noexcept -> T* { return ptr_ + size_; }
1784
1785 auto begin() const noexcept -> const T* { return ptr_; }
1786 auto end() const noexcept -> const T* { return ptr_ + size_; }
1787
1788
1789 constexpr auto size() const noexcept -> size_t { return size_; }
1790
1791
1792 constexpr auto capacity() const noexcept -> size_t { return capacity_; }
1793
1794
1795 FMT_CONSTEXPR auto data() noexcept -> T* { return ptr_; }
1796 FMT_CONSTEXPR auto data() const noexcept -> const T* { return ptr_; }
1797
1798
1799 FMT_CONSTEXPR void clear() { size_ = 0; }
1800
1801
1802
1803 FMT_CONSTEXPR void try_resize(size_t count) {
1804 try_reserve(count);
1805 size_ = min_of(count, capacity_);
1806 }
1807
1808
1809
1810
1811
1812 FMT_CONSTEXPR void try_reserve(size_t new_capacity) {
1813 if (new_capacity > capacity_) grow_(*this, new_capacity);
1814 }
1815
1816 FMT_CONSTEXPR void push_back(const T& value) {
1817 try_reserve(size_ + 1);
1818 ptr_[size_++] = value;
1819 }
1820
1821
1822 template <typename U>
1823
1824
1825 #if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1940
1826 FMT_CONSTEXPR20
1827 #endif
1828 void
1829 append(const U* begin, const U* end) {
1830 while (begin != end) {
1831 auto count = to_unsigned(end - begin);
1832 try_reserve(size_ + count);
1833 auto free_cap = capacity_ - size_;
1834 if (free_cap < count) count = free_cap;
1835
1836 T* out = ptr_ + size_;
1837 for (size_t i = 0; i < count; ++i) out[i] = begin[i];
1838 size_ += count;
1839 begin += count;
1840 }
1841 }
1842
1843 template <typename Idx> FMT_CONSTEXPR auto operator[](Idx index) -> T& {
1844 return ptr_[index];
1845 }
1846 template <typename Idx>
1847 FMT_CONSTEXPR auto operator[](Idx index) const -> const T& {
1848 return ptr_[index];
1849 }
1850 };
1851
1852 struct buffer_traits {
1853 constexpr explicit buffer_traits(size_t) {}
1854 constexpr auto count() const -> size_t { return 0; }
1855 constexpr auto limit(size_t size) const -> size_t { return size; }
1856 };
1857
1858 class fixed_buffer_traits {
1859 private:
1860 size_t count_ = 0;
1861 size_t limit_;
1862
1863 public:
1864 constexpr explicit fixed_buffer_traits(size_t limit) : limit_(limit) {}
1865 constexpr auto count() const -> size_t { return count_; }
1866 FMT_CONSTEXPR auto limit(size_t size) -> size_t {
1867 size_t n = limit_ > count_ ? limit_ - count_ : 0;
1868 count_ += size;
1869 return min_of(size, n);
1870 }
1871 };
1872
1873
1874 template <typename OutputIt, typename T, typename Traits = buffer_traits>
1875 class iterator_buffer : public Traits, public buffer<T> {
1876 private:
1877 OutputIt out_;
1878 enum { buffer_size = 256 };
1879 T data_[buffer_size];
1880
1881 static FMT_CONSTEXPR void grow(buffer<T>& buf, size_t) {
1882 if (buf.size() == buffer_size) static_cast<iterator_buffer&>(buf).flush();
1883 }
1884
1885 void flush() {
1886 auto size = this->size();
1887 this->clear();
1888 const T* begin = data_;
1889 const T* end = begin + this->limit(size);
1890 while (begin != end) *out_++ = *begin++;
1891 }
1892
1893 public:
1894 explicit iterator_buffer(OutputIt out, size_t n = buffer_size)
1895 : Traits(n), buffer<T>(grow, data_, 0, buffer_size), out_(out) {}
1896 iterator_buffer(iterator_buffer&& other) noexcept
1897 : Traits(other),
1898 buffer<T>(grow, data_, 0, buffer_size),
1899 out_(other.out_) {}
1900 ~iterator_buffer() {
1901
1902 FMT_TRY { flush(); }
1903 FMT_CATCH(...) {}
1904 }
1905
1906 auto out() -> OutputIt {
1907 flush();
1908 return out_;
1909 }
1910 auto count() const -> size_t { return Traits::count() + this->size(); }
1911 };
1912
1913 template <typename T>
1914 class iterator_buffer<T*, T, fixed_buffer_traits> : public fixed_buffer_traits,
1915 public buffer<T> {
1916 private:
1917 T* out_;
1918 enum { buffer_size = 256 };
1919 T data_[buffer_size];
1920
1921 static FMT_CONSTEXPR void grow(buffer<T>& buf, size_t) {
1922 if (buf.size() == buf.capacity())
1923 static_cast<iterator_buffer&>(buf).flush();
1924 }
1925
1926 void flush() {
1927 size_t n = this->limit(this->size());
1928 if (this->data() == out_) {
1929 out_ += n;
1930 this->set(data_, buffer_size);
1931 }
1932 this->clear();
1933 }
1934
1935 public:
1936 explicit iterator_buffer(T* out, size_t n = buffer_size)
1937 : fixed_buffer_traits(n), buffer<T>(grow, out, 0, n), out_(out) {}
1938 iterator_buffer(iterator_buffer&& other) noexcept
1939 : fixed_buffer_traits(other),
1940 buffer<T>(static_cast<iterator_buffer&&>(other)),
1941 out_(other.out_) {
1942 if (this->data() != out_) {
1943 this->set(data_, buffer_size);
1944 this->clear();
1945 }
1946 }
1947 ~iterator_buffer() { flush(); }
1948
1949 auto out() -> T* {
1950 flush();
1951 return out_;
1952 }
1953 auto count() const -> size_t {
1954 return fixed_buffer_traits::count() + this->size();
1955 }
1956 };
1957
1958 template <typename T> class iterator_buffer<T*, T> : public buffer<T> {
1959 public:
1960 explicit iterator_buffer(T* out, size_t = 0)
1961 : buffer<T>([](buffer<T>&, size_t) {}, out, 0, ~size_t()) {}
1962
1963 auto out() -> T* { return &*this->end(); }
1964 };
1965
1966 template <typename Container>
1967 class container_buffer : public buffer<typename Container::value_type> {
1968 private:
1969 using value_type = typename Container::value_type;
1970
1971 static FMT_CONSTEXPR void grow(buffer<value_type>& buf, size_t capacity) {
1972 auto& self = static_cast<container_buffer&>(buf);
1973 self.container.resize(capacity);
1974 self.set(&self.container[0], capacity);
1975 }
1976
1977 public:
1978 Container& container;
1979
1980 explicit container_buffer(Container& c)
1981 : buffer<value_type>(grow, c.size()), container(c) {}
1982 };
1983
1984
1985 template <typename OutputIt>
1986 class iterator_buffer<
1987 OutputIt,
1988 enable_if_t<is_back_insert_iterator<OutputIt>::value &&
1989 is_contiguous<typename OutputIt::container_type>::value,
1990 typename OutputIt::container_type::value_type>>
1991 : public container_buffer<typename OutputIt::container_type> {
1992 private:
1993 using base = container_buffer<typename OutputIt::container_type>;
1994
1995 public:
1996 explicit iterator_buffer(typename OutputIt::container_type& c) : base(c) {}
1997 explicit iterator_buffer(OutputIt out, size_t = 0)
1998 : base(get_container(out)) {}
1999
2000 auto out() -> OutputIt { return OutputIt(this->container); }
2001 };
2002
2003
2004 template <typename T = char> class counting_buffer : public buffer<T> {
2005 private:
2006 enum { buffer_size = 256 };
2007 T data_[buffer_size];
2008 size_t count_ = 0;
2009
2010 static FMT_CONSTEXPR void grow(buffer<T>& buf, size_t) {
2011 if (buf.size() != buffer_size) return;
2012 static_cast<counting_buffer&>(buf).count_ += buf.size();
2013 buf.clear();
2014 }
2015
2016 public:
2017 FMT_CONSTEXPR counting_buffer() : buffer<T>(grow, data_, 0, buffer_size) {}
2018
2019 constexpr auto count() const noexcept -> size_t {
2020 return count_ + this->size();
2021 }
2022 };
2023
2024 template <typename T>
2025 struct is_back_insert_iterator<basic_appender<T>> : std::true_type {};
2026
2027 template <typename OutputIt, typename InputIt, typename = void>
2028 struct has_back_insert_iterator_container_append : std::false_type {};
2029 template <typename OutputIt, typename InputIt>
2030 struct has_back_insert_iterator_container_append<
2031 OutputIt, InputIt,
2032 void_t<decltype(get_container(std::declval<OutputIt>())
2033 .append(std::declval<InputIt>(),
2034 std::declval<InputIt>()))>> : std::true_type {};
2035
2036
2037 template <typename T, typename InputIt, typename OutputIt,
2038 FMT_ENABLE_IF(is_back_insert_iterator<OutputIt>::value&&
2039 has_back_insert_iterator_container_append<
2040 OutputIt, InputIt>::value)>
2041 FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out)
2042 -> OutputIt {
2043 get_container(out).append(begin, end);
2044 return out;
2045 }
2046
2047 template <typename T, typename InputIt, typename OutputIt,
2048 FMT_ENABLE_IF(is_back_insert_iterator<OutputIt>::value &&
2049 !has_back_insert_iterator_container_append<
2050 OutputIt, InputIt>::value)>
2051 FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out)
2052 -> OutputIt {
2053 auto& c = get_container(out);
2054 c.insert(c.end(), begin, end);
2055 return out;
2056 }
2057
2058 template <typename T, typename InputIt, typename OutputIt,
2059 FMT_ENABLE_IF(!is_back_insert_iterator<OutputIt>::value)>
2060 FMT_CONSTEXPR auto copy(InputIt begin, InputIt end, OutputIt out) -> OutputIt {
2061 while (begin != end) *out++ = static_cast<T>(*begin++);
2062 return out;
2063 }
2064
2065 template <typename T, typename V, typename OutputIt>
2066 FMT_CONSTEXPR auto copy(basic_string_view<V> s, OutputIt out) -> OutputIt {
2067 return copy<T>(s.begin(), s.end(), out);
2068 }
2069
2070 template <typename It, typename Enable = std::true_type>
2071 struct is_buffer_appender : std::false_type {};
2072 template <typename It>
2073 struct is_buffer_appender<
2074 It, bool_constant<
2075 is_back_insert_iterator<It>::value &&
2076 std::is_base_of<buffer<typename It::container_type::value_type>,
2077 typename It::container_type>::value>>
2078 : std::true_type {};
2079
2080
2081 template <typename T, typename OutputIt,
2082 FMT_ENABLE_IF(!is_buffer_appender<OutputIt>::value)>
2083 auto get_buffer(OutputIt out) -> iterator_buffer<OutputIt, T> {
2084 return iterator_buffer<OutputIt, T>(out);
2085 }
2086 template <typename T, typename OutputIt,
2087 FMT_ENABLE_IF(is_buffer_appender<OutputIt>::value)>
2088 auto get_buffer(OutputIt out) -> buffer<T>& {
2089 return get_container(out);
2090 }
2091
2092 template <typename Buf, typename OutputIt>
2093 auto get_iterator(Buf& buf, OutputIt) -> decltype(buf.out()) {
2094 return buf.out();
2095 }
2096 template <typename T, typename OutputIt>
2097 auto get_iterator(buffer<T>&, OutputIt out) -> OutputIt {
2098 return out;
2099 }
2100
2101
2102 template <typename T, typename Char> struct type_is_unformattable_for;
2103
2104 template <typename Char> struct string_value {
2105 const Char* data;
2106 size_t size;
2107 auto str() const -> basic_string_view<Char> { return {data, size}; }
2108 };
2109
2110 template <typename Context> struct custom_value {
2111 using char_type = typename Context::char_type;
2112 void* value;
2113 void (*format)(void* arg, parse_context<char_type>& parse_ctx, Context& ctx);
2114 };
2115
2116 template <typename Char> struct named_arg_value {
2117 const named_arg_info<Char>* data;
2118 size_t size;
2119 };
2120
2121 struct custom_tag {};
2122
2123 #if !FMT_BUILTIN_TYPES
2124 # define FMT_BUILTIN , monostate
2125 #else
2126 # define FMT_BUILTIN
2127 #endif
2128
2129
2130 template <typename Context> class value {
2131 public:
2132 using char_type = typename Context::char_type;
2133
2134 union {
2135 monostate no_value;
2136 int int_value;
2137 unsigned uint_value;
2138 long long long_long_value;
2139 unsigned long long ulong_long_value;
2140 int128_opt int128_value;
2141 uint128_opt uint128_value;
2142 bool bool_value;
2143 char_type char_value;
2144 float float_value;
2145 double double_value;
2146 long double long_double_value;
2147 const void* pointer;
2148 string_value<char_type> string;
2149 custom_value<Context> custom;
2150 named_arg_value<char_type> named_args;
2151 };
2152
2153 constexpr FMT_INLINE value() : no_value() {}
2154 constexpr FMT_INLINE value(signed char x) : int_value(x) {}
2155 constexpr FMT_INLINE value(unsigned char x FMT_BUILTIN) : uint_value(x) {}
2156 constexpr FMT_INLINE value(signed short x) : int_value(x) {}
2157 constexpr FMT_INLINE value(unsigned short x FMT_BUILTIN) : uint_value(x) {}
2158 constexpr FMT_INLINE value(int x) : int_value(x) {}
2159 constexpr FMT_INLINE value(unsigned x FMT_BUILTIN) : uint_value(x) {}
2160 FMT_CONSTEXPR FMT_INLINE value(long x FMT_BUILTIN) : value(long_type(x)) {}
2161 FMT_CONSTEXPR FMT_INLINE value(unsigned long x FMT_BUILTIN)
2162 : value(ulong_type(x)) {}
2163 constexpr FMT_INLINE value(long long x FMT_BUILTIN) : long_long_value(x) {}
2164 constexpr FMT_INLINE value(unsigned long long x FMT_BUILTIN)
2165 : ulong_long_value(x) {}
2166 FMT_INLINE value(int128_opt x FMT_BUILTIN) : int128_value(x) {}
2167 FMT_INLINE value(uint128_opt x FMT_BUILTIN) : uint128_value(x) {}
2168 constexpr FMT_INLINE value(bool x FMT_BUILTIN) : bool_value(x) {}
2169
2170 template <int N>
2171 constexpr FMT_INLINE value(bitint<N> x FMT_BUILTIN) : long_long_value(x) {
2172 static_assert(N <= 64, "unsupported _BitInt");
2173 }
2174 template <int N>
2175 constexpr FMT_INLINE value(ubitint<N> x FMT_BUILTIN) : ulong_long_value(x) {
2176 static_assert(N <= 64, "unsupported _BitInt");
2177 }
2178
2179 template <typename T, FMT_ENABLE_IF(is_char<T>::value)>
2180 constexpr FMT_INLINE value(T x FMT_BUILTIN) : char_value(x) {
2181 static_assert(
2182 std::is_same<T, char>::value || std::is_same<T, char_type>::value,
2183 "mixing character types is disallowed");
2184 }
2185
2186 constexpr FMT_INLINE value(float x FMT_BUILTIN) : float_value(x) {}
2187 constexpr FMT_INLINE value(double x FMT_BUILTIN) : double_value(x) {}
2188 FMT_INLINE value(long double x FMT_BUILTIN) : long_double_value(x) {}
2189
2190 FMT_CONSTEXPR FMT_INLINE value(char_type* x FMT_BUILTIN) {
2191 string.data = x;
2192 if (is_constant_evaluated()) string.size = 0;
2193 }
2194 FMT_CONSTEXPR FMT_INLINE value(const char_type* x FMT_BUILTIN) {
2195 string.data = x;
2196 if (is_constant_evaluated()) string.size = 0;
2197 }
2198 template <typename T, typename C = char_t<T>,
2199 FMT_ENABLE_IF(!std::is_pointer<T>::value)>
2200 FMT_CONSTEXPR value(const T& x FMT_BUILTIN) {
2201 static_assert(std::is_same<C, char_type>::value,
2202 "mixing character types is disallowed");
2203 auto sv = to_string_view(x);
2204 string.data = sv.data();
2205 string.size = sv.size();
2206 }
2207 FMT_INLINE value(void* x FMT_BUILTIN) : pointer(x) {}
2208 FMT_INLINE value(const void* x FMT_BUILTIN) : pointer(x) {}
2209 FMT_INLINE value(volatile void* x FMT_BUILTIN)
2210 : pointer(const_cast<const void*>(x)) {}
2211 FMT_INLINE value(const volatile void* x FMT_BUILTIN)
2212 : pointer(const_cast<const void*>(x)) {}
2213 FMT_INLINE value(nullptr_t) : pointer(nullptr) {}
2214
2215 template <typename T, FMT_ENABLE_IF(std::is_pointer<T>::value ||
2216 std::is_member_pointer<T>::value)>
2217 value(const T&) {
2218
2219
2220
2221 static_assert(sizeof(T) == 0,
2222 "formatting of non-void pointers is disallowed");
2223 }
2224
2225 template <typename T, FMT_ENABLE_IF(use_format_as<T>::value)>
2226 value(const T& x) : value(format_as(x)) {}
2227 template <typename T, FMT_ENABLE_IF(use_format_as_member<T>::value)>
2228 value(const T& x) : value(formatter<T>::format_as(x)) {}
2229
2230 template <typename T, FMT_ENABLE_IF(is_named_arg<T>::value)>
2231 value(const T& named_arg) : value(named_arg.value) {}
2232
2233 template <typename T,
2234 FMT_ENABLE_IF(use_formatter<T>::value || !FMT_BUILTIN_TYPES)>
2235 FMT_CONSTEXPR20 FMT_INLINE value(T& x) : value(x, custom_tag()) {}
2236
2237 FMT_ALWAYS_INLINE value(const named_arg_info<char_type>* args, size_t size)
2238 : named_args{args, size} {}
2239
2240 private:
2241 template <typename T, FMT_ENABLE_IF(has_formatter<T, char_type>())>
2242 FMT_CONSTEXPR value(T& x, custom_tag) {
2243 using value_type = remove_const_t<T>;
2244
2245 if (!is_constant_evaluated()) {
2246 custom.value =
2247 const_cast<char*>(&reinterpret_cast<const volatile char&>(x));
2248 } else {
2249 custom.value = nullptr;
2250 #if defined(__cpp_if_constexpr)
2251 if constexpr (std::is_same<decltype(&x), remove_reference_t<T>*>::value)
2252 custom.value = const_cast<value_type*>(&x);
2253 #endif
2254 }
2255 custom.format = format_custom<value_type, formatter<value_type, char_type>>;
2256 }
2257
2258 template <typename T, FMT_ENABLE_IF(!has_formatter<T, char_type>())>
2259 FMT_CONSTEXPR value(const T&, custom_tag) {
2260
2261
2262 type_is_unformattable_for<T, char_type> _;
2263 }
2264
2265
2266 template <typename T, typename Formatter>
2267 static void format_custom(void* arg, parse_context<char_type>& parse_ctx,
2268 Context& ctx) {
2269 auto f = Formatter();
2270 parse_ctx.advance_to(f.parse(parse_ctx));
2271 using qualified_type =
2272 conditional_t<has_formatter<const T, char_type>(), const T, T>;
2273
2274 const auto& cf = f;
2275 ctx.advance_to(cf.format(*static_cast<qualified_type*>(arg), ctx));
2276 }
2277 };
2278
2279 enum { packed_arg_bits = 4 };
2280
2281 enum { max_packed_args = 62 / packed_arg_bits };
2282 enum : unsigned long long { is_unpacked_bit = 1ULL << 63 };
2283 enum : unsigned long long { has_named_args_bit = 1ULL << 62 };
2284
2285 template <typename It, typename T, typename Enable = void>
2286 struct is_output_iterator : std::false_type {};
2287
2288 template <> struct is_output_iterator<appender, char> : std::true_type {};
2289
2290 template <typename It, typename T>
2291 struct is_output_iterator<
2292 It, T,
2293 enable_if_t<std::is_assignable<decltype(*std::declval<decay_t<It>&>()++),
2294 T>::value>> : std::true_type {};
2295
2296 #ifndef FMT_USE_LOCALE
2297 # define FMT_USE_LOCALE (FMT_OPTIMIZE_SIZE <= 1)
2298 #endif
2299
2300
2301 class locale_ref {
2302 #if FMT_USE_LOCALE
2303 private:
2304 const void* locale_;
2305
2306 public:
2307 constexpr locale_ref() : locale_(nullptr) {}
2308 template <typename Locale> locale_ref(const Locale& loc);
2309
2310 inline explicit operator bool() const noexcept { return locale_ != nullptr; }
2311 #endif
2312
2313 public:
2314 template <typename Locale> auto get() const -> Locale;
2315 };
2316
2317 template <typename> constexpr auto encode_types() -> unsigned long long {
2318 return 0;
2319 }
2320
2321 template <typename Context, typename Arg, typename... Args>
2322 constexpr auto encode_types() -> unsigned long long {
2323 return static_cast<unsigned>(stored_type_constant<Arg, Context>::value) |
2324 (encode_types<Context, Args...>() << packed_arg_bits);
2325 }
2326
2327 template <typename Context, typename... T, size_t NUM_ARGS = sizeof...(T)>
2328 constexpr auto make_descriptor() -> unsigned long long {
2329 return NUM_ARGS <= max_packed_args ? encode_types<Context, T...>()
2330 : is_unpacked_bit | NUM_ARGS;
2331 }
2332
2333 template <typename Context, int NUM_ARGS>
2334 using arg_t = conditional_t<NUM_ARGS <= max_packed_args, value<Context>,
2335 basic_format_arg<Context>>;
2336
2337 template <typename Context, int NUM_ARGS, int NUM_NAMED_ARGS,
2338 unsigned long long DESC>
2339 struct named_arg_store {
2340
2341 arg_t<Context, NUM_ARGS> args[1 + NUM_ARGS];
2342 named_arg_info<typename Context::char_type> named_args[NUM_NAMED_ARGS];
2343
2344 template <typename... T>
2345 FMT_CONSTEXPR FMT_ALWAYS_INLINE named_arg_store(T&... values)
2346 : args{{named_args, NUM_NAMED_ARGS}, values...} {
2347 int arg_index = 0, named_arg_index = 0;
2348 FMT_APPLY_VARIADIC(
2349 init_named_arg(named_args, arg_index, named_arg_index, values));
2350 }
2351
2352 named_arg_store(named_arg_store&& rhs) {
2353 args[0] = {named_args, NUM_NAMED_ARGS};
2354 for (size_t i = 1; i < sizeof(args) / sizeof(*args); ++i)
2355 args[i] = rhs.args[i];
2356 for (size_t i = 0; i < NUM_NAMED_ARGS; ++i)
2357 named_args[i] = rhs.named_args[i];
2358 }
2359
2360 named_arg_store(const named_arg_store& rhs) = delete;
2361 named_arg_store& operator=(const named_arg_store& rhs) = delete;
2362 named_arg_store& operator=(named_arg_store&& rhs) = delete;
2363 operator const arg_t<Context, NUM_ARGS>*() const { return args + 1; }
2364 };
2365
2366
2367
2368
2369 template <typename Context, int NUM_ARGS, int NUM_NAMED_ARGS,
2370 unsigned long long DESC>
2371 struct format_arg_store {
2372
2373 using type =
2374 conditional_t<NUM_NAMED_ARGS == 0,
2375 arg_t<Context, NUM_ARGS>[max_of(1, NUM_ARGS)],
2376 named_arg_store<Context, NUM_ARGS, NUM_NAMED_ARGS, DESC>>;
2377 type args;
2378 };
2379
2380
2381 template <typename T, typename Char, type TYPE> struct native_formatter {
2382 private:
2383 dynamic_format_specs<Char> specs_;
2384
2385 public:
2386 using nonlocking = void;
2387
2388 FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
2389 if (ctx.begin() == ctx.end() || *ctx.begin() == '}') return ctx.begin();
2390 auto end = parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, TYPE);
2391 if (const_check(TYPE == type::char_type)) check_char_specs(specs_);
2392 return end;
2393 }
2394
2395 template <type U = TYPE,
2396 FMT_ENABLE_IF(U == type::string_type || U == type::cstring_type ||
2397 U == type::char_type)>
2398 FMT_CONSTEXPR void set_debug_format(bool set = true) {
2399 specs_.set_type(set ? presentation_type::debug : presentation_type::none);
2400 }
2401
2402 FMT_PRAGMA_CLANG(diagnostic ignored "-Wundefined-inline")
2403 template <typename FormatContext>
2404 FMT_CONSTEXPR auto format(const T& val, FormatContext& ctx) const
2405 -> decltype(ctx.out());
2406 };
2407
2408 template <typename T, typename Enable = void>
2409 struct locking
2410 : bool_constant<mapped_type_constant<T>::value == type::custom_type> {};
2411 template <typename T>
2412 struct locking<T, void_t<typename formatter<remove_cvref_t<T>>::nonlocking>>
2413 : std::false_type {};
2414
2415 template <typename T = int> FMT_CONSTEXPR inline auto is_locking() -> bool {
2416 return locking<T>::value;
2417 }
2418 template <typename T1, typename T2, typename... Tail>
2419 FMT_CONSTEXPR inline auto is_locking() -> bool {
2420 return locking<T1>::value || is_locking<T2, Tail...>();
2421 }
2422
2423 FMT_API void vformat_to(buffer<char>& buf, string_view fmt, format_args args,
2424 locale_ref loc = {});
2425
2426 #if FMT_WIN32
2427 FMT_API void vprint_mojibake(FILE*, string_view, format_args, bool);
2428 #else
2429 inline void vprint_mojibake(FILE*, string_view, const format_args&, bool) {}
2430 #endif
2431 }
2432
2433
2434
2435 template <typename Char>
2436 FMT_CONSTEXPR void parse_context<Char>::do_check_arg_id(int arg_id) {
2437
2438
2439 if (detail::is_constant_evaluated() && use_constexpr_cast) {
2440 auto ctx = static_cast<detail::compile_parse_context<Char>*>(this);
2441 if (arg_id >= ctx->num_args()) report_error("argument not found");
2442 }
2443 }
2444
2445 template <typename Char>
2446 FMT_CONSTEXPR void parse_context<Char>::check_dynamic_spec(int arg_id) {
2447 using detail::compile_parse_context;
2448 if (detail::is_constant_evaluated() && use_constexpr_cast)
2449 static_cast<compile_parse_context<Char>*>(this)->check_dynamic_spec(arg_id);
2450 }
2451
2452 FMT_BEGIN_EXPORT
2453
2454
2455
2456 template <typename T> class basic_appender {
2457 protected:
2458 detail::buffer<T>* container;
2459
2460 public:
2461 using container_type = detail::buffer<T>;
2462
2463 FMT_CONSTEXPR basic_appender(detail::buffer<T>& buf) : container(&buf) {}
2464
2465 FMT_CONSTEXPR20 auto operator=(T c) -> basic_appender& {
2466 container->push_back(c);
2467 return *this;
2468 }
2469 FMT_CONSTEXPR20 auto operator*() -> basic_appender& { return *this; }
2470 FMT_CONSTEXPR20 auto operator++() -> basic_appender& { return *this; }
2471 FMT_CONSTEXPR20 auto operator++(int) -> basic_appender { return *this; }
2472 };
2473
2474
2475
2476 template <typename Context> class basic_format_arg {
2477 private:
2478 detail::value<Context> value_;
2479 detail::type type_;
2480
2481 friend class basic_format_args<Context>;
2482
2483 using char_type = typename Context::char_type;
2484
2485 public:
2486 class handle {
2487 private:
2488 detail::custom_value<Context> custom_;
2489
2490 public:
2491 explicit handle(detail::custom_value<Context> custom) : custom_(custom) {}
2492
2493 void format(parse_context<char_type>& parse_ctx, Context& ctx) const {
2494 custom_.format(custom_.value, parse_ctx, ctx);
2495 }
2496 };
2497
2498 constexpr basic_format_arg() : type_(detail::type::none_type) {}
2499 basic_format_arg(const detail::named_arg_info<char_type>* args, size_t size)
2500 : value_(args, size) {}
2501 template <typename T>
2502 basic_format_arg(T&& val)
2503 : value_(val), type_(detail::stored_type_constant<T, Context>::value) {}
2504
2505 constexpr explicit operator bool() const noexcept {
2506 return type_ != detail::type::none_type;
2507 }
2508 auto type() const -> detail::type { return type_; }
2509
2510
2511
2512
2513
2514
2515 template <typename Visitor>
2516 FMT_CONSTEXPR FMT_INLINE auto visit(Visitor&& vis) const -> decltype(vis(0)) {
2517 using detail::map;
2518 switch (type_) {
2519 case detail::type::none_type: break;
2520 case detail::type::int_type: return vis(value_.int_value);
2521 case detail::type::uint_type: return vis(value_.uint_value);
2522 case detail::type::long_long_type: return vis(value_.long_long_value);
2523 case detail::type::ulong_long_type: return vis(value_.ulong_long_value);
2524 case detail::type::int128_type: return vis(map(value_.int128_value));
2525 case detail::type::uint128_type: return vis(map(value_.uint128_value));
2526 case detail::type::bool_type: return vis(value_.bool_value);
2527 case detail::type::char_type: return vis(value_.char_value);
2528 case detail::type::float_type: return vis(value_.float_value);
2529 case detail::type::double_type: return vis(value_.double_value);
2530 case detail::type::long_double_type: return vis(value_.long_double_value);
2531 case detail::type::cstring_type: return vis(value_.string.data);
2532 case detail::type::string_type: return vis(value_.string.str());
2533 case detail::type::pointer_type: return vis(value_.pointer);
2534 case detail::type::custom_type: return vis(handle(value_.custom));
2535 }
2536 return vis(monostate());
2537 }
2538
2539 auto format_custom(const char_type* parse_begin,
2540 parse_context<char_type>& parse_ctx, Context& ctx)
2541 -> bool {
2542 if (type_ != detail::type::custom_type) return false;
2543 parse_ctx.advance_to(parse_begin);
2544 value_.custom.format(value_.custom.value, parse_ctx, ctx);
2545 return true;
2546 }
2547 };
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557 template <typename Context> class basic_format_args {
2558 private:
2559
2560
2561
2562
2563 unsigned long long desc_;
2564 union {
2565
2566
2567
2568
2569
2570 const detail::value<Context>* values_;
2571 const basic_format_arg<Context>* args_;
2572 };
2573
2574 constexpr auto is_packed() const -> bool {
2575 return (desc_ & detail::is_unpacked_bit) == 0;
2576 }
2577 constexpr auto has_named_args() const -> bool {
2578 return (desc_ & detail::has_named_args_bit) != 0;
2579 }
2580
2581 FMT_CONSTEXPR auto type(int index) const -> detail::type {
2582 int shift = index * detail::packed_arg_bits;
2583 unsigned mask = (1 << detail::packed_arg_bits) - 1;
2584 return static_cast<detail::type>((desc_ >> shift) & mask);
2585 }
2586
2587 template <int NUM_ARGS, int NUM_NAMED_ARGS, unsigned long long DESC>
2588 using store =
2589 detail::format_arg_store<Context, NUM_ARGS, NUM_NAMED_ARGS, DESC>;
2590
2591 public:
2592 using format_arg = basic_format_arg<Context>;
2593
2594 constexpr basic_format_args() : desc_(0), args_(nullptr) {}
2595
2596
2597 template <int NUM_ARGS, int NUM_NAMED_ARGS, unsigned long long DESC,
2598 FMT_ENABLE_IF(NUM_ARGS <= detail::max_packed_args)>
2599 constexpr FMT_ALWAYS_INLINE basic_format_args(
2600 const store<NUM_ARGS, NUM_NAMED_ARGS, DESC>& s)
2601 : desc_(DESC | (NUM_NAMED_ARGS != 0 ? +detail::has_named_args_bit : 0)),
2602 values_(s.args) {}
2603
2604 template <int NUM_ARGS, int NUM_NAMED_ARGS, unsigned long long DESC,
2605 FMT_ENABLE_IF(NUM_ARGS > detail::max_packed_args)>
2606 constexpr basic_format_args(const store<NUM_ARGS, NUM_NAMED_ARGS, DESC>& s)
2607 : desc_(DESC | (NUM_NAMED_ARGS != 0 ? +detail::has_named_args_bit : 0)),
2608 args_(s.args) {}
2609
2610
2611 constexpr basic_format_args(const format_arg* args, int count,
2612 bool has_named = false)
2613 : desc_(detail::is_unpacked_bit | detail::to_unsigned(count) |
2614 (has_named ? +detail::has_named_args_bit : 0)),
2615 args_(args) {}
2616
2617
2618 FMT_CONSTEXPR auto get(int id) const -> format_arg {
2619 auto arg = format_arg();
2620 if (!is_packed()) {
2621 if (id < max_size()) arg = args_[id];
2622 return arg;
2623 }
2624 if (static_cast<unsigned>(id) >= detail::max_packed_args) return arg;
2625 arg.type_ = type(id);
2626 if (arg.type_ != detail::type::none_type) arg.value_ = values_[id];
2627 return arg;
2628 }
2629
2630 template <typename Char>
2631 auto get(basic_string_view<Char> name) const -> format_arg {
2632 int id = get_id(name);
2633 return id >= 0 ? get(id) : format_arg();
2634 }
2635
2636 template <typename Char>
2637 FMT_CONSTEXPR auto get_id(basic_string_view<Char> name) const -> int {
2638 if (!has_named_args()) return -1;
2639 const auto& named_args =
2640 (is_packed() ? values_[-1] : args_[-1].value_).named_args;
2641 for (size_t i = 0; i < named_args.size; ++i) {
2642 if (named_args.data[i].name == name) return named_args.data[i].id;
2643 }
2644 return -1;
2645 }
2646
2647 auto max_size() const -> int {
2648 unsigned long long max_packed = detail::max_packed_args;
2649 return static_cast<int>(is_packed() ? max_packed
2650 : desc_ & ~detail::is_unpacked_bit);
2651 }
2652 };
2653
2654
2655 class context {
2656 private:
2657 appender out_;
2658 format_args args_;
2659 FMT_NO_UNIQUE_ADDRESS detail::locale_ref loc_;
2660
2661 public:
2662
2663 using char_type = char;
2664
2665 using iterator = appender;
2666 using format_arg = basic_format_arg<context>;
2667 using parse_context_type FMT_DEPRECATED = parse_context<>;
2668 template <typename T> using formatter_type FMT_DEPRECATED = formatter<T>;
2669 enum { builtin_types = FMT_BUILTIN_TYPES };
2670
2671
2672
2673 FMT_CONSTEXPR context(iterator out, format_args args,
2674 detail::locale_ref loc = {})
2675 : out_(out), args_(args), loc_(loc) {}
2676 context(context&&) = default;
2677 context(const context&) = delete;
2678 void operator=(const context&) = delete;
2679
2680 FMT_CONSTEXPR auto arg(int id) const -> format_arg { return args_.get(id); }
2681 inline auto arg(string_view name) const -> format_arg {
2682 return args_.get(name);
2683 }
2684 FMT_CONSTEXPR auto arg_id(string_view name) const -> int {
2685 return args_.get_id(name);
2686 }
2687 auto args() const -> const format_args& { return args_; }
2688
2689
2690 FMT_CONSTEXPR auto out() const -> iterator { return out_; }
2691
2692
2693 FMT_CONSTEXPR void advance_to(iterator) {}
2694
2695 FMT_CONSTEXPR auto locale() const -> detail::locale_ref { return loc_; }
2696 };
2697
2698 template <typename Char = char> struct runtime_format_string {
2699 basic_string_view<Char> str;
2700 };
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710 inline auto runtime(string_view s) -> runtime_format_string<> { return {{s}}; }
2711
2712
2713
2714 template <typename... T> struct fstring {
2715 private:
2716 static constexpr int num_static_named_args =
2717 detail::count_static_named_args<T...>();
2718
2719 using checker = detail::format_string_checker<
2720 char, static_cast<int>(sizeof...(T)), num_static_named_args,
2721 num_static_named_args != detail::count_named_args<T...>()>;
2722
2723 using arg_pack = detail::arg_pack<T...>;
2724
2725 public:
2726 string_view str;
2727 using t = fstring;
2728
2729
2730 template <size_t N>
2731 FMT_CONSTEVAL FMT_ALWAYS_INLINE fstring(const char (&s)[N]) : str(s, N - 1) {
2732 using namespace detail;
2733 static_assert(count<(is_view<remove_cvref_t<T>>::value &&
2734 std::is_reference<T>::value)...>() == 0,
2735 "passing views as lvalues is disallowed");
2736 if (FMT_USE_CONSTEVAL) parse_format_string<char>(s, checker(s, arg_pack()));
2737 #ifdef FMT_ENFORCE_COMPILE_STRING
2738 static_assert(
2739 FMT_USE_CONSTEVAL && sizeof(s) != 0,
2740 "FMT_ENFORCE_COMPILE_STRING requires format strings to use FMT_STRING");
2741 #endif
2742 }
2743 template <typename S,
2744 FMT_ENABLE_IF(std::is_convertible<const S&, string_view>::value)>
2745 FMT_CONSTEVAL FMT_ALWAYS_INLINE fstring(const S& s) : str(s) {
2746 auto sv = string_view(str);
2747 if (FMT_USE_CONSTEVAL)
2748 detail::parse_format_string<char>(sv, checker(sv, arg_pack()));
2749 #ifdef FMT_ENFORCE_COMPILE_STRING
2750 static_assert(
2751 FMT_USE_CONSTEVAL && sizeof(s) != 0,
2752 "FMT_ENFORCE_COMPILE_STRING requires format strings to use FMT_STRING");
2753 #endif
2754 }
2755 template <typename S,
2756 FMT_ENABLE_IF(std::is_base_of<detail::compile_string, S>::value&&
2757 std::is_same<typename S::char_type, char>::value)>
2758 FMT_ALWAYS_INLINE fstring(const S&) : str(S()) {
2759 FMT_CONSTEXPR auto sv = string_view(S());
2760 FMT_CONSTEXPR int unused =
2761 (parse_format_string(sv, checker(sv, arg_pack())), 0);
2762 detail::ignore_unused(unused);
2763 }
2764 fstring(runtime_format_string<> fmt) : str(fmt.str) {}
2765
2766
2767 FMT_ALWAYS_INLINE operator const string_view&() const { return str; }
2768 auto get() const -> string_view { return str; }
2769 };
2770
2771 template <typename... T> using format_string = typename fstring<T...>::t;
2772
2773 template <typename T, typename Char = char>
2774 using is_formattable = bool_constant<!std::is_same<
2775 detail::mapped_t<conditional_t<std::is_void<T>::value, int*, T>, Char>,
2776 void>::value>;
2777 #ifdef __cpp_concepts
2778 template <typename T, typename Char = char>
2779 concept formattable = is_formattable<remove_reference_t<T>, Char>::value;
2780 #endif
2781
2782 template <typename T, typename Char>
2783 using has_formatter FMT_DEPRECATED = std::is_constructible<formatter<T, Char>>;
2784
2785
2786 template <typename T, typename Char>
2787 struct formatter<T, Char,
2788 enable_if_t<detail::type_constant<T, Char>::value !=
2789 detail::type::custom_type>>
2790 : detail::native_formatter<T, Char, detail::type_constant<T, Char>::value> {
2791 };
2792
2793
2794
2795
2796
2797
2798
2799
2800 template <typename Context = context, typename... T,
2801 int NUM_ARGS = sizeof...(T),
2802 int NUM_NAMED_ARGS = detail::count_named_args<T...>(),
2803 unsigned long long DESC = detail::make_descriptor<Context, T...>()>
2804 constexpr FMT_ALWAYS_INLINE auto make_format_args(T&... args)
2805 -> detail::format_arg_store<Context, NUM_ARGS, NUM_NAMED_ARGS, DESC> {
2806
2807 FMT_PRAGMA_GCC(diagnostic ignored "-Wconversion")
2808 return {{args...}};
2809 }
2810
2811 template <typename... T>
2812 using vargs =
2813 detail::format_arg_store<context, sizeof...(T),
2814 detail::count_named_args<T...>(),
2815 detail::make_descriptor<context, T...>()>;
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825 template <typename Char, typename T>
2826 inline auto arg(const Char* name, const T& arg) -> detail::named_arg<Char, T> {
2827 return {name, arg};
2828 }
2829
2830
2831 template <typename OutputIt,
2832 FMT_ENABLE_IF(detail::is_output_iterator<remove_cvref_t<OutputIt>,
2833 char>::value)>
2834 auto vformat_to(OutputIt&& out, string_view fmt, format_args args)
2835 -> remove_cvref_t<OutputIt> {
2836 auto&& buf = detail::get_buffer<char>(out);
2837 detail::vformat_to(buf, fmt, args, {});
2838 return detail::get_iterator(buf, out);
2839 }
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851 template <typename OutputIt, typename... T,
2852 FMT_ENABLE_IF(detail::is_output_iterator<remove_cvref_t<OutputIt>,
2853 char>::value)>
2854 FMT_INLINE auto format_to(OutputIt&& out, format_string<T...> fmt, T&&... args)
2855 -> remove_cvref_t<OutputIt> {
2856 return vformat_to(out, fmt.str, vargs<T...>{{args...}});
2857 }
2858
2859 template <typename OutputIt> struct format_to_n_result {
2860
2861 OutputIt out;
2862
2863 size_t size;
2864 };
2865
2866 template <typename OutputIt, typename... T,
2867 FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
2868 auto vformat_to_n(OutputIt out, size_t n, string_view fmt, format_args args)
2869 -> format_to_n_result<OutputIt> {
2870 using traits = detail::fixed_buffer_traits;
2871 auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n);
2872 detail::vformat_to(buf, fmt, args, {});
2873 return {buf.out(), buf.count()};
2874 }
2875
2876
2877
2878
2879
2880
2881
2882 template <typename OutputIt, typename... T,
2883 FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
2884 FMT_INLINE auto format_to_n(OutputIt out, size_t n, format_string<T...> fmt,
2885 T&&... args) -> format_to_n_result<OutputIt> {
2886 return vformat_to_n(out, n, fmt.str, vargs<T...>{{args...}});
2887 }
2888
2889 struct format_to_result {
2890
2891 char* out;
2892
2893 bool truncated;
2894
2895 FMT_CONSTEXPR operator char*() const {
2896
2897 if (truncated) report_error("output is truncated");
2898 return out;
2899 }
2900 };
2901
2902 template <size_t N>
2903 auto vformat_to(char (&out)[N], string_view fmt, format_args args)
2904 -> format_to_result {
2905 auto result = vformat_to_n(out, N, fmt, args);
2906 return {result.out, result.size > N};
2907 }
2908
2909 template <size_t N, typename... T>
2910 FMT_INLINE auto format_to(char (&out)[N], format_string<T...> fmt, T&&... args)
2911 -> format_to_result {
2912 auto result = vformat_to_n(out, N, fmt.str, vargs<T...>{{args...}});
2913 return {result.out, result.size > N};
2914 }
2915
2916
2917 template <typename... T>
2918 FMT_NODISCARD FMT_INLINE auto formatted_size(format_string<T...> fmt,
2919 T&&... args) -> size_t {
2920 auto buf = detail::counting_buffer<>();
2921 detail::vformat_to(buf, fmt.str, vargs<T...>{{args...}}, {});
2922 return buf.count();
2923 }
2924
2925 FMT_API void vprint(string_view fmt, format_args args);
2926 FMT_API void vprint(FILE* f, string_view fmt, format_args args);
2927 FMT_API void vprintln(FILE* f, string_view fmt, format_args args);
2928 FMT_API void vprint_buffered(FILE* f, string_view fmt, format_args args);
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938 template <typename... T>
2939 FMT_INLINE void print(format_string<T...> fmt, T&&... args) {
2940 vargs<T...> va = {{args...}};
2941 if (detail::const_check(!detail::use_utf8))
2942 return detail::vprint_mojibake(stdout, fmt.str, va, false);
2943 return detail::is_locking<T...>() ? vprint_buffered(stdout, fmt.str, va)
2944 : vprint(fmt.str, va);
2945 }
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955 template <typename... T>
2956 FMT_INLINE void print(FILE* f, format_string<T...> fmt, T&&... args) {
2957 vargs<T...> va = {{args...}};
2958 if (detail::const_check(!detail::use_utf8))
2959 return detail::vprint_mojibake(f, fmt.str, va, false);
2960 return detail::is_locking<T...>() ? vprint_buffered(f, fmt.str, va)
2961 : vprint(f, fmt.str, va);
2962 }
2963
2964
2965
2966 template <typename... T>
2967 FMT_INLINE void println(FILE* f, format_string<T...> fmt, T&&... args) {
2968 vargs<T...> va = {{args...}};
2969 return detail::const_check(detail::use_utf8)
2970 ? vprintln(f, fmt.str, va)
2971 : detail::vprint_mojibake(f, fmt.str, va, true);
2972 }
2973
2974
2975
2976 template <typename... T>
2977 FMT_INLINE void println(format_string<T...> fmt, T&&... args) {
2978 return fmt::println(stdout, fmt, static_cast<T&&>(args)...);
2979 }
2980
2981 FMT_END_EXPORT
2982 FMT_PRAGMA_CLANG(diagnostic pop)
2983 FMT_PRAGMA_GCC(pop_options)
2984 FMT_END_NAMESPACE
2985
2986 #ifdef FMT_HEADER_ONLY
2987 # include "format.h"
2988 #endif
2989 #endif