Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-24 10:32:35

0001 // Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
0002 // Distributed under the MIT License (http://opensource.org/licenses/MIT)
0003 
0004 #pragma once
0005 
0006 #ifndef SPDLOG_HEADER_ONLY
0007     #include <spdlog/pattern_formatter.h>
0008 #endif
0009 
0010 #include <spdlog/details/fmt_helper.h>
0011 #include <spdlog/details/log_msg.h>
0012 #include <spdlog/details/os.h>
0013 
0014 #ifndef SPDLOG_NO_TLS
0015     #include <spdlog/mdc.h>
0016 #endif
0017 
0018 #include <spdlog/fmt/fmt.h>
0019 #include <spdlog/formatter.h>
0020 
0021 #include <algorithm>
0022 #include <array>
0023 #include <cctype>
0024 #include <chrono>
0025 #include <cstring>
0026 #include <ctime>
0027 #include <iterator>
0028 #include <memory>
0029 #include <mutex>
0030 #include <string>
0031 #include <thread>
0032 #include <utility>
0033 #include <vector>
0034 
0035 namespace spdlog {
0036 namespace details {
0037 
0038 ///////////////////////////////////////////////////////////////////////
0039 // name & level pattern appender
0040 ///////////////////////////////////////////////////////////////////////
0041 
0042 class scoped_padder {
0043 public:
0044     scoped_padder(size_t wrapped_size, const padding_info &padinfo, memory_buf_t &dest)
0045         : padinfo_(padinfo),
0046           dest_(dest) {
0047         remaining_pad_ = static_cast<long>(padinfo.width_) - static_cast<long>(wrapped_size);
0048         if (remaining_pad_ <= 0) {
0049             return;
0050         }
0051 
0052         if (padinfo_.side_ == padding_info::pad_side::left) {
0053             pad_it(remaining_pad_);
0054             remaining_pad_ = 0;
0055         } else if (padinfo_.side_ == padding_info::pad_side::center) {
0056             auto half_pad = remaining_pad_ / 2;
0057             auto reminder = remaining_pad_ & 1;
0058             pad_it(half_pad);
0059             remaining_pad_ = half_pad + reminder;  // for the right side
0060         }
0061     }
0062 
0063     template <typename T>
0064     static unsigned int count_digits(T n) {
0065         return fmt_helper::count_digits(n);
0066     }
0067 
0068     ~scoped_padder() {
0069         if (remaining_pad_ >= 0) {
0070             pad_it(remaining_pad_);
0071         } else if (padinfo_.truncate_) {
0072             long new_size = static_cast<long>(dest_.size()) + remaining_pad_;
0073             if (new_size < 0) {
0074                 new_size = 0;
0075             }
0076             dest_.resize(static_cast<size_t>(new_size));
0077         }
0078     }
0079 
0080 private:
0081     void pad_it(long count) {
0082         fmt_helper::append_string_view(string_view_t(spaces_.data(), static_cast<size_t>(count)),
0083                                        dest_);
0084     }
0085 
0086     const padding_info &padinfo_;
0087     memory_buf_t &dest_;
0088     long remaining_pad_;
0089     string_view_t spaces_{"                                                                ", 64};
0090 };
0091 
0092 struct null_scoped_padder {
0093     null_scoped_padder(size_t /*wrapped_size*/,
0094                        const padding_info & /*padinfo*/,
0095                        memory_buf_t & /*dest*/) {}
0096 
0097     template <typename T>
0098     static unsigned int count_digits(T /* number */) {
0099         return 0;
0100     }
0101 };
0102 
0103 template <typename ScopedPadder>
0104 class name_formatter final : public flag_formatter {
0105 public:
0106     explicit name_formatter(padding_info padinfo)
0107         : flag_formatter(padinfo) {}
0108 
0109     void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override {
0110         ScopedPadder p(msg.logger_name.size(), padinfo_, dest);
0111         fmt_helper::append_string_view(msg.logger_name, dest);
0112     }
0113 };
0114 
0115 // log level appender
0116 template <typename ScopedPadder>
0117 class level_formatter final : public flag_formatter {
0118 public:
0119     explicit level_formatter(padding_info padinfo)
0120         : flag_formatter(padinfo) {}
0121 
0122     void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override {
0123         const string_view_t &level_name = level::to_string_view(msg.level);
0124         ScopedPadder p(level_name.size(), padinfo_, dest);
0125         fmt_helper::append_string_view(level_name, dest);
0126     }
0127 };
0128 
0129 // short log level appender
0130 template <typename ScopedPadder>
0131 class short_level_formatter final : public flag_formatter {
0132 public:
0133     explicit short_level_formatter(padding_info padinfo)
0134         : flag_formatter(padinfo) {}
0135 
0136     void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override {
0137         string_view_t level_name{level::to_short_c_str(msg.level)};
0138         ScopedPadder p(level_name.size(), padinfo_, dest);
0139         fmt_helper::append_string_view(level_name, dest);
0140     }
0141 };
0142 
0143 ///////////////////////////////////////////////////////////////////////
0144 // Date time pattern appenders
0145 ///////////////////////////////////////////////////////////////////////
0146 
0147 static const char *ampm(const tm &t) { return t.tm_hour >= 12 ? "PM" : "AM"; }
0148 
0149 static int to12h(const tm &t) { return t.tm_hour > 12 ? t.tm_hour - 12 : t.tm_hour; }
0150 
0151 // Abbreviated weekday name
0152 static std::array<const char *, 7> days{{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}};
0153 
0154 template <typename ScopedPadder>
0155 class a_formatter final : public flag_formatter {
0156 public:
0157     explicit a_formatter(padding_info padinfo)
0158         : flag_formatter(padinfo) {}
0159 
0160     void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override {
0161         string_view_t field_value{days[static_cast<size_t>(tm_time.tm_wday)]};
0162         ScopedPadder p(field_value.size(), padinfo_, dest);
0163         fmt_helper::append_string_view(field_value, dest);
0164     }
0165 };
0166 
0167 // Full weekday name
0168 static std::array<const char *, 7> full_days{
0169     {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}};
0170 
0171 template <typename ScopedPadder>
0172 class A_formatter : public flag_formatter {
0173 public:
0174     explicit A_formatter(padding_info padinfo)
0175         : flag_formatter(padinfo) {}
0176 
0177     void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override {
0178         string_view_t field_value{full_days[static_cast<size_t>(tm_time.tm_wday)]};
0179         ScopedPadder p(field_value.size(), padinfo_, dest);
0180         fmt_helper::append_string_view(field_value, dest);
0181     }
0182 };
0183 
0184 // Abbreviated month
0185 static const std::array<const char *, 12> months{
0186     {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}};
0187 
0188 template <typename ScopedPadder>
0189 class b_formatter final : public flag_formatter {
0190 public:
0191     explicit b_formatter(padding_info padinfo)
0192         : flag_formatter(padinfo) {}
0193 
0194     void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override {
0195         string_view_t field_value{months[static_cast<size_t>(tm_time.tm_mon)]};
0196         ScopedPadder p(field_value.size(), padinfo_, dest);
0197         fmt_helper::append_string_view(field_value, dest);
0198     }
0199 };
0200 
0201 // Full month name
0202 static const std::array<const char *, 12> full_months{{"January", "February", "March", "April",
0203                                                        "May", "June", "July", "August", "September",
0204                                                        "October", "November", "December"}};
0205 
0206 template <typename ScopedPadder>
0207 class B_formatter final : public flag_formatter {
0208 public:
0209     explicit B_formatter(padding_info padinfo)
0210         : flag_formatter(padinfo) {}
0211 
0212     void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override {
0213         string_view_t field_value{full_months[static_cast<size_t>(tm_time.tm_mon)]};
0214         ScopedPadder p(field_value.size(), padinfo_, dest);
0215         fmt_helper::append_string_view(field_value, dest);
0216     }
0217 };
0218 
0219 // Date and time representation (Thu Aug 23 15:35:46 2014)
0220 template <typename ScopedPadder>
0221 class c_formatter final : public flag_formatter {
0222 public:
0223     explicit c_formatter(padding_info padinfo)
0224         : flag_formatter(padinfo) {}
0225 
0226     void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override {
0227         const size_t field_size = 24;
0228         ScopedPadder p(field_size, padinfo_, dest);
0229 
0230         fmt_helper::append_string_view(days[static_cast<size_t>(tm_time.tm_wday)], dest);
0231         dest.push_back(' ');
0232         fmt_helper::append_string_view(months[static_cast<size_t>(tm_time.tm_mon)], dest);
0233         dest.push_back(' ');
0234         fmt_helper::append_int(tm_time.tm_mday, dest);
0235         dest.push_back(' ');
0236         // time
0237 
0238         fmt_helper::pad2(tm_time.tm_hour, dest);
0239         dest.push_back(':');
0240         fmt_helper::pad2(tm_time.tm_min, dest);
0241         dest.push_back(':');
0242         fmt_helper::pad2(tm_time.tm_sec, dest);
0243         dest.push_back(' ');
0244         fmt_helper::append_int(tm_time.tm_year + 1900, dest);
0245     }
0246 };
0247 
0248 // year - 2 digit
0249 template <typename ScopedPadder>
0250 class C_formatter final : public flag_formatter {
0251 public:
0252     explicit C_formatter(padding_info padinfo)
0253         : flag_formatter(padinfo) {}
0254 
0255     void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override {
0256         const size_t field_size = 2;
0257         ScopedPadder p(field_size, padinfo_, dest);
0258         fmt_helper::pad2(tm_time.tm_year % 100, dest);
0259     }
0260 };
0261 
0262 // Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01
0263 template <typename ScopedPadder>
0264 class D_formatter final : public flag_formatter {
0265 public:
0266     explicit D_formatter(padding_info padinfo)
0267         : flag_formatter(padinfo) {}
0268 
0269     void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override {
0270         const size_t field_size = 8;
0271         ScopedPadder p(field_size, padinfo_, dest);
0272 
0273         fmt_helper::pad2(tm_time.tm_mon + 1, dest);
0274         dest.push_back('/');
0275         fmt_helper::pad2(tm_time.tm_mday, dest);
0276         dest.push_back('/');
0277         fmt_helper::pad2(tm_time.tm_year % 100, dest);
0278     }
0279 };
0280 
0281 // year - 4 digit
0282 template <typename ScopedPadder>
0283 class Y_formatter final : public flag_formatter {
0284 public:
0285     explicit Y_formatter(padding_info padinfo)
0286         : flag_formatter(padinfo) {}
0287 
0288     void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override {
0289         const size_t field_size = 4;
0290         ScopedPadder p(field_size, padinfo_, dest);
0291         fmt_helper::append_int(tm_time.tm_year + 1900, dest);
0292     }
0293 };
0294 
0295 // month 1-12
0296 template <typename ScopedPadder>
0297 class m_formatter final : public flag_formatter {
0298 public:
0299     explicit m_formatter(padding_info padinfo)
0300         : flag_formatter(padinfo) {}
0301 
0302     void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override {
0303         const size_t field_size = 2;
0304         ScopedPadder p(field_size, padinfo_, dest);
0305         fmt_helper::pad2(tm_time.tm_mon + 1, dest);
0306     }
0307 };
0308 
0309 // day of month 1-31
0310 template <typename ScopedPadder>
0311 class d_formatter final : public flag_formatter {
0312 public:
0313     explicit d_formatter(padding_info padinfo)
0314         : flag_formatter(padinfo) {}
0315 
0316     void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override {
0317         const size_t field_size = 2;
0318         ScopedPadder p(field_size, padinfo_, dest);
0319         fmt_helper::pad2(tm_time.tm_mday, dest);
0320     }
0321 };
0322 
0323 // hours in 24 format 0-23
0324 template <typename ScopedPadder>
0325 class H_formatter final : public flag_formatter {
0326 public:
0327     explicit H_formatter(padding_info padinfo)
0328         : flag_formatter(padinfo) {}
0329 
0330     void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override {
0331         const size_t field_size = 2;
0332         ScopedPadder p(field_size, padinfo_, dest);
0333         fmt_helper::pad2(tm_time.tm_hour, dest);
0334     }
0335 };
0336 
0337 // hours in 12 format 1-12
0338 template <typename ScopedPadder>
0339 class I_formatter final : public flag_formatter {
0340 public:
0341     explicit I_formatter(padding_info padinfo)
0342         : flag_formatter(padinfo) {}
0343 
0344     void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override {
0345         const size_t field_size = 2;
0346         ScopedPadder p(field_size, padinfo_, dest);
0347         fmt_helper::pad2(to12h(tm_time), dest);
0348     }
0349 };
0350 
0351 // minutes 0-59
0352 template <typename ScopedPadder>
0353 class M_formatter final : public flag_formatter {
0354 public:
0355     explicit M_formatter(padding_info padinfo)
0356         : flag_formatter(padinfo) {}
0357 
0358     void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override {
0359         const size_t field_size = 2;
0360         ScopedPadder p(field_size, padinfo_, dest);
0361         fmt_helper::pad2(tm_time.tm_min, dest);
0362     }
0363 };
0364 
0365 // seconds 0-59
0366 template <typename ScopedPadder>
0367 class S_formatter final : public flag_formatter {
0368 public:
0369     explicit S_formatter(padding_info padinfo)
0370         : flag_formatter(padinfo) {}
0371 
0372     void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override {
0373         const size_t field_size = 2;
0374         ScopedPadder p(field_size, padinfo_, dest);
0375         fmt_helper::pad2(tm_time.tm_sec, dest);
0376     }
0377 };
0378 
0379 // milliseconds
0380 template <typename ScopedPadder>
0381 class e_formatter final : public flag_formatter {
0382 public:
0383     explicit e_formatter(padding_info padinfo)
0384         : flag_formatter(padinfo) {}
0385 
0386     void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override {
0387         auto millis = fmt_helper::time_fraction<std::chrono::milliseconds>(msg.time);
0388         const size_t field_size = 3;
0389         ScopedPadder p(field_size, padinfo_, dest);
0390         fmt_helper::pad3(static_cast<uint32_t>(millis.count()), dest);
0391     }
0392 };
0393 
0394 // microseconds
0395 template <typename ScopedPadder>
0396 class f_formatter final : public flag_formatter {
0397 public:
0398     explicit f_formatter(padding_info padinfo)
0399         : flag_formatter(padinfo) {}
0400 
0401     void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override {
0402         auto micros = fmt_helper::time_fraction<std::chrono::microseconds>(msg.time);
0403 
0404         const size_t field_size = 6;
0405         ScopedPadder p(field_size, padinfo_, dest);
0406         fmt_helper::pad6(static_cast<size_t>(micros.count()), dest);
0407     }
0408 };
0409 
0410 // nanoseconds
0411 template <typename ScopedPadder>
0412 class F_formatter final : public flag_formatter {
0413 public:
0414     explicit F_formatter(padding_info padinfo)
0415         : flag_formatter(padinfo) {}
0416 
0417     void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override {
0418         auto ns = fmt_helper::time_fraction<std::chrono::nanoseconds>(msg.time);
0419         const size_t field_size = 9;
0420         ScopedPadder p(field_size, padinfo_, dest);
0421         fmt_helper::pad9(static_cast<size_t>(ns.count()), dest);
0422     }
0423 };
0424 
0425 // seconds since epoch
0426 template <typename ScopedPadder>
0427 class E_formatter final : public flag_formatter {
0428 public:
0429     explicit E_formatter(padding_info padinfo)
0430         : flag_formatter(padinfo) {}
0431 
0432     void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override {
0433         const size_t field_size = 10;
0434         ScopedPadder p(field_size, padinfo_, dest);
0435         auto duration = msg.time.time_since_epoch();
0436         auto seconds = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
0437         fmt_helper::append_int(seconds, dest);
0438     }
0439 };
0440 
0441 // AM/PM
0442 template <typename ScopedPadder>
0443 class p_formatter final : public flag_formatter {
0444 public:
0445     explicit p_formatter(padding_info padinfo)
0446         : flag_formatter(padinfo) {}
0447 
0448     void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override {
0449         const size_t field_size = 2;
0450         ScopedPadder p(field_size, padinfo_, dest);
0451         fmt_helper::append_string_view(ampm(tm_time), dest);
0452     }
0453 };
0454 
0455 // 12 hour clock 02:55:02 pm
0456 template <typename ScopedPadder>
0457 class r_formatter final : public flag_formatter {
0458 public:
0459     explicit r_formatter(padding_info padinfo)
0460         : flag_formatter(padinfo) {}
0461 
0462     void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override {
0463         const size_t field_size = 11;
0464         ScopedPadder p(field_size, padinfo_, dest);
0465 
0466         fmt_helper::pad2(to12h(tm_time), dest);
0467         dest.push_back(':');
0468         fmt_helper::pad2(tm_time.tm_min, dest);
0469         dest.push_back(':');
0470         fmt_helper::pad2(tm_time.tm_sec, dest);
0471         dest.push_back(' ');
0472         fmt_helper::append_string_view(ampm(tm_time), dest);
0473     }
0474 };
0475 
0476 // 24-hour HH:MM time, equivalent to %H:%M
0477 template <typename ScopedPadder>
0478 class R_formatter final : public flag_formatter {
0479 public:
0480     explicit R_formatter(padding_info padinfo)
0481         : flag_formatter(padinfo) {}
0482 
0483     void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override {
0484         const size_t field_size = 5;
0485         ScopedPadder p(field_size, padinfo_, dest);
0486 
0487         fmt_helper::pad2(tm_time.tm_hour, dest);
0488         dest.push_back(':');
0489         fmt_helper::pad2(tm_time.tm_min, dest);
0490     }
0491 };
0492 
0493 // ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S
0494 template <typename ScopedPadder>
0495 class T_formatter final : public flag_formatter {
0496 public:
0497     explicit T_formatter(padding_info padinfo)
0498         : flag_formatter(padinfo) {}
0499 
0500     void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override {
0501         const size_t field_size = 8;
0502         ScopedPadder p(field_size, padinfo_, dest);
0503 
0504         fmt_helper::pad2(tm_time.tm_hour, dest);
0505         dest.push_back(':');
0506         fmt_helper::pad2(tm_time.tm_min, dest);
0507         dest.push_back(':');
0508         fmt_helper::pad2(tm_time.tm_sec, dest);
0509     }
0510 };
0511 
0512 // ISO 8601 offset from UTC in timezone (+-HH:MM)
0513 template <typename ScopedPadder>
0514 class z_formatter final : public flag_formatter {
0515 public:
0516     explicit z_formatter(padding_info padinfo)
0517         : flag_formatter(padinfo) {}
0518 
0519     z_formatter() = default;
0520     z_formatter(const z_formatter &) = delete;
0521     z_formatter &operator=(const z_formatter &) = delete;
0522 
0523     void format(const details::log_msg &msg, const std::tm &tm_time, memory_buf_t &dest) override {
0524         const size_t field_size = 6;
0525         ScopedPadder p(field_size, padinfo_, dest);
0526 
0527         auto total_minutes = get_cached_offset(msg, tm_time);
0528         bool is_negative = total_minutes < 0;
0529         if (is_negative) {
0530             total_minutes = -total_minutes;
0531             dest.push_back('-');
0532         } else {
0533             dest.push_back('+');
0534         }
0535 
0536         fmt_helper::pad2(total_minutes / 60, dest);  // hours
0537         dest.push_back(':');
0538         fmt_helper::pad2(total_minutes % 60, dest);  // minutes
0539     }
0540 
0541 private:
0542     log_clock::time_point last_update_{std::chrono::seconds(0)};
0543     int offset_minutes_{0};
0544 
0545     int get_cached_offset(const log_msg &msg, const std::tm &tm_time) {
0546         // refresh every 10 seconds
0547         if (msg.time - last_update_ >= std::chrono::seconds(10)) {
0548             offset_minutes_ = os::utc_minutes_offset(tm_time);
0549             last_update_ = msg.time;
0550         }
0551         return offset_minutes_;
0552     }
0553 };
0554 
0555 // Thread id
0556 template <typename ScopedPadder>
0557 class t_formatter final : public flag_formatter {
0558 public:
0559     explicit t_formatter(padding_info padinfo)
0560         : flag_formatter(padinfo) {}
0561 
0562     void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override {
0563         const auto field_size = ScopedPadder::count_digits(msg.thread_id);
0564         ScopedPadder p(field_size, padinfo_, dest);
0565         fmt_helper::append_int(msg.thread_id, dest);
0566     }
0567 };
0568 
0569 // Current pid
0570 template <typename ScopedPadder>
0571 class pid_formatter final : public flag_formatter {
0572 public:
0573     explicit pid_formatter(padding_info padinfo)
0574         : flag_formatter(padinfo) {}
0575 
0576     void format(const details::log_msg &, const std::tm &, memory_buf_t &dest) override {
0577         const auto pid = static_cast<uint32_t>(details::os::pid());
0578         auto field_size = ScopedPadder::count_digits(pid);
0579         ScopedPadder p(field_size, padinfo_, dest);
0580         fmt_helper::append_int(pid, dest);
0581     }
0582 };
0583 
0584 template <typename ScopedPadder>
0585 class v_formatter final : public flag_formatter {
0586 public:
0587     explicit v_formatter(padding_info padinfo)
0588         : flag_formatter(padinfo) {}
0589 
0590     void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override {
0591         ScopedPadder p(msg.payload.size(), padinfo_, dest);
0592         fmt_helper::append_string_view(msg.payload, dest);
0593     }
0594 };
0595 
0596 class ch_formatter final : public flag_formatter {
0597 public:
0598     explicit ch_formatter(char ch)
0599         : ch_(ch) {}
0600 
0601     void format(const details::log_msg &, const std::tm &, memory_buf_t &dest) override {
0602         dest.push_back(ch_);
0603     }
0604 
0605 private:
0606     char ch_;
0607 };
0608 
0609 // aggregate user chars to display as is
0610 class aggregate_formatter final : public flag_formatter {
0611 public:
0612     aggregate_formatter() = default;
0613 
0614     void add_ch(char ch) { str_ += ch; }
0615     void format(const details::log_msg &, const std::tm &, memory_buf_t &dest) override {
0616         fmt_helper::append_string_view(str_, dest);
0617     }
0618 
0619 private:
0620     std::string str_;
0621 };
0622 
0623 // mark the color range. expect it to be in the form of "%^colored text%$"
0624 class color_start_formatter final : public flag_formatter {
0625 public:
0626     explicit color_start_formatter(padding_info padinfo)
0627         : flag_formatter(padinfo) {}
0628 
0629     void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override {
0630         msg.color_range_start = dest.size();
0631     }
0632 };
0633 
0634 class color_stop_formatter final : public flag_formatter {
0635 public:
0636     explicit color_stop_formatter(padding_info padinfo)
0637         : flag_formatter(padinfo) {}
0638 
0639     void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override {
0640         msg.color_range_end = dest.size();
0641     }
0642 };
0643 
0644 // print source location
0645 template <typename ScopedPadder>
0646 class source_location_formatter final : public flag_formatter {
0647 public:
0648     explicit source_location_formatter(padding_info padinfo)
0649         : flag_formatter(padinfo) {}
0650 
0651     void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override {
0652         if (msg.source.empty()) {
0653             ScopedPadder p(0, padinfo_, dest);
0654             return;
0655         }
0656 
0657         size_t text_size;
0658         if (padinfo_.enabled()) {
0659             // calc text size for padding based on "filename:line"
0660             text_size = std::char_traits<char>::length(msg.source.filename) +
0661                         ScopedPadder::count_digits(msg.source.line) + 1;
0662         } else {
0663             text_size = 0;
0664         }
0665 
0666         ScopedPadder p(text_size, padinfo_, dest);
0667         fmt_helper::append_string_view(msg.source.filename, dest);
0668         dest.push_back(':');
0669         fmt_helper::append_int(msg.source.line, dest);
0670     }
0671 };
0672 
0673 // print source filename
0674 template <typename ScopedPadder>
0675 class source_filename_formatter final : public flag_formatter {
0676 public:
0677     explicit source_filename_formatter(padding_info padinfo)
0678         : flag_formatter(padinfo) {}
0679 
0680     void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override {
0681         if (msg.source.empty()) {
0682             ScopedPadder p(0, padinfo_, dest);
0683             return;
0684         }
0685         size_t text_size =
0686             padinfo_.enabled() ? std::char_traits<char>::length(msg.source.filename) : 0;
0687         ScopedPadder p(text_size, padinfo_, dest);
0688         fmt_helper::append_string_view(msg.source.filename, dest);
0689     }
0690 };
0691 
0692 template <typename ScopedPadder>
0693 class short_filename_formatter final : public flag_formatter {
0694 public:
0695     explicit short_filename_formatter(padding_info padinfo)
0696         : flag_formatter(padinfo) {}
0697 
0698 #ifdef _MSC_VER
0699     #pragma warning(push)
0700     #pragma warning(disable : 4127)  // consider using 'if constexpr' instead
0701 #endif                               // _MSC_VER
0702     static const char *basename(const char *filename) {
0703         // if the size is 2 (1 character + null terminator) we can use the more efficient strrchr
0704         // the branch will be elided by optimizations
0705         if (sizeof(os::folder_seps) == 2) {
0706             const char *rv = std::strrchr(filename, os::folder_seps[0]);
0707             return rv != nullptr ? rv + 1 : filename;
0708         } else {
0709             const std::reverse_iterator<const char *> begin(filename + std::strlen(filename));
0710             const std::reverse_iterator<const char *> end(filename);
0711 
0712             const auto it = std::find_first_of(begin, end, std::begin(os::folder_seps),
0713                                                std::end(os::folder_seps) - 1);
0714             return it != end ? it.base() : filename;
0715         }
0716     }
0717 #ifdef _MSC_VER
0718     #pragma warning(pop)
0719 #endif  // _MSC_VER
0720 
0721     void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override {
0722         if (msg.source.empty()) {
0723             ScopedPadder p(0, padinfo_, dest);
0724             return;
0725         }
0726         auto filename = basename(msg.source.filename);
0727         size_t text_size = padinfo_.enabled() ? std::char_traits<char>::length(filename) : 0;
0728         ScopedPadder p(text_size, padinfo_, dest);
0729         fmt_helper::append_string_view(filename, dest);
0730     }
0731 };
0732 
0733 template <typename ScopedPadder>
0734 class source_linenum_formatter final : public flag_formatter {
0735 public:
0736     explicit source_linenum_formatter(padding_info padinfo)
0737         : flag_formatter(padinfo) {}
0738 
0739     void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override {
0740         if (msg.source.empty()) {
0741             ScopedPadder p(0, padinfo_, dest);
0742             return;
0743         }
0744 
0745         auto field_size = ScopedPadder::count_digits(msg.source.line);
0746         ScopedPadder p(field_size, padinfo_, dest);
0747         fmt_helper::append_int(msg.source.line, dest);
0748     }
0749 };
0750 
0751 // print source funcname
0752 template <typename ScopedPadder>
0753 class source_funcname_formatter final : public flag_formatter {
0754 public:
0755     explicit source_funcname_formatter(padding_info padinfo)
0756         : flag_formatter(padinfo) {}
0757 
0758     void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override {
0759         if (msg.source.empty()) {
0760             ScopedPadder p(0, padinfo_, dest);
0761             return;
0762         }
0763         size_t text_size =
0764             padinfo_.enabled() ? std::char_traits<char>::length(msg.source.funcname) : 0;
0765         ScopedPadder p(text_size, padinfo_, dest);
0766         fmt_helper::append_string_view(msg.source.funcname, dest);
0767     }
0768 };
0769 
0770 // print elapsed time since last message
0771 template <typename ScopedPadder, typename Units>
0772 class elapsed_formatter final : public flag_formatter {
0773 public:
0774     using DurationUnits = Units;
0775 
0776     explicit elapsed_formatter(padding_info padinfo)
0777         : flag_formatter(padinfo),
0778           last_message_time_(log_clock::now()) {}
0779 
0780     void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override {
0781         auto delta = (std::max)(msg.time - last_message_time_, log_clock::duration::zero());
0782         auto delta_units = std::chrono::duration_cast<DurationUnits>(delta);
0783         last_message_time_ = msg.time;
0784         auto delta_count = static_cast<size_t>(delta_units.count());
0785         auto n_digits = static_cast<size_t>(ScopedPadder::count_digits(delta_count));
0786         ScopedPadder p(n_digits, padinfo_, dest);
0787         fmt_helper::append_int(delta_count, dest);
0788     }
0789 
0790 private:
0791     log_clock::time_point last_message_time_;
0792 };
0793 
0794 // Class for formatting Mapped Diagnostic Context (MDC) in log messages.
0795 // Example: [logger-name] [info] [mdc_key_1:mdc_value_1 mdc_key_2:mdc_value_2] some message
0796 #ifndef SPDLOG_NO_TLS
0797 template <typename ScopedPadder>
0798 class mdc_formatter : public flag_formatter {
0799 public:
0800     explicit mdc_formatter(padding_info padinfo)
0801         : flag_formatter(padinfo) {}
0802 
0803     void format(const details::log_msg &, const std::tm &, memory_buf_t &dest) override {
0804         auto &mdc_map = mdc::get_context();
0805         if (mdc_map.empty()) {
0806             ScopedPadder p(0, padinfo_, dest);
0807             return;
0808         } else {
0809             format_mdc(mdc_map, dest);
0810         }
0811     }
0812 
0813     void format_mdc(const mdc::mdc_map_t &mdc_map, memory_buf_t &dest) {
0814         auto last_element = --mdc_map.end();
0815         for (auto it = mdc_map.begin(); it != mdc_map.end(); ++it) {
0816             auto &pair = *it;
0817             const auto &key = pair.first;
0818             const auto &value = pair.second;
0819             size_t content_size = key.size() + value.size() + 1;  // 1 for ':'
0820 
0821             if (it != last_element) {
0822                 content_size++;  // 1 for ' '
0823             }
0824 
0825             ScopedPadder p(content_size, padinfo_, dest);
0826             fmt_helper::append_string_view(key, dest);
0827             fmt_helper::append_string_view(":", dest);
0828             fmt_helper::append_string_view(value, dest);
0829             if (it != last_element) {
0830                 fmt_helper::append_string_view(" ", dest);
0831             }
0832         }
0833     }
0834 };
0835 #endif
0836 
0837 // Full info formatter
0838 // pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] [%s:%#] %v
0839 class full_formatter final : public flag_formatter {
0840 public:
0841     explicit full_formatter(padding_info padinfo)
0842         : flag_formatter(padinfo) {}
0843 
0844     void format(const details::log_msg &msg, const std::tm &tm_time, memory_buf_t &dest) override {
0845         using std::chrono::duration_cast;
0846         using std::chrono::milliseconds;
0847         using std::chrono::seconds;
0848 
0849         // cache the date/time part for the next second.
0850         auto duration = msg.time.time_since_epoch();
0851         auto secs = duration_cast<seconds>(duration);
0852 
0853         if (cache_timestamp_ != secs || cached_datetime_.size() == 0) {
0854             cached_datetime_.clear();
0855             cached_datetime_.push_back('[');
0856             fmt_helper::append_int(tm_time.tm_year + 1900, cached_datetime_);
0857             cached_datetime_.push_back('-');
0858 
0859             fmt_helper::pad2(tm_time.tm_mon + 1, cached_datetime_);
0860             cached_datetime_.push_back('-');
0861 
0862             fmt_helper::pad2(tm_time.tm_mday, cached_datetime_);
0863             cached_datetime_.push_back(' ');
0864 
0865             fmt_helper::pad2(tm_time.tm_hour, cached_datetime_);
0866             cached_datetime_.push_back(':');
0867 
0868             fmt_helper::pad2(tm_time.tm_min, cached_datetime_);
0869             cached_datetime_.push_back(':');
0870 
0871             fmt_helper::pad2(tm_time.tm_sec, cached_datetime_);
0872             cached_datetime_.push_back('.');
0873 
0874             cache_timestamp_ = secs;
0875         }
0876         dest.append(cached_datetime_.begin(), cached_datetime_.end());
0877 
0878         auto millis = fmt_helper::time_fraction<milliseconds>(msg.time);
0879         fmt_helper::pad3(static_cast<uint32_t>(millis.count()), dest);
0880         dest.push_back(']');
0881         dest.push_back(' ');
0882 
0883         // append logger name if exists
0884         if (msg.logger_name.size() > 0) {
0885             dest.push_back('[');
0886             fmt_helper::append_string_view(msg.logger_name, dest);
0887             dest.push_back(']');
0888             dest.push_back(' ');
0889         }
0890 
0891         dest.push_back('[');
0892         // wrap the level name with color
0893         msg.color_range_start = dest.size();
0894         // fmt_helper::append_string_view(level::to_c_str(msg.level), dest);
0895         fmt_helper::append_string_view(level::to_string_view(msg.level), dest);
0896         msg.color_range_end = dest.size();
0897         dest.push_back(']');
0898         dest.push_back(' ');
0899 
0900         // add source location if present
0901         if (!msg.source.empty()) {
0902             dest.push_back('[');
0903             const char *filename =
0904                 details::short_filename_formatter<details::null_scoped_padder>::basename(
0905                     msg.source.filename);
0906             fmt_helper::append_string_view(filename, dest);
0907             dest.push_back(':');
0908             fmt_helper::append_int(msg.source.line, dest);
0909             dest.push_back(']');
0910             dest.push_back(' ');
0911         }
0912 
0913 #ifndef SPDLOG_NO_TLS
0914         // add mdc if present
0915         auto &mdc_map = mdc::get_context();
0916         if (!mdc_map.empty()) {
0917             dest.push_back('[');
0918             mdc_formatter_.format_mdc(mdc_map, dest);
0919             dest.push_back(']');
0920             dest.push_back(' ');
0921         }
0922 #endif
0923         // fmt_helper::append_string_view(msg.msg(), dest);
0924         fmt_helper::append_string_view(msg.payload, dest);
0925     }
0926 
0927 private:
0928     std::chrono::seconds cache_timestamp_{0};
0929     memory_buf_t cached_datetime_;
0930 
0931 #ifndef SPDLOG_NO_TLS
0932     mdc_formatter<null_scoped_padder> mdc_formatter_{padding_info {}};
0933 #endif
0934 };
0935 
0936 }  // namespace details
0937 
0938 SPDLOG_INLINE pattern_formatter::pattern_formatter(std::string pattern,
0939                                                    pattern_time_type time_type,
0940                                                    std::string eol,
0941                                                    custom_flags custom_user_flags)
0942     : pattern_(std::move(pattern)),
0943       eol_(std::move(eol)),
0944       pattern_time_type_(time_type),
0945       need_localtime_(false),
0946       last_log_secs_(0),
0947       custom_handlers_(std::move(custom_user_flags)) {
0948     std::memset(&cached_tm_, 0, sizeof(cached_tm_));
0949     compile_pattern_(pattern_);
0950 }
0951 
0952 // use by default full formatter for if pattern is not given
0953 SPDLOG_INLINE pattern_formatter::pattern_formatter(pattern_time_type time_type, std::string eol)
0954     : pattern_("%+"),
0955       eol_(std::move(eol)),
0956       pattern_time_type_(time_type),
0957       need_localtime_(true),
0958       last_log_secs_(0) {
0959     std::memset(&cached_tm_, 0, sizeof(cached_tm_));
0960     formatters_.push_back(details::make_unique<details::full_formatter>(details::padding_info{}));
0961 }
0962 
0963 SPDLOG_INLINE std::unique_ptr<formatter> pattern_formatter::clone() const {
0964     custom_flags cloned_custom_formatters;
0965     for (auto &it : custom_handlers_) {
0966         cloned_custom_formatters[it.first] = it.second->clone();
0967     }
0968     auto cloned = details::make_unique<pattern_formatter>(pattern_, pattern_time_type_, eol_,
0969                                                           std::move(cloned_custom_formatters));
0970     cloned->need_localtime(need_localtime_);
0971 #if defined(__GNUC__) && __GNUC__ < 5
0972     return std::move(cloned);
0973 #else
0974     return cloned;
0975 #endif
0976 }
0977 
0978 SPDLOG_INLINE void pattern_formatter::format(const details::log_msg &msg, memory_buf_t &dest) {
0979     if (need_localtime_) {
0980         const auto secs =
0981             std::chrono::duration_cast<std::chrono::seconds>(msg.time.time_since_epoch());
0982         if (secs != last_log_secs_) {
0983             cached_tm_ = get_time_(msg);
0984             last_log_secs_ = secs;
0985         }
0986     }
0987 
0988     for (auto &f : formatters_) {
0989         f->format(msg, cached_tm_, dest);
0990     }
0991     // write eol
0992     details::fmt_helper::append_string_view(eol_, dest);
0993 }
0994 
0995 SPDLOG_INLINE void pattern_formatter::set_pattern(std::string pattern) {
0996     pattern_ = std::move(pattern);
0997     need_localtime_ = false;
0998     compile_pattern_(pattern_);
0999 }
1000 
1001 SPDLOG_INLINE void pattern_formatter::need_localtime(bool need) { need_localtime_ = need; }
1002 
1003 SPDLOG_INLINE std::tm pattern_formatter::get_time_(const details::log_msg &msg) {
1004     if (pattern_time_type_ == pattern_time_type::local) {
1005         return details::os::localtime(log_clock::to_time_t(msg.time));
1006     }
1007     return details::os::gmtime(log_clock::to_time_t(msg.time));
1008 }
1009 
1010 template <typename Padder>
1011 SPDLOG_INLINE void pattern_formatter::handle_flag_(char flag, details::padding_info padding) {
1012     // process custom flags
1013     auto it = custom_handlers_.find(flag);
1014     if (it != custom_handlers_.end()) {
1015         auto custom_handler = it->second->clone();
1016         custom_handler->set_padding_info(padding);
1017         formatters_.push_back(std::move(custom_handler));
1018         return;
1019     }
1020 
1021     // process built-in flags
1022     switch (flag) {
1023         case ('+'):  // default formatter
1024             formatters_.push_back(details::make_unique<details::full_formatter>(padding));
1025             need_localtime_ = true;
1026             break;
1027 
1028         case 'n':  // logger name
1029             formatters_.push_back(details::make_unique<details::name_formatter<Padder>>(padding));
1030             break;
1031 
1032         case 'l':  // level
1033             formatters_.push_back(details::make_unique<details::level_formatter<Padder>>(padding));
1034             break;
1035 
1036         case 'L':  // short level
1037             formatters_.push_back(
1038                 details::make_unique<details::short_level_formatter<Padder>>(padding));
1039             break;
1040 
1041         case ('t'):  // thread id
1042             formatters_.push_back(details::make_unique<details::t_formatter<Padder>>(padding));
1043             break;
1044 
1045         case ('v'):  // the message text
1046             formatters_.push_back(details::make_unique<details::v_formatter<Padder>>(padding));
1047             break;
1048 
1049         case ('a'):  // weekday
1050             formatters_.push_back(details::make_unique<details::a_formatter<Padder>>(padding));
1051             need_localtime_ = true;
1052             break;
1053 
1054         case ('A'):  // short weekday
1055             formatters_.push_back(details::make_unique<details::A_formatter<Padder>>(padding));
1056             need_localtime_ = true;
1057             break;
1058 
1059         case ('b'):
1060         case ('h'):  // month
1061             formatters_.push_back(details::make_unique<details::b_formatter<Padder>>(padding));
1062             need_localtime_ = true;
1063             break;
1064 
1065         case ('B'):  // short month
1066             formatters_.push_back(details::make_unique<details::B_formatter<Padder>>(padding));
1067             need_localtime_ = true;
1068             break;
1069 
1070         case ('c'):  // datetime
1071             formatters_.push_back(details::make_unique<details::c_formatter<Padder>>(padding));
1072             need_localtime_ = true;
1073             break;
1074 
1075         case ('C'):  // year 2 digits
1076             formatters_.push_back(details::make_unique<details::C_formatter<Padder>>(padding));
1077             need_localtime_ = true;
1078             break;
1079 
1080         case ('Y'):  // year 4 digits
1081             formatters_.push_back(details::make_unique<details::Y_formatter<Padder>>(padding));
1082             need_localtime_ = true;
1083             break;
1084 
1085         case ('D'):
1086         case ('x'):  // datetime MM/DD/YY
1087             formatters_.push_back(details::make_unique<details::D_formatter<Padder>>(padding));
1088             need_localtime_ = true;
1089             break;
1090 
1091         case ('m'):  // month 1-12
1092             formatters_.push_back(details::make_unique<details::m_formatter<Padder>>(padding));
1093             need_localtime_ = true;
1094             break;
1095 
1096         case ('d'):  // day of month 1-31
1097             formatters_.push_back(details::make_unique<details::d_formatter<Padder>>(padding));
1098             need_localtime_ = true;
1099             break;
1100 
1101         case ('H'):  // hours 24
1102             formatters_.push_back(details::make_unique<details::H_formatter<Padder>>(padding));
1103             need_localtime_ = true;
1104             break;
1105 
1106         case ('I'):  // hours 12
1107             formatters_.push_back(details::make_unique<details::I_formatter<Padder>>(padding));
1108             need_localtime_ = true;
1109             break;
1110 
1111         case ('M'):  // minutes
1112             formatters_.push_back(details::make_unique<details::M_formatter<Padder>>(padding));
1113             need_localtime_ = true;
1114             break;
1115 
1116         case ('S'):  // seconds
1117             formatters_.push_back(details::make_unique<details::S_formatter<Padder>>(padding));
1118             need_localtime_ = true;
1119             break;
1120 
1121         case ('e'):  // milliseconds
1122             formatters_.push_back(details::make_unique<details::e_formatter<Padder>>(padding));
1123             break;
1124 
1125         case ('f'):  // microseconds
1126             formatters_.push_back(details::make_unique<details::f_formatter<Padder>>(padding));
1127             break;
1128 
1129         case ('F'):  // nanoseconds
1130             formatters_.push_back(details::make_unique<details::F_formatter<Padder>>(padding));
1131             break;
1132 
1133         case ('E'):  // seconds since epoch
1134             formatters_.push_back(details::make_unique<details::E_formatter<Padder>>(padding));
1135             break;
1136 
1137         case ('p'):  // am/pm
1138             formatters_.push_back(details::make_unique<details::p_formatter<Padder>>(padding));
1139             need_localtime_ = true;
1140             break;
1141 
1142         case ('r'):  // 12 hour clock 02:55:02 pm
1143             formatters_.push_back(details::make_unique<details::r_formatter<Padder>>(padding));
1144             need_localtime_ = true;
1145             break;
1146 
1147         case ('R'):  // 24-hour HH:MM time
1148             formatters_.push_back(details::make_unique<details::R_formatter<Padder>>(padding));
1149             need_localtime_ = true;
1150             break;
1151 
1152         case ('T'):
1153         case ('X'):  // ISO 8601 time format (HH:MM:SS)
1154             formatters_.push_back(details::make_unique<details::T_formatter<Padder>>(padding));
1155             need_localtime_ = true;
1156             break;
1157 
1158         case ('z'):  // timezone
1159             formatters_.push_back(details::make_unique<details::z_formatter<Padder>>(padding));
1160             need_localtime_ = true;
1161             break;
1162 
1163         case ('P'):  // pid
1164             formatters_.push_back(details::make_unique<details::pid_formatter<Padder>>(padding));
1165             break;
1166 
1167         case ('^'):  // color range start
1168             formatters_.push_back(details::make_unique<details::color_start_formatter>(padding));
1169             break;
1170 
1171         case ('$'):  // color range end
1172             formatters_.push_back(details::make_unique<details::color_stop_formatter>(padding));
1173             break;
1174 
1175         case ('@'):  // source location (filename:filenumber)
1176             formatters_.push_back(
1177                 details::make_unique<details::source_location_formatter<Padder>>(padding));
1178             break;
1179 
1180         case ('s'):  // short source filename - without directory name
1181             formatters_.push_back(
1182                 details::make_unique<details::short_filename_formatter<Padder>>(padding));
1183             break;
1184 
1185         case ('g'):  // full source filename
1186             formatters_.push_back(
1187                 details::make_unique<details::source_filename_formatter<Padder>>(padding));
1188             break;
1189 
1190         case ('#'):  // source line number
1191             formatters_.push_back(
1192                 details::make_unique<details::source_linenum_formatter<Padder>>(padding));
1193             break;
1194 
1195         case ('!'):  // source funcname
1196             formatters_.push_back(
1197                 details::make_unique<details::source_funcname_formatter<Padder>>(padding));
1198             break;
1199 
1200         case ('%'):  // % char
1201             formatters_.push_back(details::make_unique<details::ch_formatter>('%'));
1202             break;
1203 
1204         case ('u'):  // elapsed time since last log message in nanos
1205             formatters_.push_back(
1206                 details::make_unique<details::elapsed_formatter<Padder, std::chrono::nanoseconds>>(
1207                     padding));
1208             break;
1209 
1210         case ('i'):  // elapsed time since last log message in micros
1211             formatters_.push_back(
1212                 details::make_unique<details::elapsed_formatter<Padder, std::chrono::microseconds>>(
1213                     padding));
1214             break;
1215 
1216         case ('o'):  // elapsed time since last log message in millis
1217             formatters_.push_back(
1218                 details::make_unique<details::elapsed_formatter<Padder, std::chrono::milliseconds>>(
1219                     padding));
1220             break;
1221 
1222         case ('O'):  // elapsed time since last log message in seconds
1223             formatters_.push_back(
1224                 details::make_unique<details::elapsed_formatter<Padder, std::chrono::seconds>>(
1225                     padding));
1226             break;
1227 
1228 #ifndef SPDLOG_NO_TLS  // mdc formatter requires TLS support
1229         case ('&'):
1230             formatters_.push_back(details::make_unique<details::mdc_formatter<Padder>>(padding));
1231             break;
1232 #endif
1233 
1234         default:  // Unknown flag appears as is
1235             auto unknown_flag = details::make_unique<details::aggregate_formatter>();
1236 
1237             if (!padding.truncate_) {
1238                 unknown_flag->add_ch('%');
1239                 unknown_flag->add_ch(flag);
1240                 formatters_.push_back((std::move(unknown_flag)));
1241             }
1242             // fix issue #1617 (prev char was '!' and should have been treated as funcname flag
1243             // instead of truncating flag) spdlog::set_pattern("[%10!] %v") => "[      main] some
1244             // message" spdlog::set_pattern("[%3!!] %v") => "[mai] some message"
1245             else {
1246                 padding.truncate_ = false;
1247                 formatters_.push_back(
1248                     details::make_unique<details::source_funcname_formatter<Padder>>(padding));
1249                 unknown_flag->add_ch(flag);
1250                 formatters_.push_back((std::move(unknown_flag)));
1251             }
1252 
1253             break;
1254     }
1255 }
1256 
1257 // Extract given pad spec (e.g. %8X, %=8X, %-8!X, %8!X, %=8!X, %-8!X, %+8!X)
1258 // Advance the given it pass the end of the padding spec found (if any)
1259 // Return padding.
1260 SPDLOG_INLINE details::padding_info pattern_formatter::handle_padspec_(
1261     std::string::const_iterator &it, std::string::const_iterator end) {
1262     using details::padding_info;
1263     using details::scoped_padder;
1264     const size_t max_width = 64;
1265     if (it == end) {
1266         return padding_info{};
1267     }
1268 
1269     padding_info::pad_side side;
1270     switch (*it) {
1271         case '-':
1272             side = padding_info::pad_side::right;
1273             ++it;
1274             break;
1275         case '=':
1276             side = padding_info::pad_side::center;
1277             ++it;
1278             break;
1279         default:
1280             side = details::padding_info::pad_side::left;
1281             break;
1282     }
1283 
1284     if (it == end || !std::isdigit(static_cast<unsigned char>(*it))) {
1285         return padding_info{};  // no padding if no digit found here
1286     }
1287 
1288     auto width = static_cast<size_t>(*it) - '0';
1289     for (++it; it != end && std::isdigit(static_cast<unsigned char>(*it)); ++it) {
1290         auto digit = static_cast<size_t>(*it) - '0';
1291         width = width * 10 + digit;
1292     }
1293 
1294     // search for the optional truncate marker '!'
1295     bool truncate;
1296     if (it != end && *it == '!') {
1297         truncate = true;
1298         ++it;
1299     } else {
1300         truncate = false;
1301     }
1302     return details::padding_info{std::min<size_t>(width, max_width), side, truncate};
1303 }
1304 
1305 SPDLOG_INLINE void pattern_formatter::compile_pattern_(const std::string &pattern) {
1306     auto end = pattern.end();
1307     std::unique_ptr<details::aggregate_formatter> user_chars;
1308     formatters_.clear();
1309     for (auto it = pattern.begin(); it != end; ++it) {
1310         if (*it == '%') {
1311             if (user_chars)  // append user chars found so far
1312             {
1313                 formatters_.push_back(std::move(user_chars));
1314             }
1315 
1316             auto padding = handle_padspec_(++it, end);
1317 
1318             if (it != end) {
1319                 if (padding.enabled()) {
1320                     handle_flag_<details::scoped_padder>(*it, padding);
1321                 } else {
1322                     handle_flag_<details::null_scoped_padder>(*it, padding);
1323                 }
1324             } else {
1325                 break;
1326             }
1327         } else  // chars not following the % sign should be displayed as is
1328         {
1329             if (!user_chars) {
1330                 user_chars = details::make_unique<details::aggregate_formatter>();
1331             }
1332             user_chars->add_ch(*it);
1333         }
1334     }
1335     if (user_chars)  // append raw chars found so far
1336     {
1337         formatters_.push_back(std::move(user_chars));
1338     }
1339 }
1340 }  // namespace spdlog