Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 10:12:44

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