From f830ef05f0f37a57416ab477ee9c4eb08f149a75 Mon Sep 17 00:00:00 2001
From: Tommaso Tocci <tommaso@tocci.pro>
Date: Tue, 11 Jun 2019 11:50:07 +0200
Subject: [PATCH] deps: update spdlog v1.2.0 -> v1.3.1

---
 external/spdlog/async.h                     |   14 +-
 external/spdlog/common.h                    |  117 +-
 external/spdlog/details/async_logger_impl.h |    8 +-
 external/spdlog/details/file_helper.h       |    2 +-
 external/spdlog/details/fmt_helper.h        |  103 +-
 external/spdlog/details/log_msg.h           |   26 +-
 external/spdlog/details/logger_impl.h       |   96 +-
 external/spdlog/details/os.h                |   11 +-
 external/spdlog/details/pattern_formatter.h |  770 ++++++++--
 external/spdlog/details/periodic_worker.h   |    2 +-
 external/spdlog/details/registry.h          |   97 +-
 external/spdlog/details/thread_pool.h       |   34 +-
 external/spdlog/fmt/bin_to_hex.h            |    4 +-
 external/spdlog/fmt/bundled/chrono.h        |  452 ++++++
 external/spdlog/fmt/bundled/color.h         |  577 ++++++++
 external/spdlog/fmt/bundled/colors.h        |  257 ----
 external/spdlog/fmt/bundled/core.h          |  854 +++++------
 external/spdlog/fmt/bundled/format-inl.h    |  504 ++++---
 external/spdlog/fmt/bundled/format.h        | 1465 ++++++++-----------
 external/spdlog/fmt/bundled/locale.h        |   77 +
 external/spdlog/fmt/bundled/ostream.h       |   20 +-
 external/spdlog/fmt/bundled/posix.h         |    4 +-
 external/spdlog/fmt/bundled/printf.h        |  367 +++--
 external/spdlog/fmt/bundled/time.h          |   38 +-
 external/spdlog/logger.h                    |   41 +-
 external/spdlog/sinks/android_sink.h        |    6 +-
 external/spdlog/sinks/ansicolor_sink.h      |    4 +
 external/spdlog/sinks/basic_file_sink.h     |    6 +-
 external/spdlog/sinks/daily_file_sink.h     |    8 +-
 external/spdlog/sinks/dist_sink.h           |    4 +
 external/spdlog/sinks/msvc_sink.h           |    4 +
 external/spdlog/sinks/null_sink.h           |   21 +
 external/spdlog/sinks/ostream_sink.h        |    4 +
 external/spdlog/sinks/rotating_file_sink.h  |  305 ++--
 external/spdlog/sinks/sink.h                |    2 +-
 external/spdlog/sinks/stdout_color_sinks.h  |    3 +
 external/spdlog/sinks/stdout_sinks.h        |    5 +-
 external/spdlog/sinks/syslog_sink.h         |    7 +-
 external/spdlog/sinks/wincolor_sink.h       |    4 +
 external/spdlog/spdlog.h                    |  259 +++-
 external/spdlog/tweakme.h                   |   28 +-
 external/spdlog/version.h                   |    4 +-
 42 files changed, 4303 insertions(+), 2311 deletions(-)
 create mode 100644 external/spdlog/fmt/bundled/chrono.h
 create mode 100644 external/spdlog/fmt/bundled/color.h
 delete mode 100644 external/spdlog/fmt/bundled/colors.h
 create mode 100644 external/spdlog/fmt/bundled/locale.h

diff --git a/external/spdlog/async.h b/external/spdlog/async.h
index 9c3e95512..971becd70 100644
--- a/external/spdlog/async.h
+++ b/external/spdlog/async.h
@@ -37,7 +37,7 @@ template<async_overflow_policy OverflowPolicy = async_overflow_policy::block>
 struct async_factory_impl
 {
     template<typename Sink, typename... SinkArgs>
-    static std::shared_ptr<async_logger> create(const std::string &logger_name, SinkArgs &&... args)
+    static std::shared_ptr<async_logger> create(std::string logger_name, SinkArgs &&... args)
     {
         auto &registry_inst = details::registry::instance();
 
@@ -51,8 +51,8 @@ struct async_factory_impl
         }
 
         auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
-        auto new_logger = std::make_shared<async_logger>(logger_name, std::move(sink), std::move(tp), OverflowPolicy);
-        registry_inst.register_and_init(new_logger);
+        auto new_logger = std::make_shared<async_logger>(std::move(logger_name), std::move(sink), std::move(tp), OverflowPolicy);
+        registry_inst.initialize_logger(new_logger);
         return new_logger;
     }
 };
@@ -61,15 +61,15 @@ using async_factory = async_factory_impl<async_overflow_policy::block>;
 using async_factory_nonblock = async_factory_impl<async_overflow_policy::overrun_oldest>;
 
 template<typename Sink, typename... SinkArgs>
-inline std::shared_ptr<spdlog::logger> create_async(const std::string &logger_name, SinkArgs &&... sink_args)
+inline std::shared_ptr<spdlog::logger> create_async(std::string logger_name, SinkArgs &&... sink_args)
 {
-    return async_factory::create<Sink>(logger_name, std::forward<SinkArgs>(sink_args)...);
+    return async_factory::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
 }
 
 template<typename Sink, typename... SinkArgs>
-inline std::shared_ptr<spdlog::logger> create_async_nb(const std::string &logger_name, SinkArgs &&... sink_args)
+inline std::shared_ptr<spdlog::logger> create_async_nb(std::string logger_name, SinkArgs &&... sink_args)
 {
-    return async_factory_nonblock::create<Sink>(logger_name, std::forward<SinkArgs>(sink_args)...);
+    return async_factory_nonblock::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
 }
 
 // set global thread pool.
diff --git a/external/spdlog/common.h b/external/spdlog/common.h
index b61473462..d078a1ab3 100644
--- a/external/spdlog/common.h
+++ b/external/spdlog/common.h
@@ -14,6 +14,7 @@
 #include <memory>
 #include <stdexcept>
 #include <string>
+#include <cstring>
 #include <type_traits>
 #include <unordered_map>
 
@@ -24,6 +25,8 @@
 
 #include "spdlog/details/null_mutex.h"
 
+#include "spdlog/fmt/fmt.h"
+
 // visual studio upto 2013 does not support noexcept nor constexpr
 #if defined(_MSC_VER) && (_MSC_VER < 1900)
 #define SPDLOG_NOEXCEPT throw()
@@ -41,7 +44,29 @@
 #define SPDLOG_DEPRECATED
 #endif
 
-#include "spdlog/fmt/fmt.h"
+// disable thread local on msvc 2013
+#ifndef SPDLOG_NO_TLS
+#if (defined(_MSC_VER) && (_MSC_VER < 1900)) || defined(__cplusplus_winrt)
+#define SPDLOG_NO_TLS 1
+#endif
+#endif
+
+// Get the basename of __FILE__ (at compile time if possible)
+#if FMT_HAS_FEATURE(__builtin_strrchr)
+#define SPDLOG_STRRCHR(str, sep) __builtin_strrchr(str, sep)
+#else
+#define SPDLOG_STRRCHR(str, sep) strrchr(str, sep)
+#endif //__builtin_strrchr not defined
+
+#ifdef _WIN32
+#define SPDLOG_FILE_BASENAME(file) SPDLOG_STRRCHR("\\" file, '\\') + 1
+#else
+#define SPDLOG_FILE_BASENAME(file) SPDLOG_STRRCHR("/" file, '/') + 1
+#endif
+
+#ifndef SPDLOG_FUNCTION
+#define SPDLOG_FUNCTION __FUNCTION__
+#endif
 
 namespace spdlog {
 
@@ -56,23 +81,42 @@ using sink_ptr = std::shared_ptr<sinks::sink>;
 using sinks_init_list = std::initializer_list<sink_ptr>;
 using log_err_handler = std::function<void(const std::string &err_msg)>;
 
+// string_view type - either std::string_view or fmt::string_view (pre c++17)
+#if defined(FMT_USE_STD_STRING_VIEW)
+using string_view_t = std::string_view;
+#else
+using string_view_t = fmt::string_view;
+#endif
+
 #if defined(SPDLOG_NO_ATOMIC_LEVELS)
 using level_t = details::null_atomic_int;
 #else
 using level_t = std::atomic<int>;
 #endif
 
+#define SPDLOG_LEVEL_TRACE 0
+#define SPDLOG_LEVEL_DEBUG 1
+#define SPDLOG_LEVEL_INFO 2
+#define SPDLOG_LEVEL_WARN 3
+#define SPDLOG_LEVEL_ERROR 4
+#define SPDLOG_LEVEL_CRITICAL 5
+#define SPDLOG_LEVEL_OFF 6
+
+#if !defined(SPDLOG_ACTIVE_LEVEL)
+#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
+#endif
+
 // Log level enum
 namespace level {
 enum level_enum
 {
-    trace = 0,
-    debug = 1,
-    info = 2,
-    warn = 3,
-    err = 4,
-    critical = 5,
-    off = 6
+    trace = SPDLOG_LEVEL_TRACE,
+    debug = SPDLOG_LEVEL_DEBUG,
+    info = SPDLOG_LEVEL_INFO,
+    warn = SPDLOG_LEVEL_WARN,
+    err = SPDLOG_LEVEL_ERROR,
+    critical = SPDLOG_LEVEL_CRITICAL,
+    off = SPDLOG_LEVEL_OFF,
 };
 
 #if !defined(SPDLOG_LEVEL_NAMES)
@@ -81,13 +125,13 @@ enum level_enum
         "trace", "debug", "info", "warning", "error", "critical", "off"                                                                    \
     }
 #endif
-static const char *level_names[] SPDLOG_LEVEL_NAMES;
 
+static string_view_t level_string_views[] SPDLOG_LEVEL_NAMES;
 static const char *short_level_names[]{"T", "D", "I", "W", "E", "C", "O"};
 
-inline const char *to_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
+inline string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
 {
-    return level_names[l];
+    return level_string_views[l];
 }
 
 inline const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
@@ -97,17 +141,16 @@ inline const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
 
 inline spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT
 {
-    static std::unordered_map<std::string, level_enum> name_to_level = // map string->level
-        {{level_names[0], level::trace},                               // trace
-            {level_names[1], level::debug},                            // debug
-            {level_names[2], level::info},                             // info
-            {level_names[3], level::warn},                             // warn
-            {level_names[4], level::err},                              // err
-            {level_names[5], level::critical},                         // critical
-            {level_names[6], level::off}};                             // off
-
-    auto lvl_it = name_to_level.find(name);
-    return lvl_it != name_to_level.end() ? lvl_it->second : level::off;
+    int level = 0;
+    for (const auto &level_str : level_string_views)
+    {
+        if (level_str == name)
+        {
+            return static_cast<level::level_enum>(level);
+        }
+        level++;
+    }
+    return level::off;
 }
 
 using level_hasher = std::hash<int>;
@@ -159,16 +202,30 @@ using filename_t = std::wstring;
 using filename_t = std::string;
 #endif
 
-#define SPDLOG_CATCH_AND_HANDLE                                                                                                            \
-    catch (const std::exception &ex)                                                                                                       \
-    {                                                                                                                                      \
-        err_handler_(ex.what());                                                                                                           \
-    }                                                                                                                                      \
-    catch (...)                                                                                                                            \
-    {                                                                                                                                      \
-        err_handler_("Unknown exeption in logger");                                                                                        \
+struct source_loc
+{
+    SPDLOG_CONSTEXPR source_loc()
+        : filename{""}
+        , line{0}
+        , funcname{""}
+    {
+    }
+    SPDLOG_CONSTEXPR source_loc(const char *filename, int line, const char *funcname)
+        : filename{filename}
+        , line{static_cast<uint32_t>(line)}
+        , funcname{funcname}
+    {
     }
 
+    SPDLOG_CONSTEXPR bool empty() const SPDLOG_NOEXCEPT
+    {
+        return line == 0;
+    }
+    const char *filename;
+    uint32_t line;
+    const char *funcname;
+};
+
 namespace details {
 // make_unique support for pre c++14
 
diff --git a/external/spdlog/details/async_logger_impl.h b/external/spdlog/details/async_logger_impl.h
index e604d4edd..aafcae656 100644
--- a/external/spdlog/details/async_logger_impl.h
+++ b/external/spdlog/details/async_logger_impl.h
@@ -18,20 +18,20 @@ template<typename It>
 inline spdlog::async_logger::async_logger(
     std::string logger_name, It begin, It end, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy)
     : logger(std::move(logger_name), begin, end)
-    , thread_pool_(tp)
+    , thread_pool_(std::move(tp))
     , overflow_policy_(overflow_policy)
 {
 }
 
 inline spdlog::async_logger::async_logger(
     std::string logger_name, sinks_init_list sinks_list, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy)
-    : async_logger(std::move(logger_name), sinks_list.begin(), sinks_list.end(), tp, overflow_policy)
+    : async_logger(std::move(logger_name), sinks_list.begin(), sinks_list.end(), std::move(tp), overflow_policy)
 {
 }
 
 inline spdlog::async_logger::async_logger(
     std::string logger_name, sink_ptr single_sink, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy)
-    : async_logger(std::move(logger_name), {single_sink}, tp, overflow_policy)
+    : async_logger(std::move(logger_name), {std::move(single_sink)}, std::move(tp), overflow_policy)
 {
 }
 
@@ -43,7 +43,7 @@ inline void spdlog::async_logger::sink_it_(details::log_msg &msg)
 #endif
     if (auto pool_ptr = thread_pool_.lock())
     {
-        pool_ptr->post_log(shared_from_this(), std::move(msg), overflow_policy_);
+        pool_ptr->post_log(shared_from_this(), msg, overflow_policy_);
     }
     else
     {
diff --git a/external/spdlog/details/file_helper.h b/external/spdlog/details/file_helper.h
index f72820004..8c1132d9c 100644
--- a/external/spdlog/details/file_helper.h
+++ b/external/spdlog/details/file_helper.h
@@ -122,7 +122,7 @@ public:
     // ".mylog" => (".mylog". "")
     // "my_folder/.mylog" => ("my_folder/.mylog", "")
     // "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
-    static std::tuple<filename_t, filename_t> split_by_extenstion(const spdlog::filename_t &fname)
+    static std::tuple<filename_t, filename_t> split_by_extension(const spdlog::filename_t &fname)
     {
         auto ext_index = fname.rfind('.');
 
diff --git a/external/spdlog/details/fmt_helper.h b/external/spdlog/details/fmt_helper.h
index 1518b2c0f..d76aac452 100644
--- a/external/spdlog/details/fmt_helper.h
+++ b/external/spdlog/details/fmt_helper.h
@@ -4,7 +4,8 @@
 
 #pragma once
 
-#include "chrono"
+#include <chrono>
+#include <type_traits>
 #include "spdlog/fmt/fmt.h"
 
 // Some fmt helpers to efficiently format and pad ints and strings
@@ -13,17 +14,9 @@ namespace details {
 namespace fmt_helper {
 
 template<size_t Buffer_Size>
-inline void append_str(const std::string &str, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
+inline spdlog::string_view_t to_string_view(const fmt::basic_memory_buffer<char, Buffer_Size> &buf) SPDLOG_NOEXCEPT
 {
-    auto *str_ptr = str.data();
-    dest.append(str_ptr, str_ptr + str.size());
-}
-
-template<size_t Buffer_Size>
-inline void append_c_str(const char *c_str, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
-{
-    auto len = std::char_traits<char>::length(c_str);
-    dest.append(c_str, c_str + len);
+    return spdlog::string_view_t(buf.data(), buf.size());
 }
 
 template<size_t Buffer_Size1, size_t Buffer_Size2>
@@ -33,6 +26,16 @@ inline void append_buf(const fmt::basic_memory_buffer<char, Buffer_Size1> &buf,
     dest.append(buf_ptr, buf_ptr + buf.size());
 }
 
+template<size_t Buffer_Size>
+inline void append_string_view(spdlog::string_view_t view, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
+{
+    auto *buf_ptr = view.data();
+    if (buf_ptr != nullptr)
+    {
+        dest.append(buf_ptr, buf_ptr + view.size());
+    }
+}
+
 template<typename T, size_t Buffer_Size>
 inline void append_int(T n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
 {
@@ -40,73 +43,65 @@ inline void append_int(T n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
     dest.append(i.data(), i.data() + i.size());
 }
 
+template<typename T>
+inline unsigned count_digits(T n)
+{
+    using count_type = typename std::conditional<(sizeof(T) > sizeof(uint32_t)), uint64_t, uint32_t>::type;
+    return static_cast<unsigned>(fmt::internal::count_digits(static_cast<count_type>(n)));
+}
+
 template<size_t Buffer_Size>
 inline void pad2(int n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
 {
     if (n > 99)
     {
         append_int(n, dest);
-        return;
     }
-    if (n > 9) // 10-99
+    else if (n > 9) // 10-99
     {
         dest.push_back(static_cast<char>('0' + n / 10));
         dest.push_back(static_cast<char>('0' + n % 10));
-        return;
     }
-    if (n >= 0) // 0-9
+    else if (n >= 0) // 0-9
     {
         dest.push_back('0');
         dest.push_back(static_cast<char>('0' + n));
-        return;
     }
-    // negatives (unlikely, but just in case, let fmt deal with it)
-    fmt::format_to(dest, "{:02}", n);
+    else // negatives (unlikely, but just in case, let fmt deal with it)
+    {
+        fmt::format_to(dest, "{:02}", n);
+    }
 }
 
-template<size_t Buffer_Size>
-inline void pad3(int n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
+template<typename T, size_t Buffer_Size>
+inline void pad_uint(T n, unsigned int width, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
 {
-    if (n > 999)
+    static_assert(std::is_unsigned<T>::value, "pad_uint must get unsigned T");
+    auto digits = count_digits(n);
+    if (width > digits)
     {
-        append_int(n, dest);
-        return;
+        const char *zeroes = "0000000000000000000";
+        dest.append(zeroes, zeroes + width - digits);
     }
+    append_int(n, dest);
+}
 
-    if (n > 99) // 100-999
-    {
-        dest.push_back(static_cast<char>('0' + n / 100));
-        pad2(n % 100, dest);
-        return;
-    }
-    if (n > 9) // 10-99
-    {
-        dest.push_back('0');
-        dest.push_back(static_cast<char>('0' + n / 10));
-        dest.push_back(static_cast<char>('0' + n % 10));
-        return;
-    }
-    if (n >= 0)
-    {
-        dest.push_back('0');
-        dest.push_back('0');
-        dest.push_back(static_cast<char>('0' + n));
-        return;
-    }
-    // negatives (unlikely, but just in case let fmt deal with it)
-    fmt::format_to(dest, "{:03}", n);
+template<typename T, size_t Buffer_Size>
+inline void pad3(T n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
+{
+    pad_uint(n, 3, dest);
 }
 
-template<size_t Buffer_Size>
-inline void pad6(size_t n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
+template<typename T, size_t Buffer_Size>
+inline void pad6(T n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
 {
-    if (n > 99999)
-    {
-        append_int(n, dest);
-        return;
-    }
-    pad3(static_cast<int>(n / 1000), dest);
-    pad3(static_cast<int>(n % 1000), dest);
+    pad_uint(n, 6, dest);
+}
+
+template<typename T, size_t Buffer_Size>
+inline void pad9(T n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
+{
+    pad_uint(n, 9, dest);
 }
 
 // return fraction of a second of the given time_point.
diff --git a/external/spdlog/details/log_msg.h b/external/spdlog/details/log_msg.h
index 49987515b..69422ba37 100644
--- a/external/spdlog/details/log_msg.h
+++ b/external/spdlog/details/log_msg.h
@@ -15,9 +15,8 @@ namespace spdlog {
 namespace details {
 struct log_msg
 {
-    log_msg() = default;
 
-    log_msg(const std::string *loggers_name, level::level_enum lvl)
+    log_msg(source_loc loc, const std::string *loggers_name, level::level_enum lvl, string_view_t view)
         : logger_name(loggers_name)
         , level(lvl)
 #ifndef SPDLOG_NO_DATETIME
@@ -27,23 +26,30 @@ struct log_msg
 #ifndef SPDLOG_NO_THREAD_ID
         , thread_id(os::thread_id())
 #endif
+        , source(loc)
+        , payload(view)
     {
     }
 
-    log_msg(const log_msg &other) = delete;
-    log_msg(log_msg &&other) = delete;
-    log_msg &operator=(log_msg &&other) = delete;
+    log_msg(const std::string *loggers_name, level::level_enum lvl, string_view_t view)
+        : log_msg(source_loc{}, loggers_name, lvl, view)
+    {
+    }
+
+    log_msg(const log_msg &other) = default;
 
     const std::string *logger_name{nullptr};
-    level::level_enum level;
+    level::level_enum level{level::off};
     log_clock::time_point time;
-    size_t thread_id;
-    fmt::memory_buffer raw;
-    size_t msg_id;
+    size_t thread_id{0};
+    size_t msg_id{0};
 
-    // info about wrapping the formatted text with color (updated by pattern_formatter).
+    // wrapping the formatted text with color (updated by pattern_formatter).
     mutable size_t color_range_start{0};
     mutable size_t color_range_end{0};
+
+    source_loc source;
+    const string_view_t payload;
 };
 } // namespace details
 } // namespace spdlog
diff --git a/external/spdlog/details/logger_impl.h b/external/spdlog/details/logger_impl.h
index 46301ea1e..0212ede53 100644
--- a/external/spdlog/details/logger_impl.h
+++ b/external/spdlog/details/logger_impl.h
@@ -10,19 +10,23 @@
 #include <memory>
 #include <string>
 
+#define SPDLOG_CATCH_AND_HANDLE                                                                                                            \
+    catch (const std::exception &ex)                                                                                                       \
+    {                                                                                                                                      \
+        err_handler_(ex.what());                                                                                                           \
+    }                                                                                                                                      \
+    catch (...)                                                                                                                            \
+    {                                                                                                                                      \
+        err_handler_("Unknown exception in logger");                                                                                       \
+    }
+
 // create logger with given name, sinks and the default pattern formatter
 // all other ctors will call this one
 template<typename It>
 inline spdlog::logger::logger(std::string logger_name, It begin, It end)
     : name_(std::move(logger_name))
     , sinks_(begin, end)
-    , level_(level::info)
-    , flush_level_(level::off)
-    , last_err_time_(0)
-    , msg_counter_(1) // message counter will start from 1. 0-message id will be
-                      // reserved for controll messages
 {
-    err_handler_ = [this](const std::string &msg) { this->default_err_handler_(msg); };
 }
 
 // ctor with sinks as init list
@@ -54,7 +58,7 @@ inline void spdlog::logger::set_pattern(std::string pattern, pattern_time_type t
 }
 
 template<typename... Args>
-inline void spdlog::logger::log(level::level_enum lvl, const char *fmt, const Args &... args)
+inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const char *fmt, const Args &... args)
 {
     if (!should_log(lvl))
     {
@@ -63,15 +67,43 @@ inline void spdlog::logger::log(level::level_enum lvl, const char *fmt, const Ar
 
     try
     {
-        details::log_msg log_msg(&name_, lvl);
-        fmt::format_to(log_msg.raw, fmt, args...);
+        using details::fmt_helper::to_string_view;
+        fmt::memory_buffer buf;
+        fmt::format_to(buf, fmt, args...);
+        details::log_msg log_msg(source, &name_, lvl, to_string_view(buf));
         sink_it_(log_msg);
     }
     SPDLOG_CATCH_AND_HANDLE
 }
 
 template<typename... Args>
+inline void spdlog::logger::log(level::level_enum lvl, const char *fmt, const Args &... args)
+{
+    log(source_loc{}, lvl, fmt, args...);
+}
+
+inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const char *msg)
+{
+    if (!should_log(lvl))
+    {
+        return;
+    }
+
+    try
+    {
+        details::log_msg log_msg(source, &name_, lvl, spdlog::string_view_t(msg));
+        sink_it_(log_msg);
+    }
+    SPDLOG_CATCH_AND_HANDLE
+}
+
 inline void spdlog::logger::log(level::level_enum lvl, const char *msg)
+{
+    log(source_loc{}, lvl, msg);
+}
+
+template<class T, typename std::enable_if<std::is_convertible<T, spdlog::string_view_t>::value, T>::type *>
+inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const T &msg)
 {
     if (!should_log(lvl))
     {
@@ -79,15 +111,20 @@ inline void spdlog::logger::log(level::level_enum lvl, const char *msg)
     }
     try
     {
-        details::log_msg log_msg(&name_, lvl);
-        details::fmt_helper::append_c_str(msg, log_msg.raw);
+        details::log_msg log_msg(source, &name_, lvl, msg);
         sink_it_(log_msg);
     }
     SPDLOG_CATCH_AND_HANDLE
 }
 
-template<typename T>
+template<class T, typename std::enable_if<std::is_convertible<T, spdlog::string_view_t>::value, T>::type *>
 inline void spdlog::logger::log(level::level_enum lvl, const T &msg)
+{
+    log(source_loc{}, lvl, msg);
+}
+
+template<class T, typename std::enable_if<!std::is_convertible<T, spdlog::string_view_t>::value, T>::type *>
+inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const T &msg)
 {
     if (!should_log(lvl))
     {
@@ -95,13 +132,21 @@ inline void spdlog::logger::log(level::level_enum lvl, const T &msg)
     }
     try
     {
-        details::log_msg log_msg(&name_, lvl);
-        fmt::format_to(log_msg.raw, "{}", msg);
+        using details::fmt_helper::to_string_view;
+        fmt::memory_buffer buf;
+        fmt::format_to(buf, "{}", msg);
+        details::log_msg log_msg(source, &name_, lvl, to_string_view(buf));
         sink_it_(log_msg);
     }
     SPDLOG_CATCH_AND_HANDLE
 }
 
+template<class T, typename std::enable_if<!std::is_convertible<T, spdlog::string_view_t>::value, T>::type *>
+inline void spdlog::logger::log(level::level_enum lvl, const T &msg)
+{
+    log(source_loc{}, lvl, msg);
+}
+
 template<typename... Args>
 inline void spdlog::logger::trace(const char *fmt, const Args &... args)
 {
@@ -198,7 +243,7 @@ inline void wbuf_to_utf8buf(const fmt::wmemory_buffer &wbuf, fmt::memory_buffer
 }
 
 template<typename... Args>
-inline void spdlog::logger::log(level::level_enum lvl, const wchar_t *fmt, const Args &... args)
+inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const wchar_t *fmt, const Args &... args)
 {
     if (!should_log(lvl))
     {
@@ -208,15 +253,23 @@ inline void spdlog::logger::log(level::level_enum lvl, const wchar_t *fmt, const
     try
     {
         // format to wmemory_buffer and convert to utf8
-        details::log_msg log_msg(&name_, lvl);
+        using details::fmt_helper::to_string_view;
         fmt::wmemory_buffer wbuf;
         fmt::format_to(wbuf, fmt, args...);
-        wbuf_to_utf8buf(wbuf, log_msg.raw);
+        fmt::memory_buffer buf;
+        wbuf_to_utf8buf(wbuf, buf);
+        details::log_msg log_msg(source, &name_, lvl, to_string_view(buf));
         sink_it_(log_msg);
     }
     SPDLOG_CATCH_AND_HANDLE
 }
 
+template<typename... Args>
+inline void spdlog::logger::log(level::level_enum lvl, const wchar_t *fmt, const Args &... args)
+{
+    log(source_loc{}, lvl, fmt, args...);
+}
+
 template<typename... Args>
 inline void spdlog::logger::trace(const wchar_t *fmt, const Args &... args)
 {
@@ -273,7 +326,7 @@ inline void spdlog::logger::set_error_handler(spdlog::log_err_handler err_handle
     err_handler_ = std::move(err_handler);
 }
 
-inline spdlog::log_err_handler spdlog::logger::error_handler()
+inline spdlog::log_err_handler spdlog::logger::error_handler() const
 {
     return err_handler_;
 }
@@ -303,6 +356,11 @@ inline bool spdlog::logger::should_flush_(const details::log_msg &msg)
     return (msg.level >= flush_level) && (msg.level != level::off);
 }
 
+inline spdlog::level::level_enum spdlog::logger::default_level()
+{
+    return static_cast<spdlog::level::level_enum>(SPDLOG_ACTIVE_LEVEL);
+}
+
 inline spdlog::level::level_enum spdlog::logger::level() const
 {
     return static_cast<spdlog::level::level_enum>(level_.load(std::memory_order_relaxed));
@@ -332,7 +390,7 @@ inline void spdlog::logger::sink_it_(details::log_msg &msg)
 
     if (should_flush_(msg))
     {
-        flush();
+        flush_();
     }
 }
 
diff --git a/external/spdlog/details/os.h b/external/spdlog/details/os.h
index aa802abbe..646805e60 100644
--- a/external/spdlog/details/os.h
+++ b/external/spdlog/details/os.h
@@ -185,7 +185,7 @@ inline int rename(const filename_t &filename1, const filename_t &filename2) SPDL
 }
 
 // Return if file exists
-inline bool file_exists(const filename_t &filename)  SPDLOG_NOEXCEPT
+inline bool file_exists(const filename_t &filename) SPDLOG_NOEXCEPT
 {
 #ifdef _WIN32
 #ifdef SPDLOG_WCHAR_FILENAMES
@@ -210,10 +210,10 @@ inline size_t filesize(FILE *f)
 #if defined(_WIN32) && !defined(__CYGWIN__)
     int fd = _fileno(f);
 #if _WIN64 // 64 bits
-    struct _stat64 st;
-    if (_fstat64(fd, &st) == 0)
+    __int64 ret = _filelengthi64(fd);
+    if (ret >= 0)
     {
-        return st.st_size;
+        return static_cast<size_t>(ret);
     }
 
 #else // windows 32 bits
@@ -338,8 +338,7 @@ inline size_t _thread_id() SPDLOG_NOEXCEPT
 // Return current thread id as size_t (from thread local storage)
 inline size_t thread_id() SPDLOG_NOEXCEPT
 {
-#if defined(SPDLOG_DISABLE_TID_CACHING) || (defined(_MSC_VER) && (_MSC_VER < 1900)) || defined(__cplusplus_winrt) ||                       \
-    (defined(__clang__) && !__has_feature(cxx_thread_local))
+#if defined(SPDLOG_NO_TLS)
     return _thread_id();
 #else // cache thread id in tls
     static thread_local const size_t tid = _thread_id();
diff --git a/external/spdlog/details/pattern_formatter.h b/external/spdlog/details/pattern_formatter.h
index 14fb124cb..c0ad86e8e 100644
--- a/external/spdlog/details/pattern_formatter.h
+++ b/external/spdlog/details/pattern_formatter.h
@@ -14,6 +14,7 @@
 #include <array>
 #include <chrono>
 #include <ctime>
+#include <cctype>
 #include <memory>
 #include <mutex>
 #include <string>
@@ -24,39 +25,167 @@
 namespace spdlog {
 namespace details {
 
+// padding information.
+struct padding_info
+{
+    enum pad_side
+    {
+        left,
+        right,
+        center
+    };
+
+    padding_info() = default;
+    padding_info(size_t width, padding_info::pad_side side)
+        : width_(width)
+        , side_(side)
+    {
+    }
+
+    bool enabled() const
+    {
+        return width_ != 0;
+    }
+    const size_t width_ = 0;
+    const pad_side side_ = left;
+};
+
+class scoped_pad
+{
+public:
+    scoped_pad(size_t wrapped_size, padding_info &padinfo, fmt::memory_buffer &dest)
+        : padinfo_(padinfo)
+        , dest_(dest)
+    {
+
+        if (padinfo_.width_ <= wrapped_size)
+        {
+            total_pad_ = 0;
+            return;
+        }
+
+        total_pad_ = padinfo.width_ - wrapped_size;
+        if (padinfo_.side_ == padding_info::left)
+        {
+            pad_it(total_pad_);
+            total_pad_ = 0;
+        }
+        else if (padinfo_.side_ == padding_info::center)
+        {
+            auto half_pad = total_pad_ / 2;
+            auto reminder = total_pad_ & 1;
+            pad_it(half_pad);
+            total_pad_ = half_pad + reminder; // for the right side
+        }
+    }
+
+    scoped_pad(spdlog::string_view_t txt, padding_info &padinfo, fmt::memory_buffer &dest)
+        : scoped_pad(txt.size(), padinfo, dest)
+    {
+    }
+
+    ~scoped_pad()
+    {
+        if (total_pad_)
+        {
+            pad_it(total_pad_);
+        }
+    }
+
+private:
+    void pad_it(size_t count)
+    {
+        // count = std::min(count, spaces_.size());
+        assert(count <= spaces_.size());
+        fmt_helper::append_string_view(string_view_t(spaces_.data(), count), dest_);
+    }
+
+    const padding_info &padinfo_;
+    fmt::memory_buffer &dest_;
+    size_t total_pad_;
+    string_view_t spaces_{"                                                                "
+                          "                                                                ",
+        128};
+};
+
 class flag_formatter
 {
 public:
+    explicit flag_formatter(padding_info padinfo)
+        : padinfo_(padinfo)
+    {
+    }
+    flag_formatter() = default;
     virtual ~flag_formatter() = default;
     virtual void format(const details::log_msg &msg, const std::tm &tm_time, fmt::memory_buffer &dest) = 0;
+
+protected:
+    padding_info padinfo_;
 };
 
 ///////////////////////////////////////////////////////////////////////
-// name & level pattern appenders
+// name & level pattern appender
 ///////////////////////////////////////////////////////////////////////
 class name_formatter : public flag_formatter
 {
+public:
+    explicit name_formatter(padding_info padinfo)
+        : flag_formatter(padinfo)
+    {
+    }
+
     void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
     {
-        fmt_helper::append_str(*msg.logger_name, dest);
+        if (padinfo_.enabled())
+        {
+            scoped_pad p(*msg.logger_name, padinfo_, dest);
+            fmt_helper::append_string_view(*msg.logger_name, dest);
+        }
+        else
+        {
+            fmt_helper::append_string_view(*msg.logger_name, dest);
+        }
     }
 };
 
 // log level appender
 class level_formatter : public flag_formatter
 {
+public:
+    explicit level_formatter(padding_info padinfo)
+        : flag_formatter(padinfo)
+    {
+    }
+
     void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
     {
-        fmt_helper::append_c_str(level::to_c_str(msg.level), dest);
+        string_view_t &level_name = level::to_string_view(msg.level);
+        if (padinfo_.enabled())
+        {
+            scoped_pad p(level_name, padinfo_, dest);
+            fmt_helper::append_string_view(level_name, dest);
+        }
+        else
+        {
+            fmt_helper::append_string_view(level_name, dest);
+        }
     }
 };
 
 // short log level appender
 class short_level_formatter : public flag_formatter
 {
+public:
+    explicit short_level_formatter(padding_info padinfo)
+        : flag_formatter(padinfo)
+    {
+    }
+
     void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
     {
-        fmt_helper::append_c_str(level::to_short_c_str(msg.level), dest);
+        string_view_t level_name{level::to_short_c_str(msg.level)};
+        scoped_pad p(level_name, padinfo_, dest);
+        fmt_helper::append_string_view(level_name, dest);
     }
 };
 
@@ -78,9 +207,17 @@ static int to12h(const tm &t)
 static const char *days[]{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
 class a_formatter : public flag_formatter
 {
+public:
+    explicit a_formatter(padding_info padinfo)
+        : flag_formatter(padinfo)
+    {
+    }
+
     void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
     {
-        fmt_helper::append_c_str(days[tm_time.tm_wday], dest);
+        string_view_t field_value{days[tm_time.tm_wday]};
+        scoped_pad p(field_value, padinfo_, dest);
+        fmt_helper::append_string_view(field_value, dest);
     }
 };
 
@@ -88,9 +225,17 @@ class a_formatter : public flag_formatter
 static const char *full_days[]{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
 class A_formatter : public flag_formatter
 {
+public:
+    explicit A_formatter(padding_info padinfo)
+        : flag_formatter(padinfo)
+    {
+    }
+
     void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
     {
-        fmt_helper::append_c_str(full_days[tm_time.tm_wday], dest);
+        string_view_t field_value{full_days[tm_time.tm_wday]};
+        scoped_pad p(field_value, padinfo_, dest);
+        fmt_helper::append_string_view(field_value, dest);
     }
 };
 
@@ -98,9 +243,17 @@ class A_formatter : public flag_formatter
 static const char *months[]{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"};
 class b_formatter : public flag_formatter
 {
+public:
+    explicit b_formatter(padding_info padinfo)
+        : flag_formatter(padinfo)
+    {
+    }
+
     void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
     {
-        fmt_helper::append_c_str(months[tm_time.tm_mon], dest);
+        string_view_t field_value{months[tm_time.tm_mon]};
+        scoped_pad p(field_value, padinfo_, dest);
+        fmt_helper::append_string_view(field_value, dest);
     }
 };
 
@@ -109,23 +262,37 @@ static const char *full_months[]{
     "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"};
 class B_formatter : public flag_formatter
 {
+public:
+    explicit B_formatter(padding_info padinfo)
+        : flag_formatter(padinfo)
+    {
+    }
+
     void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
     {
-        fmt_helper::append_c_str(full_months[tm_time.tm_mon], dest);
+        string_view_t field_value{full_months[tm_time.tm_mon]};
+        scoped_pad p(field_value, padinfo_, dest);
+        fmt_helper::append_string_view(field_value, dest);
     }
 };
 
 // Date and time representation (Thu Aug 23 15:35:46 2014)
 class c_formatter final : public flag_formatter
 {
+public:
+    explicit c_formatter(padding_info padinfo)
+        : flag_formatter(padinfo)
+    {
+    }
+
     void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
     {
-        // fmt::format_to(dest, "{} {} {} ", days[tm_time.tm_wday],
-        // months[tm_time.tm_mon], tm_time.tm_mday);
-        // date
-        fmt_helper::append_str(days[tm_time.tm_wday], dest);
+        const size_t field_size = 24;
+        scoped_pad p(field_size, padinfo_, dest);
+
+        fmt_helper::append_string_view(days[tm_time.tm_wday], dest);
         dest.push_back(' ');
-        fmt_helper::append_str(months[tm_time.tm_mon], dest);
+        fmt_helper::append_string_view(months[tm_time.tm_mon], dest);
         dest.push_back(' ');
         fmt_helper::append_int(tm_time.tm_mday, dest);
         dest.push_back(' ');
@@ -144,8 +311,16 @@ class c_formatter final : public flag_formatter
 // year - 2 digit
 class C_formatter final : public flag_formatter
 {
+public:
+    explicit C_formatter(padding_info padinfo)
+        : flag_formatter(padinfo)
+    {
+    }
+
     void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
     {
+        const size_t field_size = 2;
+        scoped_pad p(field_size, padinfo_, dest);
         fmt_helper::pad2(tm_time.tm_year % 100, dest);
     }
 };
@@ -153,8 +328,17 @@ class C_formatter final : public flag_formatter
 // Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01
 class D_formatter final : public flag_formatter
 {
+public:
+    explicit D_formatter(padding_info padinfo)
+        : flag_formatter(padinfo)
+    {
+    }
+
     void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
     {
+        const size_t field_size = 10;
+        scoped_pad p(field_size, padinfo_, dest);
+
         fmt_helper::pad2(tm_time.tm_mon + 1, dest);
         dest.push_back('/');
         fmt_helper::pad2(tm_time.tm_mday, dest);
@@ -166,8 +350,14 @@ class D_formatter final : public flag_formatter
 // year - 4 digit
 class Y_formatter final : public flag_formatter
 {
+public:
+    explicit Y_formatter(padding_info padinfo)
+        : flag_formatter(padinfo){};
+
     void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
     {
+        const size_t field_size = 4;
+        scoped_pad p(field_size, padinfo_, dest);
         fmt_helper::append_int(tm_time.tm_year + 1900, dest);
     }
 };
@@ -175,8 +365,16 @@ class Y_formatter final : public flag_formatter
 // month 1-12
 class m_formatter final : public flag_formatter
 {
+public:
+    explicit m_formatter(padding_info padinfo)
+        : flag_formatter(padinfo)
+    {
+    }
+
     void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
     {
+        const size_t field_size = 2;
+        scoped_pad p(field_size, padinfo_, dest);
         fmt_helper::pad2(tm_time.tm_mon + 1, dest);
     }
 };
@@ -184,8 +382,16 @@ class m_formatter final : public flag_formatter
 // day of month 1-31
 class d_formatter final : public flag_formatter
 {
+public:
+    explicit d_formatter(padding_info padinfo)
+        : flag_formatter(padinfo)
+    {
+    }
+
     void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
     {
+        const size_t field_size = 2;
+        scoped_pad p(field_size, padinfo_, dest);
         fmt_helper::pad2(tm_time.tm_mday, dest);
     }
 };
@@ -193,8 +399,16 @@ class d_formatter final : public flag_formatter
 // hours in 24 format 0-23
 class H_formatter final : public flag_formatter
 {
+public:
+    explicit H_formatter(padding_info padinfo)
+        : flag_formatter(padinfo)
+    {
+    }
+
     void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
     {
+        const size_t field_size = 2;
+        scoped_pad p(field_size, padinfo_, dest);
         fmt_helper::pad2(tm_time.tm_hour, dest);
     }
 };
@@ -202,8 +416,14 @@ class H_formatter final : public flag_formatter
 // hours in 12 format 1-12
 class I_formatter final : public flag_formatter
 {
+public:
+    explicit I_formatter(padding_info padinfo)
+        : flag_formatter(padinfo){};
+
     void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
     {
+        const size_t field_size = 2;
+        scoped_pad p(field_size, padinfo_, dest);
         fmt_helper::pad2(to12h(tm_time), dest);
     }
 };
@@ -211,8 +431,14 @@ class I_formatter final : public flag_formatter
 // minutes 0-59
 class M_formatter final : public flag_formatter
 {
+public:
+    explicit M_formatter(padding_info padinfo)
+        : flag_formatter(padinfo){};
+
     void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
     {
+        const size_t field_size = 2;
+        scoped_pad p(field_size, padinfo_, dest);
         fmt_helper::pad2(tm_time.tm_min, dest);
     }
 };
@@ -220,8 +446,14 @@ class M_formatter final : public flag_formatter
 // seconds 0-59
 class S_formatter final : public flag_formatter
 {
+public:
+    explicit S_formatter(padding_info padinfo)
+        : flag_formatter(padinfo){};
+
     void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
     {
+        const size_t field_size = 2;
+        scoped_pad p(field_size, padinfo_, dest);
         fmt_helper::pad2(tm_time.tm_sec, dest);
     }
 };
@@ -229,38 +461,83 @@ class S_formatter final : public flag_formatter
 // milliseconds
 class e_formatter final : public flag_formatter
 {
+public:
+    explicit e_formatter(padding_info padinfo)
+        : flag_formatter(padinfo){};
+
     void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
     {
         auto millis = fmt_helper::time_fraction<std::chrono::milliseconds>(msg.time);
-        fmt_helper::pad3(static_cast<int>(millis.count()), dest);
+        if (padinfo_.enabled())
+        {
+            const size_t field_size = 3;
+            scoped_pad p(field_size, padinfo_, dest);
+            fmt_helper::pad3(static_cast<uint32_t>(millis.count()), dest);
+        }
+        else
+        {
+            fmt_helper::pad3(static_cast<uint32_t>(millis.count()), dest);
+        }
     }
 };
 
 // microseconds
 class f_formatter final : public flag_formatter
 {
+public:
+    explicit f_formatter(padding_info padinfo)
+        : flag_formatter(padinfo){};
+
     void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
     {
         auto micros = fmt_helper::time_fraction<std::chrono::microseconds>(msg.time);
-        fmt_helper::pad6(static_cast<size_t>(micros.count()), dest);
+        if (padinfo_.enabled())
+        {
+            const size_t field_size = 6;
+            scoped_pad p(field_size, padinfo_, dest);
+            fmt_helper::pad6(static_cast<size_t>(micros.count()), dest);
+        }
+        else
+        {
+            fmt_helper::pad6(static_cast<size_t>(micros.count()), dest);
+        }
     }
 };
 
 // nanoseconds
 class F_formatter final : public flag_formatter
 {
+public:
+    explicit F_formatter(padding_info padinfo)
+        : flag_formatter(padinfo){};
+
     void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
     {
         auto ns = fmt_helper::time_fraction<std::chrono::nanoseconds>(msg.time);
-        fmt::format_to(dest, "{:09}", ns.count());
+        if (padinfo_.enabled())
+        {
+            const size_t field_size = 9;
+            scoped_pad p(field_size, padinfo_, dest);
+            fmt_helper::pad9(static_cast<size_t>(ns.count()), dest);
+        }
+        else
+        {
+            fmt_helper::pad9(static_cast<size_t>(ns.count()), dest);
+        }
     }
 };
 
 // seconds since epoch
 class E_formatter final : public flag_formatter
 {
+public:
+    explicit E_formatter(padding_info padinfo)
+        : flag_formatter(padinfo){};
+
     void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
     {
+        const size_t field_size = 10;
+        scoped_pad p(field_size, padinfo_, dest);
         auto duration = msg.time.time_since_epoch();
         auto seconds = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
         fmt_helper::append_int(seconds, dest);
@@ -270,32 +547,52 @@ class E_formatter final : public flag_formatter
 // AM/PM
 class p_formatter final : public flag_formatter
 {
+public:
+    explicit p_formatter(padding_info padinfo)
+        : flag_formatter(padinfo){};
+
     void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
     {
-        fmt_helper::append_c_str(ampm(tm_time), dest);
+        const size_t field_size = 2;
+        scoped_pad p(field_size, padinfo_, dest);
+        fmt_helper::append_string_view(ampm(tm_time), dest);
     }
 };
 
 // 12 hour clock 02:55:02 pm
 class r_formatter final : public flag_formatter
 {
+public:
+    explicit r_formatter(padding_info padinfo)
+        : flag_formatter(padinfo){};
+
     void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
     {
+        const size_t field_size = 11;
+        scoped_pad p(field_size, padinfo_, dest);
+
         fmt_helper::pad2(to12h(tm_time), dest);
         dest.push_back(':');
         fmt_helper::pad2(tm_time.tm_min, dest);
         dest.push_back(':');
         fmt_helper::pad2(tm_time.tm_sec, dest);
         dest.push_back(' ');
-        fmt_helper::append_c_str(ampm(tm_time), dest);
+        fmt_helper::append_string_view(ampm(tm_time), dest);
     }
 };
 
 // 24-hour HH:MM time, equivalent to %H:%M
 class R_formatter final : public flag_formatter
 {
+public:
+    explicit R_formatter(padding_info padinfo)
+        : flag_formatter(padinfo){};
+
     void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
     {
+        const size_t field_size = 5;
+        scoped_pad p(field_size, padinfo_, dest);
+
         fmt_helper::pad2(tm_time.tm_hour, dest);
         dest.push_back(':');
         fmt_helper::pad2(tm_time.tm_min, dest);
@@ -305,10 +602,15 @@ class R_formatter final : public flag_formatter
 // ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S
 class T_formatter final : public flag_formatter
 {
+public:
+    explicit T_formatter(padding_info padinfo)
+        : flag_formatter(padinfo){};
+
     void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
     {
-        // fmt::format_to(dest, "{:02}:{:02}:{:02}", tm_time.tm_hour,
-        // tm_time.tm_min, tm_time.tm_sec);
+        const size_t field_size = 8;
+        scoped_pad p(field_size, padinfo_, dest);
+
         fmt_helper::pad2(tm_time.tm_hour, dest);
         dest.push_back(':');
         fmt_helper::pad2(tm_time.tm_min, dest);
@@ -321,6 +623,9 @@ class T_formatter final : public flag_formatter
 class z_formatter final : public flag_formatter
 {
 public:
+    explicit z_formatter(padding_info padinfo)
+        : flag_formatter(padinfo){};
+
     const std::chrono::seconds cache_refresh = std::chrono::seconds(5);
 
     z_formatter() = default;
@@ -329,6 +634,9 @@ public:
 
     void format(const details::log_msg &msg, const std::tm &tm_time, fmt::memory_buffer &dest) override
     {
+        const size_t field_size = 6;
+        scoped_pad p(field_size, padinfo_, dest);
+
 #ifdef _WIN32
         int total_minutes = get_cached_offset(msg, tm_time);
 #else
@@ -373,35 +681,80 @@ private:
 // Thread id
 class t_formatter final : public flag_formatter
 {
+public:
+    explicit t_formatter(padding_info padinfo)
+        : flag_formatter(padinfo){};
+
     void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
     {
-        fmt_helper::pad6(msg.thread_id, dest);
+        if (padinfo_.enabled())
+        {
+            const auto field_size = fmt_helper::count_digits(msg.thread_id);
+            scoped_pad p(field_size, padinfo_, dest);
+            fmt_helper::append_int(msg.thread_id, dest);
+        }
+        else
+        {
+            fmt_helper::append_int(msg.thread_id, dest);
+        }
     }
 };
 
 // Current pid
 class pid_formatter final : public flag_formatter
 {
+public:
+    explicit pid_formatter(padding_info padinfo)
+        : flag_formatter(padinfo){};
+
     void format(const details::log_msg &, const std::tm &, fmt::memory_buffer &dest) override
     {
-        fmt_helper::append_int(details::os::pid(), dest);
+        const auto pid = static_cast<uint32_t>(details::os::pid());
+        if (padinfo_.enabled())
+        {
+            auto field_size = fmt_helper::count_digits(pid);
+            scoped_pad p(field_size, padinfo_, dest);
+            fmt_helper::append_int(pid, dest);
+        }
+        else
+        {
+            fmt_helper::append_int(pid, dest);
+        }
     }
 };
 
 // message counter formatter
 class i_formatter final : public flag_formatter
 {
+public:
+    explicit i_formatter(padding_info padinfo)
+        : flag_formatter(padinfo){};
+
     void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
     {
+        const size_t field_size = 6;
+        scoped_pad p(field_size, padinfo_, dest);
         fmt_helper::pad6(msg.msg_id, dest);
     }
 };
 
 class v_formatter final : public flag_formatter
 {
+public:
+    explicit v_formatter(padding_info padinfo)
+        : flag_formatter(padinfo){};
+
     void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
     {
-        fmt_helper::append_buf(msg.raw, dest);
+        if (padinfo_.enabled())
+        {
+            scoped_pad p(msg.payload, padinfo_, dest);
+            fmt_helper::append_string_view(msg.payload, dest);
+        }
+        else
+        {
+            fmt_helper::append_string_view(msg.payload, dest);
+        }
     }
 };
 
@@ -412,8 +765,11 @@ public:
         : ch_(ch)
     {
     }
+
     void format(const details::log_msg &, const std::tm &, fmt::memory_buffer &dest) override
     {
+        const size_t field_size = 1;
+        scoped_pad p(field_size, padinfo_, dest);
         dest.push_back(ch_);
     }
 
@@ -433,7 +789,7 @@ public:
     }
     void format(const details::log_msg &, const std::tm &, fmt::memory_buffer &dest) override
     {
-        fmt_helper::append_str(str_, dest);
+        fmt_helper::append_string_view(str_, dest);
     }
 
 private:
@@ -443,6 +799,12 @@ private:
 // mark the color range. expect it to be in the form of "%^colored text%$"
 class color_start_formatter final : public flag_formatter
 {
+public:
+    explicit color_start_formatter(padding_info padinfo)
+        : flag_formatter(padinfo)
+    {
+    }
+
     void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
     {
         msg.color_range_start = dest.size();
@@ -450,16 +812,117 @@ class color_start_formatter final : public flag_formatter
 };
 class color_stop_formatter final : public flag_formatter
 {
+public:
+    explicit color_stop_formatter(padding_info padinfo)
+        : flag_formatter(padinfo)
+    {
+    }
+
     void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
     {
         msg.color_range_end = dest.size();
     }
 };
 
+// print source location
+class source_location_formatter final : public flag_formatter
+{
+public:
+    explicit source_location_formatter(padding_info padinfo)
+        : flag_formatter(padinfo){};
+
+    void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
+    {
+        if (msg.source.empty())
+        {
+            return;
+        }
+        if (padinfo_.enabled())
+        {
+            const auto text_size = std::char_traits<char>::length(msg.source.filename) + fmt_helper::count_digits(msg.source.line) + 1;
+            scoped_pad p(text_size, padinfo_, dest);
+            fmt_helper::append_string_view(msg.source.filename, dest);
+            dest.push_back(':');
+            fmt_helper::append_int(msg.source.line, dest);
+        }
+        else
+        {
+            fmt_helper::append_string_view(msg.source.filename, dest);
+            dest.push_back(':');
+            fmt_helper::append_int(msg.source.line, dest);
+        }
+    }
+};
+// print source filename
+class source_filename_formatter final : public flag_formatter
+{
+public:
+    explicit source_filename_formatter(padding_info padinfo)
+        : flag_formatter(padinfo){};
+
+    void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
+    {
+        if (msg.source.empty())
+        {
+            return;
+        }
+        scoped_pad p(msg.source.filename, padinfo_, dest);
+        fmt_helper::append_string_view(msg.source.filename, dest);
+    }
+};
+
+class source_linenum_formatter final : public flag_formatter
+{
+public:
+    explicit source_linenum_formatter(padding_info padinfo)
+        : flag_formatter(padinfo){};
+
+    void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
+    {
+        if (msg.source.empty())
+        {
+            return;
+        }
+        if (padinfo_.enabled())
+        {
+            auto field_size = fmt_helper::count_digits(msg.source.line);
+            scoped_pad p(field_size, padinfo_, dest);
+            fmt_helper::append_int(msg.source.line, dest);
+        }
+        else
+        {
+            fmt_helper::append_int(msg.source.line, dest);
+        }
+    }
+};
+// print source funcname
+class source_funcname_formatter final : public flag_formatter
+{
+public:
+    explicit source_funcname_formatter(padding_info padinfo)
+        : flag_formatter(padinfo){};
+
+    void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
+    {
+        if (msg.source.empty())
+        {
+            return;
+        }
+        scoped_pad p(msg.source.funcname, padinfo_, dest);
+        fmt_helper::append_string_view(msg.source.funcname, dest);
+    }
+};
+
 // Full info formatter
 // pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %v
 class full_formatter final : public flag_formatter
 {
+public:
+    explicit full_formatter(padding_info padinfo)
+        : flag_formatter(padinfo)
+    {
+    }
+
     void format(const details::log_msg &msg, const std::tm &tm_time, fmt::memory_buffer &dest) override
     {
         using std::chrono::duration_cast;
@@ -499,7 +962,7 @@ class full_formatter final : public flag_formatter
         fmt_helper::append_buf(cached_datetime_, dest);
 
         auto millis = fmt_helper::time_fraction<milliseconds>(msg.time);
-        fmt_helper::pad3(static_cast<int>(millis.count()), dest);
+        fmt_helper::pad3(static_cast<uint32_t>(millis.count()), dest);
         dest.push_back(']');
         dest.push_back(' ');
 
@@ -508,20 +971,37 @@ class full_formatter final : public flag_formatter
 #endif
 
 #ifndef SPDLOG_NO_NAME
-        dest.push_back('[');
-        fmt_helper::append_str(*msg.logger_name, dest);
-        dest.push_back(']');
-        dest.push_back(' ');
+        if (!msg.logger_name->empty())
+        {
+            dest.push_back('[');
+            // fmt_helper::append_str(*msg.logger_name, dest);
+            fmt_helper::append_string_view(*msg.logger_name, dest);
+            dest.push_back(']');
+            dest.push_back(' ');
+        }
 #endif
 
         dest.push_back('[');
         // wrap the level name with color
         msg.color_range_start = dest.size();
-        fmt_helper::append_c_str(level::to_c_str(msg.level), dest);
+        // fmt_helper::append_string_view(level::to_c_str(msg.level), dest);
+        fmt_helper::append_string_view(level::to_string_view(msg.level), dest);
         msg.color_range_end = dest.size();
         dest.push_back(']');
         dest.push_back(' ');
-        fmt_helper::append_buf(msg.raw, dest);
+
+        // add source location if present
+        if (!msg.source.empty())
+        {
+            dest.push_back('[');
+            fmt_helper::append_string_view(msg.source.filename, dest);
+            dest.push_back(':');
+            fmt_helper::append_int(msg.source.line, dest);
+            dest.push_back(']');
+            dest.push_back(' ');
+        }
+        // fmt_helper::append_string_view(msg.msg(), dest);
+        fmt_helper::append_string_view(msg.payload, dest);
     }
 
 private:
@@ -545,6 +1025,17 @@ public:
         compile_pattern_(pattern_);
     }
 
+    // use by default full formatter for if pattern is not given
+    explicit pattern_formatter(pattern_time_type time_type = pattern_time_type::local, std::string eol = spdlog::details::os::default_eol)
+        : pattern_("%+")
+        , eol_(std::move(eol))
+        , pattern_time_type_(time_type)
+        , last_log_secs_(0)
+    {
+        std::memset(&cached_tm_, 0, sizeof(cached_tm_));
+        formatters_.push_back(details::make_unique<details::full_formatter>(details::padding_info{}));
+    }
+
     pattern_formatter(const pattern_formatter &other) = delete;
     pattern_formatter &operator=(const pattern_formatter &other) = delete;
 
@@ -568,7 +1059,7 @@ public:
             f->format(msg, cached_tm_, dest);
         }
         // write eol
-        details::fmt_helper::append_str(eol_, dest);
+        details::fmt_helper::append_string_view(eol_, dest);
     }
 
 private:
@@ -589,151 +1080,219 @@ private:
         return details::os::gmtime(log_clock::to_time_t(msg.time));
     }
 
-    void handle_flag_(char flag)
+    void handle_flag_(char flag, details::padding_info padding)
     {
         switch (flag)
         {
-        // logger name
-        case 'n':
-            formatters_.push_back(details::make_unique<details::name_formatter>());
+
+        case ('+'): // default formatter
+            formatters_.push_back(details::make_unique<details::full_formatter>(padding));
+            break;
+
+        case 'n': // logger name
+            formatters_.push_back(details::make_unique<details::name_formatter>(padding));
             break;
 
-        case 'l':
-            formatters_.push_back(details::make_unique<details::level_formatter>());
+        case 'l': // level
+            formatters_.push_back(details::make_unique<details::level_formatter>(padding));
             break;
 
-        case 'L':
-            formatters_.push_back(details::make_unique<details::short_level_formatter>());
+        case 'L': // short level
+            formatters_.push_back(details::make_unique<details::short_level_formatter>(padding));
             break;
 
-        case ('t'):
-            formatters_.push_back(details::make_unique<details::t_formatter>());
+        case ('t'): // thread id
+            formatters_.push_back(details::make_unique<details::t_formatter>(padding));
             break;
 
-        case ('v'):
-            formatters_.push_back(details::make_unique<details::v_formatter>());
+        case ('v'): // the message text
+            formatters_.push_back(details::make_unique<details::v_formatter>(padding));
             break;
 
-        case ('a'):
-            formatters_.push_back(details::make_unique<details::a_formatter>());
+        case ('a'): // weekday
+            formatters_.push_back(details::make_unique<details::a_formatter>(padding));
             break;
 
-        case ('A'):
-            formatters_.push_back(details::make_unique<details::A_formatter>());
+        case ('A'): // short weekday
+            formatters_.push_back(details::make_unique<details::A_formatter>(padding));
             break;
 
         case ('b'):
-        case ('h'):
-            formatters_.push_back(details::make_unique<details::b_formatter>());
+        case ('h'): // month
+            formatters_.push_back(details::make_unique<details::b_formatter>(padding));
             break;
 
-        case ('B'):
-            formatters_.push_back(details::make_unique<details::B_formatter>());
+        case ('B'): // short month
+            formatters_.push_back(details::make_unique<details::B_formatter>(padding));
             break;
-        case ('c'):
-            formatters_.push_back(details::make_unique<details::c_formatter>());
+
+        case ('c'): // datetime
+            formatters_.push_back(details::make_unique<details::c_formatter>(padding));
             break;
 
-        case ('C'):
-            formatters_.push_back(details::make_unique<details::C_formatter>());
+        case ('C'): // year 2 digits
+            formatters_.push_back(details::make_unique<details::C_formatter>(padding));
             break;
 
-        case ('Y'):
-            formatters_.push_back(details::make_unique<details::Y_formatter>());
+        case ('Y'): // year 4 digits
+            formatters_.push_back(details::make_unique<details::Y_formatter>(padding));
             break;
 
         case ('D'):
-        case ('x'):
-            formatters_.push_back(details::make_unique<details::D_formatter>());
+        case ('x'): // datetime MM/DD/YY
+            formatters_.push_back(details::make_unique<details::D_formatter>(padding));
             break;
 
-        case ('m'):
-            formatters_.push_back(details::make_unique<details::m_formatter>());
+        case ('m'): // month 1-12
+            formatters_.push_back(details::make_unique<details::m_formatter>(padding));
             break;
 
-        case ('d'):
-            formatters_.push_back(details::make_unique<details::d_formatter>());
+        case ('d'): // day of month 1-31
+            formatters_.push_back(details::make_unique<details::d_formatter>(padding));
             break;
 
-        case ('H'):
-            formatters_.push_back(details::make_unique<details::H_formatter>());
+        case ('H'): // hours 24
+            formatters_.push_back(details::make_unique<details::H_formatter>(padding));
             break;
 
-        case ('I'):
-            formatters_.push_back(details::make_unique<details::I_formatter>());
+        case ('I'): // hours 12
+            formatters_.push_back(details::make_unique<details::I_formatter>(padding));
             break;
 
-        case ('M'):
-            formatters_.push_back(details::make_unique<details::M_formatter>());
+        case ('M'): // minutes
+            formatters_.push_back(details::make_unique<details::M_formatter>(padding));
             break;
 
-        case ('S'):
-            formatters_.push_back(details::make_unique<details::S_formatter>());
+        case ('S'): // seconds
+            formatters_.push_back(details::make_unique<details::S_formatter>(padding));
             break;
 
-        case ('e'):
-            formatters_.push_back(details::make_unique<details::e_formatter>());
+        case ('e'): // milliseconds
+            formatters_.push_back(details::make_unique<details::e_formatter>(padding));
             break;
 
-        case ('f'):
-            formatters_.push_back(details::make_unique<details::f_formatter>());
+        case ('f'): // microseconds
+            formatters_.push_back(details::make_unique<details::f_formatter>(padding));
             break;
-        case ('F'):
-            formatters_.push_back(details::make_unique<details::F_formatter>());
+
+        case ('F'): // nanoseconds
+            formatters_.push_back(details::make_unique<details::F_formatter>(padding));
             break;
 
-        case ('E'):
-            formatters_.push_back(details::make_unique<details::E_formatter>());
+        case ('E'): // seconds since epoch
+            formatters_.push_back(details::make_unique<details::E_formatter>(padding));
             break;
 
-        case ('p'):
-            formatters_.push_back(details::make_unique<details::p_formatter>());
+        case ('p'): // am/pm
+            formatters_.push_back(details::make_unique<details::p_formatter>(padding));
             break;
 
-        case ('r'):
-            formatters_.push_back(details::make_unique<details::r_formatter>());
+        case ('r'): // 12 hour clock 02:55:02 pm
+            formatters_.push_back(details::make_unique<details::r_formatter>(padding));
             break;
 
-        case ('R'):
-            formatters_.push_back(details::make_unique<details::R_formatter>());
+        case ('R'): // 24-hour HH:MM time
+            formatters_.push_back(details::make_unique<details::R_formatter>(padding));
             break;
 
         case ('T'):
-        case ('X'):
-            formatters_.push_back(details::make_unique<details::T_formatter>());
+        case ('X'): // ISO 8601 time format (HH:MM:SS)
+            formatters_.push_back(details::make_unique<details::T_formatter>(padding));
             break;
 
-        case ('z'):
-            formatters_.push_back(details::make_unique<details::z_formatter>());
+        case ('z'): // timezone
+            formatters_.push_back(details::make_unique<details::z_formatter>(padding));
             break;
 
-        case ('+'):
-            formatters_.push_back(details::make_unique<details::full_formatter>());
+        case ('P'): // pid
+            formatters_.push_back(details::make_unique<details::pid_formatter>(padding));
             break;
 
-        case ('P'):
-            formatters_.push_back(details::make_unique<details::pid_formatter>());
-            break;
 #ifdef SPDLOG_ENABLE_MESSAGE_COUNTER
         case ('i'):
-            formatters_.push_back(details::make_unique<details::i_formatter>());
+            formatters_.push_back(details::make_unique<details::i_formatter>(padding));
             break;
 #endif
-        case ('^'):
-            formatters_.push_back(details::make_unique<details::color_start_formatter>());
+        case ('^'): // color range start
+            formatters_.push_back(details::make_unique<details::color_start_formatter>(padding));
             break;
 
-        case ('$'):
-            formatters_.push_back(details::make_unique<details::color_stop_formatter>());
+        case ('$'): // color range end
+            formatters_.push_back(details::make_unique<details::color_stop_formatter>(padding));
             break;
 
-        default: // Unknown flag appears as is
+        case ('@'): // source location (filename:filenumber)
+            formatters_.push_back(details::make_unique<details::source_location_formatter>(padding));
+            break;
+
+        case ('s'): // source filename
+            formatters_.push_back(details::make_unique<details::source_filename_formatter>(padding));
+            break;
+
+        case ('#'): // source line number
+            formatters_.push_back(details::make_unique<details::source_linenum_formatter>(padding));
+            break;
+
+        case ('!'): // source funcname
+            formatters_.push_back(details::make_unique<details::source_funcname_formatter>(padding));
+            break;
+
+        case ('%'): // % char
             formatters_.push_back(details::make_unique<details::ch_formatter>('%'));
-            formatters_.push_back(details::make_unique<details::ch_formatter>(flag));
+            break;
+
+        default: // Unknown flag appears as is
+            auto unknown_flag = details::make_unique<details::aggregate_formatter>();
+            unknown_flag->add_ch('%');
+            unknown_flag->add_ch(flag);
+            formatters_.push_back((std::move(unknown_flag)));
             break;
         }
     }
 
+    // Extract given pad spec (e.g. %8X)
+    // Advance the given it pass the end of the padding spec found (if any)
+    // Return padding.
+    details::padding_info handle_padspec_(std::string::const_iterator &it, std::string::const_iterator end)
+    {
+        using details::padding_info;
+        using details::scoped_pad;
+        const size_t max_width = 128;
+        if (it == end)
+        {
+            return padding_info{};
+        }
+
+        padding_info::pad_side side;
+        switch (*it)
+        {
+        case '-':
+            side = padding_info::right;
+            ++it;
+            break;
+        case '=':
+            side = padding_info::center;
+            ++it;
+            break;
+        default:
+            side = details::padding_info::left;
+            break;
+        }
+
+        if (it == end || !std::isdigit(static_cast<unsigned char>(*it)))
+        {
+            return padding_info{0, side};
+        }
+
+        auto width = static_cast<size_t>(*it - '0');
+        for (++it; it != end && std::isdigit(static_cast<unsigned char>(*it)); ++it)
+        {
+            auto digit = static_cast<size_t>(*it - '0');
+            width = width * 10 + digit;
+        }
+        return details::padding_info{std::min<size_t>(width, max_width), side};
+    }
+
     void compile_pattern_(const std::string &pattern)
     {
         auto end = pattern.end();
@@ -747,9 +1306,12 @@ private:
                 {
                     formatters_.push_back(std::move(user_chars));
                 }
-                if (++it != end)
+
+                auto padding = handle_padspec_(++it, end);
+
+                if (it != end)
                 {
-                    handle_flag_(*it);
+                    handle_flag_(*it, padding);
                 }
                 else
                 {
diff --git a/external/spdlog/details/periodic_worker.h b/external/spdlog/details/periodic_worker.h
index 57e5fa771..fa6488d17 100644
--- a/external/spdlog/details/periodic_worker.h
+++ b/external/spdlog/details/periodic_worker.h
@@ -10,7 +10,7 @@
 //
 // RAII over the owned thread:
 //    creates the thread on construction.
-//    stops and joins the thread on destruction.
+//    stops and joins the thread on destruction (if the thread is executing a callback, wait for it to finish first).
 
 #include <chrono>
 #include <condition_variable>
diff --git a/external/spdlog/details/registry.h b/external/spdlog/details/registry.h
index 63baf7481..ccd539557 100644
--- a/external/spdlog/details/registry.h
+++ b/external/spdlog/details/registry.h
@@ -14,6 +14,15 @@
 #include "spdlog/details/periodic_worker.h"
 #include "spdlog/logger.h"
 
+#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER
+// support for the default stdout color logger
+#ifdef _WIN32
+#include "spdlog/sinks/wincolor_sink.h"
+#else
+#include "spdlog/sinks/ansicolor_sink.h"
+#endif
+#endif // SPDLOG_DISABLE_DEFAULT_LOGGER
+
 #include <chrono>
 #include <functional>
 #include <memory>
@@ -33,18 +42,12 @@ public:
     void register_logger(std::shared_ptr<logger> new_logger)
     {
         std::lock_guard<std::mutex> lock(logger_map_mutex_);
-        auto logger_name = new_logger->name();
-        throw_if_exists_(logger_name);
-        loggers_[logger_name] = std::move(new_logger);
+        register_logger_(std::move(new_logger));
     }
 
-    void register_and_init(std::shared_ptr<logger> new_logger)
+    void initialize_logger(std::shared_ptr<logger> new_logger)
     {
         std::lock_guard<std::mutex> lock(logger_map_mutex_);
-        auto logger_name = new_logger->name();
-        throw_if_exists_(logger_name);
-
-        // set the global formatter pattern
         new_logger->set_formatter(formatter_->clone());
 
         if (err_handler_)
@@ -55,8 +58,10 @@ public:
         new_logger->set_level(level_);
         new_logger->flush_on(flush_level_);
 
-        // add to registry
-        loggers_[logger_name] = std::move(new_logger);
+        if (automatic_registration_)
+        {
+            register_logger_(std::move(new_logger));
+        }
     }
 
     std::shared_ptr<logger> get(const std::string &logger_name)
@@ -66,6 +71,38 @@ public:
         return found == loggers_.end() ? nullptr : found->second;
     }
 
+    std::shared_ptr<logger> default_logger()
+    {
+        std::lock_guard<std::mutex> lock(logger_map_mutex_);
+        return default_logger_;
+    }
+
+    // Return raw ptr to the default logger.
+    // To be used directly by the spdlog default api (e.g. spdlog::info)
+    // This make the default API faster, but cannot be used concurrently with set_default_logger().
+    // e.g do not call set_default_logger() from one thread while calling spdlog::info() from another.
+    logger *get_default_raw()
+    {
+        return default_logger_.get();
+    }
+
+    // set default logger.
+    // default logger is stored in default_logger_ (for faster retrieval) and in the loggers_ map.
+    void set_default_logger(std::shared_ptr<logger> new_default_logger)
+    {
+        std::lock_guard<std::mutex> lock(logger_map_mutex_);
+        // remove previous default logger from the map
+        if (default_logger_ != nullptr)
+        {
+            loggers_.erase(default_logger_->name());
+        }
+        if (new_default_logger != nullptr)
+        {
+            loggers_[new_default_logger->name()] = new_default_logger;
+        }
+        default_logger_ = std::move(new_default_logger);
+    }
+
     void set_tp(std::shared_ptr<thread_pool> tp)
     {
         std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
@@ -148,15 +185,20 @@ public:
     {
         std::lock_guard<std::mutex> lock(logger_map_mutex_);
         loggers_.erase(logger_name);
+        if (default_logger_ && default_logger_->name() == logger_name)
+        {
+            default_logger_.reset();
+        }
     }
 
     void drop_all()
     {
         std::lock_guard<std::mutex> lock(logger_map_mutex_);
         loggers_.clear();
+        default_logger_.reset();
     }
 
-    // clean all reasources and threads started by the registry
+    // clean all resources and threads started by the registry
     void shutdown()
     {
         {
@@ -177,6 +219,12 @@ public:
         return tp_mutex_;
     }
 
+    void set_automatic_registration(bool automatic_regsistration)
+    {
+        std::lock_guard<std::mutex> lock(logger_map_mutex_);
+        automatic_registration_ = automatic_regsistration;
+    }
+
     static registry &instance()
     {
         static registry s_instance;
@@ -185,8 +233,22 @@ public:
 
 private:
     registry()
-        : formatter_(new pattern_formatter("%+"))
+        : formatter_(new pattern_formatter())
     {
+
+#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER
+        // create default logger (ansicolor_stdout_sink_mt or wincolor_stdout_sink_mt in windows).
+#ifdef _WIN32
+        auto color_sink = std::make_shared<sinks::wincolor_stdout_sink_mt>();
+#else
+        auto color_sink = std::make_shared<sinks::ansicolor_stdout_sink_mt>();
+#endif
+
+        const char *default_logger_name = "";
+        default_logger_ = std::make_shared<spdlog::logger>(default_logger_name, std::move(color_sink));
+        loggers_[default_logger_name] = default_logger_;
+
+#endif // SPDLOG_DISABLE_DEFAULT_LOGGER
     }
 
     ~registry() = default;
@@ -199,15 +261,24 @@ private:
         }
     }
 
+    void register_logger_(std::shared_ptr<logger> new_logger)
+    {
+        auto logger_name = new_logger->name();
+        throw_if_exists_(logger_name);
+        loggers_[logger_name] = std::move(new_logger);
+    }
+
     std::mutex logger_map_mutex_, flusher_mutex_;
     std::recursive_mutex tp_mutex_;
     std::unordered_map<std::string, std::shared_ptr<logger>> loggers_;
     std::unique_ptr<formatter> formatter_;
-    level::level_enum level_ = level::info;
+    level::level_enum level_ = spdlog::logger::default_level();
     level::level_enum flush_level_ = level::off;
     log_err_handler err_handler_;
     std::shared_ptr<thread_pool> tp_;
     std::unique_ptr<periodic_worker> periodic_flusher_;
+    std::shared_ptr<logger> default_logger_;
+    bool automatic_registration_ = true;
 };
 
 } // namespace details
diff --git a/external/spdlog/details/thread_pool.h b/external/spdlog/details/thread_pool.h
index 0c716c3b2..35578971a 100644
--- a/external/spdlog/details/thread_pool.h
+++ b/external/spdlog/details/thread_pool.h
@@ -1,5 +1,6 @@
 #pragma once
 
+#include "spdlog/details/fmt_helper.h"
 #include "spdlog/details/log_msg.h"
 #include "spdlog/details/mpmc_blocking_q.h"
 #include "spdlog/details/os.h"
@@ -32,6 +33,7 @@ struct async_msg
     fmt::basic_memory_buffer<char, 176> raw;
 
     size_t msg_id;
+    source_loc source;
     async_logger_ptr worker_ptr;
 
     async_msg() = default;
@@ -48,6 +50,7 @@ struct async_msg
                                                    thread_id(other.thread_id),
                                                    raw(move(other.raw)),
                                                    msg_id(other.msg_id),
+                                                   source(other.source),
                                                    worker_ptr(std::move(other.worker_ptr))
     {
     }
@@ -60,6 +63,7 @@ struct async_msg
         thread_id = other.thread_id;
         raw = std::move(other.raw);
         msg_id = other.msg_id;
+        source = other.source;
         worker_ptr = std::move(other.worker_ptr);
         return *this;
     }
@@ -69,38 +73,45 @@ struct async_msg
 #endif
 
     // construct from log_msg with given type
-    async_msg(async_logger_ptr &&worker, async_msg_type the_type, details::log_msg &&m)
+    async_msg(async_logger_ptr &&worker, async_msg_type the_type, details::log_msg &m)
         : msg_type(the_type)
         , level(m.level)
         , time(m.time)
         , thread_id(m.thread_id)
         , msg_id(m.msg_id)
+        , source(m.source)
         , worker_ptr(std::move(worker))
     {
-        fmt_helper::append_buf(m.raw, raw);
+        fmt_helper::append_string_view(m.payload, raw);
     }
 
     async_msg(async_logger_ptr &&worker, async_msg_type the_type)
-        : async_msg(std::move(worker), the_type, details::log_msg())
+        : msg_type(the_type)
+        , level(level::off)
+        , time()
+        , thread_id(0)
+        , msg_id(0)
+        , source()
+        , worker_ptr(std::move(worker))
     {
     }
 
     explicit async_msg(async_msg_type the_type)
-        : async_msg(nullptr, the_type, details::log_msg())
+        : async_msg(nullptr, the_type)
     {
     }
 
     // copy into log_msg
-    void to_log_msg(log_msg &msg)
+    log_msg to_log_msg()
     {
-        msg.logger_name = &worker_ptr->name();
-        msg.level = level;
+        log_msg msg(&worker_ptr->name(), level, string_view_t(raw.data(), raw.size()));
         msg.time = time;
         msg.thread_id = thread_id;
-        fmt_helper::append_buf(raw, msg.raw);
         msg.msg_id = msg_id;
+        msg.source = source;
         msg.color_range_start = 0;
         msg.color_range_end = 0;
+        return msg;
     }
 };
 
@@ -149,9 +160,9 @@ public:
     thread_pool(const thread_pool &) = delete;
     thread_pool &operator=(thread_pool &&) = delete;
 
-    void post_log(async_logger_ptr &&worker_ptr, details::log_msg &&msg, async_overflow_policy overflow_policy)
+    void post_log(async_logger_ptr &&worker_ptr, details::log_msg &msg, async_overflow_policy overflow_policy)
     {
-        async_msg async_m(std::move(worker_ptr), async_msg_type::log, std::move(msg));
+        async_msg async_m(std::move(worker_ptr), async_msg_type::log, msg);
         post_async_msg_(std::move(async_m), overflow_policy);
     }
 
@@ -203,8 +214,7 @@ private:
         {
         case async_msg_type::log:
         {
-            log_msg msg;
-            incoming_async_msg.to_log_msg(msg);
+            auto msg = incoming_async_msg.to_log_msg();
             incoming_async_msg.worker_ptr->backend_log_(msg);
             return true;
         }
diff --git a/external/spdlog/fmt/bin_to_hex.h b/external/spdlog/fmt/bin_to_hex.h
index d8d0d9f9a..352338020 100644
--- a/external/spdlog/fmt/bin_to_hex.h
+++ b/external/spdlog/fmt/bin_to_hex.h
@@ -111,8 +111,8 @@ struct formatter<spdlog::details::bytes_range<T>>
     template<typename FormatContext, typename Container>
     auto format(const spdlog::details::bytes_range<Container> &the_range, FormatContext &ctx) -> decltype(ctx.out())
     {
-        constexpr const char *hex_upper = "0123456789ABCDEF";
-        constexpr const char *hex_lower = "0123456789abcdef";
+        SPDLOG_CONSTEXPR const char *hex_upper = "0123456789ABCDEF";
+        SPDLOG_CONSTEXPR const char *hex_lower = "0123456789abcdef";
         const char *hex_chars = use_uppercase ? hex_upper : hex_lower;
 
         std::size_t pos = 0;
diff --git a/external/spdlog/fmt/bundled/chrono.h b/external/spdlog/fmt/bundled/chrono.h
new file mode 100644
index 000000000..209cdc25a
--- /dev/null
+++ b/external/spdlog/fmt/bundled/chrono.h
@@ -0,0 +1,452 @@
+// Formatting library for C++ - chrono support
+//
+// Copyright (c) 2012 - present, Victor Zverovich
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+#ifndef FMT_CHRONO_H_
+#define FMT_CHRONO_H_
+
+#include "format.h"
+#include "locale.h"
+
+#include <chrono>
+#include <ctime>
+#include <locale>
+#include <sstream>
+
+FMT_BEGIN_NAMESPACE
+
+namespace internal{
+
+enum class numeric_system {
+  standard,
+  // Alternative numeric system, e.g. 十二 instead of 12 in ja_JP locale.
+  alternative
+};
+
+// Parses a put_time-like format string and invokes handler actions.
+template <typename Char, typename Handler>
+FMT_CONSTEXPR const Char *parse_chrono_format(
+    const Char *begin, const Char *end, Handler &&handler) {
+  auto ptr = begin;
+  while (ptr != end) {
+    auto c = *ptr;
+    if (c == '}') break;
+    if (c != '%') {
+      ++ptr;
+      continue;
+    }
+    if (begin != ptr)
+      handler.on_text(begin, ptr);
+    ++ptr; // consume '%'
+    if (ptr == end)
+      throw format_error("invalid format");
+    c = *ptr++;
+    switch (c) {
+    case '%':
+      handler.on_text(ptr - 1, ptr);
+      break;
+    case 'n': {
+      const char newline[] = "\n";
+      handler.on_text(newline, newline + 1);
+      break;
+    }
+    case 't': {
+      const char tab[] = "\t";
+      handler.on_text(tab, tab + 1);
+      break;
+    }
+    // Day of the week:
+    case 'a':
+      handler.on_abbr_weekday();
+      break;
+    case 'A':
+      handler.on_full_weekday();
+      break;
+    case 'w':
+      handler.on_dec0_weekday(numeric_system::standard);
+      break;
+    case 'u':
+      handler.on_dec1_weekday(numeric_system::standard);
+      break;
+    // Month:
+    case 'b':
+      handler.on_abbr_month();
+      break;
+    case 'B':
+      handler.on_full_month();
+      break;
+    // Hour, minute, second:
+    case 'H':
+      handler.on_24_hour(numeric_system::standard);
+      break;
+    case 'I':
+      handler.on_12_hour(numeric_system::standard);
+      break;
+    case 'M':
+      handler.on_minute(numeric_system::standard);
+      break;
+    case 'S':
+      handler.on_second(numeric_system::standard);
+      break;
+    // Other:
+    case 'c':
+      handler.on_datetime(numeric_system::standard);
+      break;
+    case 'x':
+      handler.on_loc_date(numeric_system::standard);
+      break;
+    case 'X':
+      handler.on_loc_time(numeric_system::standard);
+      break;
+    case 'D':
+      handler.on_us_date();
+      break;
+    case 'F':
+      handler.on_iso_date();
+      break;
+    case 'r':
+      handler.on_12_hour_time();
+      break;
+    case 'R':
+      handler.on_24_hour_time();
+      break;
+    case 'T':
+      handler.on_iso_time();
+      break;
+    case 'p':
+      handler.on_am_pm();
+      break;
+    case 'z':
+      handler.on_utc_offset();
+      break;
+    case 'Z':
+      handler.on_tz_name();
+      break;
+    // Alternative representation:
+    case 'E': {
+      if (ptr == end)
+        throw format_error("invalid format");
+      c = *ptr++;
+      switch (c) {
+      case 'c':
+        handler.on_datetime(numeric_system::alternative);
+        break;
+      case 'x':
+        handler.on_loc_date(numeric_system::alternative);
+        break;
+      case 'X':
+        handler.on_loc_time(numeric_system::alternative);
+        break;
+      default:
+        throw format_error("invalid format");
+      }
+      break;
+    }
+    case 'O':
+      if (ptr == end)
+        throw format_error("invalid format");
+      c = *ptr++;
+      switch (c) {
+      case 'w':
+        handler.on_dec0_weekday(numeric_system::alternative);
+        break;
+      case 'u':
+        handler.on_dec1_weekday(numeric_system::alternative);
+        break;
+      case 'H':
+        handler.on_24_hour(numeric_system::alternative);
+        break;
+      case 'I':
+        handler.on_12_hour(numeric_system::alternative);
+        break;
+      case 'M':
+        handler.on_minute(numeric_system::alternative);
+        break;
+      case 'S':
+        handler.on_second(numeric_system::alternative);
+        break;
+      default:
+        throw format_error("invalid format");
+      }
+      break;
+    default:
+      throw format_error("invalid format");
+    }
+    begin = ptr;
+  }
+  if (begin != ptr)
+    handler.on_text(begin, ptr);
+  return ptr;
+}
+
+struct chrono_format_checker {
+  void report_no_date() { throw format_error("no date"); }
+
+  template <typename Char>
+  void on_text(const Char *, const Char *) {}
+  void on_abbr_weekday() { report_no_date(); }
+  void on_full_weekday() { report_no_date(); }
+  void on_dec0_weekday(numeric_system) { report_no_date(); }
+  void on_dec1_weekday(numeric_system) { report_no_date(); }
+  void on_abbr_month() { report_no_date(); }
+  void on_full_month() { report_no_date(); }
+  void on_24_hour(numeric_system) {}
+  void on_12_hour(numeric_system) {}
+  void on_minute(numeric_system) {}
+  void on_second(numeric_system) {}
+  void on_datetime(numeric_system) { report_no_date(); }
+  void on_loc_date(numeric_system) { report_no_date(); }
+  void on_loc_time(numeric_system) { report_no_date(); }
+  void on_us_date() { report_no_date(); }
+  void on_iso_date() { report_no_date(); }
+  void on_12_hour_time() {}
+  void on_24_hour_time() {}
+  void on_iso_time() {}
+  void on_am_pm() {}
+  void on_utc_offset() { report_no_date(); }
+  void on_tz_name() { report_no_date(); }
+};
+
+template <typename Int>
+inline int to_int(Int value) {
+  FMT_ASSERT(value >= (std::numeric_limits<int>::min)() &&
+             value <= (std::numeric_limits<int>::max)(), "invalid value");
+  return static_cast<int>(value);
+}
+
+template <typename FormatContext, typename OutputIt>
+struct chrono_formatter {
+  FormatContext &context;
+  OutputIt out;
+  std::chrono::seconds s;
+  std::chrono::milliseconds ms;
+
+  typedef typename FormatContext::char_type char_type;
+
+  explicit chrono_formatter(FormatContext &ctx, OutputIt o)
+    : context(ctx), out(o) {}
+
+  int hour() const { return to_int((s.count() / 3600) % 24); }
+
+  int hour12() const {
+    auto hour = to_int((s.count() / 3600) % 12);
+    return hour > 0 ? hour : 12;
+  }
+
+  int minute() const { return to_int((s.count() / 60) % 60); }
+  int second() const { return to_int(s.count() % 60); }
+
+  std::tm time() const {
+    auto time = std::tm();
+    time.tm_hour = hour();
+    time.tm_min = minute();
+    time.tm_sec = second();
+    return time;
+  }
+
+  void write(int value, int width) {
+    typedef typename int_traits<int>::main_type main_type;
+    main_type n = to_unsigned(value);
+    int num_digits = internal::count_digits(n);
+    if (width > num_digits)
+      out = std::fill_n(out, width - num_digits, '0');
+    out = format_decimal<char_type>(out, n, num_digits);
+  }
+
+  void format_localized(const tm &time, const char *format) {
+    auto locale = context.locale().template get<std::locale>();
+    auto &facet = std::use_facet<std::time_put<char_type>>(locale);
+    std::basic_ostringstream<char_type> os;
+    os.imbue(locale);
+    facet.put(os, os, ' ', &time, format, format + std::strlen(format));
+    auto str = os.str();
+    std::copy(str.begin(), str.end(), out);
+  }
+
+  void on_text(const char_type *begin, const char_type *end) {
+    std::copy(begin, end, out);
+  }
+
+  // These are not implemented because durations don't have date information.
+  void on_abbr_weekday() {}
+  void on_full_weekday() {}
+  void on_dec0_weekday(numeric_system) {}
+  void on_dec1_weekday(numeric_system) {}
+  void on_abbr_month() {}
+  void on_full_month() {}
+  void on_datetime(numeric_system) {}
+  void on_loc_date(numeric_system) {}
+  void on_loc_time(numeric_system) {}
+  void on_us_date() {}
+  void on_iso_date() {}
+  void on_utc_offset() {}
+  void on_tz_name() {}
+
+  void on_24_hour(numeric_system ns) {
+    if (ns == numeric_system::standard)
+      return write(hour(), 2);
+    auto time = tm();
+    time.tm_hour = hour();
+    format_localized(time, "%OH");
+  }
+
+  void on_12_hour(numeric_system ns) {
+    if (ns == numeric_system::standard)
+      return write(hour12(), 2);
+    auto time = tm();
+    time.tm_hour = hour();
+    format_localized(time, "%OI");
+  }
+
+  void on_minute(numeric_system ns) {
+    if (ns == numeric_system::standard)
+      return write(minute(), 2);
+    auto time = tm();
+    time.tm_min = minute();
+    format_localized(time, "%OM");
+  }
+
+  void on_second(numeric_system ns) {
+    if (ns == numeric_system::standard) {
+      write(second(), 2);
+      if (ms != std::chrono::milliseconds(0)) {
+        *out++ = '.';
+        write(to_int(ms.count()), 3);
+      }
+      return;
+    }
+    auto time = tm();
+    time.tm_sec = second();
+    format_localized(time, "%OS");
+  }
+
+  void on_12_hour_time() { format_localized(time(), "%r"); }
+
+  void on_24_hour_time() {
+    write(hour(), 2);
+    *out++ = ':';
+    write(minute(), 2);
+  }
+
+  void on_iso_time() {
+    on_24_hour_time();
+    *out++ = ':';
+    write(second(), 2);
+  }
+
+  void on_am_pm() { format_localized(time(), "%p"); }
+};
+}  // namespace internal
+
+template <typename Period> FMT_CONSTEXPR const char *get_units() {
+  return FMT_NULL;
+}
+template <> FMT_CONSTEXPR const char *get_units<std::atto>() { return "as"; }
+template <> FMT_CONSTEXPR const char *get_units<std::femto>() { return "fs"; }
+template <> FMT_CONSTEXPR const char *get_units<std::pico>() { return "ps"; }
+template <> FMT_CONSTEXPR const char *get_units<std::nano>() { return "ns"; }
+template <> FMT_CONSTEXPR const char *get_units<std::micro>() { return "µs"; }
+template <> FMT_CONSTEXPR const char *get_units<std::milli>() { return "ms"; }
+template <> FMT_CONSTEXPR const char *get_units<std::centi>() { return "cs"; }
+template <> FMT_CONSTEXPR const char *get_units<std::deci>() { return "ds"; }
+template <> FMT_CONSTEXPR const char *get_units<std::ratio<1>>() { return "s"; }
+template <> FMT_CONSTEXPR const char *get_units<std::deca>() { return "das"; }
+template <> FMT_CONSTEXPR const char *get_units<std::hecto>() { return "hs"; }
+template <> FMT_CONSTEXPR const char *get_units<std::kilo>() { return "ks"; }
+template <> FMT_CONSTEXPR const char *get_units<std::mega>() { return "Ms"; }
+template <> FMT_CONSTEXPR const char *get_units<std::giga>() { return "Gs"; }
+template <> FMT_CONSTEXPR const char *get_units<std::tera>() { return "Ts"; }
+template <> FMT_CONSTEXPR const char *get_units<std::peta>() { return "Ps"; }
+template <> FMT_CONSTEXPR const char *get_units<std::exa>() { return "Es"; }
+template <> FMT_CONSTEXPR const char *get_units<std::ratio<60>>() {
+  return "m";
+}
+template <> FMT_CONSTEXPR const char *get_units<std::ratio<3600>>() {
+  return "h";
+}
+
+template <typename Rep, typename Period, typename Char>
+struct formatter<std::chrono::duration<Rep, Period>, Char> {
+ private:
+  align_spec spec;
+  internal::arg_ref<Char> width_ref;
+  mutable basic_string_view<Char> format_str;
+  typedef std::chrono::duration<Rep, Period> duration;
+
+  struct spec_handler {
+    formatter &f;
+    basic_parse_context<Char> &context;
+
+    typedef internal::arg_ref<Char> arg_ref_type;
+
+    template <typename Id>
+    FMT_CONSTEXPR arg_ref_type make_arg_ref(Id arg_id) {
+      context.check_arg_id(arg_id);
+      return arg_ref_type(arg_id);
+    }
+
+    FMT_CONSTEXPR arg_ref_type make_arg_ref(internal::auto_id) {
+      return arg_ref_type(context.next_arg_id());
+    }
+
+    void on_error(const char *msg) { throw format_error(msg); }
+    void on_fill(Char fill) { f.spec.fill_ = fill; }
+    void on_align(alignment align) { f.spec.align_ = align; }
+    void on_width(unsigned width) { f.spec.width_ = width; }
+
+    template <typename Id>
+    void on_dynamic_width(Id arg_id) {
+      f.width_ref = make_arg_ref(arg_id);
+    }
+  };
+
+ public:
+  formatter() : spec() {}
+
+  FMT_CONSTEXPR auto parse(basic_parse_context<Char> &ctx)
+      -> decltype(ctx.begin()) {
+    auto begin = ctx.begin(), end = ctx.end();
+    if (begin == end) return begin;
+    spec_handler handler{*this, ctx};
+    begin = internal::parse_align(begin, end, handler);
+    if (begin == end) return begin;
+    begin = internal::parse_width(begin, end, handler);
+    end = parse_chrono_format(begin, end, internal::chrono_format_checker());
+    format_str = basic_string_view<Char>(&*begin, internal::to_unsigned(end - begin));
+    return end;
+  }
+
+  template <typename FormatContext>
+  auto format(const duration &d, FormatContext &ctx)
+      -> decltype(ctx.out()) {
+    auto begin = format_str.begin(), end = format_str.end();
+    memory_buffer buf;
+    typedef output_range<decltype(ctx.out()), Char> range;
+    basic_writer<range> w(range(ctx.out()));
+    if (begin == end || *begin == '}') {
+      if (const char *unit = get_units<Period>())
+        format_to(buf, "{}{}", d.count(), unit);
+      else if (Period::den == 1)
+        format_to(buf, "{}[{}]s", d.count(), Period::num);
+      else
+        format_to(buf, "{}[{}/{}]s", d.count(), Period::num, Period::den);
+      internal::handle_dynamic_spec<internal::width_checker>(
+        spec.width_, width_ref, ctx);
+    } else {
+      auto out = std::back_inserter(buf);
+      internal::chrono_formatter<FormatContext, decltype(out)> f(ctx, out);
+      f.s = std::chrono::duration_cast<std::chrono::seconds>(d);
+      f.ms = std::chrono::duration_cast<std::chrono::milliseconds>(d - f.s);
+      parse_chrono_format(begin, end, f);
+    }
+    w.write(buf.data(), buf.size(), spec);
+    return w.out();
+  }
+};
+
+FMT_END_NAMESPACE
+
+#endif  // FMT_CHRONO_H_
diff --git a/external/spdlog/fmt/bundled/color.h b/external/spdlog/fmt/bundled/color.h
new file mode 100644
index 000000000..5db861c93
--- /dev/null
+++ b/external/spdlog/fmt/bundled/color.h
@@ -0,0 +1,577 @@
+// Formatting library for C++ - color support
+//
+// Copyright (c) 2018 - present, Victor Zverovich and fmt contributors
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+#ifndef FMT_COLOR_H_
+#define FMT_COLOR_H_
+
+#include "format.h"
+
+FMT_BEGIN_NAMESPACE
+
+#ifdef FMT_DEPRECATED_COLORS
+
+// color and (v)print_colored are deprecated.
+enum color { black, red, green, yellow, blue, magenta, cyan, white };
+FMT_API void vprint_colored(color c, string_view format, format_args args);
+FMT_API void vprint_colored(color c, wstring_view format, wformat_args args);
+template <typename... Args>
+inline void print_colored(color c, string_view format_str,
+                          const Args & ... args) {
+  vprint_colored(c, format_str, make_format_args(args...));
+}
+template <typename... Args>
+inline void print_colored(color c, wstring_view format_str,
+                          const Args & ... args) {
+  vprint_colored(c, format_str, make_format_args<wformat_context>(args...));
+}
+
+inline void vprint_colored(color c, string_view format, format_args args) {
+  char escape[] = "\x1b[30m";
+  escape[3] = static_cast<char>('0' + c);
+  std::fputs(escape, stdout);
+  vprint(format, args);
+  std::fputs(internal::data::RESET_COLOR, stdout);
+}
+
+inline void vprint_colored(color c, wstring_view format, wformat_args args) {
+  wchar_t escape[] = L"\x1b[30m";
+  escape[3] = static_cast<wchar_t>('0' + c);
+  std::fputws(escape, stdout);
+  vprint(format, args);
+  std::fputws(internal::data::WRESET_COLOR, stdout);
+}
+
+#else
+
+enum class color : uint32_t {
+  alice_blue              = 0xF0F8FF, // rgb(240,248,255)
+  antique_white           = 0xFAEBD7, // rgb(250,235,215)
+  aqua                    = 0x00FFFF, // rgb(0,255,255)
+  aquamarine              = 0x7FFFD4, // rgb(127,255,212)
+  azure                   = 0xF0FFFF, // rgb(240,255,255)
+  beige                   = 0xF5F5DC, // rgb(245,245,220)
+  bisque                  = 0xFFE4C4, // rgb(255,228,196)
+  black                   = 0x000000, // rgb(0,0,0)
+  blanched_almond         = 0xFFEBCD, // rgb(255,235,205)
+  blue                    = 0x0000FF, // rgb(0,0,255)
+  blue_violet             = 0x8A2BE2, // rgb(138,43,226)
+  brown                   = 0xA52A2A, // rgb(165,42,42)
+  burly_wood              = 0xDEB887, // rgb(222,184,135)
+  cadet_blue              = 0x5F9EA0, // rgb(95,158,160)
+  chartreuse              = 0x7FFF00, // rgb(127,255,0)
+  chocolate               = 0xD2691E, // rgb(210,105,30)
+  coral                   = 0xFF7F50, // rgb(255,127,80)
+  cornflower_blue         = 0x6495ED, // rgb(100,149,237)
+  cornsilk                = 0xFFF8DC, // rgb(255,248,220)
+  crimson                 = 0xDC143C, // rgb(220,20,60)
+  cyan                    = 0x00FFFF, // rgb(0,255,255)
+  dark_blue               = 0x00008B, // rgb(0,0,139)
+  dark_cyan               = 0x008B8B, // rgb(0,139,139)
+  dark_golden_rod         = 0xB8860B, // rgb(184,134,11)
+  dark_gray               = 0xA9A9A9, // rgb(169,169,169)
+  dark_green              = 0x006400, // rgb(0,100,0)
+  dark_khaki              = 0xBDB76B, // rgb(189,183,107)
+  dark_magenta            = 0x8B008B, // rgb(139,0,139)
+  dark_olive_green        = 0x556B2F, // rgb(85,107,47)
+  dark_orange             = 0xFF8C00, // rgb(255,140,0)
+  dark_orchid             = 0x9932CC, // rgb(153,50,204)
+  dark_red                = 0x8B0000, // rgb(139,0,0)
+  dark_salmon             = 0xE9967A, // rgb(233,150,122)
+  dark_sea_green          = 0x8FBC8F, // rgb(143,188,143)
+  dark_slate_blue         = 0x483D8B, // rgb(72,61,139)
+  dark_slate_gray         = 0x2F4F4F, // rgb(47,79,79)
+  dark_turquoise          = 0x00CED1, // rgb(0,206,209)
+  dark_violet             = 0x9400D3, // rgb(148,0,211)
+  deep_pink               = 0xFF1493, // rgb(255,20,147)
+  deep_sky_blue           = 0x00BFFF, // rgb(0,191,255)
+  dim_gray                = 0x696969, // rgb(105,105,105)
+  dodger_blue             = 0x1E90FF, // rgb(30,144,255)
+  fire_brick              = 0xB22222, // rgb(178,34,34)
+  floral_white            = 0xFFFAF0, // rgb(255,250,240)
+  forest_green            = 0x228B22, // rgb(34,139,34)
+  fuchsia                 = 0xFF00FF, // rgb(255,0,255)
+  gainsboro               = 0xDCDCDC, // rgb(220,220,220)
+  ghost_white             = 0xF8F8FF, // rgb(248,248,255)
+  gold                    = 0xFFD700, // rgb(255,215,0)
+  golden_rod              = 0xDAA520, // rgb(218,165,32)
+  gray                    = 0x808080, // rgb(128,128,128)
+  green                   = 0x008000, // rgb(0,128,0)
+  green_yellow            = 0xADFF2F, // rgb(173,255,47)
+  honey_dew               = 0xF0FFF0, // rgb(240,255,240)
+  hot_pink                = 0xFF69B4, // rgb(255,105,180)
+  indian_red              = 0xCD5C5C, // rgb(205,92,92)
+  indigo                  = 0x4B0082, // rgb(75,0,130)
+  ivory                   = 0xFFFFF0, // rgb(255,255,240)
+  khaki                   = 0xF0E68C, // rgb(240,230,140)
+  lavender                = 0xE6E6FA, // rgb(230,230,250)
+  lavender_blush          = 0xFFF0F5, // rgb(255,240,245)
+  lawn_green              = 0x7CFC00, // rgb(124,252,0)
+  lemon_chiffon           = 0xFFFACD, // rgb(255,250,205)
+  light_blue              = 0xADD8E6, // rgb(173,216,230)
+  light_coral             = 0xF08080, // rgb(240,128,128)
+  light_cyan              = 0xE0FFFF, // rgb(224,255,255)
+  light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210)
+  light_gray              = 0xD3D3D3, // rgb(211,211,211)
+  light_green             = 0x90EE90, // rgb(144,238,144)
+  light_pink              = 0xFFB6C1, // rgb(255,182,193)
+  light_salmon            = 0xFFA07A, // rgb(255,160,122)
+  light_sea_green         = 0x20B2AA, // rgb(32,178,170)
+  light_sky_blue          = 0x87CEFA, // rgb(135,206,250)
+  light_slate_gray        = 0x778899, // rgb(119,136,153)
+  light_steel_blue        = 0xB0C4DE, // rgb(176,196,222)
+  light_yellow            = 0xFFFFE0, // rgb(255,255,224)
+  lime                    = 0x00FF00, // rgb(0,255,0)
+  lime_green              = 0x32CD32, // rgb(50,205,50)
+  linen                   = 0xFAF0E6, // rgb(250,240,230)
+  magenta                 = 0xFF00FF, // rgb(255,0,255)
+  maroon                  = 0x800000, // rgb(128,0,0)
+  medium_aquamarine       = 0x66CDAA, // rgb(102,205,170)
+  medium_blue             = 0x0000CD, // rgb(0,0,205)
+  medium_orchid           = 0xBA55D3, // rgb(186,85,211)
+  medium_purple           = 0x9370DB, // rgb(147,112,219)
+  medium_sea_green        = 0x3CB371, // rgb(60,179,113)
+  medium_slate_blue       = 0x7B68EE, // rgb(123,104,238)
+  medium_spring_green     = 0x00FA9A, // rgb(0,250,154)
+  medium_turquoise        = 0x48D1CC, // rgb(72,209,204)
+  medium_violet_red       = 0xC71585, // rgb(199,21,133)
+  midnight_blue           = 0x191970, // rgb(25,25,112)
+  mint_cream              = 0xF5FFFA, // rgb(245,255,250)
+  misty_rose              = 0xFFE4E1, // rgb(255,228,225)
+  moccasin                = 0xFFE4B5, // rgb(255,228,181)
+  navajo_white            = 0xFFDEAD, // rgb(255,222,173)
+  navy                    = 0x000080, // rgb(0,0,128)
+  old_lace                = 0xFDF5E6, // rgb(253,245,230)
+  olive                   = 0x808000, // rgb(128,128,0)
+  olive_drab              = 0x6B8E23, // rgb(107,142,35)
+  orange                  = 0xFFA500, // rgb(255,165,0)
+  orange_red              = 0xFF4500, // rgb(255,69,0)
+  orchid                  = 0xDA70D6, // rgb(218,112,214)
+  pale_golden_rod         = 0xEEE8AA, // rgb(238,232,170)
+  pale_green              = 0x98FB98, // rgb(152,251,152)
+  pale_turquoise          = 0xAFEEEE, // rgb(175,238,238)
+  pale_violet_red         = 0xDB7093, // rgb(219,112,147)
+  papaya_whip             = 0xFFEFD5, // rgb(255,239,213)
+  peach_puff              = 0xFFDAB9, // rgb(255,218,185)
+  peru                    = 0xCD853F, // rgb(205,133,63)
+  pink                    = 0xFFC0CB, // rgb(255,192,203)
+  plum                    = 0xDDA0DD, // rgb(221,160,221)
+  powder_blue             = 0xB0E0E6, // rgb(176,224,230)
+  purple                  = 0x800080, // rgb(128,0,128)
+  rebecca_purple          = 0x663399, // rgb(102,51,153)
+  red                     = 0xFF0000, // rgb(255,0,0)
+  rosy_brown              = 0xBC8F8F, // rgb(188,143,143)
+  royal_blue              = 0x4169E1, // rgb(65,105,225)
+  saddle_brown            = 0x8B4513, // rgb(139,69,19)
+  salmon                  = 0xFA8072, // rgb(250,128,114)
+  sandy_brown             = 0xF4A460, // rgb(244,164,96)
+  sea_green               = 0x2E8B57, // rgb(46,139,87)
+  sea_shell               = 0xFFF5EE, // rgb(255,245,238)
+  sienna                  = 0xA0522D, // rgb(160,82,45)
+  silver                  = 0xC0C0C0, // rgb(192,192,192)
+  sky_blue                = 0x87CEEB, // rgb(135,206,235)
+  slate_blue              = 0x6A5ACD, // rgb(106,90,205)
+  slate_gray              = 0x708090, // rgb(112,128,144)
+  snow                    = 0xFFFAFA, // rgb(255,250,250)
+  spring_green            = 0x00FF7F, // rgb(0,255,127)
+  steel_blue              = 0x4682B4, // rgb(70,130,180)
+  tan                     = 0xD2B48C, // rgb(210,180,140)
+  teal                    = 0x008080, // rgb(0,128,128)
+  thistle                 = 0xD8BFD8, // rgb(216,191,216)
+  tomato                  = 0xFF6347, // rgb(255,99,71)
+  turquoise               = 0x40E0D0, // rgb(64,224,208)
+  violet                  = 0xEE82EE, // rgb(238,130,238)
+  wheat                   = 0xF5DEB3, // rgb(245,222,179)
+  white                   = 0xFFFFFF, // rgb(255,255,255)
+  white_smoke             = 0xF5F5F5, // rgb(245,245,245)
+  yellow                  = 0xFFFF00, // rgb(255,255,0)
+  yellow_green            = 0x9ACD32  // rgb(154,205,50)
+};  // enum class color
+
+enum class terminal_color : uint8_t {
+  black = 30,
+  red,
+  green,
+  yellow,
+  blue,
+  magenta,
+  cyan,
+  white,
+  bright_black = 90,
+  bright_red,
+  bright_green,
+  bright_yellow,
+  bright_blue,
+  bright_magenta,
+  bright_cyan,
+  bright_white
+};  // enum class terminal_color
+
+enum class emphasis : uint8_t {
+  bold = 1,
+  italic = 1 << 1,
+  underline = 1 << 2,
+  strikethrough = 1 << 3
+};  // enum class emphasis
+
+// rgb is a struct for red, green and blue colors.
+// We use rgb as name because some editors will show it as color direct in the
+// editor.
+struct rgb {
+  FMT_CONSTEXPR_DECL rgb() : r(0), g(0), b(0) {}
+  FMT_CONSTEXPR_DECL rgb(uint8_t r_, uint8_t g_, uint8_t b_)
+    : r(r_), g(g_), b(b_) {}
+  FMT_CONSTEXPR_DECL rgb(uint32_t hex)
+    : r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b((hex) & 0xFF) {}
+  FMT_CONSTEXPR_DECL rgb(color hex)
+    : r((uint32_t(hex) >> 16) & 0xFF), g((uint32_t(hex) >> 8) & 0xFF),
+      b(uint32_t(hex) & 0xFF) {}
+  uint8_t r;
+  uint8_t g;
+  uint8_t b;
+};
+
+namespace internal {
+
+// color is a struct of either a rgb color or a terminal color.
+struct color_type {
+  FMT_CONSTEXPR color_type() FMT_NOEXCEPT
+    : is_rgb(), value{} {}
+  FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT
+    : is_rgb(true), value{} {
+    value.rgb_color = static_cast<uint32_t>(rgb_color);
+  }
+  FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT
+    : is_rgb(true), value{} {
+    value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16)
+       | (static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
+  }
+  FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT
+    : is_rgb(), value{} {
+    value.term_color = static_cast<uint8_t>(term_color);
+  }
+  bool is_rgb;
+  union color_union {
+    uint8_t term_color;
+    uint32_t rgb_color;
+  } value;
+};
+} // namespace internal
+
+// Experimental text formatting support.
+class text_style {
+ public:
+  FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT
+      : set_foreground_color(), set_background_color(), ems(em) {}
+
+  FMT_CONSTEXPR text_style &operator|=(const text_style &rhs) {
+    if (!set_foreground_color) {
+      set_foreground_color = rhs.set_foreground_color;
+      foreground_color = rhs.foreground_color;
+    } else if (rhs.set_foreground_color) {
+      if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
+        throw format_error("can't OR a terminal color");
+      foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
+    }
+
+    if (!set_background_color) {
+      set_background_color = rhs.set_background_color;
+      background_color = rhs.background_color;
+    } else if (rhs.set_background_color) {
+      if (!background_color.is_rgb || !rhs.background_color.is_rgb)
+        throw format_error("can't OR a terminal color");
+      background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
+    }
+
+    ems = static_cast<emphasis>(static_cast<uint8_t>(ems) |
+                                static_cast<uint8_t>(rhs.ems));
+    return *this;
+  }
+
+  friend FMT_CONSTEXPR
+  text_style operator|(text_style lhs, const text_style &rhs) {
+    return lhs |= rhs;
+  }
+
+  FMT_CONSTEXPR text_style &operator&=(const text_style &rhs) {
+    if (!set_foreground_color) {
+      set_foreground_color = rhs.set_foreground_color;
+      foreground_color = rhs.foreground_color;
+    } else if (rhs.set_foreground_color) {
+      if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
+        throw format_error("can't AND a terminal color");
+      foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color;
+    }
+
+    if (!set_background_color) {
+      set_background_color = rhs.set_background_color;
+      background_color = rhs.background_color;
+    } else if (rhs.set_background_color) {
+      if (!background_color.is_rgb || !rhs.background_color.is_rgb)
+        throw format_error("can't AND a terminal color");
+      background_color.value.rgb_color &= rhs.background_color.value.rgb_color;
+    }
+
+    ems = static_cast<emphasis>(static_cast<uint8_t>(ems) &
+                                static_cast<uint8_t>(rhs.ems));
+    return *this;
+  }
+
+  friend FMT_CONSTEXPR
+  text_style operator&(text_style lhs, const text_style &rhs) {
+    return lhs &= rhs;
+  }
+
+  FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT {
+    return set_foreground_color;
+  }
+  FMT_CONSTEXPR bool has_background() const FMT_NOEXCEPT {
+    return set_background_color;
+  }
+  FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT {
+    return static_cast<uint8_t>(ems) != 0;
+  }
+  FMT_CONSTEXPR internal::color_type get_foreground() const FMT_NOEXCEPT {
+    assert(has_foreground() && "no foreground specified for this style");
+    return foreground_color;
+  }
+  FMT_CONSTEXPR internal::color_type get_background() const FMT_NOEXCEPT {
+    assert(has_background() && "no background specified for this style");
+    return background_color;
+  }
+  FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT {
+    assert(has_emphasis() && "no emphasis specified for this style");
+    return ems;
+  }
+
+private:
+ FMT_CONSTEXPR text_style(bool is_foreground,
+                          internal::color_type text_color) FMT_NOEXCEPT
+     : set_foreground_color(),
+       set_background_color(),
+       ems() {
+   if (is_foreground) {
+     foreground_color = text_color;
+     set_foreground_color = true;
+   } else {
+     background_color = text_color;
+     set_background_color = true;
+   }
+ }
+
+  friend FMT_CONSTEXPR_DECL text_style fg(internal::color_type foreground)
+      FMT_NOEXCEPT;
+  friend FMT_CONSTEXPR_DECL text_style bg(internal::color_type background)
+      FMT_NOEXCEPT;
+
+  internal::color_type foreground_color;
+  internal::color_type background_color;
+  bool set_foreground_color;
+  bool set_background_color;
+  emphasis ems;
+};
+
+FMT_CONSTEXPR text_style fg(internal::color_type foreground) FMT_NOEXCEPT {
+  return text_style(/*is_foreground=*/true, foreground);
+}
+
+FMT_CONSTEXPR text_style bg(internal::color_type background) FMT_NOEXCEPT {
+  return text_style(/*is_foreground=*/false, background);
+}
+
+FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT {
+  return text_style(lhs) | rhs;
+}
+
+namespace internal {
+
+template <typename Char>
+struct ansi_color_escape {
+  FMT_CONSTEXPR ansi_color_escape(internal::color_type text_color,
+                                  const char * esc) FMT_NOEXCEPT {
+    // If we have a terminal color, we need to output another escape code
+    // sequence.
+    if (!text_color.is_rgb) {
+      bool is_background = esc == internal::data::BACKGROUND_COLOR;
+      uint32_t value = text_color.value.term_color;
+      // Background ASCII codes are the same as the foreground ones but with
+      // 10 more.
+      if (is_background)
+        value += 10u;
+
+      std::size_t index = 0;
+      buffer[index++] = static_cast<Char>('\x1b');
+      buffer[index++] = static_cast<Char>('[');
+
+      if (value >= 100u) {
+        buffer[index++] = static_cast<Char>('1');
+        value %= 100u;
+      }
+      buffer[index++] = static_cast<Char>('0' + value / 10u);
+      buffer[index++] = static_cast<Char>('0' + value % 10u);
+
+      buffer[index++] = static_cast<Char>('m');
+      buffer[index++] = static_cast<Char>('\0');
+      return;
+    }
+
+    for (int i = 0; i < 7; i++) {
+      buffer[i] = static_cast<Char>(esc[i]);
+    }
+    rgb color(text_color.value.rgb_color);
+    to_esc(color.r, buffer +  7, ';');
+    to_esc(color.g, buffer + 11, ';');
+    to_esc(color.b, buffer + 15, 'm');
+    buffer[19] = static_cast<Char>(0);
+  }
+  FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT {
+    uint8_t em_codes[4] = {};
+    uint8_t em_bits = static_cast<uint8_t>(em);
+    if (em_bits & static_cast<uint8_t>(emphasis::bold))
+      em_codes[0] = 1;
+    if (em_bits & static_cast<uint8_t>(emphasis::italic))
+      em_codes[1] = 3;
+    if (em_bits & static_cast<uint8_t>(emphasis::underline))
+      em_codes[2] = 4;
+    if (em_bits & static_cast<uint8_t>(emphasis::strikethrough))
+      em_codes[3] = 9;
+
+    std::size_t index = 0;
+    for (int i = 0; i < 4; ++i) {
+      if (!em_codes[i])
+        continue;
+      buffer[index++] = static_cast<Char>('\x1b');
+      buffer[index++] = static_cast<Char>('[');
+      buffer[index++] = static_cast<Char>('0' + em_codes[i]);
+      buffer[index++] = static_cast<Char>('m');
+    }
+    buffer[index++] = static_cast<Char>(0);
+  }
+  FMT_CONSTEXPR operator const Char *() const FMT_NOEXCEPT { return buffer; }
+
+private:
+  Char buffer[7u + 3u * 4u + 1u];
+
+  static FMT_CONSTEXPR void to_esc(uint8_t c, Char *out,
+                                   char delimiter) FMT_NOEXCEPT {
+    out[0] = static_cast<Char>('0' + c / 100);
+    out[1] = static_cast<Char>('0' + c / 10 % 10);
+    out[2] = static_cast<Char>('0' + c % 10);
+    out[3] = static_cast<Char>(delimiter);
+  }
+};
+
+template <typename Char>
+FMT_CONSTEXPR ansi_color_escape<Char>
+make_foreground_color(internal::color_type foreground) FMT_NOEXCEPT {
+  return ansi_color_escape<Char>(foreground, internal::data::FOREGROUND_COLOR);
+}
+
+template <typename Char>
+FMT_CONSTEXPR ansi_color_escape<Char>
+make_background_color(internal::color_type background) FMT_NOEXCEPT {
+  return ansi_color_escape<Char>(background, internal::data::BACKGROUND_COLOR);
+}
+
+template <typename Char>
+FMT_CONSTEXPR ansi_color_escape<Char>
+make_emphasis(emphasis em) FMT_NOEXCEPT {
+  return ansi_color_escape<Char>(em);
+}
+
+template <typename Char>
+inline void fputs(const Char *chars, FILE *stream) FMT_NOEXCEPT {
+  std::fputs(chars, stream);
+}
+
+template <>
+inline void fputs<wchar_t>(const wchar_t *chars, FILE *stream) FMT_NOEXCEPT {
+  std::fputws(chars, stream);
+}
+
+template <typename Char>
+inline void reset_color(FILE *stream) FMT_NOEXCEPT {
+  fputs(internal::data::RESET_COLOR, stream);
+}
+
+template <>
+inline void reset_color<wchar_t>(FILE *stream) FMT_NOEXCEPT {
+  fputs(internal::data::WRESET_COLOR, stream);
+}
+
+// The following specialiazation disables using std::FILE as a character type,
+// which is needed because or else
+//   fmt::print(stderr, fmt::emphasis::bold, "");
+// would take stderr (a std::FILE *) as the format string.
+template <>
+struct is_string<std::FILE *> : std::false_type {};
+template <>
+struct is_string<const std::FILE *> : std::false_type {};
+} // namespace internal
+
+template <
+  typename S, typename Char = typename internal::char_t<S>::type>
+void vprint(std::FILE *f, const text_style &ts, const S &format,
+            basic_format_args<typename buffer_context<Char>::type> args) {
+  bool has_style = false;
+  if (ts.has_emphasis()) {
+    has_style = true;
+    internal::fputs<Char>(
+          internal::make_emphasis<Char>(ts.get_emphasis()), f);
+  }
+  if (ts.has_foreground()) {
+    has_style = true;
+    internal::fputs<Char>(
+          internal::make_foreground_color<Char>(ts.get_foreground()), f);
+  }
+  if (ts.has_background()) {
+    has_style = true;
+    internal::fputs<Char>(
+        internal::make_background_color<Char>(ts.get_background()), f);
+  }
+  vprint(f, format, args);
+  if (has_style) {
+    internal::reset_color<Char>(f);
+  }
+}
+
+/**
+  Formats a string and prints it to the specified file stream using ANSI
+  escape sequences to specify text formatting.
+  Example:
+    fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
+               "Elapsed time: {0:.2f} seconds", 1.23);
+ */
+template <typename String, typename... Args>
+typename std::enable_if<internal::is_string<String>::value>::type print(
+    std::FILE *f, const text_style &ts, const String &format_str,
+    const Args &... args) {
+  internal::check_format_string<Args...>(format_str);
+  typedef typename internal::char_t<String>::type char_t;
+  typedef typename buffer_context<char_t>::type context_t;
+  format_arg_store<context_t, Args...> as{args...};
+  vprint(f, ts, format_str, basic_format_args<context_t>(as));
+}
+
+/**
+  Formats a string and prints it to stdout using ANSI escape sequences to
+  specify text formatting.
+  Example:
+    fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
+               "Elapsed time: {0:.2f} seconds", 1.23);
+ */
+template <typename String, typename... Args>
+typename std::enable_if<internal::is_string<String>::value>::type print(
+    const text_style &ts, const String &format_str,
+    const Args &... args) {
+  return print(stdout, ts, format_str, args...);
+}
+
+#endif
+
+FMT_END_NAMESPACE
+
+#endif  // FMT_COLOR_H_
diff --git a/external/spdlog/fmt/bundled/colors.h b/external/spdlog/fmt/bundled/colors.h
deleted file mode 100644
index 003a6679e..000000000
--- a/external/spdlog/fmt/bundled/colors.h
+++ /dev/null
@@ -1,257 +0,0 @@
-// Formatting library for C++ - the core API
-//
-// Copyright (c) 2012 - present, Victor Zverovich
-// All rights reserved.
-//
-// For the license information refer to format.h.
-//
-// Copyright (c) 2018 - present, Remotion (Igor Schulz)
-// All Rights Reserved
-// {fmt} support for rgb color output.
-
-#ifndef FMT_COLORS_H_
-#define FMT_COLORS_H_
-
-#include "format.h"
-
-FMT_BEGIN_NAMESPACE
-
-// rgb is a struct for red, green and blue colors.
-// We use rgb as name because some editors will show it as color direct in the
-// editor.
-struct rgb
-{
-    FMT_CONSTEXPR_DECL rgb()
-        : r(0)
-        , g(0)
-        , b(0)
-    {
-    }
-    FMT_CONSTEXPR_DECL rgb(uint8_t r_, uint8_t g_, uint8_t b_)
-        : r(r_)
-        , g(g_)
-        , b(b_)
-    {
-    }
-    FMT_CONSTEXPR_DECL rgb(uint32_t hex)
-        : r((hex >> 16) & 0xFF)
-        , g((hex >> 8) & 0xFF)
-        , b((hex)&0xFF)
-    {
-    }
-    uint8_t r;
-    uint8_t g;
-    uint8_t b;
-};
-
-namespace internal {
-
-FMT_CONSTEXPR inline void to_esc(uint8_t c, char out[], int offset)
-{
-    out[offset + 0] = static_cast<char>('0' + c / 100);
-    out[offset + 1] = static_cast<char>('0' + c / 10 % 10);
-    out[offset + 2] = static_cast<char>('0' + c % 10);
-}
-
-} // namespace internal
-
-FMT_FUNC void vprint_rgb(rgb fd, string_view format, format_args args)
-{
-    char escape_fd[] = "\x1b[38;2;000;000;000m";
-    static FMT_CONSTEXPR_DECL const char RESET_COLOR[] = "\x1b[0m";
-    internal::to_esc(fd.r, escape_fd, 7);
-    internal::to_esc(fd.g, escape_fd, 11);
-    internal::to_esc(fd.b, escape_fd, 15);
-
-    std::fputs(escape_fd, stdout);
-    vprint(format, args);
-    std::fputs(RESET_COLOR, stdout);
-}
-
-FMT_FUNC void vprint_rgb(rgb fd, rgb bg, string_view format, format_args args)
-{
-    char escape_fd[] = "\x1b[38;2;000;000;000m"; // foreground color
-    char escape_bg[] = "\x1b[48;2;000;000;000m"; // background color
-    static FMT_CONSTEXPR_DECL const char RESET_COLOR[] = "\x1b[0m";
-    internal::to_esc(fd.r, escape_fd, 7);
-    internal::to_esc(fd.g, escape_fd, 11);
-    internal::to_esc(fd.b, escape_fd, 15);
-
-    internal::to_esc(bg.r, escape_bg, 7);
-    internal::to_esc(bg.g, escape_bg, 11);
-    internal::to_esc(bg.b, escape_bg, 15);
-
-    std::fputs(escape_fd, stdout);
-    std::fputs(escape_bg, stdout);
-    vprint(format, args);
-    std::fputs(RESET_COLOR, stdout);
-}
-
-template<typename... Args>
-inline void print_rgb(rgb fd, string_view format_str, const Args &... args)
-{
-    vprint_rgb(fd, format_str, make_format_args(args...));
-}
-
-// rgb foreground color
-template<typename... Args>
-inline void print(rgb fd, string_view format_str, const Args &... args)
-{
-    vprint_rgb(fd, format_str, make_format_args(args...));
-}
-
-// rgb foreground color and background color
-template<typename... Args>
-inline void print(rgb fd, rgb bg, string_view format_str, const Args &... args)
-{
-    vprint_rgb(fd, bg, format_str, make_format_args(args...));
-}
-
-enum class color : uint32_t
-{
-    alice_blue = 0xF0F8FF,              // rgb(240,248,255)
-    antique_white = 0xFAEBD7,           // rgb(250,235,215)
-    aqua = 0x00FFFF,                    // rgb(0,255,255)
-    aquamarine = 0x7FFFD4,              // rgb(127,255,212)
-    azure = 0xF0FFFF,                   // rgb(240,255,255)
-    beige = 0xF5F5DC,                   // rgb(245,245,220)
-    bisque = 0xFFE4C4,                  // rgb(255,228,196)
-    black = 0x000000,                   // rgb(0,0,0)
-    blanched_almond = 0xFFEBCD,         // rgb(255,235,205)
-    blue = 0x0000FF,                    // rgb(0,0,255)
-    blue_violet = 0x8A2BE2,             // rgb(138,43,226)
-    brown = 0xA52A2A,                   // rgb(165,42,42)
-    burly_wood = 0xDEB887,              // rgb(222,184,135)
-    cadet_blue = 0x5F9EA0,              // rgb(95,158,160)
-    chartreuse = 0x7FFF00,              // rgb(127,255,0)
-    chocolate = 0xD2691E,               // rgb(210,105,30)
-    coral = 0xFF7F50,                   // rgb(255,127,80)
-    cornflower_blue = 0x6495ED,         // rgb(100,149,237)
-    cornsilk = 0xFFF8DC,                // rgb(255,248,220)
-    crimson = 0xDC143C,                 // rgb(220,20,60)
-    cyan = 0x00FFFF,                    // rgb(0,255,255)
-    dark_blue = 0x00008B,               // rgb(0,0,139)
-    dark_cyan = 0x008B8B,               // rgb(0,139,139)
-    dark_golden_rod = 0xB8860B,         // rgb(184,134,11)
-    dark_gray = 0xA9A9A9,               // rgb(169,169,169)
-    dark_green = 0x006400,              // rgb(0,100,0)
-    dark_khaki = 0xBDB76B,              // rgb(189,183,107)
-    dark_magenta = 0x8B008B,            // rgb(139,0,139)
-    dark_olive_green = 0x556B2F,        // rgb(85,107,47)
-    dark_orange = 0xFF8C00,             // rgb(255,140,0)
-    dark_orchid = 0x9932CC,             // rgb(153,50,204)
-    dark_red = 0x8B0000,                // rgb(139,0,0)
-    dark_salmon = 0xE9967A,             // rgb(233,150,122)
-    dark_sea_green = 0x8FBC8F,          // rgb(143,188,143)
-    dark_slate_blue = 0x483D8B,         // rgb(72,61,139)
-    dark_slate_gray = 0x2F4F4F,         // rgb(47,79,79)
-    dark_turquoise = 0x00CED1,          // rgb(0,206,209)
-    dark_violet = 0x9400D3,             // rgb(148,0,211)
-    deep_pink = 0xFF1493,               // rgb(255,20,147)
-    deep_sky_blue = 0x00BFFF,           // rgb(0,191,255)
-    dim_gray = 0x696969,                // rgb(105,105,105)
-    dodger_blue = 0x1E90FF,             // rgb(30,144,255)
-    fire_brick = 0xB22222,              // rgb(178,34,34)
-    floral_white = 0xFFFAF0,            // rgb(255,250,240)
-    forest_green = 0x228B22,            // rgb(34,139,34)
-    fuchsia = 0xFF00FF,                 // rgb(255,0,255)
-    gainsboro = 0xDCDCDC,               // rgb(220,220,220)
-    ghost_white = 0xF8F8FF,             // rgb(248,248,255)
-    gold = 0xFFD700,                    // rgb(255,215,0)
-    golden_rod = 0xDAA520,              // rgb(218,165,32)
-    gray = 0x808080,                    // rgb(128,128,128)
-    green = 0x008000,                   // rgb(0,128,0)
-    green_yellow = 0xADFF2F,            // rgb(173,255,47)
-    honey_dew = 0xF0FFF0,               // rgb(240,255,240)
-    hot_pink = 0xFF69B4,                // rgb(255,105,180)
-    indian_red = 0xCD5C5C,              // rgb(205,92,92)
-    indigo = 0x4B0082,                  // rgb(75,0,130)
-    ivory = 0xFFFFF0,                   // rgb(255,255,240)
-    khaki = 0xF0E68C,                   // rgb(240,230,140)
-    lavender = 0xE6E6FA,                // rgb(230,230,250)
-    lavender_blush = 0xFFF0F5,          // rgb(255,240,245)
-    lawn_green = 0x7CFC00,              // rgb(124,252,0)
-    lemon_chiffon = 0xFFFACD,           // rgb(255,250,205)
-    light_blue = 0xADD8E6,              // rgb(173,216,230)
-    light_coral = 0xF08080,             // rgb(240,128,128)
-    light_cyan = 0xE0FFFF,              // rgb(224,255,255)
-    light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210)
-    light_gray = 0xD3D3D3,              // rgb(211,211,211)
-    light_green = 0x90EE90,             // rgb(144,238,144)
-    light_pink = 0xFFB6C1,              // rgb(255,182,193)
-    light_salmon = 0xFFA07A,            // rgb(255,160,122)
-    light_sea_green = 0x20B2AA,         // rgb(32,178,170)
-    light_sky_blue = 0x87CEFA,          // rgb(135,206,250)
-    light_slate_gray = 0x778899,        // rgb(119,136,153)
-    light_steel_blue = 0xB0C4DE,        // rgb(176,196,222)
-    light_yellow = 0xFFFFE0,            // rgb(255,255,224)
-    lime = 0x00FF00,                    // rgb(0,255,0)
-    lime_green = 0x32CD32,              // rgb(50,205,50)
-    linen = 0xFAF0E6,                   // rgb(250,240,230)
-    magenta = 0xFF00FF,                 // rgb(255,0,255)
-    maroon = 0x800000,                  // rgb(128,0,0)
-    medium_aqua_marine = 0x66CDAA,      // rgb(102,205,170)
-    medium_blue = 0x0000CD,             // rgb(0,0,205)
-    medium_orchid = 0xBA55D3,           // rgb(186,85,211)
-    medium_purple = 0x9370DB,           // rgb(147,112,219)
-    medium_sea_green = 0x3CB371,        // rgb(60,179,113)
-    medium_slate_blue = 0x7B68EE,       // rgb(123,104,238)
-    medium_spring_green = 0x00FA9A,     // rgb(0,250,154)
-    medium_turquoise = 0x48D1CC,        // rgb(72,209,204)
-    medium_violet_red = 0xC71585,       // rgb(199,21,133)
-    midnight_blue = 0x191970,           // rgb(25,25,112)
-    mint_cream = 0xF5FFFA,              // rgb(245,255,250)
-    misty_rose = 0xFFE4E1,              // rgb(255,228,225)
-    moccasin = 0xFFE4B5,                // rgb(255,228,181)
-    navajo_white = 0xFFDEAD,            // rgb(255,222,173)
-    navy = 0x000080,                    // rgb(0,0,128)
-    old_lace = 0xFDF5E6,                // rgb(253,245,230)
-    olive = 0x808000,                   // rgb(128,128,0)
-    olive_drab = 0x6B8E23,              // rgb(107,142,35)
-    orange = 0xFFA500,                  // rgb(255,165,0)
-    orange_red = 0xFF4500,              // rgb(255,69,0)
-    orchid = 0xDA70D6,                  // rgb(218,112,214)
-    pale_golden_rod = 0xEEE8AA,         // rgb(238,232,170)
-    pale_green = 0x98FB98,              // rgb(152,251,152)
-    pale_turquoise = 0xAFEEEE,          // rgb(175,238,238)
-    pale_violet_red = 0xDB7093,         // rgb(219,112,147)
-    papaya_whip = 0xFFEFD5,             // rgb(255,239,213)
-    peach_puff = 0xFFDAB9,              // rgb(255,218,185)
-    peru = 0xCD853F,                    // rgb(205,133,63)
-    pink = 0xFFC0CB,                    // rgb(255,192,203)
-    plum = 0xDDA0DD,                    // rgb(221,160,221)
-    powder_blue = 0xB0E0E6,             // rgb(176,224,230)
-    purple = 0x800080,                  // rgb(128,0,128)
-    rebecca_purple = 0x663399,          // rgb(102,51,153)
-    red = 0xFF0000,                     // rgb(255,0,0)
-    rosy_brown = 0xBC8F8F,              // rgb(188,143,143)
-    royal_blue = 0x4169E1,              // rgb(65,105,225)
-    saddle_brown = 0x8B4513,            // rgb(139,69,19)
-    salmon = 0xFA8072,                  // rgb(250,128,114)
-    sandy_brown = 0xF4A460,             // rgb(244,164,96)
-    sea_green = 0x2E8B57,               // rgb(46,139,87)
-    sea_shell = 0xFFF5EE,               // rgb(255,245,238)
-    sienna = 0xA0522D,                  // rgb(160,82,45)
-    silver = 0xC0C0C0,                  // rgb(192,192,192)
-    sky_blue = 0x87CEEB,                // rgb(135,206,235)
-    slate_blue = 0x6A5ACD,              // rgb(106,90,205)
-    slate_gray = 0x708090,              // rgb(112,128,144)
-    snow = 0xFFFAFA,                    // rgb(255,250,250)
-    spring_green = 0x00FF7F,            // rgb(0,255,127)
-    steel_blue = 0x4682B4,              // rgb(70,130,180)
-    tan = 0xD2B48C,                     // rgb(210,180,140)
-    teal = 0x008080,                    // rgb(0,128,128)
-    thistle = 0xD8BFD8,                 // rgb(216,191,216)
-    tomato = 0xFF6347,                  // rgb(255,99,71)
-    turquoise = 0x40E0D0,               // rgb(64,224,208)
-    violet = 0xEE82EE,                  // rgb(238,130,238)
-    wheat = 0xF5DEB3,                   // rgb(245,222,179)
-    white = 0xFFFFFF,                   // rgb(255,255,255)
-    white_smoke = 0xF5F5F5,             // rgb(245,245,245)
-    yellow = 0xFFFF00,                  // rgb(255,255,0)
-    yellow_green = 0x9ACD32,            // rgb(154,205,50)
-};                                      // enum class colors
-
-FMT_END_NAMESPACE
-
-#endif // FMT_COLORS_H_
diff --git a/external/spdlog/fmt/bundled/core.h b/external/spdlog/fmt/bundled/core.h
index 5912afef8..50b793515 100644
--- a/external/spdlog/fmt/bundled/core.h
+++ b/external/spdlog/fmt/bundled/core.h
@@ -16,7 +16,7 @@
 #include <type_traits>
 
 // The fmt library version in the form major * 10000 + minor * 100 + patch.
-#define FMT_VERSION 50201
+#define FMT_VERSION 50300
 
 #ifdef __has_feature
 # define FMT_HAS_FEATURE(x) __has_feature(x)
@@ -25,7 +25,7 @@
 #endif
 
 #if defined(__has_include) && !defined(__INTELLISENSE__) && \
-    (!defined(__INTEL_COMPILER) || __INTEL_COMPILER >= 1600)
+    !(defined(__INTEL_COMPILER) && __INTEL_COMPILER < 1600)
 # define FMT_HAS_INCLUDE(x) __has_include(x)
 #else
 # define FMT_HAS_INCLUDE(x) 0
@@ -72,7 +72,7 @@
 
 #ifndef FMT_USE_CONSTEXPR11
 # define FMT_USE_CONSTEXPR11 \
-    (FMT_MSC_VER >= 1900 || FMT_GCC_VERSION >= 406 || FMT_USE_CONSTEXPR)
+    (FMT_USE_CONSTEXPR || FMT_GCC_VERSION >= 406 || FMT_MSC_VER >= 1900)
 #endif
 #if FMT_USE_CONSTEXPR11
 # define FMT_CONSTEXPR11 constexpr
@@ -89,9 +89,12 @@
 # endif
 #endif
 
-#if FMT_HAS_FEATURE(cxx_explicit_conversions) || FMT_MSC_VER >= 1800
+#if FMT_HAS_FEATURE(cxx_explicit_conversions) || \
+    FMT_GCC_VERSION >= 405 || FMT_MSC_VER >= 1800
+# define FMT_USE_EXPLICIT 1
 # define FMT_EXPLICIT explicit
 #else
+# define FMT_USE_EXPLICIT 0
 # define FMT_EXPLICIT
 #endif
 
@@ -104,25 +107,18 @@
 #  define FMT_NULL NULL
 # endif
 #endif
-
 #ifndef FMT_USE_NULLPTR
 # define FMT_USE_NULLPTR 0
 #endif
 
-#if FMT_HAS_CPP_ATTRIBUTE(noreturn)
-# define FMT_NORETURN [[noreturn]]
-#else
-# define FMT_NORETURN
-#endif
-
 // Check if exceptions are disabled.
-#if defined(__GNUC__) && !defined(__EXCEPTIONS)
-# define FMT_EXCEPTIONS 0
-#elif FMT_MSC_VER && !_HAS_EXCEPTIONS
-# define FMT_EXCEPTIONS 0
-#endif
 #ifndef FMT_EXCEPTIONS
-# define FMT_EXCEPTIONS 1
+# if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || \
+     FMT_MSC_VER && !_HAS_EXCEPTIONS
+#  define FMT_EXCEPTIONS 0
+# else
+#  define FMT_EXCEPTIONS 1
+# endif
 #endif
 
 // Define FMT_USE_NOEXCEPT to make fmt use noexcept (C++11 feature).
@@ -147,14 +143,6 @@
 # endif
 #endif
 
-// This is needed because GCC still uses throw() in its headers when exceptions
-// are disabled.
-#if FMT_GCC_VERSION
-# define FMT_DTOR_NOEXCEPT FMT_DETECTED_NOEXCEPT
-#else
-# define FMT_DTOR_NOEXCEPT FMT_NOEXCEPT
-#endif
-
 #ifndef FMT_BEGIN_NAMESPACE
 # if FMT_HAS_FEATURE(cxx_inline_namespaces) || FMT_GCC_VERSION >= 404 || \
      FMT_MSC_VER >= 1900
@@ -187,11 +175,10 @@
       (__cplusplus > 201402L || defined(_LIBCPP_VERSION))) || \
     (defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910)
 # include <string_view>
-# define FMT_USE_STD_STRING_VIEW
-#elif (FMT_HAS_INCLUDE(<experimental/string_view>) && \
-       __cplusplus >= 201402L)
+# define FMT_STRING_VIEW std::basic_string_view
+#elif FMT_HAS_INCLUDE(<experimental/string_view>) && __cplusplus >= 201402L
 # include <experimental/string_view>
-# define FMT_USE_EXPERIMENTAL_STRING_VIEW
+# define FMT_STRING_VIEW std::experimental::basic_string_view
 #endif
 
 // std::result_of is defined in <functional> in gcc 4.4.
@@ -223,18 +210,135 @@ FMT_CONSTEXPR typename std::make_unsigned<Int>::type to_unsigned(Int value) {
   return static_cast<typename std::make_unsigned<Int>::type>(value);
 }
 
-// A constexpr std::char_traits::length replacement for pre-C++17.
-template <typename Char>
-FMT_CONSTEXPR size_t length(const Char *s) {
-  const Char *start = s;
-  while (*s) ++s;
-  return s - start;
+/** A contiguous memory buffer with an optional growing ability. */
+template <typename T>
+class basic_buffer {
+ private:
+  basic_buffer(const basic_buffer &) = delete;
+  void operator=(const basic_buffer &) = delete;
+
+  T *ptr_;
+  std::size_t size_;
+  std::size_t capacity_;
+
+ protected:
+  // Don't initialize ptr_ since it is not accessed to save a few cycles.
+  basic_buffer(std::size_t sz) FMT_NOEXCEPT: size_(sz), capacity_(sz) {}
+
+  basic_buffer(T *p = FMT_NULL, std::size_t sz = 0, std::size_t cap = 0)
+    FMT_NOEXCEPT: ptr_(p), size_(sz), capacity_(cap) {}
+
+  /** Sets the buffer data and capacity. */
+  void set(T *buf_data, std::size_t buf_capacity) FMT_NOEXCEPT {
+    ptr_ = buf_data;
+    capacity_ = buf_capacity;
+  }
+
+  /** Increases the buffer capacity to hold at least *capacity* elements. */
+  virtual void grow(std::size_t capacity) = 0;
+
+ public:
+  typedef T value_type;
+  typedef const T &const_reference;
+
+  virtual ~basic_buffer() {}
+
+  T *begin() FMT_NOEXCEPT { return ptr_; }
+  T *end() FMT_NOEXCEPT { return ptr_ + size_; }
+
+  /** Returns the size of this buffer. */
+  std::size_t size() const FMT_NOEXCEPT { return size_; }
+
+  /** Returns the capacity of this buffer. */
+  std::size_t capacity() const FMT_NOEXCEPT { return capacity_; }
+
+  /** Returns a pointer to the buffer data. */
+  T *data() FMT_NOEXCEPT { return ptr_; }
+
+  /** Returns a pointer to the buffer data. */
+  const T *data() const FMT_NOEXCEPT { return ptr_; }
+
+  /**
+    Resizes the buffer. If T is a POD type new elements may not be initialized.
+   */
+  void resize(std::size_t new_size) {
+    reserve(new_size);
+    size_ = new_size;
+  }
+
+  /** Clears this buffer. */
+  void clear() { size_ = 0; }
+
+  /** Reserves space to store at least *capacity* elements. */
+  void reserve(std::size_t new_capacity) {
+    if (new_capacity > capacity_)
+      grow(new_capacity);
+  }
+
+  void push_back(const T &value) {
+    reserve(size_ + 1);
+    ptr_[size_++] = value;
+  }
+
+  /** Appends data to the end of the buffer. */
+  template <typename U>
+  void append(const U *begin, const U *end);
+
+  T &operator[](std::size_t index) { return ptr_[index]; }
+  const T &operator[](std::size_t index) const { return ptr_[index]; }
+};
+
+typedef basic_buffer<char> buffer;
+typedef basic_buffer<wchar_t> wbuffer;
+
+// A container-backed buffer.
+template <typename Container>
+class container_buffer : public basic_buffer<typename Container::value_type> {
+ private:
+  Container &container_;
+
+ protected:
+  void grow(std::size_t capacity) FMT_OVERRIDE {
+    container_.resize(capacity);
+    this->set(&container_[0], capacity);
+  }
+
+ public:
+  explicit container_buffer(Container &c)
+    : basic_buffer<typename Container::value_type>(c.size()), container_(c) {}
+};
+
+// Extracts a reference to the container from back_insert_iterator.
+template <typename Container>
+inline Container &get_container(std::back_insert_iterator<Container> it) {
+  typedef std::back_insert_iterator<Container> bi_iterator;
+  struct accessor: bi_iterator {
+    accessor(bi_iterator iter) : bi_iterator(iter) {}
+    using bi_iterator::container;
+  };
+  return *accessor(it).container;
 }
-#if FMT_GCC_VERSION
-FMT_CONSTEXPR size_t length(const char *s) { return std::strlen(s); }
-#endif
+
+struct error_handler {
+  FMT_CONSTEXPR error_handler() {}
+  FMT_CONSTEXPR error_handler(const error_handler &) {}
+
+  // This function is intentionally not constexpr to give a compile-time error.
+  FMT_API void on_error(const char *message);
+};
+
+template <typename T>
+struct no_formatter_error : std::false_type {};
 }  // namespace internal
 
+#if FMT_GCC_VERSION && FMT_GCC_VERSION < 405
+template <typename... T>
+struct is_constructible: std::false_type {};
+#else
+template <typename... T>
+struct is_constructible : std::is_constructible<T...> {};
+#endif
+
 /**
   An implementation of ``std::basic_string_view`` for pre-C++17. It provides a
   subset of the API. ``fmt::basic_string_view`` is used for format strings even
@@ -252,18 +356,6 @@ class basic_string_view {
   typedef Char char_type;
   typedef const Char *iterator;
 
-  // Standard basic_string_view type.
-#if defined(FMT_USE_STD_STRING_VIEW)
-  typedef std::basic_string_view<Char> type;
-#elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW)
-  typedef std::experimental::basic_string_view<Char> type;
-#else
-  struct type {
-    const char *data() const { return FMT_NULL; }
-    size_t size() const { return 0; }
-  };
-#endif
-
   FMT_CONSTEXPR basic_string_view() FMT_NOEXCEPT : data_(FMT_NULL), size_(0) {}
 
   /** Constructs a string reference object from a C string and a size. */
@@ -276,8 +368,8 @@ class basic_string_view {
     the size with ``std::char_traits<Char>::length``.
     \endrst
    */
-  FMT_CONSTEXPR basic_string_view(const Char *s)
-    : data_(s), size_(internal::length(s)) {}
+  basic_string_view(const Char *s)
+    : data_(s), size_(std::char_traits<Char>::length(s)) {}
 
   /** Constructs a string reference from a ``std::basic_string`` object. */
   template <typename Alloc>
@@ -285,8 +377,10 @@ class basic_string_view {
       const std::basic_string<Char, Alloc> &s) FMT_NOEXCEPT
   : data_(s.data()), size_(s.size()) {}
 
-  FMT_CONSTEXPR basic_string_view(type s) FMT_NOEXCEPT
+#ifdef FMT_STRING_VIEW
+  FMT_CONSTEXPR basic_string_view(FMT_STRING_VIEW<Char> s) FMT_NOEXCEPT
   : data_(s.data()), size_(s.size()) {}
+#endif
 
   /** Returns a pointer to the string data. */
   FMT_CONSTEXPR const Char *data() const { return data_; }
@@ -334,19 +428,68 @@ class basic_string_view {
 typedef basic_string_view<char> string_view;
 typedef basic_string_view<wchar_t> wstring_view;
 
+/**
+  \rst
+  The function ``to_string_view`` adapts non-intrusively any kind of string or
+  string-like type if the user provides a (possibly templated) overload of
+  ``to_string_view`` which takes an instance of the string class
+  ``StringType<Char>`` and returns a ``fmt::basic_string_view<Char>``.
+  The conversion function must live in the very same namespace as
+  ``StringType<Char>`` to be picked up by ADL. Non-templated string types
+  like f.e. QString must return a ``basic_string_view`` with a fixed matching
+  char type.
+
+  **Example**::
+
+    namespace my_ns {
+    inline string_view to_string_view(const my_string &s) {
+      return {s.data(), s.length()};
+    }
+    }
+
+    std::string message = fmt::format(my_string("The answer is {}"), 42);
+  \endrst
+ */
+template <typename Char>
+inline basic_string_view<Char>
+  to_string_view(basic_string_view<Char> s) { return s; }
+
+template <typename Char>
+inline basic_string_view<Char>
+  to_string_view(const std::basic_string<Char> &s) { return s; }
+
+template <typename Char>
+inline basic_string_view<Char> to_string_view(const Char *s) { return s; }
+
+#ifdef FMT_STRING_VIEW
+template <typename Char>
+inline basic_string_view<Char>
+  to_string_view(FMT_STRING_VIEW<Char> s) { return s; }
+#endif
+
+// A base class for compile-time strings. It is defined in the fmt namespace to
+// make formatting functions visible via ADL, e.g. format(fmt("{}"), 42).
+struct compile_string {};
+
+template <typename S>
+struct is_compile_string : std::is_base_of<compile_string, S> {};
+
+template <
+  typename S,
+  typename Enable = typename std::enable_if<is_compile_string<S>::value>::type>
+FMT_CONSTEXPR basic_string_view<typename S::char_type>
+  to_string_view(const S &s) { return s; }
+
 template <typename Context>
 class basic_format_arg;
 
 template <typename Context>
 class basic_format_args;
 
-template <typename T>
-struct no_formatter_error : std::false_type {};
-
 // A formatter for objects of type T.
 template <typename T, typename Char = char, typename Enable = void>
 struct formatter {
-  static_assert(no_formatter_error<T>::value,
+  static_assert(internal::no_formatter_error<T>::value,
     "don't know how to format the type, include fmt/ostream.h if it provides "
     "an operator<< that should be used");
 
@@ -358,143 +501,32 @@ struct formatter {
 };
 
 template <typename T, typename Char, typename Enable = void>
-struct convert_to_int {
-  enum {
-    value = !std::is_arithmetic<T>::value && std::is_convertible<T, int>::value
-  };
-};
+struct convert_to_int: std::integral_constant<
+  bool, !std::is_arithmetic<T>::value && std::is_convertible<T, int>::value> {};
 
 namespace internal {
 
-/** A contiguous memory buffer with an optional growing ability. */
-template <typename T>
-class basic_buffer {
- private:
-  basic_buffer(const basic_buffer &) = delete;
-  void operator=(const basic_buffer &) = delete;
-
-  T *ptr_;
-  std::size_t size_;
-  std::size_t capacity_;
-
- protected:
-  // Don't initialize ptr_ since it is not accessed to save a few cycles.
-  basic_buffer(std::size_t sz) FMT_NOEXCEPT: size_(sz), capacity_(sz) {}
-
-  basic_buffer(T *p = FMT_NULL, std::size_t sz = 0, std::size_t cap = 0)
-    FMT_NOEXCEPT: ptr_(p), size_(sz), capacity_(cap) {}
+struct dummy_string_view { typedef void char_type; };
+dummy_string_view to_string_view(...);
+using fmt::v5::to_string_view;
 
-  /** Sets the buffer data and capacity. */
-  void set(T *buf_data, std::size_t buf_capacity) FMT_NOEXCEPT {
-    ptr_ = buf_data;
-    capacity_ = buf_capacity;
-  }
-
-  /** Increases the buffer capacity to hold at least *capacity* elements. */
-  virtual void grow(std::size_t capacity) = 0;
-
- public:
-  typedef T value_type;
-  typedef const T &const_reference;
-
-  virtual ~basic_buffer() {}
-
-  T *begin() FMT_NOEXCEPT { return ptr_; }
-  T *end() FMT_NOEXCEPT { return ptr_ + size_; }
-
-  /** Returns the size of this buffer. */
-  std::size_t size() const FMT_NOEXCEPT { return size_; }
-
-  /** Returns the capacity of this buffer. */
-  std::size_t capacity() const FMT_NOEXCEPT { return capacity_; }
-
-  /** Returns a pointer to the buffer data. */
-  T *data() FMT_NOEXCEPT { return ptr_; }
-
-  /** Returns a pointer to the buffer data. */
-  const T *data() const FMT_NOEXCEPT { return ptr_; }
-
-  /**
-    Resizes the buffer. If T is a POD type new elements may not be initialized.
-   */
-  void resize(std::size_t new_size) {
-    reserve(new_size);
-    size_ = new_size;
-  }
-
-  /** Clears this buffer. */
-  void clear() { size_ = 0; }
-
-  /** Reserves space to store at least *capacity* elements. */
-  void reserve(std::size_t new_capacity) {
-    if (new_capacity > capacity_)
-      grow(new_capacity);
-  }
-
-  void push_back(const T &value) {
-    reserve(size_ + 1);
-    ptr_[size_++] = value;
-  }
-
-  /** Appends data to the end of the buffer. */
-  template <typename U>
-  void append(const U *begin, const U *end);
-
-  T &operator[](std::size_t index) { return ptr_[index]; }
-  const T &operator[](std::size_t index) const { return ptr_[index]; }
-};
-
-typedef basic_buffer<char> buffer;
-typedef basic_buffer<wchar_t> wbuffer;
-
-// A container-backed buffer.
-template <typename Container>
-class container_buffer : public basic_buffer<typename Container::value_type> {
- private:
-  Container &container_;
-
- protected:
-  void grow(std::size_t capacity) FMT_OVERRIDE {
-    container_.resize(capacity);
-    this->set(&container_[0], capacity);
-  }
-
- public:
-  explicit container_buffer(Container &c)
-    : basic_buffer<typename Container::value_type>(c.size()), container_(c) {}
-};
-
-struct error_handler {
-  FMT_CONSTEXPR error_handler() {}
-  FMT_CONSTEXPR error_handler(const error_handler &) {}
+// Specifies whether S is a string type convertible to fmt::basic_string_view.
+template <typename S>
+struct is_string : std::integral_constant<bool, !std::is_same<
+    dummy_string_view, decltype(to_string_view(declval<S>()))>::value> {};
 
-  // This function is intentionally not constexpr to give a compile-time error.
-  FMT_API void on_error(const char *message);
+template <typename S>
+struct char_t {
+  typedef decltype(to_string_view(declval<S>())) result;
+  typedef typename result::char_type type;
 };
 
-// Formatting of wide characters and strings into a narrow output is disallowed:
-//   fmt::format("{}", L"test"); // error
-// To fix this, use a wide format string:
-//   fmt::format(L"{}", L"test");
-template <typename Char>
-inline void require_wchar() {
-  static_assert(
-      std::is_same<wchar_t, Char>::value,
-      "formatting of wide characters into a narrow output is disallowed");
-}
-
 template <typename Char>
 struct named_arg_base;
 
 template <typename T, typename Char>
 struct named_arg;
 
-template <typename T>
-struct is_named_arg : std::false_type {};
-
-template <typename T, typename Char>
-struct is_named_arg<named_arg<T, Char>> : std::true_type {};
-
 enum type {
   none_type, named_arg_type,
   // Integer types should go first,
@@ -639,15 +671,17 @@ FMT_MAKE_VALUE_SAME(long_long_type, long long)
 FMT_MAKE_VALUE_SAME(ulong_long_type, unsigned long long)
 FMT_MAKE_VALUE(int_type, signed char, int)
 FMT_MAKE_VALUE(uint_type, unsigned char, unsigned)
-FMT_MAKE_VALUE(char_type, char, int)
 
-#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED)
+// This doesn't use FMT_MAKE_VALUE because of ambiguity in gcc 4.4.
+template <typename C, typename Char>
+FMT_CONSTEXPR typename std::enable_if<
+  std::is_same<typename C::char_type, Char>::value,
+  init<C, int, char_type>>::type make_value(Char val) { return val; }
+
 template <typename C>
-inline init<C, int, char_type> make_value(wchar_t val) {
-  require_wchar<typename C::char_type>();
-  return static_cast<int>(val);
-}
-#endif
+FMT_CONSTEXPR typename std::enable_if<
+  !std::is_same<typename C::char_type, char>::value,
+  init<C, int, char_type>>::type make_value(char val) { return val; }
 
 FMT_MAKE_VALUE(double_type, float, double)
 FMT_MAKE_VALUE_SAME(double_type, double)
@@ -695,15 +729,17 @@ inline typename std::enable_if<
 
 template <typename C, typename T, typename Char = typename C::char_type>
 inline typename std::enable_if<
-    std::is_constructible<basic_string_view<Char>, T>::value,
+    is_constructible<basic_string_view<Char>, T>::value &&
+    !internal::is_string<T>::value,
     init<C, basic_string_view<Char>, string_type>>::type
   make_value(const T &val) { return basic_string_view<Char>(val); }
 
 template <typename C, typename T, typename Char = typename C::char_type>
 inline typename std::enable_if<
-    !convert_to_int<T, Char>::value &&
+    !convert_to_int<T, Char>::value && !std::is_same<T, Char>::value &&
     !std::is_convertible<T, basic_string_view<Char>>::value &&
-    !std::is_constructible<basic_string_view<Char>, T>::value,
+    !is_constructible<basic_string_view<Char>, T>::value &&
+    !internal::is_string<T>::value,
     // Implicit conversion to std::string is not handled here because it's
     // unsafe: https://github.com/fmtlib/fmt/issues/729
     init<C, const T &, custom_type>>::type
@@ -717,8 +753,21 @@ init<C, const void*, named_arg_type>
   return static_cast<const void*>(&val);
 }
 
+template <typename C, typename S>
+FMT_CONSTEXPR11 typename std::enable_if<
+  internal::is_string<S>::value,
+  init<C, basic_string_view<typename C::char_type>, string_type>>::type
+    make_value(const S &val) {
+  // Handle adapted strings.
+  static_assert(std::is_same<
+    typename C::char_type, typename internal::char_t<S>::type>::value,
+    "mismatch between char-types of context and argument");
+  return to_string_view(val);
+}
+
 // Maximum number of arguments with packed types.
 enum { max_packed_args = 15 };
+enum : unsigned long long { is_unpacked_bit = 1ull << 63 };
 
 template <typename Context>
 class arg_map;
@@ -738,7 +787,7 @@ class basic_format_arg {
 
   template <typename Visitor, typename Ctx>
   friend FMT_CONSTEXPR typename internal::result_of<Visitor(int)>::type
-    visit(Visitor &&vis, const basic_format_arg<Ctx> &arg);
+    visit_format_arg(Visitor &&vis, const basic_format_arg<Ctx> &arg);
 
   friend class basic_format_args<Context>;
   friend class internal::arg_map<Context>;
@@ -779,7 +828,7 @@ struct monostate {};
  */
 template <typename Visitor, typename Context>
 FMT_CONSTEXPR typename internal::result_of<Visitor(int)>::type
-    visit(Visitor &&vis, const basic_format_arg<Context> &arg) {
+    visit_format_arg(Visitor &&vis, const basic_format_arg<Context> &arg) {
   typedef typename Context::char_type char_type;
   switch (arg.type_) {
   case internal::none_type:
@@ -816,6 +865,13 @@ FMT_CONSTEXPR typename internal::result_of<Visitor(int)>::type
   return vis(monostate());
 }
 
+// DEPRECATED!
+template <typename Visitor, typename Context>
+FMT_CONSTEXPR typename internal::result_of<Visitor(int)>::type
+    visit(Visitor &&vis, const basic_format_arg<Context> &arg) {
+  return visit_format_arg(std::forward<Visitor>(vis), arg);
+}
+
 // Parsing context consisting of a format string range being parsed and an
 // argument counter for automatic indexing.
 template <typename Char, typename ErrorHandler = internal::error_handler>
@@ -866,6 +922,10 @@ class basic_parse_context : private ErrorHandler {
   FMT_CONSTEXPR ErrorHandler error_handler() const { return *this; }
 };
 
+typedef basic_parse_context<char> format_parse_context;
+typedef basic_parse_context<wchar_t> wformat_parse_context;
+
+// DEPRECATED!
 typedef basic_parse_context<char> parse_context;
 typedef basic_parse_context<wchar_t> wparse_context;
 
@@ -904,10 +964,26 @@ class arg_map {
       if (it->name == name)
         return it->arg;
     }
-    return basic_format_arg<Context>();
+    return {};
   }
 };
 
+// A type-erased reference to an std::locale to avoid heavy <locale> include.
+class locale_ref {
+ private:
+  const void *locale_;  // A type-erased pointer to std::locale.
+  friend class locale;
+
+ public:
+  locale_ref() : locale_(FMT_NULL) {}
+
+  template <typename Locale>
+  explicit locale_ref(const Locale &loc);
+
+  template <typename Locale>
+  Locale get() const;
+};
+
 template <typename OutputIt, typename Context, typename Char>
 class context_base {
  public:
@@ -917,14 +993,16 @@ class context_base {
   basic_parse_context<Char> parse_context_;
   iterator out_;
   basic_format_args<Context> args_;
+  locale_ref loc_;
 
  protected:
   typedef Char char_type;
   typedef basic_format_arg<Context> format_arg;
 
   context_base(OutputIt out, basic_string_view<char_type> format_str,
-               basic_format_args<Context> ctx_args)
-  : parse_context_(format_str), out_(out), args_(ctx_args) {}
+               basic_format_args<Context> ctx_args,
+               locale_ref loc = locale_ref())
+  : parse_context_(format_str), out_(out), args_(ctx_args), loc_(loc) {}
 
   // Returns the argument with specified index.
   format_arg do_get_arg(unsigned arg_id) {
@@ -942,9 +1020,9 @@ class context_base {
   }
 
  public:
-  basic_parse_context<char_type> &parse_context() {
-    return parse_context_;
-  }
+  basic_parse_context<char_type> &parse_context() { return parse_context_; }
+  basic_format_args<Context> args() const { return args_; } // DEPRECATED!
+  basic_format_arg<Context> arg(unsigned id) const { return args_.get(id); }
 
   internal::error_handler error_handler() {
     return parse_context_.error_handler();
@@ -959,18 +1037,42 @@ class context_base {
   // Advances the begin iterator to ``it``.
   void advance_to(iterator it) { out_ = it; }
 
-  basic_format_args<Context> args() const { return args_; }
+  locale_ref locale() { return loc_; }
 };
 
-// Extracts a reference to the container from back_insert_iterator.
-template <typename Container>
-inline Container &get_container(std::back_insert_iterator<Container> it) {
-  typedef std::back_insert_iterator<Container> bi_iterator;
-  struct accessor: bi_iterator {
-    accessor(bi_iterator iter) : bi_iterator(iter) {}
-    using bi_iterator::container;
-  };
-  return *accessor(it).container;
+template <typename Context, typename T>
+struct get_type {
+  typedef decltype(make_value<Context>(
+        declval<typename std::decay<T>::type&>())) value_type;
+  static const type value = value_type::type_tag;
+};
+
+template <typename Context>
+FMT_CONSTEXPR11 unsigned long long get_types() { return 0; }
+
+template <typename Context, typename Arg, typename... Args>
+FMT_CONSTEXPR11 unsigned long long get_types() {
+  return get_type<Context, Arg>::value | (get_types<Context, Args...>() << 4);
+}
+
+template <typename Context, typename T>
+FMT_CONSTEXPR basic_format_arg<Context> make_arg(const T &value) {
+  basic_format_arg<Context> arg;
+  arg.type_ = get_type<Context, T>::value;
+  arg.value_ = make_value<Context>(value);
+  return arg;
+}
+
+template <bool IS_PACKED, typename Context, typename T>
+inline typename std::enable_if<IS_PACKED, value<Context>>::type
+    make_arg(const T &value) {
+  return make_value<Context>(value);
+}
+
+template <bool IS_PACKED, typename Context, typename T>
+inline typename std::enable_if<!IS_PACKED, basic_format_arg<Context>>::type
+    make_arg(const T &value) {
+  return make_arg<Context>(value);
 }
 }  // namespace internal
 
@@ -1005,8 +1107,9 @@ class basic_format_context :
    stored in the object so make sure they have appropriate lifetimes.
    */
   basic_format_context(OutputIt out, basic_string_view<char_type> format_str,
-                basic_format_args<basic_format_context> ctx_args)
-    : base(out, format_str, ctx_args) {}
+                       basic_format_args<basic_format_context> ctx_args,
+                       internal::locale_ref loc = internal::locale_ref())
+    : base(out, format_str, ctx_args, loc) {}
 
   format_arg next_arg() {
     return this->do_get_arg(this->parse_context().next_arg_id());
@@ -1026,43 +1129,6 @@ struct buffer_context {
 typedef buffer_context<char>::type format_context;
 typedef buffer_context<wchar_t>::type wformat_context;
 
-namespace internal {
-template <typename Context, typename T>
-struct get_type {
-  typedef decltype(make_value<Context>(
-        declval<typename std::decay<T>::type&>())) value_type;
-  static const type value = value_type::type_tag;
-};
-
-template <typename Context>
-FMT_CONSTEXPR11 unsigned long long get_types() { return 0; }
-
-template <typename Context, typename Arg, typename... Args>
-FMT_CONSTEXPR11 unsigned long long get_types() {
-  return get_type<Context, Arg>::value | (get_types<Context, Args...>() << 4);
-}
-
-template <typename Context, typename T>
-FMT_CONSTEXPR basic_format_arg<Context> make_arg(const T &value) {
-  basic_format_arg<Context> arg;
-  arg.type_ = get_type<Context, T>::value;
-  arg.value_ = make_value<Context>(value);
-  return arg;
-}
-
-template <bool IS_PACKED, typename Context, typename T>
-inline typename std::enable_if<IS_PACKED, value<Context>>::type
-    make_arg(const T &value) {
-  return make_value<Context>(value);
-}
-
-template <bool IS_PACKED, typename Context, typename T>
-inline typename std::enable_if<!IS_PACKED, basic_format_arg<Context>>::type
-    make_arg(const T &value) {
-  return make_arg<Context>(value);
-}
-}  // namespace internal
-
 /**
   \rst
   An array of references to arguments. It can be implicitly converted into
@@ -1088,17 +1154,17 @@ class format_arg_store {
 
   friend class basic_format_args<Context>;
 
-  static FMT_CONSTEXPR11 long long get_types() {
+  static FMT_CONSTEXPR11 unsigned long long get_types() {
     return IS_PACKED ?
-      static_cast<long long>(internal::get_types<Context, Args...>()) :
-      -static_cast<long long>(NUM_ARGS);
+      internal::get_types<Context, Args...>() :
+      internal::is_unpacked_bit | NUM_ARGS;
   }
 
  public:
 #if FMT_USE_CONSTEXPR11
-  static FMT_CONSTEXPR11 long long TYPES = get_types();
+  static FMT_CONSTEXPR11 unsigned long long TYPES = get_types();
 #else
-  static const long long TYPES;
+  static const unsigned long long TYPES;
 #endif
 
 #if (FMT_GCC_VERSION && FMT_GCC_VERSION <= 405) || \
@@ -1117,7 +1183,8 @@ class format_arg_store {
 
 #if !FMT_USE_CONSTEXPR11
 template <typename Context, typename ...Args>
-const long long format_arg_store<Context, Args...>::TYPES = get_types();
+const unsigned long long format_arg_store<Context, Args...>::TYPES =
+    get_types();
 #endif
 
 /**
@@ -1127,17 +1194,9 @@ const long long format_arg_store<Context, Args...>::TYPES = get_types();
   can be omitted in which case it defaults to `~fmt::context`.
   \endrst
  */
-template <typename Context, typename ...Args>
+template <typename Context = format_context, typename ...Args>
 inline format_arg_store<Context, Args...>
-    make_format_args(const Args & ... args) {
-  return format_arg_store<Context, Args...>(args...);
-}
-
-template <typename ...Args>
-inline format_arg_store<format_context, Args...>
-    make_format_args(const Args & ... args) {
-  return format_arg_store<format_context, Args...>(args...);
-}
+  make_format_args(const Args &... args) { return {args...}; }
 
 /** Formatting arguments. */
 template <typename Context>
@@ -1160,11 +1219,12 @@ class basic_format_args {
     const format_arg *args_;
   };
 
+  bool is_packed() const { return (types_ & internal::is_unpacked_bit) == 0; }
+
   typename internal::type type(unsigned index) const {
     unsigned shift = index * 4;
-    unsigned long long mask = 0xf;
     return static_cast<typename internal::type>(
-      (types_ & (mask << shift)) >> shift);
+      (types_ & (0xfull << shift)) >> shift);
   }
 
   friend class internal::arg_map<Context>;
@@ -1174,10 +1234,8 @@ class basic_format_args {
 
   format_arg do_get(size_type index) const {
     format_arg arg;
-    long long signed_types = static_cast<long long>(types_);
-    if (signed_types < 0) {
-      unsigned long long num_args =
-          static_cast<unsigned long long>(-signed_types);
+    if (!is_packed()) {
+      auto num_args = max_size();
       if (index < num_args)
         arg = args_[index];
       return arg;
@@ -1212,7 +1270,7 @@ class basic_format_args {
    \endrst
    */
   basic_format_args(const format_arg *args, size_type count)
-  : types_(-static_cast<int64_t>(count)) {
+    : types_(internal::is_unpacked_bit | count) {
     set_data(args);
   }
 
@@ -1224,34 +1282,52 @@ class basic_format_args {
     return arg;
   }
 
-  unsigned max_size() const {
-    long long signed_types = static_cast<long long>(types_);
-    return static_cast<unsigned>(
-        signed_types < 0 ?
-        -signed_types : static_cast<long long>(internal::max_packed_args));
+  size_type max_size() const {
+    unsigned long long max_packed = internal::max_packed_args;
+    return static_cast<size_type>(
+      is_packed() ? max_packed : types_ & ~internal::is_unpacked_bit);
   }
 };
 
 /** An alias to ``basic_format_args<context>``. */
 // It is a separate type rather than a typedef to make symbols readable.
-struct format_args: basic_format_args<format_context> {
+struct format_args : basic_format_args<format_context> {
   template <typename ...Args>
-  format_args(Args && ... arg)
+  format_args(Args &&... arg)
   : basic_format_args<format_context>(std::forward<Args>(arg)...) {}
 };
 struct wformat_args : basic_format_args<wformat_context> {
   template <typename ...Args>
-  wformat_args(Args && ... arg)
+  wformat_args(Args &&... arg)
   : basic_format_args<wformat_context>(std::forward<Args>(arg)...) {}
 };
 
+#define FMT_ENABLE_IF_T(B, T) typename std::enable_if<B, T>::type
+
+#ifndef FMT_USE_ALIAS_TEMPLATES
+# define FMT_USE_ALIAS_TEMPLATES FMT_HAS_FEATURE(cxx_alias_templates)
+#endif
+#if FMT_USE_ALIAS_TEMPLATES
+/** String's character type. */
+template <typename S>
+using char_t = FMT_ENABLE_IF_T(
+  internal::is_string<S>::value, typename internal::char_t<S>::type);
+#define FMT_CHAR(S) fmt::char_t<S>
+#else
+template <typename S>
+struct char_t : std::enable_if<
+    internal::is_string<S>::value, typename internal::char_t<S>::type> {};
+#define FMT_CHAR(S) typename char_t<S>::type
+#endif
+
 namespace internal {
 template <typename Char>
 struct named_arg_base {
   basic_string_view<Char> name;
 
   // Serialized value<context>.
-  mutable char data[sizeof(basic_format_arg<format_context>)];
+  mutable char data[
+    sizeof(basic_format_arg<typename buffer_context<Char>::type>)];
 
   named_arg_base(basic_string_view<Char> nm) : name(nm) {}
 
@@ -1270,6 +1346,36 @@ struct named_arg : named_arg_base<Char> {
   named_arg(basic_string_view<Char> name, const T &val)
     : named_arg_base<Char>(name), value(val) {}
 };
+
+template <typename... Args, typename S>
+inline typename std::enable_if<!is_compile_string<S>::value>::type
+  check_format_string(const S &) {}
+template <typename... Args, typename S>
+typename std::enable_if<is_compile_string<S>::value>::type
+  check_format_string(S);
+
+template <typename S, typename... Args>
+struct checked_args : format_arg_store<
+  typename buffer_context<FMT_CHAR(S)>::type, Args...> {
+  typedef typename buffer_context<FMT_CHAR(S)>::type context;
+
+  checked_args(const S &format_str, const Args &... args):
+    format_arg_store<context, Args...>(args...) {
+    internal::check_format_string<Args...>(format_str);
+  }
+
+  basic_format_args<context> operator*() const { return *this; }
+};
+
+template <typename Char>
+std::basic_string<Char> vformat(
+  basic_string_view<Char> format_str,
+  basic_format_args<typename buffer_context<Char>::type> args);
+
+template <typename Char>
+typename buffer_context<Char>::type::iterator vformat_to(
+  internal::basic_buffer<Char> &buf, basic_string_view<Char> format_str,
+  basic_format_args<typename buffer_context<Char>::type> args);
 }
 
 /**
@@ -1283,142 +1389,55 @@ struct named_arg : named_arg_base<Char> {
  */
 template <typename T>
 inline internal::named_arg<T, char> arg(string_view name, const T &arg) {
-  return internal::named_arg<T, char>(name, arg);
+  return {name, arg};
 }
 
 template <typename T>
 inline internal::named_arg<T, wchar_t> arg(wstring_view name, const T &arg) {
-  return internal::named_arg<T, wchar_t>(name, arg);
+  return {name, arg};
 }
 
-// This function template is deleted intentionally to disable nested named
-// arguments as in ``format("{}", arg("a", arg("b", 42)))``.
+// Disable nested named arguments, e.g. ``arg("a", arg("b", 42))``.
 template <typename S, typename T, typename Char>
 void arg(S, internal::named_arg<T, Char>) = delete;
 
-// A base class for compile-time strings. It is defined in the fmt namespace to
-// make formatting functions visible via ADL, e.g. format(fmt("{}"), 42).
-struct compile_string {};
-
-namespace internal {
-// If S is a format string type, format_string_traints<S>::char_type gives its
-// character type.
-template <typename S, typename Enable = void>
-struct format_string_traits {
- private:
-  // Use constructability as a way to detect if format_string_traits is
-  // specialized because other methods are broken on MSVC2013.
-  format_string_traits();
-};
-
-template <typename Char>
-struct format_string_traits_base { typedef Char char_type; };
-
-template <typename Char>
-struct format_string_traits<Char *> : format_string_traits_base<Char> {};
-
-template <typename Char>
-struct format_string_traits<const Char *> : format_string_traits_base<Char> {};
-
-template <typename Char, std::size_t N>
-struct format_string_traits<Char[N]> : format_string_traits_base<Char> {};
-
-template <typename Char, std::size_t N>
-struct format_string_traits<const Char[N]> : format_string_traits_base<Char> {};
-
-template <typename Char>
-struct format_string_traits<std::basic_string<Char>> :
-    format_string_traits_base<Char> {};
-
-template <typename S>
-struct format_string_traits<
-    S, typename std::enable_if<std::is_base_of<
-         basic_string_view<typename S::char_type>, S>::value>::type> :
-    format_string_traits_base<typename S::char_type> {};
-
-template <typename S>
-struct is_format_string :
-    std::integral_constant<
-      bool, std::is_constructible<format_string_traits<S>>::value> {};
-
-template <typename S>
-struct is_compile_string :
-    std::integral_constant<bool, std::is_base_of<compile_string, S>::value> {};
-
-template <typename... Args, typename S>
-inline typename std::enable_if<!is_compile_string<S>::value>::type
-    check_format_string(const S &) {}
-template <typename... Args, typename S>
-typename std::enable_if<is_compile_string<S>::value>::type
-    check_format_string(S);
-
-template <typename Char>
-std::basic_string<Char> vformat(
-    basic_string_view<Char> format_str,
-    basic_format_args<typename buffer_context<Char>::type> args);
-}  // namespace internal
-
-format_context::iterator vformat_to(
-    internal::buffer &buf, string_view format_str, format_args args);
-wformat_context::iterator vformat_to(
-    internal::wbuffer &buf, wstring_view format_str, wformat_args args);
-
 template <typename Container>
-struct is_contiguous : std::false_type {};
+struct is_contiguous: std::false_type {};
 
 template <typename Char>
-struct is_contiguous<std::basic_string<Char>> : std::true_type {};
+struct is_contiguous<std::basic_string<Char> >: std::true_type {};
 
 template <typename Char>
-struct is_contiguous<internal::basic_buffer<Char>> : std::true_type {};
+struct is_contiguous<internal::basic_buffer<Char> >: std::true_type {};
 
 /** Formats a string and writes the output to ``out``. */
-template <typename Container>
-typename std::enable_if<
-  is_contiguous<Container>::value, std::back_insert_iterator<Container>>::type
-    vformat_to(std::back_insert_iterator<Container> out,
-               string_view format_str, format_args args) {
-  internal::container_buffer<Container> buf(internal::get_container(out));
-  vformat_to(buf, format_str, args);
-  return out;
-}
-
-template <typename Container>
+template <typename Container, typename S>
 typename std::enable_if<
-  is_contiguous<Container>::value, std::back_insert_iterator<Container>>::type
-  vformat_to(std::back_insert_iterator<Container> out,
-             wstring_view format_str, wformat_args args) {
+    is_contiguous<Container>::value, std::back_insert_iterator<Container>>::type
+  vformat_to(
+    std::back_insert_iterator<Container> out,
+    const S &format_str,
+    basic_format_args<typename buffer_context<FMT_CHAR(S)>::type> args) {
   internal::container_buffer<Container> buf(internal::get_container(out));
-  vformat_to(buf, format_str, args);
+  internal::vformat_to(buf, to_string_view(format_str), args);
   return out;
 }
 
-template <typename Container, typename... Args>
-inline typename std::enable_if<
-  is_contiguous<Container>::value, std::back_insert_iterator<Container>>::type
-    format_to(std::back_insert_iterator<Container> out,
-              string_view format_str, const Args & ... args) {
-  format_arg_store<format_context, Args...> as{args...};
-  return vformat_to(out, format_str, as);
-}
-
-template <typename Container, typename... Args>
+template <typename Container, typename S, typename... Args>
 inline typename std::enable_if<
-  is_contiguous<Container>::value, std::back_insert_iterator<Container>>::type
-    format_to(std::back_insert_iterator<Container> out,
-              wstring_view format_str, const Args & ... args) {
-  return vformat_to(out, format_str,
-                    make_format_args<wformat_context>(args...));
+  is_contiguous<Container>::value && internal::is_string<S>::value,
+  std::back_insert_iterator<Container>>::type
+    format_to(std::back_insert_iterator<Container> out, const S &format_str,
+              const Args &... args) {
+  internal::checked_args<S, Args...> ca(format_str, args...);
+  return vformat_to(out, to_string_view(format_str), *ca);
 }
 
-template <
-    typename String,
-    typename Char = typename internal::format_string_traits<String>::char_type>
+template <typename S, typename Char = FMT_CHAR(S)>
 inline std::basic_string<Char> vformat(
-    const String &format_str,
+    const S &format_str,
     basic_format_args<typename buffer_context<Char>::type> args) {
-  // Convert format string to string_view to reduce the number of overloads.
-  return internal::vformat(basic_string_view<Char>(format_str), args);
+  return internal::vformat(to_string_view(format_str), args);
 }
 
 /**
@@ -1431,19 +1450,12 @@ inline std::basic_string<Char> vformat(
     std::string message = fmt::format("The answer is {}", 42);
   \endrst
 */
-template <typename String, typename... Args>
-inline std::basic_string<
-  typename internal::format_string_traits<String>::char_type>
-    format(const String &format_str, const Args & ... args) {
-  internal::check_format_string<Args...>(format_str);
-  // This should be just
-  //   return vformat(format_str, make_format_args(args...));
-  // but gcc has trouble optimizing the latter, so break it down.
-  typedef typename internal::format_string_traits<String>::char_type char_t;
-  typedef typename buffer_context<char_t>::type context_t;
-  format_arg_store<context_t, Args...> as{args...};
+template <typename S, typename... Args>
+inline std::basic_string<FMT_CHAR(S)> format(
+    const S &format_str, const Args &... args) {
   return internal::vformat(
-      basic_string_view<char_t>(format_str), basic_format_args<context_t>(as));
+    to_string_view(format_str),
+    *internal::checked_args<S, Args...>(format_str, args...));
 }
 
 FMT_API void vprint(std::FILE *f, string_view format_str, format_args args);
@@ -1451,27 +1463,20 @@ FMT_API void vprint(std::FILE *f, wstring_view format_str, wformat_args args);
 
 /**
   \rst
-  Prints formatted data to the file *f*.
+  Prints formatted data to the file *f*. For wide format strings,
+  *f* should be in wide-oriented mode set via ``fwide(f, 1)`` or
+  ``_setmode(_fileno(f), _O_U8TEXT)`` on Windows.
 
   **Example**::
 
     fmt::print(stderr, "Don't {}!", "panic");
   \endrst
  */
-template <typename... Args>
-inline void print(std::FILE *f, string_view format_str, const Args & ... args) {
-  format_arg_store<format_context, Args...> as(args...);
-  vprint(f, format_str, as);
-}
-/**
-  Prints formatted data to the file *f* which should be in wide-oriented mode
-  set via ``fwide(f, 1)`` or ``_setmode(_fileno(f), _O_U8TEXT)`` on Windows.
- */
-template <typename... Args>
-inline void print(std::FILE *f, wstring_view format_str,
-                  const Args & ... args) {
-  format_arg_store<wformat_context, Args...> as(args...);
-  vprint(f, format_str, as);
+template <typename S, typename... Args>
+inline FMT_ENABLE_IF_T(internal::is_string<S>::value, void)
+    print(std::FILE *f, const S &format_str, const Args &... args) {
+  vprint(f, to_string_view(format_str),
+         internal::checked_args<S, Args...>(format_str, args...));
 }
 
 FMT_API void vprint(string_view format_str, format_args args);
@@ -1486,16 +1491,11 @@ FMT_API void vprint(wstring_view format_str, wformat_args args);
     fmt::print("Elapsed time: {0:.2f} seconds", 1.23);
   \endrst
  */
-template <typename... Args>
-inline void print(string_view format_str, const Args & ... args) {
-  format_arg_store<format_context, Args...> as{args...};
-  vprint(format_str, as);
-}
-
-template <typename... Args>
-inline void print(wstring_view format_str, const Args & ... args) {
-  format_arg_store<wformat_context, Args...> as(args...);
-  vprint(format_str, as);
+template <typename S, typename... Args>
+inline FMT_ENABLE_IF_T(internal::is_string<S>::value, void)
+    print(const S &format_str, const Args &... args) {
+  vprint(to_string_view(format_str),
+         internal::checked_args<S, Args...>(format_str, args...));
 }
 FMT_END_NAMESPACE
 
diff --git a/external/spdlog/fmt/bundled/format-inl.h b/external/spdlog/fmt/bundled/format-inl.h
index 56c4d581d..552c94303 100644
--- a/external/spdlog/fmt/bundled/format-inl.h
+++ b/external/spdlog/fmt/bundled/format-inl.h
@@ -136,12 +136,14 @@ int safe_strerror(
             ERANGE : result;
     }
 
+#if !FMT_MSC_VER
     // Fallback to strerror if strerror_r and strerror_s are not available.
     int fallback(internal::null<>) {
       errno = 0;
       buffer_ = strerror(error_code_);
       return errno;
     }
+#endif
 
    public:
     dispatcher(int err_code, char *&buf, std::size_t buf_size)
@@ -170,7 +172,7 @@ void format_error_code(internal::buffer &out, int error_code,
     abs_value = 0 - abs_value;
     ++error_code_size;
   }
-  error_code_size += internal::count_digits(abs_value);
+  error_code_size += internal::to_unsigned(internal::count_digits(abs_value));
   writer w(out);
   if (message.size() <= inline_buffer_size - error_code_size) {
     w.write(message);
@@ -192,34 +194,39 @@ void report_error(FormatFunc func, int error_code,
 }
 }  // namespace
 
-#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
-class locale {
- private:
-  std::locale locale_;
-
- public:
-  explicit locale(std::locale loc = std::locale()) : locale_(loc) {}
-  std::locale get() { return locale_; }
-};
-
-FMT_FUNC size_t internal::count_code_points(u8string_view s) {
+FMT_FUNC size_t internal::count_code_points(basic_string_view<char8_t> s) {
   const char8_t *data = s.data();
-  int num_code_points = 0;
+  size_t num_code_points = 0;
   for (size_t i = 0, size = s.size(); i != size; ++i) {
-    if ((data[i].value & 0xc0) != 0x80)
+    if ((data[i] & 0xc0) != 0x80)
       ++num_code_points;
   }
   return num_code_points;
 }
 
+#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
+namespace internal {
+
+template <typename Locale>
+locale_ref::locale_ref(const Locale &loc) : locale_(&loc) {
+  static_assert(std::is_same<Locale, std::locale>::value, "");
+}
+
+template <typename Locale>
+Locale locale_ref::get() const {
+  static_assert(std::is_same<Locale, std::locale>::value, "");
+  return locale_ ? *static_cast<const std::locale*>(locale_) : std::locale();
+}
+
 template <typename Char>
-FMT_FUNC Char internal::thousands_sep(locale_provider *lp) {
-  std::locale loc = lp ? lp->locale().get() : std::locale();
-  return std::use_facet<std::numpunct<Char>>(loc).thousands_sep();
+FMT_FUNC Char thousands_sep_impl(locale_ref loc) {
+  return std::use_facet<std::numpunct<Char> >(
+    loc.get<std::locale>()).thousands_sep();
+}
 }
 #else
 template <typename Char>
-FMT_FUNC Char internal::thousands_sep(locale_provider *lp) {
+FMT_FUNC Char internal::thousands_sep_impl(locale_ref) {
   return FMT_STATIC_THOUSANDS_SEPARATOR;
 }
 #endif
@@ -236,19 +243,19 @@ FMT_FUNC void system_error::init(
 namespace internal {
 template <typename T>
 int char_traits<char>::format_float(
-    char *buffer, std::size_t size, const char *format, int precision, T value) {
+    char *buf, std::size_t size, const char *format, int precision, T value) {
   return precision < 0 ?
-      FMT_SNPRINTF(buffer, size, format, value) :
-      FMT_SNPRINTF(buffer, size, format, precision, value);
+      FMT_SNPRINTF(buf, size, format, value) :
+      FMT_SNPRINTF(buf, size, format, precision, value);
 }
 
 template <typename T>
 int char_traits<wchar_t>::format_float(
-    wchar_t *buffer, std::size_t size, const wchar_t *format, int precision,
+    wchar_t *buf, std::size_t size, const wchar_t *format, int precision,
     T value) {
   return precision < 0 ?
-      FMT_SWPRINTF(buffer, size, format, value) :
-      FMT_SWPRINTF(buffer, size, format, precision, value);
+      FMT_SWPRINTF(buf, size, format, value) :
+      FMT_SWPRINTF(buf, size, format, precision, value);
 }
 
 template <typename T>
@@ -337,6 +344,8 @@ const int16_t basic_data<T>::POW10_EXPONENTS[] = {
     827,   853,   880,   907,   933,   960,   986,  1013,  1039,  1066
 };
 
+template <typename T> const char basic_data<T>::FOREGROUND_COLOR[] = "\x1b[38;2;";
+template <typename T> const char basic_data<T>::BACKGROUND_COLOR[] = "\x1b[48;2;";
 template <typename T> const char basic_data<T>::RESET_COLOR[] = "\x1b[0m";
 template <typename T> const wchar_t basic_data<T>::WRESET_COLOR[] = L"\x1b[0m";
 
@@ -363,7 +372,7 @@ class fp {
     sizeof(significand_type) * char_size;
 
   fp(): f(0), e(0) {}
-  fp(uint64_t f, int e): f(f), e(e) {}
+  fp(uint64_t f_val, int e_val): f(f_val), e(e_val) {}
 
   // Constructs fp from an IEEE754 double. It is a template to prevent compile
   // errors on platforms where double is not IEEE754.
@@ -454,19 +463,28 @@ FMT_FUNC fp get_cached_power(int min_exponent, int &pow10_exponent) {
   return fp(data::POW10_SIGNIFICANDS[index], data::POW10_EXPONENTS[index]);
 }
 
+FMT_FUNC bool grisu2_round(
+    char *buf, int &size, int max_digits, uint64_t delta,
+    uint64_t remainder, uint64_t exp, uint64_t diff, int &exp10) {
+  while (remainder < diff && delta - remainder >= exp &&
+        (remainder + exp < diff || diff - remainder > remainder + exp - diff)) {
+    --buf[size - 1];
+    remainder += exp;
+  }
+  if (size > max_digits) {
+    --size;
+    ++exp10;
+    if (buf[size] >= '5')
+      return false;
+  }
+  return true;
+}
+
 // Generates output using Grisu2 digit-gen algorithm.
-FMT_FUNC void grisu2_gen_digits(
-    const fp &scaled_value, const fp &scaled_upper, uint64_t delta,
-    char *buffer, size_t &size, int &dec_exp) {
-  internal::fp one(1ull << -scaled_upper.e, scaled_upper.e);
-  // hi (p1 in Grisu) contains the most significant digits of scaled_upper.
-  // hi = floor(scaled_upper / one).
-  uint32_t hi = static_cast<uint32_t>(scaled_upper.f >> -one.e);
-  // lo (p2 in Grisu) contains the least significants digits of scaled_upper.
-  // lo = scaled_upper mod 1.
-  uint64_t lo = scaled_upper.f & (one.f - 1);
-  size = 0;
-  auto exp = count_digits(hi);  // kappa in Grisu.
+FMT_FUNC bool grisu2_gen_digits(
+    char *buf, int &size, uint32_t hi, uint64_t lo, int &exp,
+    uint64_t delta, const fp &one, const fp &diff, int max_digits) {
+  // Generate digits for the most significant part (hi).
   while (exp > 0) {
     uint32_t digit = 0;
     // This optimization by miloyip reduces the number of integer divisions by
@@ -486,208 +504,304 @@ FMT_FUNC void grisu2_gen_digits(
       FMT_ASSERT(false, "invalid number of digits");
     }
     if (digit != 0 || size != 0)
-      buffer[size++] = static_cast<char>('0' + digit);
+      buf[size++] = static_cast<char>('0' + digit);
     --exp;
     uint64_t remainder = (static_cast<uint64_t>(hi) << -one.e) + lo;
-    if (remainder <= delta) {
-      dec_exp += exp;
-      // TODO: use scaled_value
-      (void)scaled_value;
-      return;
+    if (remainder <= delta || size > max_digits) {
+      return grisu2_round(
+            buf, size, max_digits, delta, remainder,
+            static_cast<uint64_t>(data::POWERS_OF_10_32[exp]) << -one.e,
+            diff.f, exp);
     }
   }
+  // Generate digits for the least significant part (lo).
   for (;;) {
     lo *= 10;
     delta *= 10;
     char digit = static_cast<char>(lo >> -one.e);
     if (digit != 0 || size != 0)
-      buffer[size++] = static_cast<char>('0' + digit);
+      buf[size++] = static_cast<char>('0' + digit);
     lo &= one.f - 1;
     --exp;
-    if (lo < delta) {
-      dec_exp += exp;
-      return;
+    if (lo < delta || size > max_digits) {
+      return grisu2_round(buf, size, max_digits, delta, lo, one.f,
+                          diff.f * data::POWERS_OF_10_32[-exp], exp);
     }
   }
 }
 
-FMT_FUNC void grisu2_format_positive(double value, char *buffer, size_t &size,
-                                     int &dec_exp) {
-  FMT_ASSERT(value > 0, "value is nonpositive");
-  fp fp_value(value);
-  fp lower, upper;  // w^- and w^+ in the Grisu paper.
-  fp_value.compute_boundaries(lower, upper);
-  // Find a cached power of 10 close to 1 / upper.
-  const int min_exp = -60;  // alpha in Grisu.
-  auto dec_pow = get_cached_power(  // \tilde{c}_{-k} in Grisu.
-      min_exp - (upper.e + fp::significand_size), dec_exp);
-  dec_exp = -dec_exp;
-  fp_value.normalize();
-  fp scaled_value = fp_value * dec_pow;
-  fp scaled_lower = lower * dec_pow;  // \tilde{M}^- in Grisu.
-  fp scaled_upper = upper * dec_pow;  // \tilde{M}^+ in Grisu.
-  ++scaled_lower.f;  // \tilde{M}^- + 1 ulp -> M^-_{\uparrow}.
-  --scaled_upper.f;  // \tilde{M}^+ - 1 ulp -> M^+_{\downarrow}.
-  uint64_t delta = scaled_upper.f - scaled_lower.f;
-  grisu2_gen_digits(scaled_value, scaled_upper, delta, buffer, size, dec_exp);
-}
-
-FMT_FUNC void round(char *buffer, size_t &size, int &exp,
-                    int digits_to_remove) {
-  size -= to_unsigned(digits_to_remove);
-  exp += digits_to_remove;
-  int digit = buffer[size] - '0';
-  // TODO: proper rounding and carry
-  if (digit > 5 || (digit == 5 && (digits_to_remove > 1 ||
-                                   (buffer[size - 1] - '0') % 2) != 0)) {
-    ++buffer[size - 1];
+#if FMT_CLANG_VERSION
+# define FMT_FALLTHROUGH [[clang::fallthrough]];
+#elif FMT_GCC_VERSION >= 700
+# define FMT_FALLTHROUGH [[gnu::fallthrough]];
+#else
+# define FMT_FALLTHROUGH
+#endif
+
+struct gen_digits_params {
+  int num_digits;
+  bool fixed;
+  bool upper;
+  bool trailing_zeros;
+};
+
+struct prettify_handler {
+  char *data;
+  ptrdiff_t size;
+  buffer &buf;
+
+  explicit prettify_handler(buffer &b, ptrdiff_t n)
+    : data(b.data()), size(n), buf(b) {}
+  ~prettify_handler() {
+    assert(buf.size() >= to_unsigned(size));
+    buf.resize(to_unsigned(size));
   }
-}
 
-// Writes the exponent exp in the form "[+-]d{1,3}" to buffer.
-FMT_FUNC char *write_exponent(char *buffer, int exp) {
+  template <typename F>
+  void insert(ptrdiff_t pos, ptrdiff_t n, F f) {
+    std::memmove(data + pos + n, data + pos, to_unsigned(size - pos));
+    f(data + pos);
+    size += n;
+  }
+
+  void insert(ptrdiff_t pos, char c) {
+    std::memmove(data + pos + 1, data + pos, to_unsigned(size - pos));
+    data[pos] = c;
+    ++size;
+  }
+
+  void append(ptrdiff_t n, char c) {
+    std::uninitialized_fill_n(data + size, n, c);
+    size += n;
+  }
+
+  void append(char c) { data[size++] = c; }
+
+  void remove_trailing(char c) {
+    while (data[size - 1] == c) --size;
+  }
+};
+
+// Writes the exponent exp in the form "[+-]d{2,3}" to buffer.
+template <typename Handler>
+FMT_FUNC void write_exponent(int exp, Handler &&h) {
   FMT_ASSERT(-1000 < exp && exp < 1000, "exponent out of range");
   if (exp < 0) {
-    *buffer++ = '-';
+    h.append('-');
     exp = -exp;
   } else {
-    *buffer++ = '+';
+    h.append('+');
   }
   if (exp >= 100) {
-    *buffer++ = static_cast<char>('0' + exp / 100);
+    h.append(static_cast<char>('0' + exp / 100));
     exp %= 100;
     const char *d = data::DIGITS + exp * 2;
-    *buffer++ = d[0];
-    *buffer++ = d[1];
+    h.append(d[0]);
+    h.append(d[1]);
   } else {
     const char *d = data::DIGITS + exp * 2;
-    *buffer++ = d[0];
-    *buffer++ = d[1];
+    h.append(d[0]);
+    h.append(d[1]);
   }
-  return buffer;
-}
-
-FMT_FUNC void format_exp_notation(
-    char *buffer, size_t &size, int exp, int precision, bool upper) {
-  // Insert a decimal point after the first digit and add an exponent.
-  std::memmove(buffer + 2, buffer + 1, size - 1);
-  buffer[1] = '.';
-  exp += static_cast<int>(size) - 1;
-  int num_digits = precision - static_cast<int>(size) + 1;
-  if (num_digits > 0) {
-    std::uninitialized_fill_n(buffer + size + 1, num_digits, '0');
-    size += to_unsigned(num_digits);
-  } else if (num_digits < 0) {
-    round(buffer, size, exp, -num_digits);
-  }
-  char *p = buffer + size + 1;
-  *p++ = upper ? 'E' : 'e';
-  size = to_unsigned(write_exponent(p, exp) - buffer);
 }
 
-// Prettifies the output of the Grisu2 algorithm.
-// The number is given as v = buffer * 10^exp.
-FMT_FUNC void grisu2_prettify(char *buffer, size_t &size, int exp,
-                              int precision, bool upper) {
+struct fill {
+  size_t n;
+  void operator()(char *buf) const {
+    buf[0] = '0';
+    buf[1] = '.';
+    std::uninitialized_fill_n(buf + 2, n, '0');
+  }
+};
+
+// The number is given as v = f * pow(10, exp), where f has size digits.
+template <typename Handler>
+FMT_FUNC void grisu2_prettify(const gen_digits_params &params,
+                              int size, int exp, Handler &&handler) {
+  if (!params.fixed) {
+    // Insert a decimal point after the first digit and add an exponent.
+    handler.insert(1, '.');
+    exp += size - 1;
+    if (size < params.num_digits)
+      handler.append(params.num_digits - size, '0');
+    handler.append(params.upper ? 'E' : 'e');
+    write_exponent(exp, handler);
+    return;
+  }
   // pow(10, full_exp - 1) <= v <= pow(10, full_exp).
-  int int_size = static_cast<int>(size);
-  int full_exp = int_size + exp;
+  int full_exp = size + exp;
   const int exp_threshold = 21;
-  if (int_size <= full_exp && full_exp <= exp_threshold) {
+  if (size <= full_exp && full_exp <= exp_threshold) {
     // 1234e7 -> 12340000000[.0+]
-    std::uninitialized_fill_n(buffer + int_size, full_exp - int_size, '0');
-    char *p = buffer + full_exp;
-    if (precision > 0) {
-      *p++ = '.';
-      std::uninitialized_fill_n(p, precision, '0');
-      p += precision;
+    handler.append(full_exp - size, '0');
+    int num_zeros = params.num_digits - full_exp;
+    if (num_zeros > 0 && params.trailing_zeros) {
+      handler.append('.');
+      handler.append(num_zeros, '0');
     }
-    size = to_unsigned(p - buffer);
-  } else if (0 < full_exp && full_exp <= exp_threshold) {
+  } else if (full_exp > 0) {
     // 1234e-2 -> 12.34[0+]
-    int fractional_size = -exp;
-    std::memmove(buffer + full_exp + 1, buffer + full_exp,
-                 to_unsigned(fractional_size));
-    buffer[full_exp] = '.';
-    int num_zeros = precision - fractional_size;
-    if (num_zeros > 0) {
-      std::uninitialized_fill_n(buffer + size + 1, num_zeros, '0');
-      size += to_unsigned(num_zeros);
+    handler.insert(full_exp, '.');
+    if (!params.trailing_zeros) {
+      // Remove trailing zeros.
+      handler.remove_trailing('0');
+    } else if (params.num_digits > size) {
+      // Add trailing zeros.
+      ptrdiff_t num_zeros = params.num_digits - size;
+      handler.append(num_zeros, '0');
     }
-    ++size;
-  } else if (-6 < full_exp && full_exp <= 0) {
-    // 1234e-6 -> 0.001234
-    int offset = 2 - full_exp;
-    std::memmove(buffer + offset, buffer, size);
-    buffer[0] = '0';
-    buffer[1] = '.';
-    std::uninitialized_fill_n(buffer + 2, -full_exp, '0');
-    size = to_unsigned(int_size + offset);
   } else {
-    format_exp_notation(buffer, size, exp, precision, upper);
+    // 1234e-6 -> 0.001234
+    handler.insert(0, 2 - full_exp, fill{to_unsigned(-full_exp)});
   }
 }
 
-#if FMT_CLANG_VERSION
-# define FMT_FALLTHROUGH [[clang::fallthrough]];
-#elif FMT_GCC_VERSION >= 700
-# define FMT_FALLTHROUGH [[gnu::fallthrough]];
-#else
-# define FMT_FALLTHROUGH
-#endif
+struct char_counter {
+  ptrdiff_t size;
 
-// Formats a nonnegative value using Grisu2 algorithm. Grisu2 doesn't give any
-// guarantees on the shortness of the result.
-FMT_FUNC void grisu2_format(double value, char *buffer, size_t &size, char type,
-                            int precision, bool write_decimal_point) {
-  FMT_ASSERT(value >= 0, "value is negative");
-  int dec_exp = 0;  // K in Grisu.
-  if (value > 0) {
-    grisu2_format_positive(value, buffer, size, dec_exp);
-  } else {
-    *buffer = '0';
-    size = 1;
-  }
-  const int default_precision = 6;
-  if (precision < 0)
-    precision = default_precision;
-  bool upper = false;
-  switch (type) {
+  template <typename F>
+  void insert(ptrdiff_t, ptrdiff_t n, F) { size += n; }
+  void insert(ptrdiff_t, char) { ++size; }
+  void append(ptrdiff_t n, char) { size += n; }
+  void append(char) { ++size; }
+  void remove_trailing(char) {}
+};
+
+// Converts format specifiers into parameters for digit generation and computes
+// output buffer size for a number in the range [pow(10, exp - 1), pow(10, exp)
+// or 0 if exp == 1.
+FMT_FUNC gen_digits_params process_specs(const core_format_specs &specs,
+                                         int exp, buffer &buf) {
+  auto params = gen_digits_params();
+  int num_digits = specs.precision >= 0 ? specs.precision : 6;
+  switch (specs.type) {
   case 'G':
-    upper = true;
+    params.upper = true;
     FMT_FALLTHROUGH
-  case '\0': case 'g': {
-    int digits_to_remove = static_cast<int>(size) - precision;
-    if (digits_to_remove > 0) {
-      round(buffer, size, dec_exp, digits_to_remove);
-      // Remove trailing zeros.
-      while (size > 0 && buffer[size - 1] == '0') {
-        --size;
-        ++dec_exp;
-      }
+  case '\0': case 'g':
+    params.trailing_zeros = (specs.flags & HASH_FLAG) != 0;
+    if (-4 <= exp && exp < num_digits + 1) {
+      params.fixed = true;
+      if (!specs.type && params.trailing_zeros && exp >= 0)
+        num_digits = exp + 1;
     }
-    precision = 0;
     break;
-  }
   case 'F':
-    upper = true;
+    params.upper = true;
     FMT_FALLTHROUGH
   case 'f': {
-    int digits_to_remove = -dec_exp - precision;
-    if (digits_to_remove > 0) {
-      if (digits_to_remove >= static_cast<int>(size))
-        digits_to_remove = static_cast<int>(size) - 1;
-      round(buffer, size, dec_exp, digits_to_remove);
-    }
+    params.fixed = true;
+    params.trailing_zeros = true;
+    int adjusted_min_digits = num_digits + exp;
+    if (adjusted_min_digits > 0)
+      num_digits = adjusted_min_digits;
     break;
   }
-  case 'e': case 'E':
-    format_exp_notation(buffer, size, dec_exp, precision, type == 'E');
-    return;
+  case 'E':
+    params.upper = true;
+    FMT_FALLTHROUGH
+  case 'e':
+    ++num_digits;
+    break;
+  }
+  params.num_digits = num_digits;
+  char_counter counter{num_digits};
+  grisu2_prettify(params, params.num_digits, exp - num_digits, counter);
+  buf.resize(to_unsigned(counter.size));
+  return params;
+}
+
+template <typename Double>
+FMT_FUNC typename std::enable_if<sizeof(Double) == sizeof(uint64_t), bool>::type
+    grisu2_format(Double value, buffer &buf, core_format_specs specs) {
+  FMT_ASSERT(value >= 0, "value is negative");
+  if (value == 0) {
+    gen_digits_params params = process_specs(specs, 1, buf);
+    const size_t size = 1;
+    buf[0] = '0';
+    grisu2_prettify(params, size, 0, prettify_handler(buf, size));
+    return true;
+  }
+
+  fp fp_value(value);
+  fp lower, upper;  // w^- and w^+ in the Grisu paper.
+  fp_value.compute_boundaries(lower, upper);
+
+  // Find a cached power of 10 close to 1 / upper and use it to scale upper.
+  const int min_exp = -60;  // alpha in Grisu.
+  int cached_exp = 0;  // K in Grisu.
+  auto cached_pow = get_cached_power(  // \tilde{c}_{-k} in Grisu.
+      min_exp - (upper.e + fp::significand_size), cached_exp);
+  cached_exp = -cached_exp;
+  upper = upper * cached_pow;  // \tilde{M}^+ in Grisu.
+  --upper.f;  // \tilde{M}^+ - 1 ulp -> M^+_{\downarrow}.
+  fp one(1ull << -upper.e, upper.e);
+  // hi (p1 in Grisu) contains the most significant digits of scaled_upper.
+  // hi = floor(upper / one).
+  uint32_t hi = static_cast<uint32_t>(upper.f >> -one.e);
+  int exp = count_digits(hi);  // kappa in Grisu.
+  gen_digits_params params = process_specs(specs, cached_exp + exp, buf);
+  fp_value.normalize();
+  fp scaled_value = fp_value * cached_pow;
+  lower = lower * cached_pow;  // \tilde{M}^- in Grisu.
+  ++lower.f;  // \tilde{M}^- + 1 ulp -> M^-_{\uparrow}.
+  uint64_t delta = upper.f - lower.f;
+  fp diff = upper - scaled_value; // wp_w in Grisu.
+  // lo (p2 in Grisu) contains the least significants digits of scaled_upper.
+  // lo = supper % one.
+  uint64_t lo = upper.f & (one.f - 1);
+  int size = 0;
+  if (!grisu2_gen_digits(buf.data(), size, hi, lo, exp, delta, one, diff,
+                         params.num_digits)) {
+    buf.clear();
+    return false;
+  }
+  grisu2_prettify(params, size, cached_exp + exp, prettify_handler(buf, size));
+  return true;
+}
+
+template <typename Double>
+void sprintf_format(Double value, internal::buffer &buf,
+                    core_format_specs spec) {
+  // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
+  FMT_ASSERT(buf.capacity() != 0, "empty buffer");
+
+  // Build format string.
+  enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg
+  char format[MAX_FORMAT_SIZE];
+  char *format_ptr = format;
+  *format_ptr++ = '%';
+  if (spec.has(HASH_FLAG))
+    *format_ptr++ = '#';
+  if (spec.precision >= 0) {
+    *format_ptr++ = '.';
+    *format_ptr++ = '*';
+  }
+  if (std::is_same<Double, long double>::value)
+    *format_ptr++ = 'L';
+  *format_ptr++ = spec.type;
+  *format_ptr = '\0';
+
+  // Format using snprintf.
+  char *start = FMT_NULL;
+  for (;;) {
+    std::size_t buffer_size = buf.capacity();
+    start = &buf[0];
+    int result = internal::char_traits<char>::format_float(
+        start, buffer_size, format, spec.precision, value);
+    if (result >= 0) {
+      unsigned n = internal::to_unsigned(result);
+      if (n < buf.capacity()) {
+        buf.resize(n);
+        break;  // The buffer is large enough - continue with formatting.
+      }
+      buf.reserve(n + 1);
+    } else {
+      // If result is negative we ask to increase the capacity by at least 1,
+      // but as std::vector, the buffer grows exponentially.
+      buf.reserve(buf.capacity() + 1);
+    }
   }
-  if (write_decimal_point && precision < 1)
-    precision = 1;
-  grisu2_prettify(buffer, size, dec_exp, precision, upper);
 }
 }  // namespace internal
 
@@ -812,11 +926,6 @@ FMT_FUNC void format_system_error(
   format_error_code(out, error_code, message);
 }
 
-template <typename Char>
-void basic_fixed_buffer<Char>::grow(std::size_t) {
-  FMT_THROW(std::runtime_error("buffer overflow"));
-}
-
 FMT_FUNC void internal::error_handler::on_error(const char *message) {
   FMT_THROW(format_error(message));
 }
@@ -835,13 +944,14 @@ FMT_FUNC void report_windows_error(
 
 FMT_FUNC void vprint(std::FILE *f, string_view format_str, format_args args) {
   memory_buffer buffer;
-  vformat_to(buffer, format_str, args);
+  internal::vformat_to(buffer, format_str,
+                       basic_format_args<buffer_context<char>::type>(args));
   std::fwrite(buffer.data(), 1, buffer.size(), f);
 }
 
 FMT_FUNC void vprint(std::FILE *f, wstring_view format_str, wformat_args args) {
   wmemory_buffer buffer;
-  vformat_to(buffer, format_str, args);
+  internal::vformat_to(buffer, format_str, args);
   std::fwrite(buffer.data(), sizeof(wchar_t), buffer.size(), f);
 }
 
@@ -853,10 +963,6 @@ FMT_FUNC void vprint(wstring_view format_str, wformat_args args) {
   vprint(stdout, format_str, args);
 }
 
-#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
-FMT_FUNC locale locale_provider::locale() { return fmt::locale(); }
-#endif
-
 FMT_END_NAMESPACE
 
 #ifdef _MSC_VER
diff --git a/external/spdlog/fmt/bundled/format.h b/external/spdlog/fmt/bundled/format.h
index 9f522f39b..1bb24a529 100644
--- a/external/spdlog/fmt/bundled/format.h
+++ b/external/spdlog/fmt/bundled/format.h
@@ -66,9 +66,9 @@
 // many valid cases.
 # pragma GCC diagnostic ignored "-Wshadow"
 
-// Disable the warning about implicit conversions that may change the sign of
-// an integer; silencing it otherwise would require many explicit casts.
-# pragma GCC diagnostic ignored "-Wsign-conversion"
+// Disable the warning about nonliteral format strings because we construct
+// them dynamically when falling back to snprintf for FP formatting.
+# pragma GCC diagnostic ignored "-Wformat-nonliteral"
 #endif
 
 # if FMT_CLANG_VERSION
@@ -163,6 +163,7 @@ FMT_END_NAMESPACE
 
 #ifndef FMT_USE_GRISU
 # define FMT_USE_GRISU 0
+//# define FMT_USE_GRISU std::numeric_limits<double>::is_iec559
 #endif
 
 // __builtin_clz is broken in clang with Microsoft CodeGen:
@@ -177,14 +178,6 @@ FMT_END_NAMESPACE
 # endif
 #endif
 
-// A workaround for gcc 4.4 that doesn't support union members with ctors.
-#if (FMT_GCC_VERSION && FMT_GCC_VERSION <= 404) || \
-    (FMT_MSC_VER && FMT_MSC_VER <= 1800)
-# define FMT_UNION struct
-#else
-# define FMT_UNION union
-#endif
-
 // Some compilers masquerade as both MSVC and GCC-likes or otherwise support
 // __builtin_clz and __builtin_clzll, so only define FMT_BUILTIN_CLZ using the
 // MSVC intrinsics if the clz and clzll builtins are not available.
@@ -278,24 +271,13 @@ struct dummy_int {
 };
 typedef std::numeric_limits<internal::dummy_int> fputil;
 
-// Dummy implementations of system functions such as signbit and ecvt called
-// if the latter are not available.
-inline dummy_int signbit(...) { return dummy_int(); }
-inline dummy_int _ecvt_s(...) { return dummy_int(); }
+// Dummy implementations of system functions called if the latter are not
+// available.
 inline dummy_int isinf(...) { return dummy_int(); }
 inline dummy_int _finite(...) { return dummy_int(); }
 inline dummy_int isnan(...) { return dummy_int(); }
 inline dummy_int _isnan(...) { return dummy_int(); }
 
-inline bool use_grisu() {
-  return FMT_USE_GRISU && std::numeric_limits<double>::is_iec559;
-}
-
-// Formats value using Grisu2 algorithm:
-// https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf
-FMT_API void grisu2_format(double value, char *buffer, size_t &size, char type,
-                           int precision, bool write_decimal_point);
-
 template <typename Allocator>
 typename Allocator::value_type *allocate(Allocator& alloc, std::size_t n) {
 #if __cplusplus >= 201103L || FMT_MSC_VER >= 1700
@@ -316,7 +298,7 @@ namespace std {
 // Standard permits specialization of std::numeric_limits. This specialization
 // is used to resolve ambiguity between isinf and std::isinf in glibc:
 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=48891
-// and the same for isnan and signbit.
+// and the same for isnan.
 template <>
 class numeric_limits<fmt::internal::dummy_int> :
     public std::numeric_limits<int> {
@@ -327,7 +309,7 @@ class numeric_limits<fmt::internal::dummy_int> :
     using namespace fmt::internal;
     // The resolution "priority" is:
     // isinf macro > std::isinf > ::isinf > fmt::internal::isinf
-    if (const_check(sizeof(isinf(x)) != sizeof(dummy_int)))
+    if (const_check(sizeof(isinf(x)) != sizeof(fmt::internal::dummy_int)))
       return isinf(x) != 0;
     return !_finite(static_cast<double>(x));
   }
@@ -340,19 +322,6 @@ class numeric_limits<fmt::internal::dummy_int> :
       return isnan(x) != 0;
     return _isnan(static_cast<double>(x)) != 0;
   }
-
-  // Portable version of signbit.
-  static bool isnegative(double x) {
-    using namespace fmt::internal;
-    if (const_check(sizeof(signbit(x)) != sizeof(fmt::internal::dummy_int)))
-      return signbit(x) != 0;
-    if (x < 0) return true;
-    if (!isnotanumber(x)) return false;
-    int dec = 0, sign = 0;
-    char buffer[2];  // The buffer size must be >= 2 or _ecvt_s will fail.
-    _ecvt_s(buffer, sizeof(buffer), x, 0, &dec, &sign);
-    return sign != 0;
-  }
 };
 }  // namespace std
 
@@ -431,48 +400,32 @@ void basic_buffer<T>::append(const U *begin, const U *end) {
 }
 }  // namespace internal
 
+// C++20 feature test, since r346892 Clang considers char8_t a fundamental
+// type in this mode. If this is the case __cpp_char8_t will be defined.
+#if !defined(__cpp_char8_t)
 // A UTF-8 code unit type.
-struct char8_t {
-  char value;
-  FMT_CONSTEXPR explicit operator bool() const FMT_NOEXCEPT {
-    return value != 0;
-  }
-};
+enum char8_t: unsigned char {};
+#endif
 
 // A UTF-8 string view.
 class u8string_view : public basic_string_view<char8_t> {
- private:
-  typedef basic_string_view<char8_t> base;
-
  public:
-  using basic_string_view::basic_string_view;
-  using basic_string_view::char_type;
-
-  u8string_view(const char *s)
-    : base(reinterpret_cast<const char8_t*>(s)) {}
+  typedef char8_t char_type;
 
-  u8string_view(const char *s, size_t count) FMT_NOEXCEPT
-    : base(reinterpret_cast<const char8_t*>(s), count) {}
+  u8string_view(const char *s):
+    basic_string_view<char8_t>(reinterpret_cast<const char8_t*>(s)) {}
+  u8string_view(const char *s, size_t count) FMT_NOEXCEPT:
+    basic_string_view<char8_t>(reinterpret_cast<const char8_t*>(s), count) {}
 };
 
 #if FMT_USE_USER_DEFINED_LITERALS
 inline namespace literals {
 inline u8string_view operator"" _u(const char *s, std::size_t n) {
-  return u8string_view(s, n);
+  return {s, n};
 }
 }
 #endif
 
-// A wrapper around std::locale used to reduce compile times since <locale>
-// is very heavy.
-class locale;
-
-class locale_provider {
- public:
-  virtual ~locale_provider() {}
-  virtual fmt::locale locale();
-};
-
 // The number of characters to store in the basic_memory_buffer object itself
 // to avoid dynamic memory allocation.
 enum { inline_buffer_size = 500 };
@@ -497,7 +450,7 @@ enum { inline_buffer_size = 500 };
      fmt::memory_buffer out;
      format_to(out, "The answer is {}.", 42);
 
-  This will write the following output to the ``out`` object:
+  This will append the following output to the ``out`` object:
 
   .. code-block:: none
 
@@ -522,6 +475,9 @@ class basic_memory_buffer: private Allocator, public internal::basic_buffer<T> {
   void grow(std::size_t size) FMT_OVERRIDE;
 
  public:
+  typedef T value_type;
+  typedef const T &const_reference;
+
   explicit basic_memory_buffer(const Allocator &alloc = Allocator())
       : Allocator(alloc) {
     this->set(store_, SIZE);
@@ -597,43 +553,6 @@ void basic_memory_buffer<T, SIZE, Allocator>::grow(std::size_t size) {
 typedef basic_memory_buffer<char> memory_buffer;
 typedef basic_memory_buffer<wchar_t> wmemory_buffer;
 
-/**
-  \rst
-  A fixed-size memory buffer. For a dynamically growing buffer use
-  :class:`fmt::basic_memory_buffer`.
-
-  Trying to increase the buffer size past the initial capacity will throw
-  ``std::runtime_error``.
-  \endrst
- */
-template <typename Char>
-class basic_fixed_buffer : public internal::basic_buffer<Char> {
- public:
-  /**
-   \rst
-   Constructs a :class:`fmt::basic_fixed_buffer` object for *array* of the
-   given size.
-   \endrst
-   */
-  basic_fixed_buffer(Char *array, std::size_t size) {
-    this->set(array, size);
-  }
-
-  /**
-   \rst
-   Constructs a :class:`fmt::basic_fixed_buffer` object for *array* of the
-   size known at compile time.
-   \endrst
-   */
-  template <std::size_t SIZE>
-  explicit basic_fixed_buffer(Char (&array)[SIZE]) {
-    this->set(array, SIZE);
-  }
-
- protected:
-  FMT_API void grow(std::size_t size) FMT_OVERRIDE;
-};
-
 namespace internal {
 
 template <typename Char>
@@ -690,98 +609,6 @@ class null_terminating_iterator;
 template <typename Char>
 FMT_CONSTEXPR_DECL const Char *pointer_from(null_terminating_iterator<Char> it);
 
-// An iterator that produces a null terminator on *end. This simplifies parsing
-// and allows comparing the performance of processing a null-terminated string
-// vs string_view.
-template <typename Char>
-class null_terminating_iterator {
- public:
-  typedef std::ptrdiff_t difference_type;
-  typedef Char value_type;
-  typedef const Char* pointer;
-  typedef const Char& reference;
-  typedef std::random_access_iterator_tag iterator_category;
-
-  null_terminating_iterator() : ptr_(0), end_(0) {}
-
-  FMT_CONSTEXPR null_terminating_iterator(const Char *ptr, const Char *end)
-    : ptr_(ptr), end_(end) {}
-
-  template <typename Range>
-  FMT_CONSTEXPR explicit null_terminating_iterator(const Range &r)
-    : ptr_(r.begin()), end_(r.end()) {}
-
-  FMT_CONSTEXPR null_terminating_iterator &operator=(const Char *ptr) {
-    assert(ptr <= end_);
-    ptr_ = ptr;
-    return *this;
-  }
-
-  FMT_CONSTEXPR Char operator*() const {
-    return ptr_ != end_ ? *ptr_ : 0;
-  }
-
-  FMT_CONSTEXPR null_terminating_iterator operator++() {
-    ++ptr_;
-    return *this;
-  }
-
-  FMT_CONSTEXPR null_terminating_iterator operator++(int) {
-    null_terminating_iterator result(*this);
-    ++ptr_;
-    return result;
-  }
-
-  FMT_CONSTEXPR null_terminating_iterator operator--() {
-    --ptr_;
-    return *this;
-  }
-
-  FMT_CONSTEXPR null_terminating_iterator operator+(difference_type n) {
-    return null_terminating_iterator(ptr_ + n, end_);
-  }
-
-  FMT_CONSTEXPR null_terminating_iterator operator-(difference_type n) {
-    return null_terminating_iterator(ptr_ - n, end_);
-  }
-
-  FMT_CONSTEXPR null_terminating_iterator operator+=(difference_type n) {
-    ptr_ += n;
-    return *this;
-  }
-
-  FMT_CONSTEXPR difference_type operator-(
-      null_terminating_iterator other) const {
-    return ptr_ - other.ptr_;
-  }
-
-  FMT_CONSTEXPR bool operator!=(null_terminating_iterator other) const {
-    return ptr_ != other.ptr_;
-  }
-
-  bool operator>=(null_terminating_iterator other) const {
-    return ptr_ >= other.ptr_;
-  }
-
-  // This should be a friend specialization pointer_from<Char> but the latter
-  // doesn't compile by gcc 5.1 due to a compiler bug.
-  template <typename CharT>
-  friend FMT_CONSTEXPR_DECL const CharT *pointer_from(
-      null_terminating_iterator<CharT> it);
-
- private:
-  const Char *ptr_;
-  const Char *end_;
-};
-
-template <typename T>
-FMT_CONSTEXPR const T *pointer_from(const T *p) { return p; }
-
-template <typename Char>
-FMT_CONSTEXPR const Char *pointer_from(null_terminating_iterator<Char> it) {
-  return it.ptr_;
-}
-
 // An output iterator that counts the number of objects written to it and
 // discards them.
 template <typename T>
@@ -816,35 +643,49 @@ class counting_iterator {
   T &operator*() const { return blackhole_; }
 };
 
+template <typename OutputIt>
+class truncating_iterator_base {
+ protected:
+  OutputIt out_;
+  std::size_t limit_;
+  std::size_t count_;
+
+  truncating_iterator_base(OutputIt out, std::size_t limit)
+    : out_(out), limit_(limit), count_(0) {}
+
+ public:
+  typedef std::output_iterator_tag iterator_category;
+  typedef void difference_type;
+  typedef void pointer;
+  typedef void reference;
+  typedef truncating_iterator_base _Unchecked_type; // Mark iterator as checked.
+
+  OutputIt base() const { return out_; }
+  std::size_t count() const { return count_; }
+};
+
 // An output iterator that truncates the output and counts the number of objects
 // written to it.
+template <typename OutputIt, typename Enable = typename std::is_void<
+    typename std::iterator_traits<OutputIt>::value_type>::type>
+class truncating_iterator;
+
 template <typename OutputIt>
-class truncating_iterator {
- private:
+class truncating_iterator<OutputIt, std::false_type>:
+  public truncating_iterator_base<OutputIt> {
   typedef std::iterator_traits<OutputIt> traits;
 
-  OutputIt out_;
-  std::size_t limit_;
-  std::size_t count_;
   mutable typename traits::value_type blackhole_;
 
  public:
-  typedef std::output_iterator_tag iterator_category;
   typedef typename traits::value_type value_type;
-  typedef typename traits::difference_type difference_type;
-  typedef typename traits::pointer pointer;
-  typedef typename traits::reference reference;
-  typedef truncating_iterator _Unchecked_type;  // Mark iterator as checked.
 
   truncating_iterator(OutputIt out, std::size_t limit)
-    : out_(out), limit_(limit), count_(0) {}
-
-  OutputIt base() const { return out_; }
-  std::size_t count() const { return count_; }
+    : truncating_iterator_base<OutputIt>(out, limit) {}
 
   truncating_iterator& operator++() {
-    if (count_++ < limit_)
-      ++out_;
+    if (this->count_++ < this->limit_)
+      ++this->out_;
     return *this;
   }
 
@@ -854,7 +695,29 @@ class truncating_iterator {
     return it;
   }
 
-  reference operator*() const { return count_ < limit_ ? *out_ : blackhole_; }
+  value_type& operator*() const {
+    return this->count_ < this->limit_ ? *this->out_ : blackhole_;
+  }
+};
+
+template <typename OutputIt>
+class truncating_iterator<OutputIt, std::true_type>:
+  public truncating_iterator_base<OutputIt> {
+ public:
+  typedef typename OutputIt::container_type::value_type value_type;
+
+  truncating_iterator(OutputIt out, std::size_t limit)
+    : truncating_iterator_base<OutputIt>(out, limit) {}
+
+  truncating_iterator& operator=(value_type val) {
+    if (this->count_++ < this->limit_)
+      this->out_ = val;
+    return *this;
+  }
+
+  truncating_iterator& operator++() { return *this; }
+  truncating_iterator& operator++(int) { return *this; }
+  truncating_iterator& operator*() { return *this; }
 };
 
 // Returns true if value is negative, false otherwise.
@@ -888,6 +751,8 @@ struct FMT_API basic_data {
   static const uint64_t POW10_SIGNIFICANDS[];
   static const int16_t POW10_EXPONENTS[];
   static const char DIGITS[];
+  static const char FOREGROUND_COLOR[];
+  static const char BACKGROUND_COLOR[];
   static const char RESET_COLOR[];
   static const wchar_t WRESET_COLOR[];
 };
@@ -901,16 +766,16 @@ typedef basic_data<> data;
 #ifdef FMT_BUILTIN_CLZLL
 // Returns the number of decimal digits in n. Leading zeros are not counted
 // except for n == 0 in which case count_digits returns 1.
-inline unsigned count_digits(uint64_t n) {
+inline int count_digits(uint64_t n) {
   // Based on http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
   // and the benchmark https://github.com/localvoid/cxx-benchmark-count-digits.
   int t = (64 - FMT_BUILTIN_CLZLL(n | 1)) * 1233 >> 12;
-  return to_unsigned(t) - (n < data::ZERO_OR_POWERS_OF_10_64[t]) + 1;
+  return t - (n < data::ZERO_OR_POWERS_OF_10_64[t]) + 1;
 }
 #else
 // Fallback version of count_digits used when __builtin_clz is not available.
-inline unsigned count_digits(uint64_t n) {
-  unsigned count = 1;
+inline int count_digits(uint64_t n) {
+  int count = 1;
   for (;;) {
     // Integer division is slow so do it for a group of four digits instead
     // of for every digit. The idea comes from the talk by Alexandrescu
@@ -925,8 +790,33 @@ inline unsigned count_digits(uint64_t n) {
 }
 #endif
 
+template <typename Char>
+inline size_t count_code_points(basic_string_view<Char> s) { return s.size(); }
+
 // Counts the number of code points in a UTF-8 string.
-FMT_API size_t count_code_points(u8string_view s);
+FMT_API size_t count_code_points(basic_string_view<char8_t> s);
+
+inline char8_t to_char8_t(char c) { return static_cast<char8_t>(c); }
+
+template <typename InputIt, typename OutChar>
+struct needs_conversion: std::integral_constant<bool,
+  std::is_same<
+    typename std::iterator_traits<InputIt>::value_type, char>::value &&
+  std::is_same<OutChar, char8_t>::value> {};
+
+template <typename OutChar, typename InputIt, typename OutputIt>
+typename std::enable_if<
+  !needs_conversion<InputIt, OutChar>::value, OutputIt>::type
+    copy_str(InputIt begin, InputIt end, OutputIt it) {
+  return std::copy(begin, end, it);
+}
+
+template <typename OutChar, typename InputIt, typename OutputIt>
+typename std::enable_if<
+  needs_conversion<InputIt, OutChar>::value, OutputIt>::type
+    copy_str(InputIt begin, InputIt end, OutputIt it) {
+  return std::transform(begin, end, it, to_char8_t);
+}
 
 #if FMT_HAS_CPP_ATTRIBUTE(always_inline)
 # define FMT_ALWAYS_INLINE __attribute__((always_inline))
@@ -1006,9 +896,9 @@ class decimal_formatter_null : public decimal_formatter {
 
 #ifdef FMT_BUILTIN_CLZ
 // Optional version of count_digits for better performance on 32-bit platforms.
-inline unsigned count_digits(uint32_t n) {
+inline int count_digits(uint32_t n) {
   int t = (32 - FMT_BUILTIN_CLZ(n | 1)) * 1233 >> 12;
-  return to_unsigned(t) - (n < data::ZERO_OR_POWERS_OF_10_32[t]) + 1;
+  return t - (n < data::ZERO_OR_POWERS_OF_10_32[t]) + 1;
 }
 #endif
 
@@ -1018,6 +908,8 @@ struct no_thousands_sep {
 
   template <typename Char>
   void operator()(Char *) {}
+
+  enum { size = 0 };
 };
 
 // A functor that adds a thousands separator.
@@ -1042,17 +934,30 @@ class add_thousands_sep {
     std::uninitialized_copy(sep_.data(), sep_.data() + sep_.size(),
                             internal::make_checked(buffer, sep_.size()));
   }
+
+  enum { size = 1 };
 };
 
 template <typename Char>
-FMT_API Char thousands_sep(locale_provider *lp);
+FMT_API Char thousands_sep_impl(locale_ref loc);
+
+template <typename Char>
+inline Char thousands_sep(locale_ref loc) {
+  return Char(thousands_sep_impl<char>(loc));
+}
+
+template <>
+inline wchar_t thousands_sep(locale_ref loc) {
+  return thousands_sep_impl<wchar_t>(loc);
+}
 
 // Formats a decimal unsigned integer value writing into buffer.
 // thousands_sep is a functor that is called after writing each char to
 // add a thousands separator if necessary.
 template <typename UInt, typename Char, typename ThousandsSep>
-inline Char *format_decimal(Char *buffer, UInt value, unsigned num_digits,
+inline Char *format_decimal(Char *buffer, UInt value, int num_digits,
                             ThousandsSep thousands_sep) {
+  FMT_ASSERT(num_digits >= 0, "invalid digit count");
   buffer += num_digits;
   Char *end = buffer;
   while (value >= 100) {
@@ -1061,58 +966,63 @@ inline Char *format_decimal(Char *buffer, UInt value, unsigned num_digits,
     // "Three Optimization Tips for C++". See speed-test for a comparison.
     unsigned index = static_cast<unsigned>((value % 100) * 2);
     value /= 100;
-    *--buffer = data::DIGITS[index + 1];
+    *--buffer = static_cast<Char>(data::DIGITS[index + 1]);
     thousands_sep(buffer);
-    *--buffer = data::DIGITS[index];
+    *--buffer = static_cast<Char>(data::DIGITS[index]);
     thousands_sep(buffer);
   }
   if (value < 10) {
-    *--buffer = static_cast<char>('0' + value);
+    *--buffer = static_cast<Char>('0' + value);
     return end;
   }
   unsigned index = static_cast<unsigned>(value * 2);
-  *--buffer = data::DIGITS[index + 1];
+  *--buffer = static_cast<Char>(data::DIGITS[index + 1]);
   thousands_sep(buffer);
-  *--buffer = data::DIGITS[index];
+  *--buffer = static_cast<Char>(data::DIGITS[index]);
   return end;
 }
 
-template <typename UInt, typename Iterator, typename ThousandsSep>
+template <typename OutChar, typename UInt, typename Iterator,
+          typename ThousandsSep>
 inline Iterator format_decimal(
-    Iterator out, UInt value, unsigned num_digits, ThousandsSep sep) {
+    Iterator out, UInt value, int num_digits, ThousandsSep sep) {
+  FMT_ASSERT(num_digits >= 0, "invalid digit count");
   typedef typename ThousandsSep::char_type char_type;
-  // Buffer should be large enough to hold all digits (digits10 + 1) and null.
-  char_type buffer[std::numeric_limits<UInt>::digits10 + 2];
-  format_decimal(buffer, value, num_digits, sep);
-  return std::copy_n(buffer, num_digits, out);
+  // Buffer should be large enough to hold all digits (<= digits10 + 1).
+  enum { max_size = std::numeric_limits<UInt>::digits10 + 1 };
+  FMT_ASSERT(ThousandsSep::size <= 1, "invalid separator");
+  char_type buffer[max_size + max_size / 3];
+  auto end = format_decimal(buffer, value, num_digits, sep);
+  return internal::copy_str<OutChar>(buffer, end, out);
 }
 
-template <typename It, typename UInt>
-inline It format_decimal(It out, UInt value, unsigned num_digits) {
-  return format_decimal(out, value, num_digits, no_thousands_sep());
+template <typename OutChar, typename It, typename UInt>
+inline It format_decimal(It out, UInt value, int num_digits) {
+  return format_decimal<OutChar>(out, value, num_digits, no_thousands_sep());
 }
 
 template <unsigned BASE_BITS, typename Char, typename UInt>
-inline Char *format_uint(Char *buffer, UInt value, unsigned num_digits,
+inline Char *format_uint(Char *buffer, UInt value, int num_digits,
                          bool upper = false) {
   buffer += num_digits;
   Char *end = buffer;
   do {
     const char *digits = upper ? "0123456789ABCDEF" : "0123456789abcdef";
     unsigned digit = (value & ((1 << BASE_BITS) - 1));
-    *--buffer = BASE_BITS < 4 ? static_cast<char>('0' + digit) : digits[digit];
+    *--buffer = static_cast<Char>(BASE_BITS < 4 ? static_cast<char>('0' + digit)
+                                                : digits[digit]);
   } while ((value >>= BASE_BITS) != 0);
   return end;
 }
 
-template <unsigned BASE_BITS, typename It, typename UInt>
-inline It format_uint(It out, UInt value, unsigned num_digits,
+template <unsigned BASE_BITS, typename Char, typename It, typename UInt>
+inline It format_uint(It out, UInt value, int num_digits,
                       bool upper = false) {
   // Buffer should be large enough to hold all digits (digits / BASE_BITS + 1)
   // and null.
   char buffer[std::numeric_limits<UInt>::digits / BASE_BITS + 2];
   format_uint<BASE_BITS>(buffer, value, num_digits, upper);
-  return std::copy_n(buffer, num_digits, out);
+  return internal::copy_str<Char>(buffer, buffer + num_digits, out);
 }
 
 #ifndef _WIN32
@@ -1171,72 +1081,35 @@ enum alignment {
 };
 
 // Flags.
-enum {SIGN_FLAG = 1, PLUS_FLAG = 2, MINUS_FLAG = 4, HASH_FLAG = 8};
-
-enum format_spec_tag {fill_tag, align_tag, width_tag, type_tag};
-
-// Format specifier.
-template <typename T, format_spec_tag>
-class format_spec {
- private:
-  T value_;
-
- public:
-  typedef T value_type;
-
-  explicit format_spec(T value) : value_(value) {}
-
-  T value() const { return value_; }
-};
-
-// template <typename Char>
-// typedef format_spec<Char, fill_tag> fill_spec;
-template <typename Char>
-class fill_spec : public format_spec<Char, fill_tag> {
- public:
-  explicit fill_spec(Char value) : format_spec<Char, fill_tag>(value) {}
-};
-
-typedef format_spec<unsigned, width_tag> width_spec;
-typedef format_spec<char, type_tag> type_spec;
-
-// An empty format specifier.
-struct empty_spec {};
+enum { SIGN_FLAG = 1, PLUS_FLAG = 2, MINUS_FLAG = 4, HASH_FLAG = 8 };
 
 // An alignment specifier.
-struct align_spec : empty_spec {
+struct align_spec {
   unsigned width_;
   // Fill is always wchar_t and cast to char if necessary to avoid having
   // two specialization of AlignSpec and its subclasses.
   wchar_t fill_;
   alignment align_;
 
-  FMT_CONSTEXPR align_spec(
-      unsigned width, wchar_t fill, alignment align = ALIGN_DEFAULT)
-  : width_(width), fill_(fill), align_(align) {}
-
+  FMT_CONSTEXPR align_spec() : width_(0), fill_(' '), align_(ALIGN_DEFAULT) {}
   FMT_CONSTEXPR unsigned width() const { return width_; }
   FMT_CONSTEXPR wchar_t fill() const { return fill_; }
   FMT_CONSTEXPR alignment align() const { return align_; }
+};
+
+struct core_format_specs {
+  int precision;
+  uint_least8_t flags;
+  char type;
 
-  int precision() const { return -1; }
+  FMT_CONSTEXPR core_format_specs() : precision(-1), flags(0), type(0) {}
+  FMT_CONSTEXPR bool has(unsigned f) const { return (flags & f) != 0; }
 };
 
 // Format specifiers.
 template <typename Char>
-class basic_format_specs : public align_spec {
- public:
-  unsigned flags_;
-  int precision_;
-  Char type_;
-
-  FMT_CONSTEXPR basic_format_specs(
-      unsigned width = 0, char type = 0, wchar_t fill = ' ')
-  : align_spec(width, fill), flags_(0), precision_(-1), type_(type) {}
-
-  FMT_CONSTEXPR bool flag(unsigned f) const { return (flags_ & f) != 0; }
-  FMT_CONSTEXPR int precision() const { return precision_; }
-  FMT_CONSTEXPR Char type() const { return type_; }
+struct basic_format_specs : align_spec, core_format_specs {
+  FMT_CONSTEXPR basic_format_specs() {}
 };
 
 typedef basic_format_specs<char> format_specs;
@@ -1251,13 +1124,20 @@ FMT_CONSTEXPR unsigned basic_parse_context<Char, ErrorHandler>::next_arg_id() {
 
 namespace internal {
 
-template <typename S>
-struct format_string_traits<
-  S, typename std::enable_if<std::is_base_of<compile_string, S>::value>::type>:
-    format_string_traits_base<char> {};
+// Formats value using Grisu2 algorithm:
+// https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf
+template <typename Double>
+FMT_API typename std::enable_if<sizeof(Double) == sizeof(uint64_t), bool>::type
+  grisu2_format(Double value, buffer &buf, core_format_specs);
+template <typename Double>
+inline typename std::enable_if<sizeof(Double) != sizeof(uint64_t), bool>::type
+  grisu2_format(Double, buffer &, core_format_specs) { return false; }
 
-template <typename Char, typename Handler>
-FMT_CONSTEXPR void handle_int_type_spec(Char spec, Handler &&handler) {
+template <typename Double>
+void sprintf_format(Double, internal::buffer &, core_format_specs);
+
+template <typename Handler>
+FMT_CONSTEXPR void handle_int_type_spec(char spec, Handler &&handler) {
   switch (spec) {
   case 0: case 'd':
     handler.on_dec();
@@ -1279,8 +1159,8 @@ FMT_CONSTEXPR void handle_int_type_spec(Char spec, Handler &&handler) {
   }
 }
 
-template <typename Char, typename Handler>
-FMT_CONSTEXPR void handle_float_type_spec(Char spec, Handler &&handler) {
+template <typename Handler>
+FMT_CONSTEXPR void handle_float_type_spec(char spec, Handler &&handler) {
   switch (spec) {
   case 0: case 'g': case 'G':
     handler.on_general();
@@ -1304,8 +1184,8 @@ template <typename Char, typename Handler>
 FMT_CONSTEXPR void handle_char_specs(
     const basic_format_specs<Char> *specs, Handler &&handler) {
   if (!specs) return handler.on_char();
-  if (specs->type() && specs->type() != 'c') return handler.on_int();
-  if (specs->align() == ALIGN_NUMERIC || specs->flag(~0u) != 0)
+  if (specs->type && specs->type != 'c') return handler.on_int();
+  if (specs->align() == ALIGN_NUMERIC || specs->flags != 0)
     handler.on_error("invalid format specifier for char");
   handler.on_char();
 }
@@ -1364,13 +1244,13 @@ class float_type_checker : private ErrorHandler {
   }
 };
 
-template <typename ErrorHandler, typename CharType>
+template <typename ErrorHandler>
 class char_specs_checker : public ErrorHandler {
  private:
-  CharType type_;
+  char type_;
 
  public:
-  FMT_CONSTEXPR char_specs_checker(CharType type, ErrorHandler eh)
+  FMT_CONSTEXPR char_specs_checker(char type, ErrorHandler eh)
     : ErrorHandler(eh), type_(type) {}
 
   FMT_CONSTEXPR void on_int() {
@@ -1394,8 +1274,7 @@ void arg_map<Context>::init(const basic_format_args<Context> &args) {
   if (map_)
     return;
   map_ = new entry[args.max_size()];
-  bool use_values = args.type(max_packed_args - 1) == internal::none_type;
-  if (use_values) {
+  if (args.is_packed()) {
     for (unsigned i = 0;/*nothing*/; ++i) {
       internal::type arg_type = args.type(i);
       switch (arg_type) {
@@ -1436,21 +1315,25 @@ class arg_formatter_base {
 
   struct char_writer {
     char_type value;
+
+    size_t size() const { return 1; }
+    size_t width() const { return 1; }
+
     template <typename It>
     void operator()(It &&it) const { *it++ = value; }
   };
 
   void write_char(char_type value) {
     if (specs_)
-      writer_.write_padded(1, *specs_, char_writer{value});
+      writer_.write_padded(*specs_, char_writer{value});
     else
       writer_.write(value);
   }
 
   void write_pointer(const void *p) {
     format_specs specs = specs_ ? *specs_ : format_specs();
-    specs.flags_ = HASH_FLAG;
-    specs.type_ = 'x';
+    specs.flags = HASH_FLAG;
+    specs.type = 'x';
     writer_.write_int(reinterpret_cast<uintptr_t>(p), specs);
   }
 
@@ -1461,7 +1344,7 @@ class arg_formatter_base {
 
   void write(bool value) {
     string_view sv(value ? "true" : "false");
-    specs_ ? writer_.write_str(sv, *specs_) : writer_.write(sv);
+    specs_ ? writer_.write(sv, *specs_) : writer_.write(sv);
   }
 
   void write(const char_type *value) {
@@ -1469,11 +1352,12 @@ class arg_formatter_base {
       FMT_THROW(format_error("string pointer is null"));
     auto length = std::char_traits<char_type>::length(value);
     basic_string_view<char_type> sv(value, length);
-    specs_ ? writer_.write_str(sv, *specs_) : writer_.write(sv);
+    specs_ ? writer_.write(sv, *specs_) : writer_.write(sv);
   }
 
  public:
-  arg_formatter_base(Range r, format_specs *s): writer_(r), specs_(s) {}
+  arg_formatter_base(Range r, format_specs *s, locale_ref loc)
+    : writer_(r, loc), specs_(s) {}
 
   iterator operator()(monostate) {
     FMT_ASSERT(false, "invalid argument type");
@@ -1481,12 +1365,13 @@ class arg_formatter_base {
   }
 
   template <typename T>
-  typename std::enable_if<std::is_integral<T>::value, iterator>::type
-      operator()(T value) {
+  typename std::enable_if<
+    std::is_integral<T>::value || std::is_same<T, char_type>::value,
+    iterator>::type operator()(T value) {
     // MSVC2013 fails to compile separate overloads for bool and char_type so
     // use std::is_same instead.
     if (std::is_same<T, bool>::value) {
-      if (specs_ && specs_->type_)
+      if (specs_ && specs_->type)
         return (*this)(value ? 1 : 0);
       write(value != 0);
     } else if (std::is_same<T, char_type>::value) {
@@ -1535,15 +1420,15 @@ class arg_formatter_base {
   iterator operator()(const char_type *value) {
     if (!specs_) return write(value), out();
     internal::handle_cstring_type_spec(
-          specs_->type_, cstring_spec_handler(*this, value));
+          specs_->type, cstring_spec_handler(*this, value));
     return out();
   }
 
   iterator operator()(basic_string_view<char_type> value) {
     if (specs_) {
       internal::check_string_type_spec(
-            specs_->type_, internal::error_handler());
-      writer_.write_str(value, *specs_);
+            specs_->type, internal::error_handler());
+      writer_.write(value, *specs_);
     } else {
       writer_.write(value);
     }
@@ -1552,7 +1437,7 @@ class arg_formatter_base {
 
   iterator operator()(const void *value) {
     if (specs_)
-      check_pointer_type_spec(specs_->type_, internal::error_handler());
+      check_pointer_type_spec(specs_->type, internal::error_handler());
     write_pointer(value);
     return out();
   }
@@ -1563,40 +1448,16 @@ FMT_CONSTEXPR bool is_name_start(Char c) {
   return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c;
 }
 
-// DEPRECATED: Parses the input as an unsigned integer. This function assumes
-// that the first character is a digit and presence of a non-digit character at
-// the end.
-// it: an iterator pointing to the beginning of the input range.
-template <typename Iterator, typename ErrorHandler>
-FMT_CONSTEXPR unsigned parse_nonnegative_int(Iterator &it, ErrorHandler &&eh) {
-  assert('0' <= *it && *it <= '9');
-  unsigned value = 0;
-  // Convert to unsigned to prevent a warning.
-  unsigned max_int = (std::numeric_limits<int>::max)();
-  unsigned big = max_int / 10;
-  do {
-    // Check for overflow.
-    if (value > big) {
-      value = max_int + 1;
-      break;
-    }
-    value = value * 10 + unsigned(*it - '0');
-    // Workaround for MSVC "setup_exception stack overflow" error:
-    auto next = it;
-    ++next;
-    it = next;
-  } while ('0' <= *it && *it <= '9');
-  if (value > max_int)
-    eh.on_error("number is too big");
-  return value;
-}
-
 // Parses the range [begin, end) as an unsigned integer. This function assumes
 // that the range is non-empty and the first character is a digit.
 template <typename Char, typename ErrorHandler>
 FMT_CONSTEXPR unsigned parse_nonnegative_int(
     const Char *&begin, const Char *end, ErrorHandler &&eh) {
   assert(begin != end && '0' <= *begin && *begin <= '9');
+  if (*begin == '0') {
+    ++begin;
+    return 0;
+  }
   unsigned value = 0;
   // Convert to unsigned to prevent a warning.
   unsigned max_int = (std::numeric_limits<int>::max)();
@@ -1607,7 +1468,8 @@ FMT_CONSTEXPR unsigned parse_nonnegative_int(
       value = max_int + 1;
       break;
     }
-    value = value * 10 + unsigned(*begin++ - '0');
+    value = value * 10 + unsigned(*begin - '0');
+    ++begin;
   } while (begin != end && '0' <= *begin && *begin <= '9');
   if (value > max_int)
     eh.on_error("number is too big");
@@ -1695,14 +1557,14 @@ class specs_setter {
   explicit FMT_CONSTEXPR specs_setter(basic_format_specs<Char> &specs):
     specs_(specs) {}
 
-  FMT_CONSTEXPR specs_setter(const specs_setter &other) : specs_(other.specs_) {}
+  FMT_CONSTEXPR specs_setter(const specs_setter &other): specs_(other.specs_) {}
 
   FMT_CONSTEXPR void on_align(alignment align) { specs_.align_ = align; }
   FMT_CONSTEXPR void on_fill(Char fill) { specs_.fill_ = fill; }
-  FMT_CONSTEXPR void on_plus() { specs_.flags_ |= SIGN_FLAG | PLUS_FLAG; }
-  FMT_CONSTEXPR void on_minus() { specs_.flags_ |= MINUS_FLAG; }
-  FMT_CONSTEXPR void on_space() { specs_.flags_ |= SIGN_FLAG; }
-  FMT_CONSTEXPR void on_hash() { specs_.flags_ |= HASH_FLAG; }
+  FMT_CONSTEXPR void on_plus() { specs_.flags |= SIGN_FLAG | PLUS_FLAG; }
+  FMT_CONSTEXPR void on_minus() { specs_.flags |= MINUS_FLAG; }
+  FMT_CONSTEXPR void on_space() { specs_.flags |= SIGN_FLAG; }
+  FMT_CONSTEXPR void on_hash() { specs_.flags |= HASH_FLAG; }
 
   FMT_CONSTEXPR void on_zero() {
     specs_.align_ = ALIGN_NUMERIC;
@@ -1711,11 +1573,13 @@ class specs_setter {
 
   FMT_CONSTEXPR void on_width(unsigned width) { specs_.width_ = width; }
   FMT_CONSTEXPR void on_precision(unsigned precision) {
-    specs_.precision_ = static_cast<int>(precision);
+    specs_.precision = static_cast<int>(precision);
   }
   FMT_CONSTEXPR void end_precision() {}
 
-  FMT_CONSTEXPR void on_type(Char type) { specs_.type_ = type; }
+  FMT_CONSTEXPR void on_type(Char type) {
+    specs_.type = static_cast<char>(type);
+  }
 
  protected:
   basic_format_specs<Char> &specs_;
@@ -1789,8 +1653,9 @@ template <template <typename> class Handler, typename T,
           typename Context, typename ErrorHandler>
 FMT_CONSTEXPR void set_dynamic_spec(
     T &value, basic_format_arg<Context> arg, ErrorHandler eh) {
-  unsigned long long big_value = fmt::visit(Handler<ErrorHandler>(eh), arg);
-  if (big_value > (std::numeric_limits<int>::max)())
+  unsigned long long big_value =
+      visit_format_arg(Handler<ErrorHandler>(eh), arg);
+  if (big_value > to_unsigned((std::numeric_limits<int>::max)()))
     eh.on_error("number is too big");
   value = static_cast<T>(big_value);
 }
@@ -1816,7 +1681,7 @@ class specs_handler: public specs_setter<typename Context::char_type> {
   template <typename Id>
   FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) {
     set_dynamic_spec<precision_checker>(
-          this->specs_.precision_, get_arg(arg_id), context_.error_handler());
+          this->specs_.precision, get_arg(arg_id), context_.error_handler());
   }
 
   void on_error(const char *message) {
@@ -1844,7 +1709,9 @@ struct arg_ref {
 
   FMT_CONSTEXPR arg_ref() : kind(NONE), index(0) {}
   FMT_CONSTEXPR explicit arg_ref(unsigned index) : kind(INDEX), index(index) {}
-  explicit arg_ref(basic_string_view<Char> name) : kind(NAME), name(name) {}
+  explicit arg_ref(basic_string_view<Char> nm) : kind(NAME) {
+    name = {nm.data(), nm.size()};
+  }
 
   FMT_CONSTEXPR arg_ref &operator=(unsigned idx) {
     kind = INDEX;
@@ -1853,9 +1720,9 @@ struct arg_ref {
   }
 
   Kind kind;
-  FMT_UNION {
+  union {
     unsigned index;
-    basic_string_view<Char> name;
+    string_value<Char> name;  // This is not string_view because of gcc 4.4.
   };
 };
 
@@ -1915,36 +1782,6 @@ class dynamic_specs_handler :
   ParseContext &context_;
 };
 
-template <typename Iterator, typename IDHandler>
-FMT_CONSTEXPR Iterator parse_arg_id(Iterator it, IDHandler &&handler) {
-  typedef typename std::iterator_traits<Iterator>::value_type char_type;
-  char_type c = *it;
-  if (c == '}' || c == ':') {
-    handler();
-    return it;
-  }
-  if (c >= '0' && c <= '9') {
-    unsigned index = parse_nonnegative_int(it, handler);
-    if (*it != '}' && *it != ':') {
-      handler.on_error("invalid format string");
-      return it;
-    }
-    handler(index);
-    return it;
-  }
-  if (!is_name_start(c)) {
-    handler.on_error("invalid format string");
-    return it;
-  }
-  auto start = it;
-  do {
-    c = *++it;
-  } while (is_name_start(c) || ('0' <= c && c <= '9'));
-  handler(basic_string_view<char_type>(
-            pointer_from(start), to_unsigned(it - start)));
-  return it;
-}
-
 template <typename Char, typename IDHandler>
 FMT_CONSTEXPR const Char *parse_arg_id(
     const Char *begin, const Char *end, IDHandler &&handler) {
@@ -1963,8 +1800,8 @@ FMT_CONSTEXPR const Char *parse_arg_id(
     return handler.on_error("invalid format string"), begin;
   auto it = begin;
   do {
-    c = *++it;
-  } while (it != end && (is_name_start(c) || ('0' <= c && c <= '9')));
+    ++it;
+  } while (it != end && (is_name_start(c = *it) || ('0' <= c && c <= '9')));
   handler(basic_string_view<Char>(begin, to_unsigned(it - begin)));
   return it;
 }
@@ -2005,112 +1842,127 @@ struct precision_adapter {
   SpecHandler &handler;
 };
 
-// Parses standard format specifiers and sends notifications about parsed
-// components to handler.
-// it: an iterator pointing to the beginning of a null-terminated range of
-//     characters, possibly emulated via null_terminating_iterator, representing
-//     format specifiers.
-template <typename Iterator, typename SpecHandler>
-FMT_CONSTEXPR Iterator parse_format_specs(Iterator it, SpecHandler &&handler) {
-  typedef typename std::iterator_traits<Iterator>::value_type char_type;
-  char_type c = *it;
-  if (c == '}' || !c)
-    return it;
-
-  // Parse fill and alignment.
+// Parses fill and alignment.
+template <typename Char, typename Handler>
+FMT_CONSTEXPR const Char *parse_align(
+    const Char *begin, const Char *end, Handler &&handler) {
+  FMT_ASSERT(begin != end, "");
   alignment align = ALIGN_DEFAULT;
-  int i = 1;
+  int i = 0;
+  if (begin + 1 != end) ++i;
   do {
-    auto p = it + i;
-    switch (*p) {
-      case '<':
-        align = ALIGN_LEFT;
-        break;
-      case '>':
-        align = ALIGN_RIGHT;
-        break;
-      case '=':
-        align = ALIGN_NUMERIC;
-        break;
-      case '^':
-        align = ALIGN_CENTER;
-        break;
+    switch (static_cast<char>(begin[i])) {
+    case '<':
+      align = ALIGN_LEFT;
+      break;
+    case '>':
+      align = ALIGN_RIGHT;
+      break;
+    case '=':
+      align = ALIGN_NUMERIC;
+      break;
+    case '^':
+      align = ALIGN_CENTER;
+      break;
     }
     if (align != ALIGN_DEFAULT) {
-      if (p != it) {
-        if (c == '{') {
-          handler.on_error("invalid fill character '{'");
-          return it;
-        }
-        it += 2;
+      if (i > 0) {
+        auto c = *begin;
+        if (c == '{')
+          return handler.on_error("invalid fill character '{'"), begin;
+        begin += 2;
         handler.on_fill(c);
-      } else ++it;
+      } else ++begin;
       handler.on_align(align);
       break;
     }
-  } while (--i >= 0);
+  } while (i-- > 0);
+  return begin;
+}
+
+template <typename Char, typename Handler>
+FMT_CONSTEXPR const Char *parse_width(
+    const Char *begin, const Char *end, Handler &&handler) {
+  FMT_ASSERT(begin != end, "");
+  if ('0' <= *begin && *begin <= '9') {
+    handler.on_width(parse_nonnegative_int(begin, end, handler));
+  } else if (*begin == '{') {
+    ++begin;
+    if (begin != end)
+      begin = parse_arg_id(begin, end, width_adapter<Handler, Char>(handler));
+    if (begin == end || *begin != '}')
+      return handler.on_error("invalid format string"), begin;
+    ++begin;
+  }
+  return begin;
+}
+
+// Parses standard format specifiers and sends notifications about parsed
+// components to handler.
+template <typename Char, typename SpecHandler>
+FMT_CONSTEXPR const Char *parse_format_specs(
+    const Char *begin, const Char *end, SpecHandler &&handler) {
+  if (begin == end || *begin == '}')
+    return begin;
+
+  begin = parse_align(begin, end, handler);
+  if (begin == end) return begin;
 
   // Parse sign.
-  switch (*it) {
-    case '+':
-      handler.on_plus();
-      ++it;
-      break;
-    case '-':
-      handler.on_minus();
-      ++it;
-      break;
-    case ' ':
-      handler.on_space();
-      ++it;
-      break;
+  switch (static_cast<char>(*begin)) {
+  case '+':
+    handler.on_plus();
+    ++begin;
+    break;
+  case '-':
+    handler.on_minus();
+    ++begin;
+    break;
+  case ' ':
+    handler.on_space();
+    ++begin;
+    break;
   }
+  if (begin == end) return begin;
 
-  if (*it == '#') {
+  if (*begin == '#') {
     handler.on_hash();
-    ++it;
+    if (++begin == end) return begin;
   }
 
   // Parse zero flag.
-  if (*it == '0') {
+  if (*begin == '0') {
     handler.on_zero();
-    ++it;
+    if (++begin == end) return begin;
   }
 
-  // Parse width.
-  if ('0' <= *it && *it <= '9') {
-    handler.on_width(parse_nonnegative_int(it, handler));
-  } else if (*it == '{') {
-    it = parse_arg_id(it + 1, width_adapter<SpecHandler, char_type>(handler));
-    if (*it++ != '}') {
-      handler.on_error("invalid format string");
-      return it;
-    }
-  }
+  begin = parse_width(begin, end, handler);
+  if (begin == end) return begin;
 
   // Parse precision.
-  if (*it == '.') {
-    ++it;
-    if ('0' <= *it && *it <= '9') {
-      handler.on_precision(parse_nonnegative_int(it, handler));
-    } else if (*it == '{') {
-      it = parse_arg_id(
-            it + 1, precision_adapter<SpecHandler, char_type>(handler));
-      if (*it++ != '}') {
-        handler.on_error("invalid format string");
-        return it;
+  if (*begin == '.') {
+    ++begin;
+    auto c = begin != end ? *begin : 0;
+    if ('0' <= c && c <= '9') {
+      handler.on_precision(parse_nonnegative_int(begin, end, handler));
+    } else if (c == '{') {
+      ++begin;
+      if (begin != end) {
+        begin = parse_arg_id(
+              begin, end, precision_adapter<SpecHandler, Char>(handler));
       }
+      if (begin == end || *begin++ != '}')
+        return handler.on_error("invalid format string"), begin;
     } else {
-      handler.on_error("missing precision specifier");
-      return it;
+      return handler.on_error("missing precision specifier"), begin;
     }
     handler.end_precision();
   }
 
   // Parse type.
-  if (*it != '}' && *it)
-    handler.on_type(*it++);
-  return it;
+  if (begin != end && *begin != '}')
+    handler.on_type(*begin++);
+  return begin;
 }
 
 // Return the result via the out param to workaround gcc bug 77539.
@@ -2126,7 +1978,7 @@ FMT_CONSTEXPR bool find(Ptr first, Ptr last, T value, Ptr &out) {
 template <>
 inline bool find<false, char>(
     const char *first, const char *last, char value, const char *&out) {
-  out = static_cast<const char*>(std::memchr(first, value, last - first));
+  out = static_cast<const char*>(std::memchr(first, value, internal::to_unsigned(last - first)));
   return out != FMT_NULL;
 }
 
@@ -2162,7 +2014,8 @@ FMT_CONSTEXPR void parse_format_string(
     }
     Handler &handler_;
   } write{handler};
-  auto begin = format_str.data(), end = begin + format_str.size();
+  auto begin = format_str.data();
+  auto end = begin + format_str.size();
   while (begin != end) {
     // Doing two passes with memchr (one for '{' and another for '}') is up to
     // 2.5x faster than the naive one-pass implementation on big format strings.
@@ -2173,22 +2026,20 @@ FMT_CONSTEXPR void parse_format_string(
     ++p;
     if (p == end)
       return handler.on_error("invalid format string");
-    if (*p == '}') {
+    if (static_cast<char>(*p) == '}') {
       handler.on_arg_id();
       handler.on_replacement_field(p);
     } else if (*p == '{') {
       handler.on_text(p, p + 1);
     } else {
       p = parse_arg_id(p, end, id_adapter<Handler, Char>{handler});
-      Char c = p != end ? *p : 0;
+      Char c = p != end ? *p : Char();
       if (c == '}') {
         handler.on_replacement_field(p);
       } else if (c == ':') {
-        internal::null_terminating_iterator<Char> it(p + 1, end);
-        it = handler.on_format_specs(it);
-        if (*it != '}')
+        p = handler.on_format_specs(p + 1, end);
+        if (p == end || *p != '}')
           return handler.on_error("unknown format specifier");
-        p = pointer_from(it);
       } else {
         return handler.on_error("missing '}' in format string");
       }
@@ -2210,11 +2061,9 @@ class format_string_checker {
  public:
   explicit FMT_CONSTEXPR format_string_checker(
       basic_string_view<Char> format_str, ErrorHandler eh)
-    : arg_id_(-1), context_(format_str, eh),
+    : arg_id_((std::numeric_limits<unsigned>::max)()), context_(format_str, eh),
       parse_funcs_{&parse_format_specs<Args, parse_context_type>...} {}
 
-  typedef internal::null_terminating_iterator<Char> iterator;
-
   FMT_CONSTEXPR void on_text(const Char *, const Char *) {}
 
   FMT_CONSTEXPR void on_arg_id() {
@@ -2230,11 +2079,10 @@ class format_string_checker {
 
   FMT_CONSTEXPR void on_replacement_field(const Char *) {}
 
-  FMT_CONSTEXPR const Char *on_format_specs(iterator it) {
-    auto p = pointer_from(it);
-    context_.advance_to(p);
-    return to_unsigned(arg_id_) < NUM_ARGS ?
-          parse_funcs_[arg_id_](context_) : p;
+  FMT_CONSTEXPR const Char *on_format_specs(const Char *begin, const Char *) {
+    context_.advance_to(begin);
+    return arg_id_ < NUM_ARGS ?
+          parse_funcs_[arg_id_](context_) : begin;
   }
 
   FMT_CONSTEXPR void on_error(const char *message) {
@@ -2246,32 +2094,32 @@ class format_string_checker {
   enum { NUM_ARGS = sizeof...(Args) };
 
   FMT_CONSTEXPR void check_arg_id() {
-    if (internal::to_unsigned(arg_id_) >= NUM_ARGS)
+    if (arg_id_ >= NUM_ARGS)
       context_.on_error("argument index out of range");
   }
 
   // Format specifier parsing function.
   typedef const Char *(*parse_func)(parse_context_type &);
 
-  int arg_id_;
+  unsigned arg_id_;
   parse_context_type context_;
   parse_func parse_funcs_[NUM_ARGS > 0 ? NUM_ARGS : 1];
 };
 
 template <typename Char, typename ErrorHandler, typename... Args>
-FMT_CONSTEXPR bool check_format_string(
+FMT_CONSTEXPR bool do_check_format_string(
     basic_string_view<Char> s, ErrorHandler eh = ErrorHandler()) {
   format_string_checker<Char, ErrorHandler, Args...> checker(s, eh);
   parse_format_string<true>(s, checker);
   return true;
 }
 
-template <typename... Args, typename String>
-typename std::enable_if<is_compile_string<String>::value>::type
-    check_format_string(String format_str) {
-  FMT_CONSTEXPR_DECL bool invalid_format =
-      internal::check_format_string<char, internal::error_handler, Args...>(
-        string_view(format_str.data(), format_str.size()));
+template <typename... Args, typename S>
+typename std::enable_if<is_compile_string<S>::value>::type
+    check_format_string(S format_str) {
+  typedef typename S::char_type char_t;
+  FMT_CONSTEXPR_DECL bool invalid_format = internal::do_check_format_string<
+      char_t, internal::error_handler, Args...>(to_string_view(format_str));
   (void)invalid_format;
 }
 
@@ -2295,7 +2143,8 @@ void handle_dynamic_spec(
     break;
   case arg_ref<char_type>::NAME:
     internal::set_dynamic_spec<Handler>(
-          value, ctx.get_arg(ref.name), ctx.error_handler());
+          value, ctx.get_arg({ref.name.value, ref.name.size}),
+          ctx.error_handler());
     break;
   }
 }
@@ -2326,8 +2175,8 @@ class arg_formatter:
     *spec* contains format specifier information for standard argument types.
     \endrst
    */
-  explicit arg_formatter(context_type &ctx, format_specs *spec = {})
-  : base(Range(ctx.out()), spec), ctx_(ctx) {}
+  explicit arg_formatter(context_type &ctx, format_specs *spec = FMT_NULL)
+  : base(Range(ctx.out()), spec, ctx.locale()), ctx_(ctx) {}
 
   // Deprecated.
   arg_formatter(context_type &ctx, format_specs &spec)
@@ -2375,7 +2224,7 @@ class system_error : public std::runtime_error {
    \endrst
   */
   template <typename... Args>
-  system_error(int error_code, string_view message, const Args & ... args)
+  system_error(int error_code, string_view message, const Args &... args)
     : std::runtime_error("") {
     init(error_code, message, make_format_args(args...));
   }
@@ -2415,9 +2264,7 @@ class basic_writer {
 
  private:
   iterator out_;  // Output iterator.
-  std::unique_ptr<locale_provider> locale_;
-
-  iterator out() const { return out_; }
+  internal::locale_ref locale_;
 
   // Attempts to reserve space for n extra characters in the output range.
   // Returns a pointer to the reserved range or a reference to out_.
@@ -2429,19 +2276,44 @@ class basic_writer {
   //   <left-padding><value><right-padding>
   // where <value> is written by f(it).
   template <typename F>
-  void write_padded(std::size_t size, const align_spec &spec, F &&f);
+  void write_padded(const align_spec &spec, F &&f) {
+    unsigned width = spec.width(); // User-perceived width (in code points).
+    size_t size = f.size(); // The number of code units.
+    size_t num_code_points = width != 0 ? f.width() : size;
+    if (width <= num_code_points)
+      return f(reserve(size));
+    auto &&it = reserve(width + (size - num_code_points));
+    char_type fill = static_cast<char_type>(spec.fill());
+    std::size_t padding = width - num_code_points;
+    if (spec.align() == ALIGN_RIGHT) {
+      it = std::fill_n(it, padding, fill);
+      f(it);
+    } else if (spec.align() == ALIGN_CENTER) {
+      std::size_t left_padding = padding / 2;
+      it = std::fill_n(it, left_padding, fill);
+      f(it);
+      it = std::fill_n(it, padding - left_padding, fill);
+    } else {
+      f(it);
+      it = std::fill_n(it, padding, fill);
+    }
+  }
 
   template <typename F>
   struct padded_int_writer {
+    size_t size_;
     string_view prefix;
     char_type fill;
     std::size_t padding;
     F f;
 
+    size_t size() const { return size_; }
+    size_t width() const { return size_; }
+
     template <typename It>
     void operator()(It &&it) const {
       if (prefix.size() != 0)
-        it = std::copy_n(prefix.data(), prefix.size(), it);
+        it = internal::copy_str<char_type>(prefix.begin(), prefix.end(), it);
       it = std::fill_n(it, padding, fill);
       f(it);
     }
@@ -2451,9 +2323,9 @@ class basic_writer {
   //   <left-padding><prefix><numeric-padding><digits><right-padding>
   // where <digits> are written by f(it).
   template <typename Spec, typename F>
-  void write_int(unsigned num_digits, string_view prefix,
+  void write_int(int num_digits, string_view prefix,
                  const Spec &spec, F f) {
-    std::size_t size = prefix.size() + num_digits;
+    std::size_t size = prefix.size() + internal::to_unsigned(num_digits);
     char_type fill = static_cast<char_type>(spec.fill());
     std::size_t padding = 0;
     if (spec.align() == ALIGN_NUMERIC) {
@@ -2461,15 +2333,15 @@ class basic_writer {
         padding = spec.width() - size;
         size = spec.width();
       }
-    } else if (spec.precision() > static_cast<int>(num_digits)) {
-      size = prefix.size() + static_cast<std::size_t>(spec.precision());
-      padding = static_cast<std::size_t>(spec.precision()) - num_digits;
-      fill = '0';
+    } else if (spec.precision > num_digits) {
+      size = prefix.size() + internal::to_unsigned(spec.precision);
+      padding = internal::to_unsigned(spec.precision - num_digits);
+      fill = static_cast<char_type>('0');
     }
     align_spec as = spec;
     if (spec.align() == ALIGN_DEFAULT)
       as.align_ = ALIGN_RIGHT;
-    write_padded(size, as, padded_int_writer<F>{prefix, fill, padding, f});
+    write_padded(as, padded_int_writer<F>{size, prefix, fill, padding, f});
   }
 
   // Writes a decimal integer.
@@ -2480,11 +2352,11 @@ class basic_writer {
     bool is_negative = internal::is_negative(value);
     if (is_negative)
       abs_value = 0 - abs_value;
-    unsigned num_digits = internal::count_digits(abs_value);
-    auto &&it = reserve((is_negative ? 1 : 0) + num_digits);
+    int num_digits = internal::count_digits(abs_value);
+    auto &&it = reserve((is_negative ? 1 : 0) + static_cast<size_t>(num_digits));
     if (is_negative)
-      *it++ = '-';
-    it = internal::format_decimal(it, abs_value, num_digits);
+      *it++ = static_cast<char_type>('-');
+    it = internal::format_decimal<char_type>(it, abs_value, num_digits);
   }
 
   // The handle_int_type_spec handler that writes an integer.
@@ -2502,9 +2374,9 @@ class basic_writer {
 
     // Counts the number of digits in abs_value. BITS = log2(radix).
     template <unsigned BITS>
-    unsigned count_digits() const {
+    int count_digits() const {
       unsigned_type n = abs_value;
-      unsigned num_digits = 0;
+      int num_digits = 0;
       do {
         ++num_digits;
       } while ((n >>= BITS) != 0);
@@ -2518,45 +2390,45 @@ class basic_writer {
         prefix[0] = '-';
         ++prefix_size;
         abs_value = 0 - abs_value;
-      } else if (spec.flag(SIGN_FLAG)) {
-        prefix[0] = spec.flag(PLUS_FLAG) ? '+' : ' ';
+      } else if (spec.has(SIGN_FLAG)) {
+        prefix[0] = spec.has(PLUS_FLAG) ? '+' : ' ';
         ++prefix_size;
       }
     }
 
     struct dec_writer {
       unsigned_type abs_value;
-      unsigned num_digits;
+      int num_digits;
 
       template <typename It>
       void operator()(It &&it) const {
-        it = internal::format_decimal(it, abs_value, num_digits);
+        it = internal::format_decimal<char_type>(it, abs_value, num_digits);
       }
     };
 
     void on_dec() {
-      unsigned num_digits = internal::count_digits(abs_value);
+      int num_digits = internal::count_digits(abs_value);
       writer.write_int(num_digits, get_prefix(), spec,
                        dec_writer{abs_value, num_digits});
     }
 
     struct hex_writer {
       int_writer &self;
-      unsigned num_digits;
+      int num_digits;
 
       template <typename It>
       void operator()(It &&it) const {
-        it = internal::format_uint<4>(it, self.abs_value, num_digits,
-                                      self.spec.type() != 'x');
+        it = internal::format_uint<4, char_type>(
+              it, self.abs_value, num_digits, self.spec.type != 'x');
       }
     };
 
     void on_hex() {
-      if (spec.flag(HASH_FLAG)) {
+      if (spec.has(HASH_FLAG)) {
         prefix[prefix_size++] = '0';
-        prefix[prefix_size++] = static_cast<char>(spec.type());
+        prefix[prefix_size++] = static_cast<char>(spec.type);
       }
-      unsigned num_digits = count_digits<4>();
+      int num_digits = count_digits<4>();
       writer.write_int(num_digits, get_prefix(), spec,
                        hex_writer{*this, num_digits});
     }
@@ -2564,28 +2436,28 @@ class basic_writer {
     template <int BITS>
     struct bin_writer {
       unsigned_type abs_value;
-      unsigned num_digits;
+      int num_digits;
 
       template <typename It>
       void operator()(It &&it) const {
-        it = internal::format_uint<BITS>(it, abs_value, num_digits);
+        it = internal::format_uint<BITS, char_type>(it, abs_value, num_digits);
       }
     };
 
     void on_bin() {
-      if (spec.flag(HASH_FLAG)) {
+      if (spec.has(HASH_FLAG)) {
         prefix[prefix_size++] = '0';
-        prefix[prefix_size++] = static_cast<char>(spec.type());
+        prefix[prefix_size++] = static_cast<char>(spec.type);
       }
-      unsigned num_digits = count_digits<1>();
+      int num_digits = count_digits<1>();
       writer.write_int(num_digits, get_prefix(), spec,
                        bin_writer<1>{abs_value, num_digits});
     }
 
     void on_oct() {
-      unsigned num_digits = count_digits<3>();
-      if (spec.flag(HASH_FLAG) &&
-          spec.precision() <= static_cast<int>(num_digits)) {
+      int num_digits = count_digits<3>();
+      if (spec.has(HASH_FLAG) &&
+          spec.precision <= num_digits) {
         // Octal prefix '0' is counted as a digit, so only add it if precision
         // is not greater than the number of digits.
         prefix[prefix_size++] = '0';
@@ -2598,21 +2470,21 @@ class basic_writer {
 
     struct num_writer {
       unsigned_type abs_value;
-      unsigned size;
+      int size;
       char_type sep;
 
       template <typename It>
       void operator()(It &&it) const {
         basic_string_view<char_type> s(&sep, SEP_SIZE);
-        it = format_decimal(it, abs_value, size,
-                            internal::add_thousands_sep<char_type>(s));
+        it = internal::format_decimal<char_type>(
+              it, abs_value, size, internal::add_thousands_sep<char_type>(s));
       }
     };
 
     void on_num() {
-      unsigned num_digits = internal::count_digits(abs_value);
-      char_type sep = internal::thousands_sep<char_type>(writer.locale_.get());
-      unsigned size = num_digits + SEP_SIZE * ((num_digits - 1) / 3);
+      int num_digits = internal::count_digits(abs_value);
+      char_type sep = internal::thousands_sep<char_type>(writer.locale_);
+      int size = num_digits + SEP_SIZE * ((num_digits - 1) / 3);
       writer.write_int(size, get_prefix(), spec,
                        num_writer{abs_value, size, sep});
     }
@@ -2625,7 +2497,7 @@ class basic_writer {
   // Writes a formatted integer.
   template <typename T, typename Spec>
   void write_int(T value, const Spec &spec) {
-    internal::handle_int_type_spec(spec.type(),
+    internal::handle_int_type_spec(spec.type,
                                    int_writer<T, Spec>(*this, value, spec));
   }
 
@@ -2635,71 +2507,68 @@ class basic_writer {
     char sign;
     const char *str;
 
+    size_t size() const {
+      return static_cast<std::size_t>(INF_SIZE + (sign ? 1 : 0));
+    }
+    size_t width() const { return size(); }
+
     template <typename It>
     void operator()(It &&it) const {
       if (sign)
-        *it++ = sign;
-      it = std::copy_n(str, static_cast<std::size_t>(INF_SIZE), it);
+        *it++ = static_cast<char_type>(sign);
+      it = internal::copy_str<char_type>(
+            str, str + static_cast<std::size_t>(INF_SIZE), it);
     }
   };
 
   struct double_writer {
     size_t n;
     char sign;
-    basic_memory_buffer<char_type> &buffer;
+    internal::buffer &buffer;
+
+    size_t size() const { return buffer.size() + (sign ? 1 : 0); }
+    size_t width() const { return size(); }
 
     template <typename It>
     void operator()(It &&it) {
       if (sign) {
-        *it++ = sign;
+        *it++ = static_cast<char_type>(sign);
         --n;
       }
-      it = std::copy_n(buffer.begin(), n, it);
+      it = internal::copy_str<char_type>(buffer.begin(), buffer.end(), it);
     }
   };
 
   // Formats a floating-point number (double or long double).
   template <typename T>
   void write_double(T value, const format_specs &spec);
-  template <typename T>
-  void write_double_sprintf(T value, const format_specs &spec,
-                            internal::basic_buffer<char_type>& buffer);
 
   template <typename Char>
   struct str_writer {
     const Char *s;
-    std::size_t size;
+    size_t size_;
+
+    size_t size() const { return size_; }
+    size_t width() const {
+      return internal::count_code_points(basic_string_view<Char>(s, size_));
+    }
 
     template <typename It>
     void operator()(It &&it) const {
-      it = std::copy_n(s, size, it);
+      it = internal::copy_str<char_type>(s, s + size_, it);
     }
   };
 
-  // Writes a formatted string.
-  template <typename Char>
-  void write_str(const Char *s, std::size_t size, const align_spec &spec) {
-    write_padded(size, spec, str_writer<Char>{s, size});
-  }
-
-  template <typename Char>
-  void write_str(basic_string_view<Char> str, const format_specs &spec);
-
-  // Appends floating-point length specifier to the format string.
-  // The second argument is only used for overload resolution.
-  void append_float_length(char_type *&format_ptr, long double) {
-    *format_ptr++ = 'L';
-  }
-
-  template<typename T>
-  void append_float_length(char_type *&, T) {}
-
   template <typename Char>
   friend class internal::arg_formatter_base;
 
  public:
   /** Constructs a ``basic_writer`` object. */
-  explicit basic_writer(Range out): out_(out.begin()) {}
+  explicit basic_writer(
+      Range out, internal::locale_ref loc = internal::locale_ref())
+    : out_(out.begin()), locale_(loc) {}
+
+  iterator out() const { return out_; }
 
   void write(int value) { write_decimal(value); }
   void write(long value) { write_decimal(value); }
@@ -2740,9 +2609,8 @@ class basic_writer {
   void write(char value) {
     *reserve(1) = value;
   }
-
   void write(wchar_t value) {
-    internal::require_wchar<char_type>();
+    static_assert(std::is_same<char_type, wchar_t>::value, "");
     *reserve(1) = value;
   }
 
@@ -2753,72 +2621,45 @@ class basic_writer {
    */
   void write(string_view value) {
     auto &&it = reserve(value.size());
-    it = std::copy(value.begin(), value.end(), it);
+    it = internal::copy_str<char_type>(value.begin(), value.end(), it);
   }
-
   void write(wstring_view value) {
-    internal::require_wchar<char_type>();
+    static_assert(std::is_same<char_type, wchar_t>::value, "");
     auto &&it = reserve(value.size());
     it = std::copy(value.begin(), value.end(), it);
   }
 
-  template <typename... FormatSpecs>
-  void write(basic_string_view<char_type> str, FormatSpecs... specs) {
-    write_str(str, format_specs(specs...));
+  // Writes a formatted string.
+  template <typename Char>
+  void write(const Char *s, std::size_t size, const align_spec &spec) {
+    write_padded(spec, str_writer<Char>{s, size});
+  }
+
+  template <typename Char>
+  void write(basic_string_view<Char> s,
+             const format_specs &spec = format_specs()) {
+    const Char *data = s.data();
+    std::size_t size = s.size();
+    if (spec.precision >= 0 && internal::to_unsigned(spec.precision) < size)
+      size = internal::to_unsigned(spec.precision);
+    write(data, size, spec);
   }
 
   template <typename T>
   typename std::enable_if<std::is_same<T, void>::value>::type
       write(const T *p) {
     format_specs specs;
-    specs.flags_ = HASH_FLAG;
-    specs.type_ = 'x';
+    specs.flags = HASH_FLAG;
+    specs.type = 'x';
     write_int(reinterpret_cast<uintptr_t>(p), specs);
   }
 };
 
-template <typename Range>
-template <typename F>
-void basic_writer<Range>::write_padded(
-    std::size_t size, const align_spec &spec, F &&f) {
-  unsigned width = spec.width();
-  if (width <= size)
-    return f(reserve(size));
-  auto &&it = reserve(width);
-  char_type fill = static_cast<char_type>(spec.fill());
-  std::size_t padding = width - size;
-  if (spec.align() == ALIGN_RIGHT) {
-    it = std::fill_n(it, padding, fill);
-    f(it);
-  } else if (spec.align() == ALIGN_CENTER) {
-    std::size_t left_padding = padding / 2;
-    it = std::fill_n(it, left_padding, fill);
-    f(it);
-    it = std::fill_n(it, padding - left_padding, fill);
-  } else {
-    f(it);
-    it = std::fill_n(it, padding, fill);
-  }
-}
-
-template <typename Range>
-template <typename Char>
-void basic_writer<Range>::write_str(
-    basic_string_view<Char> s, const format_specs &spec) {
-  const Char *data = s.data();
-  std::size_t size = s.size();
-  std::size_t precision = static_cast<std::size_t>(spec.precision_);
-  if (spec.precision_ >= 0 && precision < size)
-    size = precision;
-  write_str(data, size, spec);
-}
-
-template <typename Char>
 struct float_spec_handler {
-  Char type;
+  char type;
   bool upper;
 
-  explicit float_spec_handler(Char t) : type(t), upper(false) {}
+  explicit float_spec_handler(char t) : type(t), upper(false) {}
 
   void on_general() {
     if (type == 'G')
@@ -2856,17 +2697,17 @@ template <typename Range>
 template <typename T>
 void basic_writer<Range>::write_double(T value, const format_specs &spec) {
   // Check type.
-  float_spec_handler<char_type> handler(spec.type());
-  internal::handle_float_type_spec(spec.type(), handler);
+  float_spec_handler handler(static_cast<char>(spec.type));
+  internal::handle_float_type_spec(handler.type, handler);
 
   char sign = 0;
-  // Use isnegative instead of value < 0 because the latter is always
+  // Use signbit instead of value < 0 because the latter is always
   // false for NaN.
-  if (internal::fputil::isnegative(static_cast<double>(value))) {
+  if (std::signbit(value)) {
     sign = '-';
     value = -value;
-  } else if (spec.flag(SIGN_FLAG)) {
-    sign = spec.flag(PLUS_FLAG) ? '+' : ' ';
+  } else if (spec.has(SIGN_FLAG)) {
+    sign = spec.has(PLUS_FLAG) ? '+' : ' ';
   }
 
   struct write_inf_or_nan_t {
@@ -2874,8 +2715,7 @@ void basic_writer<Range>::write_double(T value, const format_specs &spec) {
     format_specs spec;
     char sign;
     void operator()(const char *str) const {
-      writer.write_padded(INF_SIZE + (sign ? 1 : 0), spec,
-                          inf_or_nan_writer{sign, str});
+      writer.write_padded(spec, inf_or_nan_writer{sign, str});
     }
   } write_inf_or_nan = {*this, spec, sign};
 
@@ -2886,28 +2726,21 @@ void basic_writer<Range>::write_double(T value, const format_specs &spec) {
   if (internal::fputil::isinfinity(value))
     return write_inf_or_nan(handler.upper ? "INF" : "inf");
 
-  basic_memory_buffer<char_type> buffer;
-  char type = static_cast<char>(spec.type());
-  if (internal::const_check(
-        internal::use_grisu() && sizeof(T) <= sizeof(double)) &&
-      type != 'a' && type != 'A') {
-    char buf[100]; // TODO: correct buffer size
-    size_t size = 0;
-    internal::grisu2_format(static_cast<double>(value), buf, size, type,
-                            spec.precision(), spec.flag(HASH_FLAG));
-    FMT_ASSERT(size <= 100, "buffer overflow");
-    buffer.append(buf, buf + size); // TODO: avoid extra copy
-  } else {
+  memory_buffer buffer;
+  bool use_grisu = FMT_USE_GRISU && sizeof(T) <= sizeof(double) &&
+      spec.type != 'a' && spec.type != 'A' &&
+      internal::grisu2_format(static_cast<double>(value), buffer, spec);
+  if (!use_grisu) {
     format_specs normalized_spec(spec);
-    normalized_spec.type_ = handler.type;
-    write_double_sprintf(value, normalized_spec, buffer);
+    normalized_spec.type = handler.type;
+    internal::sprintf_format(value, buffer, normalized_spec);
   }
   size_t n = buffer.size();
   align_spec as = spec;
   if (spec.align() == ALIGN_NUMERIC) {
     if (sign) {
       auto &&it = reserve(1);
-      *it++ = sign;
+      *it++ = static_cast<char_type>(sign);
       sign = 0;
       if (as.width_)
         --as.width_;
@@ -2919,53 +2752,7 @@ void basic_writer<Range>::write_double(T value, const format_specs &spec) {
     if (sign)
       ++n;
   }
-  write_padded(n, as, double_writer{n, sign, buffer});
-}
-
-template <typename Range>
-template <typename T>
-void basic_writer<Range>::write_double_sprintf(
-    T value, const format_specs &spec,
-    internal::basic_buffer<char_type>& buffer) {
-  // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
-  FMT_ASSERT(buffer.capacity() != 0, "empty buffer");
-
-  // Build format string.
-  enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg
-  char_type format[MAX_FORMAT_SIZE];
-  char_type *format_ptr = format;
-  *format_ptr++ = '%';
-  if (spec.flag(HASH_FLAG))
-    *format_ptr++ = '#';
-  if (spec.precision() >= 0) {
-    *format_ptr++ = '.';
-    *format_ptr++ = '*';
-  }
-
-  append_float_length(format_ptr, value);
-  *format_ptr++ = spec.type();
-  *format_ptr = '\0';
-
-  // Format using snprintf.
-  char_type *start = FMT_NULL;
-  for (;;) {
-    std::size_t buffer_size = buffer.capacity();
-    start = &buffer[0];
-    int result = internal::char_traits<char_type>::format_float(
-        start, buffer_size, format, spec.precision(), value);
-    if (result >= 0) {
-      unsigned n = internal::to_unsigned(result);
-      if (n < buffer.capacity()) {
-        buffer.resize(n);
-        break;  // The buffer is large enough - continue with formatting.
-      }
-      buffer.reserve(n + 1);
-    } else {
-      // If result is negative we ask to increase the capacity by at least 1,
-      // but as std::vector, the buffer grows exponentially.
-      buffer.reserve(buffer.capacity() + 1);
-    }
-  }
+  write_padded(as, double_writer{n, sign, buffer});
 }
 
 // Reports a system error without throwing an exception.
@@ -3010,7 +2797,7 @@ class windows_error : public system_error {
    \endrst
   */
   template <typename... Args>
-  windows_error(int error_code, string_view message, const Args & ... args) {
+  windows_error(int error_code, string_view message, const Args &... args) {
     init(error_code, message, make_format_args(args...));
   }
 };
@@ -3033,7 +2820,7 @@ class format_int {
 
   // Formats value in reverse and returns a pointer to the beginning.
   char *format_decimal(unsigned long long value) {
-    char *ptr = buffer_ + BUFFER_SIZE - 1;
+    char *ptr = buffer_ + (BUFFER_SIZE - 1);  // Parens to workaround MSVC bug.
     while (value >= 100) {
       // Integer division is slow so do it for a group of two digits instead
       // of for every digit. The idea comes from the talk by Alexandrescu
@@ -3099,6 +2886,7 @@ class format_int {
   std::string str() const { return std::string(str_, size()); }
 };
 
+// DEPRECATED!
 // Formats a decimal integer value writing into buffer and returns
 // a pointer to the end of the formatted string. This function doesn't
 // write a terminating null character.
@@ -3120,8 +2908,9 @@ inline void format_decimal(char *&buffer, T value) {
     *buffer++ = internal::data::DIGITS[index + 1];
     return;
   }
-  unsigned num_digits = internal::count_digits(abs_value);
-  internal::format_decimal(buffer, abs_value, num_digits);
+  int num_digits = internal::count_digits(abs_value);
+  internal::format_decimal<char>(
+        internal::make_checked(buffer, internal::to_unsigned(num_digits)), abs_value, num_digits);
   buffer += num_digits;
 }
 
@@ -3136,14 +2925,13 @@ struct formatter<
   // terminating '}'.
   template <typename ParseContext>
   FMT_CONSTEXPR typename ParseContext::iterator parse(ParseContext &ctx) {
-    auto it = internal::null_terminating_iterator<Char>(ctx);
     typedef internal::dynamic_specs_handler<ParseContext> handler_type;
     auto type = internal::get_type<
       typename buffer_context<Char>::type, T>::value;
     internal::specs_checker<handler_type>
         handler(handler_type(specs_, ctx), type);
-    it = parse_format_specs(it, handler);
-    auto type_spec = specs_.type();
+    auto it = parse_format_specs(ctx.begin(), ctx.end(), handler);
+    auto type_spec = specs_.type;
     auto eh = ctx.error_handler();
     switch (type) {
     case internal::none_type:
@@ -3161,8 +2949,7 @@ struct formatter<
     case internal::char_type:
       handle_char_specs(
           &specs_,
-          internal::char_specs_checker<decltype(eh), decltype(type_spec)>(
-              type_spec, eh));
+          internal::char_specs_checker<decltype(eh)>(type_spec, eh));
       break;
     case internal::double_type:
     case internal::long_double_type:
@@ -3184,7 +2971,7 @@ struct formatter<
       // formatter specializations.
       break;
     }
-    return pointer_from(it);
+    return it;
   }
 
   template <typename FormatContext>
@@ -3192,10 +2979,10 @@ struct formatter<
     internal::handle_dynamic_spec<internal::width_checker>(
       specs_.width_, specs_.width_ref, ctx);
     internal::handle_dynamic_spec<internal::precision_checker>(
-      specs_.precision_, specs_.precision_ref, ctx);
+      specs_.precision, specs_.precision_ref, ctx);
     typedef output_range<typename FormatContext::iterator,
                          typename FormatContext::char_type> range_type;
-    return fmt::visit(arg_formatter<range_type>(ctx, &specs_),
+    return visit_format_arg(arg_formatter<range_type>(ctx, &specs_),
                       internal::make_arg<FormatContext>(val));
   }
 
@@ -3227,11 +3014,9 @@ class dynamic_formatter {
  public:
   template <typename ParseContext>
   auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
-    auto it = internal::null_terminating_iterator<Char>(ctx);
     // Checks are deferred to formatting time when the argument type is known.
     internal::dynamic_specs_handler<ParseContext> handler(specs_, ctx);
-    it = parse_format_specs(it, handler);
-    return pointer_from(it);
+    return parse_format_specs(ctx.begin(), ctx.end(), handler);
   }
 
   template <typename T, typename FormatContext>
@@ -3240,23 +3025,18 @@ class dynamic_formatter {
     internal::specs_checker<null_handler>
         checker(null_handler(), internal::get_type<FormatContext, T>::value);
     checker.on_align(specs_.align());
-    if (specs_.flags_ == 0) {
-      // Do nothing.
-    } else if (specs_.flag(SIGN_FLAG)) {
-      if (specs_.flag(PLUS_FLAG))
-        checker.on_plus();
-      else
-        checker.on_space();
-    } else if (specs_.flag(MINUS_FLAG)) {
+    if (specs_.flags == 0);  // Do nothing.
+    else if (specs_.has(SIGN_FLAG))
+      specs_.has(PLUS_FLAG) ? checker.on_plus() : checker.on_space();
+    else if (specs_.has(MINUS_FLAG))
       checker.on_minus();
-    } else if (specs_.flag(HASH_FLAG)) {
+    else if (specs_.has(HASH_FLAG))
       checker.on_hash();
-    }
-    if (specs_.precision_ != -1)
+    if (specs_.precision != -1)
       checker.end_precision();
     typedef output_range<typename FormatContext::iterator,
                          typename FormatContext::char_type> range;
-    fmt::visit(arg_formatter<range>(ctx, &specs_),
+    visit_format_arg(arg_formatter<range>(ctx, &specs_),
                internal::make_arg<FormatContext>(val));
     return ctx.out();
   }
@@ -3267,7 +3047,7 @@ class dynamic_formatter {
     internal::handle_dynamic_spec<internal::width_checker>(
       specs_.width_, specs_.width_ref, ctx);
     internal::handle_dynamic_spec<internal::precision_checker>(
-      specs_.precision_, specs_.precision_ref, ctx);
+      specs_.precision, specs_.precision_ref, ctx);
   }
 
   internal::dynamic_format_specs<Char> specs_;
@@ -3286,12 +3066,12 @@ typename basic_format_context<Range, Char>::format_arg
 
 template <typename ArgFormatter, typename Char, typename Context>
 struct format_handler : internal::error_handler {
-  typedef internal::null_terminating_iterator<Char> iterator;
   typedef typename ArgFormatter::range range;
 
   format_handler(range r, basic_string_view<Char> str,
-                 basic_format_args<Context> format_args)
-    : context(r.begin(), str, format_args) {}
+                 basic_format_args<Context> format_args,
+                 internal::locale_ref loc)
+    : context(r.begin(), str, format_args, loc) {}
 
   void on_text(const Char *begin, const Char *end) {
     auto size = internal::to_unsigned(end - begin);
@@ -3312,25 +3092,27 @@ struct format_handler : internal::error_handler {
 
   void on_replacement_field(const Char *p) {
     context.parse_context().advance_to(p);
-    if (!fmt::visit(internal::custom_formatter<Char, Context>(context), arg))
-      context.advance_to(fmt::visit(ArgFormatter(context), arg));
+    internal::custom_formatter<Char, Context> f(context);
+    if (!visit_format_arg(f, arg))
+      context.advance_to(visit_format_arg(ArgFormatter(context), arg));
   }
 
-  iterator on_format_specs(iterator it) {
-    auto& parse_ctx = context.parse_context();
-    parse_ctx.advance_to(pointer_from(it));
-    if (fmt::visit(internal::custom_formatter<Char, Context>(context), arg))
-      return iterator(parse_ctx);
+  const Char *on_format_specs(const Char *begin, const Char *end) {
+    auto &parse_ctx = context.parse_context();
+    parse_ctx.advance_to(begin);
+    internal::custom_formatter<Char, Context> f(context);
+    if (visit_format_arg(f, arg))
+      return parse_ctx.begin();
     basic_format_specs<Char> specs;
     using internal::specs_handler;
     internal::specs_checker<specs_handler<Context>>
         handler(specs_handler<Context>(specs, context), arg.type());
-    it = parse_format_specs(it, handler);
-    if (*it != '}')
+    begin = parse_format_specs(begin, end, handler);
+    if (begin == end || *begin != '}')
       on_error("missing '}' in format string");
-    parse_ctx.advance_to(pointer_from(it));
-    context.advance_to(fmt::visit(ArgFormatter(context, &specs), arg));
-    return it;
+    parse_ctx.advance_to(begin);
+    context.advance_to(visit_format_arg(ArgFormatter(context, &specs), arg));
+    return begin;
   }
 
   Context context;
@@ -3339,10 +3121,12 @@ struct format_handler : internal::error_handler {
 
 /** Formats arguments and writes the output to the range. */
 template <typename ArgFormatter, typename Char, typename Context>
-typename Context::iterator vformat_to(typename ArgFormatter::range out,
-                                      basic_string_view<Char> format_str,
-                                      basic_format_args<Context> args) {
-  format_handler<ArgFormatter, Char, Context> h(out, format_str, args);
+typename Context::iterator vformat_to(
+    typename ArgFormatter::range out,
+    basic_string_view<Char> format_str,
+    basic_format_args<Context> args,
+    internal::locale_ref loc = internal::locale_ref()) {
+  format_handler<ArgFormatter, Char, Context> h(out, format_str, args, loc);
   internal::parse_format_string<false>(format_str, h);
   return h.context.out();
 }
@@ -3445,31 +3229,80 @@ std::basic_string<Char> to_string(const basic_memory_buffer<Char, SIZE> &buf) {
   return std::basic_string<Char>(buf.data(), buf.size());
 }
 
-inline format_context::iterator vformat_to(
-    internal::buffer &buf, string_view format_str, format_args args) {
-  typedef back_insert_range<internal::buffer> range;
-  return vformat_to<arg_formatter<range>>(buf, format_str, args);
+template <typename Char>
+typename buffer_context<Char>::type::iterator internal::vformat_to(
+    internal::basic_buffer<Char> &buf, basic_string_view<Char> format_str,
+    basic_format_args<typename buffer_context<Char>::type> args) {
+  typedef back_insert_range<internal::basic_buffer<Char> > range;
+  return vformat_to<arg_formatter<range>>(
+    buf, to_string_view(format_str), args);
 }
 
-inline wformat_context::iterator vformat_to(
-    internal::wbuffer &buf, wstring_view format_str, wformat_args args) {
-  typedef back_insert_range<internal::wbuffer> range;
-  return vformat_to<arg_formatter<range>>(buf, format_str, args);
+template <typename S, typename Char = FMT_CHAR(S)>
+inline typename buffer_context<Char>::type::iterator vformat_to(
+    internal::basic_buffer<Char> &buf, const S &format_str,
+    basic_format_args<typename buffer_context<Char>::type> args) {
+  return internal::vformat_to(buf, to_string_view(format_str), args);
 }
 
 template <
-    typename String, typename... Args,
+    typename S, typename... Args,
     std::size_t SIZE = inline_buffer_size,
-    typename Char = typename internal::format_string_traits<String>::char_type>
+    typename Char = typename internal::char_t<S>::type>
 inline typename buffer_context<Char>::type::iterator format_to(
-    basic_memory_buffer<Char, SIZE> &buf, const String &format_str,
-    const Args & ... args) {
+    basic_memory_buffer<Char, SIZE> &buf, const S &format_str,
+    const Args &... args) {
   internal::check_format_string<Args...>(format_str);
-  return vformat_to(
-        buf, basic_string_view<Char>(format_str),
-        make_format_args<typename buffer_context<Char>::type>(args...));
+  typedef typename buffer_context<Char>::type context;
+  format_arg_store<context, Args...> as{args...};
+  return internal::vformat_to(buf, to_string_view(format_str),
+                              basic_format_args<context>(as));
 }
 
+namespace internal {
+
+// Detect the iterator category of *any* given type in a SFINAE-friendly way.
+// Unfortunately, older implementations of std::iterator_traits are not safe
+// for use in a SFINAE-context.
+
+// the gist of C++17's void_t magic
+template<typename... Ts>
+struct void_ { typedef void type; };
+
+template <typename T, typename Enable = void>
+struct it_category : std::false_type {};
+
+template <typename T>
+struct it_category<T*> { typedef std::random_access_iterator_tag type; };
+
+template <typename T>
+struct it_category<T, typename void_<typename T::iterator_category>::type> {
+  typedef typename T::iterator_category type;
+};
+
+// Detect if *any* given type models the OutputIterator concept.
+template <typename It>
+class is_output_iterator {
+  // Check for mutability because all iterator categories derived from
+  // std::input_iterator_tag *may* also meet the requirements of an
+  // OutputIterator, thereby falling into the category of 'mutable iterators'
+  // [iterator.requirements.general] clause 4.
+  // The compiler reveals this property only at the point of *actually
+  // dereferencing* the iterator!
+  template <typename U>
+  static decltype(*(internal::declval<U>())) test(std::input_iterator_tag);
+  template <typename U>
+  static char& test(std::output_iterator_tag);
+  template <typename U>
+  static const char& test(...);
+
+  typedef decltype(test<It>(typename it_category<It>::type{})) type;
+  typedef typename std::remove_reference<type>::type result;
+ public:
+  static const bool value = !std::is_const<result>::value;
+};
+} // internal
+
 template <typename OutputIt, typename Char = char>
 //using format_context_t = basic_format_context<OutputIt, Char>;
 struct format_context_t { typedef basic_format_context<OutputIt, Char> type; };
@@ -3481,18 +3314,14 @@ struct format_args_t {
     typename format_context_t<OutputIt, Char>::type> type;
 };
 
-template <typename OutputIt, typename... Args>
-inline OutputIt vformat_to(OutputIt out, string_view format_str,
-                           typename format_args_t<OutputIt>::type args) {
-  typedef output_range<OutputIt, char> range;
-  return vformat_to<arg_formatter<range>>(range(out), format_str, args);
-}
-template <typename OutputIt, typename... Args>
-inline OutputIt vformat_to(
-    OutputIt out, wstring_view format_str,
-    typename format_args_t<OutputIt, wchar_t>::type args) {
-  typedef output_range<OutputIt, wchar_t> range;
-  return vformat_to<arg_formatter<range>>(range(out), format_str, args);
+template <typename String, typename OutputIt, typename... Args>
+inline typename std::enable_if<internal::is_output_iterator<OutputIt>::value,
+                               OutputIt>::type
+    vformat_to(OutputIt out, const String &format_str,
+               typename format_args_t<OutputIt, FMT_CHAR(String)>::type args) {
+  typedef output_range<OutputIt, FMT_CHAR(String)> range;
+  return vformat_to<arg_formatter<range>>(range(out),
+                                          to_string_view(format_str), args);
 }
 
 /**
@@ -3506,11 +3335,16 @@ inline OutputIt vformat_to(
    fmt::format_to(std::back_inserter(out), "{}", 42);
  \endrst
  */
-template <typename OutputIt, typename... Args>
-inline OutputIt format_to(OutputIt out, string_view format_str,
-                          const Args & ... args) {
-  return vformat_to(out, format_str,
-      make_format_args<typename format_context_t<OutputIt>::type>(args...));
+template <typename OutputIt, typename S, typename... Args>
+inline FMT_ENABLE_IF_T(
+    internal::is_string<S>::value &&
+    internal::is_output_iterator<OutputIt>::value, OutputIt)
+    format_to(OutputIt out, const S &format_str, const Args &... args) {
+  internal::check_format_string<Args...>(format_str);
+  typedef typename format_context_t<OutputIt, FMT_CHAR(S)>::type context;
+  format_arg_store<context, Args...> as{args...};
+  return vformat_to(out, to_string_view(format_str),
+                    basic_format_args<context>(as));
 }
 
 template <typename OutputIt>
@@ -3521,23 +3355,30 @@ struct format_to_n_result {
   std::size_t size;
 };
 
-template <typename OutputIt>
-using format_to_n_context = typename fmt::format_context_t<
-  fmt::internal::truncating_iterator<OutputIt>>::type;
+template <typename OutputIt, typename Char = typename OutputIt::value_type>
+struct format_to_n_context :
+  format_context_t<fmt::internal::truncating_iterator<OutputIt>, Char> {};
 
-template <typename OutputIt>
-using format_to_n_args = fmt::basic_format_args<format_to_n_context<OutputIt>>;
+template <typename OutputIt, typename Char = typename OutputIt::value_type>
+struct format_to_n_args {
+  typedef basic_format_args<
+    typename format_to_n_context<OutputIt, Char>::type> type;
+};
 
-template <typename OutputIt, typename ...Args>
-inline format_arg_store<format_to_n_context<OutputIt>, Args...>
-    make_format_to_n_args(const Args & ... args) {
-  return format_arg_store<format_to_n_context<OutputIt>, Args...>(args...);
+template <typename OutputIt, typename Char, typename ...Args>
+inline format_arg_store<
+  typename format_to_n_context<OutputIt, Char>::type, Args...>
+    make_format_to_n_args(const Args &... args) {
+  return format_arg_store<
+    typename format_to_n_context<OutputIt, Char>::type, Args...>(args...);
 }
 
-template <typename OutputIt, typename... Args>
-inline format_to_n_result<OutputIt> vformat_to_n(
-    OutputIt out, std::size_t n, string_view format_str,
-    format_to_n_args<OutputIt> args) {
+template <typename OutputIt, typename Char, typename... Args>
+inline typename std::enable_if<
+    internal::is_output_iterator<OutputIt>::value,
+    format_to_n_result<OutputIt>>::type vformat_to_n(
+    OutputIt out, std::size_t n, basic_string_view<Char> format_str,
+    typename format_to_n_args<OutputIt, Char>::type args) {
   typedef internal::truncating_iterator<OutputIt> It;
   auto it = vformat_to(It(out, n), format_str, args);
   return {it.base(), it.count()};
@@ -3550,20 +3391,19 @@ inline format_to_n_result<OutputIt> vformat_to_n(
  end of the output range.
  \endrst
  */
-template <typename OutputIt, typename... Args>
-inline format_to_n_result<OutputIt> format_to_n(
-    OutputIt out, std::size_t n, string_view format_str, const Args &... args) {
-  return vformat_to_n<OutputIt>(
-    out, n, format_str, make_format_to_n_args<OutputIt>(args...));
-}
-template <typename OutputIt, typename... Args>
-inline format_to_n_result<OutputIt> format_to_n(
-    OutputIt out, std::size_t n, wstring_view format_str,
-    const Args &... args) {
-  typedef internal::truncating_iterator<OutputIt> It;
-  auto it = vformat_to(It(out, n), format_str,
-      make_format_args<typename format_context_t<It, wchar_t>::type>(args...));
-  return {it.base(), it.count()};
+template <typename OutputIt, typename S, typename... Args>
+inline FMT_ENABLE_IF_T(
+    internal::is_string<S>::value &&
+    internal::is_output_iterator<OutputIt>::value,
+    format_to_n_result<OutputIt>)
+    format_to_n(OutputIt out, std::size_t n, const S &format_str,
+                const Args &... args) {
+  internal::check_format_string<Args...>(format_str);
+  typedef FMT_CHAR(S) Char;
+  format_arg_store<
+      typename format_to_n_context<OutputIt, Char>::type, Args...> as(args...);
+  return vformat_to_n(out, n, to_string_view(format_str),
+                      typename format_to_n_args<OutputIt, Char>::type(as));
 }
 
 template <typename Char>
@@ -3571,24 +3411,17 @@ inline std::basic_string<Char> internal::vformat(
     basic_string_view<Char> format_str,
     basic_format_args<typename buffer_context<Char>::type> args) {
   basic_memory_buffer<Char> buffer;
-  vformat_to(buffer, format_str, args);
+  internal::vformat_to(buffer, format_str, args);
   return fmt::to_string(buffer);
 }
 
-template <typename String, typename... Args>
-inline typename std::enable_if<internal::is_compile_string<String>::value>::type
-    print(String format_str, const Args & ... args) {
-  internal::check_format_string<Args...>(format_str);
-  return vprint(format_str.data(), make_format_args(args...));
-}
-
 /**
   Returns the number of characters in the output of
   ``format(format_str, args...)``.
  */
 template <typename... Args>
 inline std::size_t formatted_size(string_view format_str,
-                                  const Args & ... args) {
+                                  const Args &... args) {
   auto it = format_to(internal::counting_iterator<char>(), format_str, args...);
   return it.count();
 }
@@ -3604,7 +3437,7 @@ class udl_formatter {
   std::basic_string<Char> operator()(const Args &... args) const {
     FMT_CONSTEXPR_DECL Char s[] = {CHARS..., '\0'};
     FMT_CONSTEXPR_DECL bool invalid_format =
-        check_format_string<Char, error_handler, Args...>(
+        do_check_format_string<Char, error_handler, Args...>(
           basic_string_view<Char>(s, sizeof...(CHARS)));
     (void)invalid_format;
     return format(s, args...);
@@ -3616,7 +3449,7 @@ struct udl_formatter {
   const Char *str;
 
   template <typename... Args>
-  auto operator()(Args && ... args) const
+  auto operator()(Args &&... args) const
                   -> decltype(format(str, std::forward<Args>(args)...)) {
     return format(str, std::forward<Args>(args)...);
   }
@@ -3678,13 +3511,15 @@ operator"" _a(const wchar_t *s, std::size_t) { return {s}; }
 FMT_END_NAMESPACE
 
 #define FMT_STRING(s) [] { \
-    typedef typename std::decay<decltype(s)>::type pointer; \
-    struct S : fmt::compile_string { \
-      static FMT_CONSTEXPR pointer data() { return s; } \
-      static FMT_CONSTEXPR size_t size() { return sizeof(s); } \
-      explicit operator fmt::string_view() const { return s; } \
+    typedef typename std::remove_cv<std::remove_pointer< \
+      typename std::decay<decltype(s)>::type>::type>::type ct; \
+    struct str : fmt::compile_string { \
+      typedef ct char_type; \
+      FMT_CONSTEXPR operator fmt::basic_string_view<ct>() const { \
+        return {s, sizeof(s) / sizeof(ct) - 1}; \
+      } \
     }; \
-    return S{}; \
+    return str{}; \
   }()
 
 #if defined(FMT_STRING_ALIAS) && FMT_STRING_ALIAS
diff --git a/external/spdlog/fmt/bundled/locale.h b/external/spdlog/fmt/bundled/locale.h
new file mode 100644
index 000000000..8e021bc49
--- /dev/null
+++ b/external/spdlog/fmt/bundled/locale.h
@@ -0,0 +1,77 @@
+// Formatting library for C++ - std::locale support
+//
+// Copyright (c) 2012 - present, Victor Zverovich
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+#ifndef FMT_LOCALE_H_
+#define FMT_LOCALE_H_
+
+#include "format.h"
+#include <locale>
+
+FMT_BEGIN_NAMESPACE
+
+namespace internal {
+template <typename Char>
+typename buffer_context<Char>::type::iterator vformat_to(
+    const std::locale &loc, basic_buffer<Char> &buf,
+    basic_string_view<Char> format_str,
+    basic_format_args<typename buffer_context<Char>::type> args) {
+  typedef back_insert_range<basic_buffer<Char> > range;
+  return vformat_to<arg_formatter<range>>(
+    buf, to_string_view(format_str), args, internal::locale_ref(loc));
+}
+
+template <typename Char>
+std::basic_string<Char> vformat(
+    const std::locale &loc, basic_string_view<Char> format_str,
+    basic_format_args<typename buffer_context<Char>::type> args) {
+  basic_memory_buffer<Char> buffer;
+  internal::vformat_to(loc, buffer, format_str, args);
+  return fmt::to_string(buffer);
+}
+}
+
+template <typename S, typename Char = FMT_CHAR(S)>
+inline std::basic_string<Char> vformat(
+    const std::locale &loc, const S &format_str,
+    basic_format_args<typename buffer_context<Char>::type> args) {
+  return internal::vformat(loc, to_string_view(format_str), args);
+}
+
+template <typename S, typename... Args>
+inline std::basic_string<FMT_CHAR(S)> format(
+    const std::locale &loc, const S &format_str, const Args &... args) {
+  return internal::vformat(
+    loc, to_string_view(format_str),
+    *internal::checked_args<S, Args...>(format_str, args...));
+}
+
+template <typename String, typename OutputIt, typename... Args>
+inline typename std::enable_if<internal::is_output_iterator<OutputIt>::value,
+                               OutputIt>::type
+    vformat_to(OutputIt out, const std::locale &loc, const String &format_str,
+               typename format_args_t<OutputIt, FMT_CHAR(String)>::type args) {
+  typedef output_range<OutputIt, FMT_CHAR(String)> range;
+  return vformat_to<arg_formatter<range>>(
+    range(out), to_string_view(format_str), args, internal::locale_ref(loc));
+}
+
+template <typename OutputIt, typename S, typename... Args>
+inline typename std::enable_if<
+    internal::is_string<S>::value &&
+    internal::is_output_iterator<OutputIt>::value, OutputIt>::type
+    format_to(OutputIt out, const std::locale &loc, const S &format_str,
+              const Args &... args) {
+  internal::check_format_string<Args...>(format_str);
+  typedef typename format_context_t<OutputIt, FMT_CHAR(S)>::type context;
+  format_arg_store<context, Args...> as{args...};
+  return vformat_to(out, loc, to_string_view(format_str),
+                    basic_format_args<context>(as));
+}
+
+FMT_END_NAMESPACE
+
+#endif  // FMT_LOCALE_H_
diff --git a/external/spdlog/fmt/bundled/ostream.h b/external/spdlog/fmt/bundled/ostream.h
index e467f1ae6..84b31cc55 100644
--- a/external/spdlog/fmt/bundled/ostream.h
+++ b/external/spdlog/fmt/bundled/ostream.h
@@ -1,6 +1,6 @@
 // Formatting library for C++ - std::ostream support
 //
-// Copyright (c) 2012 - 2016, Victor Zverovich
+// Copyright (c) 2012 - present, Victor Zverovich
 // All rights reserved.
 //
 // For the license information refer to format.h.
@@ -129,7 +129,7 @@ inline void vprint(std::basic_ostream<Char> &os,
                    basic_string_view<Char> format_str,
                    basic_format_args<typename buffer_context<Char>::type> args) {
   basic_memory_buffer<Char> buffer;
-  vformat_to(buffer, format_str, args);
+  internal::vformat_to(buffer, format_str, args);
   internal::write(os, buffer);
 }
 /**
@@ -141,16 +141,12 @@ inline void vprint(std::basic_ostream<Char> &os,
     fmt::print(cerr, "Don't {}!", "panic");
   \endrst
  */
-template <typename... Args>
-inline void print(std::ostream &os, string_view format_str,
-                  const Args & ... args) {
-  vprint<char>(os, format_str, make_format_args<format_context>(args...));
-}
-
-template <typename... Args>
-inline void print(std::wostream &os, wstring_view format_str,
-                  const Args & ... args) {
-  vprint<wchar_t>(os, format_str, make_format_args<wformat_context>(args...));
+template <typename S, typename... Args>
+inline typename std::enable_if<internal::is_string<S>::value>::type
+print(std::basic_ostream<FMT_CHAR(S)> &os, const S &format_str,
+      const Args & ... args) {
+  internal::checked_args<S, Args...> ca(format_str, args...);
+  vprint(os, to_string_view(format_str), *ca);
 }
 FMT_END_NAMESPACE
 
diff --git a/external/spdlog/fmt/bundled/posix.h b/external/spdlog/fmt/bundled/posix.h
index 7bf877b45..f4e3fad7e 100644
--- a/external/spdlog/fmt/bundled/posix.h
+++ b/external/spdlog/fmt/bundled/posix.h
@@ -137,7 +137,7 @@ class buffered_file {
   buffered_file() FMT_NOEXCEPT : file_(FMT_NULL) {}
 
   // Destroys the object closing the file it represents if any.
-  FMT_API ~buffered_file() FMT_DTOR_NOEXCEPT;
+  FMT_API ~buffered_file() FMT_NOEXCEPT;
 
  private:
   buffered_file(const buffered_file &) = delete;
@@ -223,7 +223,7 @@ class file {
   }
 
   // Destroys the object closing the file it represents if any.
-  FMT_API ~file() FMT_DTOR_NOEXCEPT;
+  FMT_API ~file() FMT_NOEXCEPT;
 
   // Returns the file descriptor.
   int descriptor() const FMT_NOEXCEPT { return fd_; }
diff --git a/external/spdlog/fmt/bundled/printf.h b/external/spdlog/fmt/bundled/printf.h
index 190784df0..6f2715d9f 100644
--- a/external/spdlog/fmt/bundled/printf.h
+++ b/external/spdlog/fmt/bundled/printf.h
@@ -16,6 +16,130 @@
 FMT_BEGIN_NAMESPACE
 namespace internal {
 
+// An iterator that produces a null terminator on *end. This simplifies parsing
+// and allows comparing the performance of processing a null-terminated string
+// vs string_view.
+template <typename Char>
+class null_terminating_iterator {
+ public:
+  typedef std::ptrdiff_t difference_type;
+  typedef Char value_type;
+  typedef const Char* pointer;
+  typedef const Char& reference;
+  typedef std::random_access_iterator_tag iterator_category;
+
+  null_terminating_iterator() : ptr_(0), end_(0) {}
+
+  FMT_CONSTEXPR null_terminating_iterator(const Char *ptr, const Char *end)
+    : ptr_(ptr), end_(end) {}
+
+  template <typename Range>
+  FMT_CONSTEXPR explicit null_terminating_iterator(const Range &r)
+    : ptr_(r.begin()), end_(r.end()) {}
+
+  FMT_CONSTEXPR null_terminating_iterator &operator=(const Char *ptr) {
+    assert(ptr <= end_);
+    ptr_ = ptr;
+    return *this;
+  }
+
+  FMT_CONSTEXPR Char operator*() const {
+    return ptr_ != end_ ? *ptr_ : Char();
+  }
+
+  FMT_CONSTEXPR null_terminating_iterator operator++() {
+    ++ptr_;
+    return *this;
+  }
+
+  FMT_CONSTEXPR null_terminating_iterator operator++(int) {
+    null_terminating_iterator result(*this);
+    ++ptr_;
+    return result;
+  }
+
+  FMT_CONSTEXPR null_terminating_iterator operator--() {
+    --ptr_;
+    return *this;
+  }
+
+  FMT_CONSTEXPR null_terminating_iterator operator+(difference_type n) {
+    return null_terminating_iterator(ptr_ + n, end_);
+  }
+
+  FMT_CONSTEXPR null_terminating_iterator operator-(difference_type n) {
+    return null_terminating_iterator(ptr_ - n, end_);
+  }
+
+  FMT_CONSTEXPR null_terminating_iterator operator+=(difference_type n) {
+    ptr_ += n;
+    return *this;
+  }
+
+  FMT_CONSTEXPR difference_type operator-(
+      null_terminating_iterator other) const {
+    return ptr_ - other.ptr_;
+  }
+
+  FMT_CONSTEXPR bool operator!=(null_terminating_iterator other) const {
+    return ptr_ != other.ptr_;
+  }
+
+  bool operator>=(null_terminating_iterator other) const {
+    return ptr_ >= other.ptr_;
+  }
+
+  // This should be a friend specialization pointer_from<Char> but the latter
+  // doesn't compile by gcc 5.1 due to a compiler bug.
+  template <typename CharT>
+  friend FMT_CONSTEXPR_DECL const CharT *pointer_from(
+      null_terminating_iterator<CharT> it);
+
+ private:
+  const Char *ptr_;
+  const Char *end_;
+};
+
+template <typename T>
+FMT_CONSTEXPR const T *pointer_from(const T *p) { return p; }
+
+template <typename Char>
+FMT_CONSTEXPR const Char *pointer_from(null_terminating_iterator<Char> it) {
+  return it.ptr_;
+}
+
+// DEPRECATED: Parses the input as an unsigned integer. This function assumes
+// that the first character is a digit and presence of a non-digit character at
+// the end.
+// it: an iterator pointing to the beginning of the input range.
+template <typename Iterator, typename ErrorHandler>
+FMT_CONSTEXPR unsigned parse_nonnegative_int(Iterator &it, ErrorHandler &&eh) {
+  assert('0' <= *it && *it <= '9');
+  if (*it == '0') {
+    ++it;
+    return 0;
+  }
+  unsigned value = 0;
+  // Convert to unsigned to prevent a warning.
+  unsigned max_int = (std::numeric_limits<int>::max)();
+  unsigned big = max_int / 10;
+  do {
+    // Check for overflow.
+    if (value > big) {
+      value = max_int + 1;
+      break;
+    }
+    value = value * 10 + unsigned(*it - '0');
+    // Workaround for MSVC "setup_exception stack overflow" error:
+    auto next = it;
+    ++next;
+    it = next;
+  } while ('0' <= *it && *it <= '9');
+  if (value > max_int)
+    eh.on_error("number is too big");
+  return value;
+}
+
 // Checks if a value fits in int - used to avoid warnings about comparing
 // signed and unsigned integers.
 template <bool IsSigned>
@@ -133,7 +257,7 @@ class arg_converter: public function<void> {
 // unsigned).
 template <typename T, typename Context, typename Char>
 void convert_arg(basic_format_arg<Context> &arg, Char type) {
-  fmt::visit(arg_converter<T, Context>(arg, type), arg);
+  visit_format_arg(arg_converter<T, Context>(arg, type), arg);
 }
 
 // Converts an integer argument to char for printf.
@@ -192,8 +316,16 @@ class printf_width_handler: public function<unsigned> {
     return 0;
   }
 };
+
+template <typename Char, typename Context>
+void printf(basic_buffer<Char> &buf, basic_string_view<Char> format,
+            basic_format_args<Context> args) {
+  Context(std::back_inserter(buf), format, args).format();
+}
 }  // namespace internal
 
+using internal::printf;  // For printing into memory_buffer.
+
 template <typename Range>
 class printf_arg_formatter;
 
@@ -222,12 +354,12 @@ class printf_arg_formatter:
   context_type &context_;
 
   void write_null_pointer(char) {
-    this->spec()->type_ = 0;
+    this->spec()->type = 0;
     this->write("(nil)");
   }
 
   void write_null_pointer(wchar_t) {
-    this->spec()->type_ = 0;
+    this->spec()->type = 0;
     this->write(L"(nil)");
   }
 
@@ -243,7 +375,8 @@ class printf_arg_formatter:
    */
   printf_arg_formatter(internal::basic_buffer<char_type> &buffer,
                        format_specs &spec, context_type &ctx)
-    : base(back_insert_range<internal::basic_buffer<char_type>>(buffer), &spec),
+    : base(back_insert_range<internal::basic_buffer<char_type>>(buffer), &spec,
+           ctx.locale()),
       context_(ctx) {}
 
   template <typename T>
@@ -253,15 +386,15 @@ class printf_arg_formatter:
     // use std::is_same instead.
     if (std::is_same<T, bool>::value) {
       format_specs &fmt_spec = *this->spec();
-      if (fmt_spec.type_ != 's')
+      if (fmt_spec.type != 's')
         return base::operator()(value ? 1 : 0);
-      fmt_spec.type_ = 0;
+      fmt_spec.type = 0;
       this->write(value != 0);
     } else if (std::is_same<T, char_type>::value) {
       format_specs &fmt_spec = *this->spec();
-      if (fmt_spec.type_ && fmt_spec.type_ != 'c')
+      if (fmt_spec.type && fmt_spec.type != 'c')
         return (*this)(static_cast<int>(value));
-      fmt_spec.flags_ = 0;
+      fmt_spec.flags = 0;
       fmt_spec.align_ = ALIGN_RIGHT;
       return base::operator()(value);
     } else {
@@ -280,7 +413,7 @@ class printf_arg_formatter:
   iterator operator()(const char *value) {
     if (value)
       base::operator()(value);
-    else if (this->spec()->type_ == 'p')
+    else if (this->spec()->type == 'p')
       write_null_pointer(char_type());
     else
       this->write("(null)");
@@ -291,7 +424,7 @@ class printf_arg_formatter:
   iterator operator()(const wchar_t *value) {
     if (value)
       base::operator()(value);
-    else if (this->spec()->type_ == 'p')
+    else if (this->spec()->type == 'p')
       write_null_pointer(char_type());
     else
       this->write(L"(null)");
@@ -310,7 +443,7 @@ class printf_arg_formatter:
   iterator operator()(const void *value) {
     if (value)
       return base::operator()(value);
-    this->spec()->type_ = 0;
+    this->spec()->type = 0;
     write_null_pointer(char_type());
     return this->out();
   }
@@ -394,16 +527,16 @@ void basic_printf_context<OutputIt, Char, AF>::parse_flags(
         spec.align_ = ALIGN_LEFT;
         break;
       case '+':
-        spec.flags_ |= SIGN_FLAG | PLUS_FLAG;
+        spec.flags |= SIGN_FLAG | PLUS_FLAG;
         break;
       case '0':
         spec.fill_ = '0';
         break;
       case ' ':
-        spec.flags_ |= SIGN_FLAG;
+        spec.flags |= SIGN_FLAG;
         break;
       case '#':
-        spec.flags_ |= HASH_FLAG;
+        spec.flags |= HASH_FLAG;
         break;
       default:
         --it;
@@ -453,8 +586,8 @@ unsigned basic_printf_context<OutputIt, Char, AF>::parse_header(
     spec.width_ = parse_nonnegative_int(it, eh);
   } else if (*it == '*') {
     ++it;
-    spec.width_ =
-        fmt::visit(internal::printf_width_handler<char_type>(spec), get_arg(it));
+    spec.width_ = visit_format_arg(
+          internal::printf_width_handler<char_type>(spec), get_arg(it));
   }
   return arg_index;
 }
@@ -486,19 +619,19 @@ void basic_printf_context<OutputIt, Char, AF>::format() {
       ++it;
       if ('0' <= *it && *it <= '9') {
         internal::error_handler eh;
-        spec.precision_ = static_cast<int>(parse_nonnegative_int(it, eh));
+        spec.precision = static_cast<int>(parse_nonnegative_int(it, eh));
       } else if (*it == '*') {
         ++it;
-        spec.precision_ =
-            fmt::visit(internal::printf_precision_handler(), get_arg(it));
+        spec.precision =
+            visit_format_arg(internal::printf_precision_handler(), get_arg(it));
       } else {
-        spec.precision_ = 0;
+        spec.precision = 0;
       }
     }
 
     format_arg arg = get_arg(it, arg_index);
-    if (spec.flag(HASH_FLAG) && fmt::visit(internal::is_zero_int(), arg))
-      spec.flags_ &= ~internal::to_unsigned<int>(HASH_FLAG);
+    if (spec.has(HASH_FLAG) && visit_format_arg(internal::is_zero_int(), arg))
+      spec.flags = static_cast<uint_least8_t>(spec.flags & (~internal::to_unsigned<int>(HASH_FLAG)));
     if (spec.fill_ == '0') {
       if (arg.is_arithmetic())
         spec.align_ = ALIGN_NUMERIC;
@@ -542,16 +675,17 @@ void basic_printf_context<OutputIt, Char, AF>::format() {
     // Parse type.
     if (!*it)
       FMT_THROW(format_error("invalid format string"));
-    spec.type_ = static_cast<char>(*it++);
+    spec.type = static_cast<char>(*it++);
     if (arg.is_integral()) {
       // Normalize type.
-      switch (spec.type_) {
+      switch (spec.type) {
       case 'i': case 'u':
-        spec.type_ = 'd';
+        spec.type = 'd';
         break;
       case 'c':
         // TODO: handle wchar_t better?
-        fmt::visit(internal::char_converter<basic_printf_context>(arg), arg);
+        visit_format_arg(
+              internal::char_converter<basic_printf_context>(arg), arg);
         break;
       }
     }
@@ -559,35 +693,50 @@ void basic_printf_context<OutputIt, Char, AF>::format() {
     start = it;
 
     // Format argument.
-    fmt::visit(AF(buffer, spec, *this), arg);
+    visit_format_arg(AF(buffer, spec, *this), arg);
   }
   buffer.append(pointer_from(start), pointer_from(it));
 }
 
-template <typename Char, typename Context>
-void printf(internal::basic_buffer<Char> &buf, basic_string_view<Char> format,
-            basic_format_args<Context> args) {
-  Context(std::back_inserter(buf), format, args).format();
-}
-
 template <typename Buffer>
-struct printf_context {
+struct basic_printf_context_t {
   typedef basic_printf_context<
     std::back_insert_iterator<Buffer>, typename Buffer::value_type> type;
 };
 
-template <typename ...Args>
-inline format_arg_store<printf_context<internal::buffer>::type, Args...>
-    make_printf_args(const Args & ... args) {
-  return format_arg_store<printf_context<internal::buffer>::type, Args...>(
-      args...);
-}
-typedef basic_format_args<printf_context<internal::buffer>::type> printf_args;
-typedef basic_format_args<printf_context<internal::wbuffer>::type> wprintf_args;
+typedef basic_printf_context_t<internal::buffer>::type printf_context;
+typedef basic_printf_context_t<internal::wbuffer>::type wprintf_context;
+
+typedef basic_format_args<printf_context> printf_args;
+typedef basic_format_args<wprintf_context> wprintf_args;
 
-inline std::string vsprintf(string_view format, printf_args args) {
-  memory_buffer buffer;
-  printf(buffer, format, args);
+/**
+  \rst
+  Constructs an `~fmt::format_arg_store` object that contains references to
+  arguments and can be implicitly converted to `~fmt::printf_args`. 
+  \endrst
+ */
+template<typename... Args>
+inline format_arg_store<printf_context, Args...>
+  make_printf_args(const Args &... args) { return {args...}; }
+
+/**
+  \rst
+  Constructs an `~fmt::format_arg_store` object that contains references to
+  arguments and can be implicitly converted to `~fmt::wprintf_args`. 
+  \endrst
+ */
+template<typename... Args>
+inline format_arg_store<wprintf_context, Args...>
+  make_wprintf_args(const Args &... args) { return {args...}; }
+
+template <typename S, typename Char = FMT_CHAR(S)>
+inline std::basic_string<Char>
+vsprintf(const S &format,
+         basic_format_args<typename basic_printf_context_t<
+           internal::basic_buffer<Char>>::type> args) {
+  basic_memory_buffer<Char> buffer;
+  printf(buffer, to_string_view(format), args);
   return to_string(buffer);
 }
 
@@ -600,30 +749,24 @@ inline std::string vsprintf(string_view format, printf_args args) {
     std::string message = fmt::sprintf("The answer is %d", 42);
   \endrst
 */
-template <typename... Args>
-inline std::string sprintf(string_view format_str, const Args & ... args) {
-  return vsprintf(format_str,
-    make_format_args<typename printf_context<internal::buffer>::type>(args...));
-}
-
-inline std::wstring vsprintf(wstring_view format, wprintf_args args) {
-  wmemory_buffer buffer;
-  printf(buffer, format, args);
-  return to_string(buffer);
-}
-
-template <typename... Args>
-inline std::wstring sprintf(wstring_view format_str, const Args & ... args) {
-  return vsprintf(format_str,
-    make_format_args<typename printf_context<internal::wbuffer>::type>(args...));
+template <typename S, typename... Args>
+inline FMT_ENABLE_IF_T(
+    internal::is_string<S>::value, std::basic_string<FMT_CHAR(S)>)
+    sprintf(const S &format, const Args & ... args) {
+  internal::check_format_string<Args...>(format);
+  typedef internal::basic_buffer<FMT_CHAR(S)> buffer;
+  typedef typename basic_printf_context_t<buffer>::type context;
+  format_arg_store<context, Args...> as{ args... };
+  return vsprintf(to_string_view(format),
+                  basic_format_args<context>(as));
 }
 
-template <typename Char>
-inline int vfprintf(std::FILE *f, basic_string_view<Char> format,
-                    basic_format_args<typename printf_context<
+template <typename S, typename Char = FMT_CHAR(S)>
+inline int vfprintf(std::FILE *f, const S &format,
+                    basic_format_args<typename basic_printf_context_t<
                       internal::basic_buffer<Char>>::type> args) {
   basic_memory_buffer<Char> buffer;
-  printf(buffer, format, args);
+  printf(buffer, to_string_view(format), args);
   std::size_t size = buffer.size();
   return std::fwrite(
     buffer.data(), sizeof(Char), size, f) < size ? -1 : static_cast<int>(size);
@@ -638,26 +781,22 @@ inline int vfprintf(std::FILE *f, basic_string_view<Char> format,
     fmt::fprintf(stderr, "Don't %s!", "panic");
   \endrst
  */
-template <typename... Args>
-inline int fprintf(std::FILE *f, string_view format_str, const Args & ... args) {
-  auto vargs = make_format_args<
-    typename printf_context<internal::buffer>::type>(args...);
-  return vfprintf<char>(f, format_str, vargs);
-}
-
-template <typename... Args>
-inline int fprintf(std::FILE *f, wstring_view format_str,
-                   const Args & ... args) {
-  return vfprintf(f, format_str,
-    make_format_args<typename printf_context<internal::wbuffer>::type>(args...));
-}
-
-inline int vprintf(string_view format, printf_args args) {
-  return vfprintf(stdout, format, args);
+template <typename S, typename... Args>
+inline FMT_ENABLE_IF_T(internal::is_string<S>::value, int)
+    fprintf(std::FILE *f, const S &format, const Args & ... args) {
+  internal::check_format_string<Args...>(format);
+  typedef internal::basic_buffer<FMT_CHAR(S)> buffer;
+  typedef typename basic_printf_context_t<buffer>::type context;
+  format_arg_store<context, Args...> as{ args... };
+  return vfprintf(f, to_string_view(format),
+                  basic_format_args<context>(as));
 }
 
-inline int vprintf(wstring_view format, wprintf_args args) {
-  return vfprintf(stdout, format, args);
+template <typename S, typename Char = FMT_CHAR(S)>
+inline int vprintf(const S &format,
+                   basic_format_args<typename basic_printf_context_t<
+                    internal::basic_buffer<Char>>::type> args) {
+  return vfprintf(stdout, to_string_view(format), args);
 }
 
 /**
@@ -669,30 +808,24 @@ inline int vprintf(wstring_view format, wprintf_args args) {
     fmt::printf("Elapsed time: %.2f seconds", 1.23);
   \endrst
  */
-template <typename... Args>
-inline int printf(string_view format_str, const Args & ... args) {
-  return vprintf(format_str,
-    make_format_args<typename printf_context<internal::buffer>::type>(args...));
+template <typename S, typename... Args>
+inline FMT_ENABLE_IF_T(internal::is_string<S>::value, int)
+    printf(const S &format_str, const Args & ... args) {
+  internal::check_format_string<Args...>(format_str);
+  typedef internal::basic_buffer<FMT_CHAR(S)> buffer;
+  typedef typename basic_printf_context_t<buffer>::type context;
+  format_arg_store<context, Args...> as{ args... };
+  return vprintf(to_string_view(format_str),
+                 basic_format_args<context>(as));
 }
 
-template <typename... Args>
-inline int printf(wstring_view format_str, const Args & ... args) {
-  return vprintf(format_str,
-    make_format_args<typename printf_context<internal::wbuffer>::type>(args...));
-}
-
-inline int vfprintf(std::ostream &os, string_view format_str,
-                    printf_args args) {
-  memory_buffer buffer;
-  printf(buffer, format_str, args);
-  internal::write(os, buffer);
-  return static_cast<int>(buffer.size());
-}
-
-inline int vfprintf(std::wostream &os, wstring_view format_str,
-                    wprintf_args args) {
-  wmemory_buffer buffer;
-  printf(buffer, format_str, args);
+template <typename S, typename Char = FMT_CHAR(S)>
+inline int vfprintf(std::basic_ostream<Char> &os,
+                    const S &format,
+                    basic_format_args<typename basic_printf_context_t<
+                      internal::basic_buffer<Char>>::type> args) {
+  basic_memory_buffer<Char> buffer;
+  printf(buffer, to_string_view(format), args);
   internal::write(os, buffer);
   return static_cast<int>(buffer.size());
 }
@@ -706,20 +839,16 @@ inline int vfprintf(std::wostream &os, wstring_view format_str,
     fmt::fprintf(cerr, "Don't %s!", "panic");
   \endrst
  */
-template <typename... Args>
-inline int fprintf(std::ostream &os, string_view format_str,
-                   const Args & ... args) {
-  auto vargs = make_format_args<
-    typename printf_context<internal::buffer>::type>(args...);
-  return vfprintf(os, format_str, vargs);
-}
-
-template <typename... Args>
-inline int fprintf(std::wostream &os, wstring_view format_str,
-                   const Args & ... args) {
-  auto vargs = make_format_args<
-    typename printf_context<internal::buffer>::type>(args...);
-  return vfprintf(os, format_str, vargs);
+template <typename S, typename... Args>
+inline FMT_ENABLE_IF_T(internal::is_string<S>::value, int)
+    fprintf(std::basic_ostream<FMT_CHAR(S)> &os,
+            const S &format_str, const Args & ... args) {
+  internal::check_format_string<Args...>(format_str);
+  typedef internal::basic_buffer<FMT_CHAR(S)> buffer;
+  typedef typename basic_printf_context_t<buffer>::type context;
+  format_arg_store<context, Args...> as{ args... };
+  return vfprintf(os, to_string_view(format_str),
+                  basic_format_args<context>(as));
 }
 FMT_END_NAMESPACE
 
diff --git a/external/spdlog/fmt/bundled/time.h b/external/spdlog/fmt/bundled/time.h
index a624058f3..fe798916f 100644
--- a/external/spdlog/fmt/bundled/time.h
+++ b/external/spdlog/fmt/bundled/time.h
@@ -1,6 +1,6 @@
 // Formatting library for C++ - time formatting
 //
-// Copyright (c) 2012 - 2016, Victor Zverovich
+// Copyright (c) 2012 - present, Victor Zverovich
 // All rights reserved.
 //
 // For the license information refer to format.h.
@@ -10,6 +10,7 @@
 
 #include "format.h"
 #include <ctime>
+#include <locale>
 
 FMT_BEGIN_NAMESPACE
 
@@ -22,7 +23,7 @@ inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); }
 inline null<> localtime_s(...) { return null<>(); }
 inline null<> gmtime_r(...) { return null<>(); }
 inline null<> gmtime_s(...) { return null<>(); }
-}
+}  // namespace internal
 
 // Thread-safe replacement for std::localtime
 inline std::tm localtime(std::time_t time) {
@@ -46,18 +47,20 @@ inline std::tm localtime(std::time_t time) {
 
     bool fallback(int res) { return res == 0; }
 
+#if !FMT_MSC_VER
     bool fallback(internal::null<>) {
       using namespace fmt::internal;
       std::tm *tm = std::localtime(&time_);
       if (tm) tm_ = *tm;
       return tm != FMT_NULL;
     }
+#endif
   };
   dispatcher lt(time);
-  if (lt.run())
-    return lt.tm_;
   // Too big time values may be unsupported.
-  FMT_THROW(format_error("time_t value out of range"));
+  if (!lt.run())
+    FMT_THROW(format_error("time_t value out of range"));
+  return lt.tm_;
 }
 
 // Thread-safe replacement for std::gmtime
@@ -82,17 +85,19 @@ inline std::tm gmtime(std::time_t time) {
 
     bool fallback(int res) { return res == 0; }
 
+#if !FMT_MSC_VER
     bool fallback(internal::null<>) {
       std::tm *tm = std::gmtime(&time_);
       if (tm) tm_ = *tm;
       return tm != FMT_NULL;
     }
+#endif
   };
   dispatcher gt(time);
-  if (gt.run())
-    return gt.tm_;
   // Too big time values may be unsupported.
-  FMT_THROW(format_error("time_t value out of range"));
+  if (!gt.run())
+    FMT_THROW(format_error("time_t value out of range"));
+  return gt.tm_;
 }
 
 namespace internal {
@@ -111,22 +116,21 @@ template <typename Char>
 struct formatter<std::tm, Char> {
   template <typename ParseContext>
   auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
-    auto it = internal::null_terminating_iterator<Char>(ctx);
-    if (*it == ':')
+    auto it = ctx.begin();
+    if (it != ctx.end() && *it == ':')
       ++it;
     auto end = it;
-    while (*end && *end != '}')
+    while (end != ctx.end() && *end != '}')
       ++end;
-    tm_format.reserve(end - it + 1);
-    using internal::pointer_from;
-    tm_format.append(pointer_from(it), pointer_from(end));
+    tm_format.reserve(internal::to_unsigned(end - it + 1));
+    tm_format.append(it, end);
     tm_format.push_back('\0');
-    return pointer_from(end);
+    return end;
   }
 
   template <typename FormatContext>
   auto format(const std::tm &tm, FormatContext &ctx) -> decltype(ctx.out()) {
-    internal::basic_buffer<Char> &buf = internal::get_container(ctx.out());
+    basic_memory_buffer<Char> buf;
     std::size_t start = buf.size();
     for (;;) {
       std::size_t size = buf.capacity() - start;
@@ -146,7 +150,7 @@ struct formatter<std::tm, Char> {
       const std::size_t MIN_GROWTH = 10;
       buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
     }
-    return ctx.out();
+    return std::copy(buf.begin(), buf.end(), ctx.out());
   }
 
   basic_memory_buffer<Char> tm_format;
diff --git a/external/spdlog/logger.h b/external/spdlog/logger.h
index 1bcf4bc29..3dbaaa641 100644
--- a/external/spdlog/logger.h
+++ b/external/spdlog/logger.h
@@ -22,7 +22,6 @@
 #include "spdlog/formatter.h"
 #include "spdlog/sinks/sink.h"
 
-#include <locale>
 #include <memory>
 #include <string>
 #include <vector>
@@ -47,8 +46,12 @@ public:
     void log(level::level_enum lvl, const char *fmt, const Args &... args);
 
     template<typename... Args>
+    void log(source_loc loc, level::level_enum lvl, const char *fmt, const Args &... args);
+
     void log(level::level_enum lvl, const char *msg);
 
+    void log(source_loc loc, level::level_enum lvl, const char *msg);
+
     template<typename... Args>
     void trace(const char *fmt, const Args &... args);
 
@@ -74,6 +77,9 @@ public:
     template<typename... Args>
     void log(level::level_enum lvl, const wchar_t *fmt, const Args &... args);
 
+    template<typename... Args>
+    void log(source_loc source, level::level_enum lvl, const wchar_t *fmt, const Args &... args);
+
     template<typename... Args>
     void trace(const wchar_t *fmt, const Args &... args);
 
@@ -94,9 +100,22 @@ public:
 #endif // _WIN32
 #endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
 
-    template<typename T>
+    // T can be statically converted to string_view
+    template<class T, typename std::enable_if<std::is_convertible<T, spdlog::string_view_t>::value, T>::type * = nullptr>
     void log(level::level_enum lvl, const T &);
 
+    // T can be statically converted to string_view
+    template<class T, typename std::enable_if<std::is_convertible<T, spdlog::string_view_t>::value, T>::type * = nullptr>
+    void log(source_loc loc, level::level_enum lvl, const T &);
+
+    // T cannot be statically converted to string_view
+    template<class T, typename std::enable_if<!std::is_convertible<T, spdlog::string_view_t>::value, T>::type * = nullptr>
+    void log(level::level_enum lvl, const T &);
+
+    // T cannot be statically converted to string_view
+    template<class T, typename std::enable_if<!std::is_convertible<T, spdlog::string_view_t>::value, T>::type * = nullptr>
+    void log(source_loc loc, level::level_enum lvl, const T &);
+
     template<typename T>
     void trace(const T &msg);
 
@@ -117,6 +136,8 @@ public:
 
     bool should_log(level::level_enum msg_level) const;
     void set_level(level::level_enum log_level);
+
+    static level::level_enum default_level();
     level::level_enum level() const;
     const std::string &name() const;
 
@@ -136,7 +157,7 @@ public:
 
     // error handler
     void set_error_handler(log_err_handler err_handler);
-    log_err_handler error_handler();
+    log_err_handler error_handler() const;
 
     // create new logger with same sinks and configuration.
     virtual std::shared_ptr<logger> clone(std::string logger_name);
@@ -147,8 +168,8 @@ protected:
 
     bool should_flush_(const details::log_msg &msg);
 
-    // default error handler: print the error to stderr with the max rate of 1
-    // message/minute
+    // default error handler.
+    // print the error to stderr with the max rate of 1 message/minute.
     void default_err_handler_(const std::string &msg);
 
     // increment the message count (only if defined(SPDLOG_ENABLE_MESSAGE_COUNTER))
@@ -156,11 +177,11 @@ protected:
 
     const std::string name_;
     std::vector<sink_ptr> sinks_;
-    spdlog::level_t level_;
-    spdlog::level_t flush_level_;
-    log_err_handler err_handler_;
-    std::atomic<time_t> last_err_time_;
-    std::atomic<size_t> msg_counter_;
+    spdlog::level_t level_{spdlog::logger::default_level()};
+    spdlog::level_t flush_level_{level::off};
+    log_err_handler err_handler_{[this](const std::string &msg) { this->default_err_handler_(msg); }};
+    std::atomic<time_t> last_err_time_{0};
+    std::atomic<size_t> msg_counter_{1};
 };
 } // namespace spdlog
 
diff --git a/external/spdlog/sinks/android_sink.h b/external/spdlog/sinks/android_sink.h
index 44bbbf8c2..ae7f77336 100644
--- a/external/spdlog/sinks/android_sink.h
+++ b/external/spdlog/sinks/android_sink.h
@@ -5,6 +5,10 @@
 
 #pragma once
 
+#ifndef SPDLOG_H
+#include "spdlog/spdlog.h"
+#endif
+
 #include "spdlog/details/fmt_helper.h"
 #include "spdlog/details/null_mutex.h"
 #include "spdlog/details/os.h"
@@ -43,7 +47,7 @@ protected:
         fmt::memory_buffer formatted;
         if (use_raw_msg_)
         {
-            details::fmt_helper::append_buf(msg.raw, formatted);
+            details::fmt_helper::append_string_view(msg.payload, formatted);
         }
         else
         {
diff --git a/external/spdlog/sinks/ansicolor_sink.h b/external/spdlog/sinks/ansicolor_sink.h
index ba94c3c8c..7c5e35390 100644
--- a/external/spdlog/sinks/ansicolor_sink.h
+++ b/external/spdlog/sinks/ansicolor_sink.h
@@ -5,6 +5,10 @@
 
 #pragma once
 
+#ifndef SPDLOG_H
+#include "spdlog/spdlog.h"
+#endif
+
 #include "spdlog/details/console_globals.h"
 #include "spdlog/details/null_mutex.h"
 #include "spdlog/details/os.h"
diff --git a/external/spdlog/sinks/basic_file_sink.h b/external/spdlog/sinks/basic_file_sink.h
index 3832efd5b..bc5fb5c36 100644
--- a/external/spdlog/sinks/basic_file_sink.h
+++ b/external/spdlog/sinks/basic_file_sink.h
@@ -4,10 +4,14 @@
 //
 
 #pragma once
+
+#ifndef SPDLOG_H
+#include "spdlog/spdlog.h"
+#endif
+
 #include "spdlog/details/file_helper.h"
 #include "spdlog/details/null_mutex.h"
 #include "spdlog/sinks/base_sink.h"
-#include "spdlog/spdlog.h"
 
 #include <mutex>
 #include <string>
diff --git a/external/spdlog/sinks/daily_file_sink.h b/external/spdlog/sinks/daily_file_sink.h
index 70a290bee..7bf7c4481 100644
--- a/external/spdlog/sinks/daily_file_sink.h
+++ b/external/spdlog/sinks/daily_file_sink.h
@@ -4,11 +4,15 @@
 //
 
 #pragma once
+
+#ifndef SPDLOG_H
+#include "spdlog/spdlog.h"
+#endif
+
 #include "spdlog/details/file_helper.h"
 #include "spdlog/details/null_mutex.h"
 #include "spdlog/fmt/fmt.h"
 #include "spdlog/sinks/base_sink.h"
-#include "spdlog/spdlog.h"
 
 #include <chrono>
 #include <cstdio>
@@ -28,7 +32,7 @@ struct daily_filename_calculator
     static filename_t calc_filename(const filename_t &filename, const tm &now_tm)
     {
         filename_t basename, ext;
-        std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename);
+        std::tie(basename, ext) = details::file_helper::split_by_extension(filename);
         std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::memory_buffer, fmt::wmemory_buffer>::type w;
         fmt::format_to(
             w, SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}{}"), basename, now_tm.tm_year + 1900, now_tm.tm_mon + 1, now_tm.tm_mday, ext);
diff --git a/external/spdlog/sinks/dist_sink.h b/external/spdlog/sinks/dist_sink.h
index 0d8533eb8..44de3913f 100644
--- a/external/spdlog/sinks/dist_sink.h
+++ b/external/spdlog/sinks/dist_sink.h
@@ -5,6 +5,10 @@
 
 #pragma once
 
+#ifndef SPDLOG_H
+#include "spdlog/spdlog.h"
+#endif
+
 #include "base_sink.h"
 #include "spdlog/details/log_msg.h"
 #include "spdlog/details/null_mutex.h"
diff --git a/external/spdlog/sinks/msvc_sink.h b/external/spdlog/sinks/msvc_sink.h
index a02849607..f06c16c75 100644
--- a/external/spdlog/sinks/msvc_sink.h
+++ b/external/spdlog/sinks/msvc_sink.h
@@ -5,6 +5,10 @@
 
 #pragma once
 
+#ifndef SPDLOG_H
+#include "spdlog/spdlog.h"
+#endif
+
 #if defined(_WIN32)
 
 #include "spdlog/details/null_mutex.h"
diff --git a/external/spdlog/sinks/null_sink.h b/external/spdlog/sinks/null_sink.h
index 73b8c2b7a..54f322c45 100644
--- a/external/spdlog/sinks/null_sink.h
+++ b/external/spdlog/sinks/null_sink.h
@@ -5,6 +5,10 @@
 
 #pragma once
 
+#ifndef SPDLOG_H
+#include "spdlog/spdlog.h"
+#endif
+
 #include "spdlog/details/null_mutex.h"
 #include "spdlog/sinks/base_sink.h"
 
@@ -25,4 +29,21 @@ using null_sink_mt = null_sink<std::mutex>;
 using null_sink_st = null_sink<details::null_mutex>;
 
 } // namespace sinks
+
+template<typename Factory = default_factory>
+inline std::shared_ptr<logger> null_logger_mt(const std::string &logger_name)
+{
+    auto null_logger = Factory::template create<sinks::null_sink_mt>(logger_name);
+    null_logger->set_level(level::off);
+    return null_logger;
+}
+
+template<typename Factory = default_factory>
+inline std::shared_ptr<logger> null_logger_st(const std::string &logger_name)
+{
+    auto null_logger = Factory::template create<sinks::null_sink_st>(logger_name);
+    null_logger->set_level(level::off);
+    return null_logger;
+}
+
 } // namespace spdlog
diff --git a/external/spdlog/sinks/ostream_sink.h b/external/spdlog/sinks/ostream_sink.h
index 5649b0b84..22e377b6e 100644
--- a/external/spdlog/sinks/ostream_sink.h
+++ b/external/spdlog/sinks/ostream_sink.h
@@ -5,6 +5,10 @@
 
 #pragma once
 
+#ifndef SPDLOG_H
+#include "spdlog/spdlog.h"
+#endif
+
 #include "spdlog/details/null_mutex.h"
 #include "spdlog/sinks/base_sink.h"
 
diff --git a/external/spdlog/sinks/rotating_file_sink.h b/external/spdlog/sinks/rotating_file_sink.h
index 6fc1627c1..a579946fa 100644
--- a/external/spdlog/sinks/rotating_file_sink.h
+++ b/external/spdlog/sinks/rotating_file_sink.h
@@ -1,150 +1,155 @@
-//
-// Copyright(c) 2015 Gabi Melman.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-//
-
-#pragma once
-#include "spdlog/details/file_helper.h"
-#include "spdlog/details/null_mutex.h"
-#include "spdlog/fmt/fmt.h"
-#include "spdlog/sinks/base_sink.h"
-#include "spdlog/spdlog.h"
-
-#include <cerrno>
-#include <chrono>
-#include <ctime>
-#include <mutex>
-#include <string>
-#include <tuple>
-
-namespace spdlog {
-namespace sinks {
-
-//
-// Rotating file sink based on size
-//
-template<typename Mutex>
-class rotating_file_sink final : public base_sink<Mutex>
-{
-public:
-    rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files)
-        : base_filename_(std::move(base_filename))
-        , max_size_(max_size)
-        , max_files_(max_files)
-    {
-        file_helper_.open(calc_filename(base_filename_, 0));
-        current_size_ = file_helper_.size(); // expensive. called only once
-    }
-
-    // calc filename according to index and file extension if exists.
-    // e.g. calc_filename("logs/mylog.txt, 3) => "logs/mylog.3.txt".
-    static filename_t calc_filename(const filename_t &filename, std::size_t index)
-    {
-        typename std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::memory_buffer, fmt::wmemory_buffer>::type w;
-        if (index != 0u)
-        {
-            filename_t basename, ext;
-            std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename);
-            fmt::format_to(w, SPDLOG_FILENAME_T("{}.{}{}"), basename, index, ext);
-        }
-        else
-        {
-            fmt::format_to(w, SPDLOG_FILENAME_T("{}"), filename);
-        }
-        return fmt::to_string(w);
-    }
-
-protected:
-    void sink_it_(const details::log_msg &msg) override
-    {
-        fmt::memory_buffer formatted;
-        sink::formatter_->format(msg, formatted);
-        current_size_ += formatted.size();
-        if (current_size_ > max_size_)
-        {
-            rotate_();
-            current_size_ = formatted.size();
-        }
-        file_helper_.write(formatted);
-    }
-
-    void flush_() override
-    {
-        file_helper_.flush();
-    }
-
-private:
-    // Rotate files:
-    // log.txt -> log.1.txt
-    // log.1.txt -> log.2.txt
-    // log.2.txt -> log.3.txt
-    // log.3.txt -> delete
-    void rotate_()
-    {
-        using details::os::filename_to_str;
-        file_helper_.close();
-        for (auto i = max_files_; i > 0; --i)
-        {
-            filename_t src = calc_filename(base_filename_, i - 1);
-            if (!details::file_helper::file_exists(src))
-            {
-                continue;
-            }
-            filename_t target = calc_filename(base_filename_, i);
-
-            if (!rename_file(src, target))
-            {
-                // if failed try again after a small delay.
-                // this is a workaround to a windows issue, where very high rotation
-                // rates can cause the rename to fail with permission denied (because of antivirus?).
-                details::os::sleep_for_millis(100);
-                if (!rename_file(src, target))
-                {
-                    file_helper_.reopen(true); // truncate the log file anyway to prevent it to grow beyond its limit!
-                    throw spdlog_ex(
-                        "rotating_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno);
-                }
-            }
-        }
-        file_helper_.reopen(true);
-    }
-
-    // delete the target if exists, and rename the src file  to target
-    // return true on success, false otherwise.
-    bool rename_file(const filename_t &src_filename, const filename_t &target_filename)
-    {
-        // try to delete the target file in case it already exists.
-        (void)details::os::remove(target_filename);
-        return details::os::rename(src_filename, target_filename) == 0;
-    }
-
-    filename_t base_filename_;
-    std::size_t max_size_;
-    std::size_t max_files_;
-    std::size_t current_size_;
-    details::file_helper file_helper_;
-};
-
-using rotating_file_sink_mt = rotating_file_sink<std::mutex>;
-using rotating_file_sink_st = rotating_file_sink<details::null_mutex>;
-
-} // namespace sinks
-
-//
-// factory functions
-//
-
-template<typename Factory = default_factory>
-inline std::shared_ptr<logger> rotating_logger_mt(
-    const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files)
-{
-    return Factory::template create<sinks::rotating_file_sink_mt>(logger_name, filename, max_file_size, max_files);
-}
-
-template<typename Factory = default_factory>
-inline std::shared_ptr<logger> rotating_logger_st(
-    const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files)
-{
-    return Factory::template create<sinks::rotating_file_sink_st>(logger_name, filename, max_file_size, max_files);
-}
-} // namespace spdlog
+//
+// Copyright(c) 2015 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+
+#ifndef SPDLOG_H
+#include "spdlog/spdlog.h"
+#endif
+
+#include "spdlog/details/file_helper.h"
+#include "spdlog/details/null_mutex.h"
+#include "spdlog/fmt/fmt.h"
+#include "spdlog/sinks/base_sink.h"
+
+#include <cerrno>
+#include <chrono>
+#include <ctime>
+#include <mutex>
+#include <string>
+#include <tuple>
+
+namespace spdlog {
+namespace sinks {
+
+//
+// Rotating file sink based on size
+//
+template<typename Mutex>
+class rotating_file_sink final : public base_sink<Mutex>
+{
+public:
+    rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files)
+        : base_filename_(std::move(base_filename))
+        , max_size_(max_size)
+        , max_files_(max_files)
+    {
+        file_helper_.open(calc_filename(base_filename_, 0));
+        current_size_ = file_helper_.size(); // expensive. called only once
+    }
+
+    // calc filename according to index and file extension if exists.
+    // e.g. calc_filename("logs/mylog.txt, 3) => "logs/mylog.3.txt".
+    static filename_t calc_filename(const filename_t &filename, std::size_t index)
+    {
+        typename std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::memory_buffer, fmt::wmemory_buffer>::type w;
+        if (index != 0u)
+        {
+            filename_t basename, ext;
+            std::tie(basename, ext) = details::file_helper::split_by_extension(filename);
+            fmt::format_to(w, SPDLOG_FILENAME_T("{}.{}{}"), basename, index, ext);
+        }
+        else
+        {
+            fmt::format_to(w, SPDLOG_FILENAME_T("{}"), filename);
+        }
+        return fmt::to_string(w);
+    }
+
+protected:
+    void sink_it_(const details::log_msg &msg) override
+    {
+        fmt::memory_buffer formatted;
+        sink::formatter_->format(msg, formatted);
+        current_size_ += formatted.size();
+        if (current_size_ > max_size_)
+        {
+            rotate_();
+            current_size_ = formatted.size();
+        }
+        file_helper_.write(formatted);
+    }
+
+    void flush_() override
+    {
+        file_helper_.flush();
+    }
+
+private:
+    // Rotate files:
+    // log.txt -> log.1.txt
+    // log.1.txt -> log.2.txt
+    // log.2.txt -> log.3.txt
+    // log.3.txt -> delete
+    void rotate_()
+    {
+        using details::os::filename_to_str;
+        file_helper_.close();
+        for (auto i = max_files_; i > 0; --i)
+        {
+            filename_t src = calc_filename(base_filename_, i - 1);
+            if (!details::file_helper::file_exists(src))
+            {
+                continue;
+            }
+            filename_t target = calc_filename(base_filename_, i);
+
+            if (!rename_file(src, target))
+            {
+                // if failed try again after a small delay.
+                // this is a workaround to a windows issue, where very high rotation
+                // rates can cause the rename to fail with permission denied (because of antivirus?).
+                details::os::sleep_for_millis(100);
+                if (!rename_file(src, target))
+                {
+                    file_helper_.reopen(true); // truncate the log file anyway to prevent it to grow beyond its limit!
+                    current_size_ = 0;
+                    throw spdlog_ex(
+                        "rotating_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno);
+                }
+            }
+        }
+        file_helper_.reopen(true);
+    }
+
+    // delete the target if exists, and rename the src file  to target
+    // return true on success, false otherwise.
+    bool rename_file(const filename_t &src_filename, const filename_t &target_filename)
+    {
+        // try to delete the target file in case it already exists.
+        (void)details::os::remove(target_filename);
+        return details::os::rename(src_filename, target_filename) == 0;
+    }
+
+    filename_t base_filename_;
+    std::size_t max_size_;
+    std::size_t max_files_;
+    std::size_t current_size_;
+    details::file_helper file_helper_;
+};
+
+using rotating_file_sink_mt = rotating_file_sink<std::mutex>;
+using rotating_file_sink_st = rotating_file_sink<details::null_mutex>;
+
+} // namespace sinks
+
+//
+// factory functions
+//
+
+template<typename Factory = default_factory>
+inline std::shared_ptr<logger> rotating_logger_mt(
+    const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files)
+{
+    return Factory::template create<sinks::rotating_file_sink_mt>(logger_name, filename, max_file_size, max_files);
+}
+
+template<typename Factory = default_factory>
+inline std::shared_ptr<logger> rotating_logger_st(
+    const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files)
+{
+    return Factory::template create<sinks::rotating_file_sink_st>(logger_name, filename, max_file_size, max_files);
+}
+} // namespace spdlog
diff --git a/external/spdlog/sinks/sink.h b/external/spdlog/sinks/sink.h
index 9f84c3787..2f1adc10b 100644
--- a/external/spdlog/sinks/sink.h
+++ b/external/spdlog/sinks/sink.h
@@ -16,7 +16,7 @@ class sink
 public:
     sink()
         : level_(level::trace)
-        , formatter_(new pattern_formatter("%+"))
+        , formatter_(new pattern_formatter())
     {
     }
 
diff --git a/external/spdlog/sinks/stdout_color_sinks.h b/external/spdlog/sinks/stdout_color_sinks.h
index 761e32a4b..74bfceb49 100644
--- a/external/spdlog/sinks/stdout_color_sinks.h
+++ b/external/spdlog/sinks/stdout_color_sinks.h
@@ -5,7 +5,10 @@
 
 #pragma once
 
+#ifndef SPDLOG_H
 #include "spdlog/spdlog.h"
+#endif
+
 #ifdef _WIN32
 #include "spdlog/sinks/wincolor_sink.h"
 #else
diff --git a/external/spdlog/sinks/stdout_sinks.h b/external/spdlog/sinks/stdout_sinks.h
index 00c6ddd1a..bf8e97908 100644
--- a/external/spdlog/sinks/stdout_sinks.h
+++ b/external/spdlog/sinks/stdout_sinks.h
@@ -5,9 +5,12 @@
 
 #pragma once
 
+#ifndef SPDLOG_H
+#include "spdlog/spdlog.h"
+#endif
+
 #include "spdlog/details/console_globals.h"
 #include "spdlog/details/null_mutex.h"
-#include "spdlog/spdlog.h"
 
 #include <cstdio>
 #include <memory>
diff --git a/external/spdlog/sinks/syslog_sink.h b/external/spdlog/sinks/syslog_sink.h
index 151e7a119..c3bcd844a 100644
--- a/external/spdlog/sinks/syslog_sink.h
+++ b/external/spdlog/sinks/syslog_sink.h
@@ -5,8 +5,11 @@
 
 #pragma once
 
-#include "spdlog/sinks/base_sink.h"
+#ifndef SPDLOG_H
 #include "spdlog/spdlog.h"
+#endif
+
+#include "spdlog/sinks/base_sink.h"
 
 #include <array>
 #include <string>
@@ -50,7 +53,7 @@ public:
 protected:
     void sink_it_(const details::log_msg &msg) override
     {
-        ::syslog(syslog_prio_from_level(msg), "%s", fmt::to_string(msg.raw).c_str());
+        ::syslog(syslog_prio_from_level(msg), "%s", fmt::to_string(msg.payload).c_str());
     }
 
     void flush_() override {}
diff --git a/external/spdlog/sinks/wincolor_sink.h b/external/spdlog/sinks/wincolor_sink.h
index 77c50a839..1fdf8c566 100644
--- a/external/spdlog/sinks/wincolor_sink.h
+++ b/external/spdlog/sinks/wincolor_sink.h
@@ -5,6 +5,10 @@
 
 #pragma once
 
+#ifndef SPDLOG_H
+#include "spdlog/spdlog.h"
+#endif
+
 #include "spdlog/common.h"
 #include "spdlog/details/console_globals.h"
 #include "spdlog/details/null_mutex.h"
diff --git a/external/spdlog/spdlog.h b/external/spdlog/spdlog.h
index 40640ab97..20ff24be5 100644
--- a/external/spdlog/spdlog.h
+++ b/external/spdlog/spdlog.h
@@ -5,6 +5,8 @@
 // spdlog main header file.
 // see example.cpp for usage example
 
+#ifndef SPDLOG_H
+#define SPDLOG_H
 #pragma once
 
 #include "spdlog/common.h"
@@ -23,12 +25,11 @@ namespace spdlog {
 struct synchronous_factory
 {
     template<typename Sink, typename... SinkArgs>
-
     static std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs &&... args)
     {
         auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
         auto new_logger = std::make_shared<logger>(std::move(logger_name), std::move(sink));
-        details::registry::instance().register_and_init(new_logger);
+        details::registry::instance().initialize_logger(new_logger);
         return new_logger;
     }
 };
@@ -124,38 +125,242 @@ inline void shutdown()
     details::registry::instance().shutdown();
 }
 
-///////////////////////////////////////////////////////////////////////////////
+// Automatic registration of loggers when using spdlog::create() or spdlog::create_async
+inline void set_automatic_registration(bool automatic_registation)
+{
+    details::registry::instance().set_automatic_registration(automatic_registation);
+}
+
+// API for using default logger (stdout_color_mt),
+// e.g: spdlog::info("Message {}", 1);
 //
-// Trace & Debug can be switched on/off at compile time for zero cost debug
-// statements.
-// Uncomment SPDLOG_DEBUG_ON/SPDLOG_TRACE_ON in tweakme.h to enable.
-// SPDLOG_TRACE(..) will also print current file and line.
+// The default logger object can be accessed using the spdlog::default_logger():
+// For example, to add another sink to it:
+// spdlog::default_logger()->sinks()->push_back(some_sink);
 //
-// Example:
-// spdlog::set_level(spdlog::level::trace);
-// SPDLOG_TRACE(my_logger, "some trace message");
-// SPDLOG_TRACE(my_logger, "another trace message {} {}", 1, 2);
-// SPDLOG_DEBUG(my_logger, "some debug message {} {}", 3, 4);
-///////////////////////////////////////////////////////////////////////////////
-
-#ifdef SPDLOG_TRACE_ON
-#define SPDLOG_STR_H(x) #x
-#define SPDLOG_STR_HELPER(x) SPDLOG_STR_H(x)
-#ifdef _MSC_VER
-#define SPDLOG_TRACE(logger, ...) logger->trace("[ " __FILE__ "(" SPDLOG_STR_HELPER(__LINE__) ") ] " __VA_ARGS__)
+// The default logger can replaced using spdlog::set_default_logger(new_logger).
+// For example, to replace it with a file logger.
+//
+// IMPORTANT:
+// The default API is thread safe (for _mt loggers), but:
+// set_default_logger() *should not* be used concurrently with the default API.
+// e.g do not call set_default_logger() from one thread while calling spdlog::info() from another.
+
+inline std::shared_ptr<spdlog::logger> default_logger()
+{
+    return details::registry::instance().default_logger();
+}
+
+inline spdlog::logger *default_logger_raw()
+{
+    return details::registry::instance().get_default_raw();
+}
+
+inline void set_default_logger(std::shared_ptr<spdlog::logger> default_logger)
+{
+    details::registry::instance().set_default_logger(std::move(default_logger));
+}
+
+template<typename... Args>
+inline void log(source_loc source, level::level_enum lvl, const char *fmt, const Args &... args)
+{
+    default_logger_raw()->log(source, lvl, fmt, args...);
+}
+
+template<typename... Args>
+inline void log(level::level_enum lvl, const char *fmt, const Args &... args)
+{
+    default_logger_raw()->log(source_loc{}, lvl, fmt, args...);
+}
+
+template<typename... Args>
+inline void trace(const char *fmt, const Args &... args)
+{
+    default_logger_raw()->trace(fmt, args...);
+}
+
+template<typename... Args>
+inline void debug(const char *fmt, const Args &... args)
+{
+    default_logger_raw()->debug(fmt, args...);
+}
+
+template<typename... Args>
+inline void info(const char *fmt, const Args &... args)
+{
+    default_logger_raw()->info(fmt, args...);
+}
+
+template<typename... Args>
+inline void warn(const char *fmt, const Args &... args)
+{
+    default_logger_raw()->warn(fmt, args...);
+}
+
+template<typename... Args>
+inline void error(const char *fmt, const Args &... args)
+{
+    default_logger_raw()->error(fmt, args...);
+}
+
+template<typename... Args>
+inline void critical(const char *fmt, const Args &... args)
+{
+    default_logger_raw()->critical(fmt, args...);
+}
+
+template<typename T>
+inline void log(level::level_enum lvl, const T &msg)
+{
+    default_logger_raw()->log(lvl, msg);
+}
+
+template<typename T>
+inline void trace(const T &msg)
+{
+    default_logger_raw()->trace(msg);
+}
+
+template<typename T>
+inline void debug(const T &msg)
+{
+    default_logger_raw()->debug(msg);
+}
+
+template<typename T>
+inline void info(const T &msg)
+{
+    default_logger_raw()->info(msg);
+}
+
+template<typename T>
+inline void warn(const T &msg)
+{
+    default_logger_raw()->warn(msg);
+}
+
+template<typename T>
+inline void error(const T &msg)
+{
+    default_logger_raw()->error(msg);
+}
+
+template<typename T>
+inline void critical(const T &msg)
+{
+    default_logger_raw()->critical(msg);
+}
+
+#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
+template<typename... Args>
+inline void log(level::level_enum lvl, const wchar_t *fmt, const Args &... args)
+{
+    default_logger_raw()->log(lvl, fmt, args...);
+}
+
+template<typename... Args>
+inline void trace(const wchar_t *fmt, const Args &... args)
+{
+    default_logger_raw()->trace(fmt, args...);
+}
+
+template<typename... Args>
+inline void debug(const wchar_t *fmt, const Args &... args)
+{
+    default_logger_raw()->debug(fmt, args...);
+}
+
+template<typename... Args>
+inline void info(const wchar_t *fmt, const Args &... args)
+{
+    default_logger_raw()->info(fmt, args...);
+}
+
+template<typename... Args>
+inline void warn(const wchar_t *fmt, const Args &... args)
+{
+    default_logger_raw()->warn(fmt, args...);
+}
+
+template<typename... Args>
+inline void error(const wchar_t *fmt, const Args &... args)
+{
+    default_logger_raw()->error(fmt, args...);
+}
+
+template<typename... Args>
+inline void critical(const wchar_t *fmt, const Args &... args)
+{
+    default_logger_raw()->critical(fmt, args...);
+}
+
+#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
+
+} // namespace spdlog
+
+//
+// enable/disable log calls at compile time according to global level.
+//
+// define SPDLOG_ACTIVE_LEVEL to one of those (before including spdlog.h):
+// SPDLOG_LEVEL_TRACE,
+// SPDLOG_LEVEL_DEBUG,
+// SPDLOG_LEVEL_INFO,
+// SPDLOG_LEVEL_WARN,
+// SPDLOG_LEVEL_ERROR,
+// SPDLOG_LEVEL_CRITICAL,
+// SPDLOG_LEVEL_OFF
+//
+
+#define SPDLOG_LOGGER_CALL(logger, level, ...)                                                                                             \
+    if (logger->should_log(level))                                                                                                         \
+    logger->log(spdlog::source_loc{SPDLOG_FILE_BASENAME(__FILE__), __LINE__, SPDLOG_FUNCTION}, level, __VA_ARGS__)
+
+#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_TRACE
+#define SPDLOG_LOGGER_TRACE(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::trace, __VA_ARGS__)
+#define SPDLOG_TRACE(...) SPDLOG_LOGGER_TRACE(spdlog::default_logger_raw(), __VA_ARGS__)
 #else
-#define SPDLOG_TRACE(logger, ...)                                                                                                          \
-    logger->trace("[ " __FILE__ ":" SPDLOG_STR_HELPER(__LINE__) " ]"                                                                       \
-                                                                " " __VA_ARGS__)
+#define SPDLOG_LOGGER_TRACE(logger, ...) (void)0
+#define SPDLOG_TRACE(...) (void)0
 #endif
+
+#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_DEBUG
+#define SPDLOG_LOGGER_DEBUG(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::debug, __VA_ARGS__)
+#define SPDLOG_DEBUG(...) SPDLOG_LOGGER_DEBUG(spdlog::default_logger_raw(), __VA_ARGS__)
 #else
-#define SPDLOG_TRACE(logger, ...) (void)0
+#define SPDLOG_LOGGER_DEBUG(logger, ...) (void)0
+#define SPDLOG_DEBUG(...) (void)0
 #endif
 
-#ifdef SPDLOG_DEBUG_ON
-#define SPDLOG_DEBUG(logger, ...) logger->debug(__VA_ARGS__)
+#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_INFO
+#define SPDLOG_LOGGER_INFO(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::info, __VA_ARGS__)
+#define SPDLOG_INFO(...) SPDLOG_LOGGER_INFO(spdlog::default_logger_raw(), __VA_ARGS__)
 #else
-#define SPDLOG_DEBUG(logger, ...) (void)0
+#define SPDLOG_LOGGER_INFO(logger, ...) (void)0
+#define SPDLOG_INFO(...) (void)0
 #endif
 
-} // namespace spdlog
+#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_WARN
+#define SPDLOG_LOGGER_WARN(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::warn, __VA_ARGS__)
+#define SPDLOG_WARN(...) SPDLOG_LOGGER_WARN(spdlog::default_logger_raw(), __VA_ARGS__)
+#else
+#define SPDLOG_LOGGER_WARN(logger, ...) (void)0
+#define SPDLOG_WARN(...) (void)0
+#endif
+
+#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_ERROR
+#define SPDLOG_LOGGER_ERROR(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::err, __VA_ARGS__)
+#define SPDLOG_ERROR(...) SPDLOG_LOGGER_ERROR(spdlog::default_logger_raw(), __VA_ARGS__)
+#else
+#define SPDLOG_LOGGER_ERROR(logger, ...) (void)0
+#define SPDLOG_ERROR(...) (void)0
+#endif
+
+#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_CRITICAL
+#define SPDLOG_LOGGER_CRITICAL(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::critical, __VA_ARGS__)
+#define SPDLOG_CRITICAL(...) SPDLOG_LOGGER_CRITICAL(spdlog::default_logger_raw(), __VA_ARGS__)
+#else
+#define SPDLOG_LOGGER_CRITICAL(logger, ...) (void)0
+#define SPDLOG_CRITICAL(...) (void)0
+#endif
+
+#endif // SPDLOG_H
diff --git a/external/spdlog/tweakme.h b/external/spdlog/tweakme.h
index dfadd5672..b3b71e4f3 100644
--- a/external/spdlog/tweakme.h
+++ b/external/spdlog/tweakme.h
@@ -45,13 +45,12 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 ///////////////////////////////////////////////////////////////////////////////
-// Uncomment to prevent spdlog from caching thread ids in thread local storage.
-// By default spdlog saves thread ids in tls to gain a few micros for each call.
+// Uncomment to prevent spdlog from using thread local storage.
 //
 // WARNING: if your program forks, UNCOMMENT this flag to prevent undefined
 // thread ids in the children logs.
 //
-// #define SPDLOG_DISABLE_TID_CACHING
+// #define SPDLOG_NO_TLS
 ///////////////////////////////////////////////////////////////////////////////
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -121,3 +120,26 @@
 // #define SPDLOG_LEVEL_NAMES { "MY TRACE", "MY DEBUG", "MY INFO", "MY WARNING",
 // "MY ERROR", "MY CRITICAL", "OFF" }
 ///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// Uncomment to disable default logger creation.
+// This might save some (very) small initialization time if no default logger is needed.
+//
+// #define SPDLOG_DISABLE_DEFAULT_LOGGER
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// Uncomment and set to compile time level with zero cost (default is INFO).
+// Macros like SPDLOG_DEBUG(..), SPDLOG_INFO(..)  will expand to empty statements if not enabled
+//
+// #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// Uncomment (and change if desired) macro to use for function names.
+// This is compiler dependent.
+// __PRETTY_FUNCTION__ might be nicer in clang/gcc, and __FUNCTION__ in msvc.
+// Defaults to __FUNCTION__ (should work on all compilers) if not defined.
+//
+// #define SPDLOG_FUNCTION __PRETTY_FUNCTION__
+///////////////////////////////////////////////////////////////////////////////
\ No newline at end of file
diff --git a/external/spdlog/version.h b/external/spdlog/version.h
index b12cb7b39..87a68bd18 100644
--- a/external/spdlog/version.h
+++ b/external/spdlog/version.h
@@ -6,7 +6,7 @@
 #pragma once
 
 #define SPDLOG_VER_MAJOR 1
-#define SPDLOG_VER_MINOR 2
-#define SPDLOG_VER_PATCH 0
+#define SPDLOG_VER_MINOR 3
+#define SPDLOG_VER_PATCH 1
 
 #define SPDLOG_VERSION (SPDLOG_VER_MAJOR * 10000 + SPDLOG_VER_MINOR * 100 + SPDLOG_VER_PATCH)
-- 
GitLab