Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-05 09:10:25

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